1. 为什么你需要了解RoutingAppender?
如果你正在开发一个稍微复杂点的应用,比如一个同时服务Web用户和内部管理员的系统,或者一个部署在不同操作系统上的服务,你很可能遇到过这样的烦恼:所有的日志都混在一个文件里。想找某个特定用户的登录记录?得在一堆无关信息里大海捞针。想单独分析Windows服务器上的错误?得先想办法把日志过滤出来。更头疼的是,有些审计日志需要发送到专门的日志分析系统(比如Flume或Kafka),而普通的调试信息只需要输出到本地文件。手动写代码去判断、去分流?那会让你的业务代码里掺杂大量日志处理逻辑,既不优雅,也难维护。
这时候,Log4j2的RoutingAppender就是你的“救星”。简单来说,它就像一个智能的日志分发中心。每一条日志事件(LogEvent)产生后,不会直接写死到某个目的地,而是先交给RoutingAppender。RoutingAppender会根据你设定的规则,比如“这条日志是不是打了AUDIT标记?”、“当前用户ID是什么?”、“服务器是Windows还是Linux?”,动态地决定把这条日志路由到哪一个具体的“处理终端”(也就是Appender)。这个“终端”可以是之前就配置好的Console、File,也可以是根据规则动态创建的RollingFile。
我最初接触它是在一个多租户的SaaS项目里。每个租户的日志需要独立存储,方便问题追踪和数据隔离。如果为每个租户预先配置上百个Appender,配置文件会爆炸,而且租户动态增减时也无法应对。用上RoutingAppender后,我们只需要一个配置,根据日志事件中携带的租户ID(放在MDC里),动态创建以租户ID命名的日志文件,一切就自动搞定了,清爽无比。
所以,无论你是想实现环境感知(不同环境日志不同)、用户/租户隔离、日志分级分类存储,还是想将关键日志同步到外部系统,RoutingAppender都能让你以声明式的方式(主要在XML或JSON配置里完成)轻松实现,无需改动业务代码。接下来,我们就一起把这个强大的工具彻底搞明白、用起来。
2. 核心机制拆解:Routes、Script与动态创建
要玩转RoutingAppender,必须吃透它的三个核心概念:Routes(路由)、Script(脚本) 和 动态Appender创建。官方文档讲得比较术语化,我用大白话和实际例子给你捋清楚。
2.1 Routes:定义你的路由规则表
你可以把<Routes>块想象成一个路由表或者一个switch-case语句。每一条<Route>就是一个分支,它有一个key作为标识。RoutingAppender会为每一条到来的日志事件计算出一个“路由键”(Route Key),然后去这个表里找匹配的key,找到了就用这个Route对应的Appender处理日志。
计算“路由键”有两种主要方式,它们互斥,只能选一种:
pattern属性:这是最简单直接的方式。你可以写一个Log4j2的查找表达式(Lookup),比如${sd:type}(表示结构化数据中的type字段)、${mdc:UserId}(表示MDC中UserId的值)。这个表达式会在日志事件发生时被求值,求值结果就是路由键。<Script>元素:这是功能最强大的方式。你可以写一段脚本(支持JavaScript、Groovy等),脚本能访问到完整的logEvent对象,这意味着你可以用任何复杂的逻辑来决定路由键,比如检查标记(Marker)、日志级别、线程名、MDC里的复杂对象等等。
2.2 Script:用代码决定日志去向
Script的灵活性是RoutingAppender的灵魂。它有两种使用位置,作用不同:
- 在
<Routes>内部的Script:这个脚本针对每一个日志事件执行。它接收logEvent变量,必须返回一个字符串作为路由键。上面多租户的例子,就是在这里用JavaScript检查logEvent.getContextMap().get("TenantId")。 - 在
<Routing>根下的Script:这个脚本只在RoutingAppender启动时执行一次,通常用来设置一个默认的、静态的路由。比如,根据系统属性决定默认将日志路由到“Windows”组还是“Linux”组的Appender。
原始文章里给了两个非常好的Script例子。第一个是启动时根据操作系统选择默认路由:
<Script name="RoutingInit" language="JavaScript"><![CDATA[
java.lang.System.getProperty("os.name").search("Windows") > -1 ? "ServiceWindows" : "ServiceOther";
]]></Script>
这个脚本只在初始化时跑一次,如果系统是Windows,后续所有日志默认走key="ServiceWindows"的路由,否则走key="ServiceOther"的路由。注意,这里说的是“默认”,如果Routes里配置了针对每个事件的Script,那个Script的优先级更高。
第二个例子是针对每个事件的Script,它展示了更精细的控制:
if (logEv
514



被折叠的 条评论
为什么被折叠?



