如何将Log4j2外部库日志单独输出至独立文件?
Got it, let’s break down why your current setup is mixing project and external library logs in the default route, and how to fix it cleanly.
What’s the Problem Right Now?
Your Routing Appender sends every log that doesn’t have a logFileName set in ThreadContext to the default route—this includes both unmarked logs from your own project and all logs from third-party libraries. We need to split these two streams apart so only external lib logs hit that default file.
Solution 1: Split Loggers and Appenders (Clean & Straightforward)
This approach uses separate Loggers for your project code and external libraries, paired with dedicated appenders. Here’s the full modified config:
<Configuration status="WARN"> <Appenders> <!-- Your existing Routing Appender, now for project logs only --> <Routing name="ProjectRoutingAppender"> <Routes pattern="${ctx:logFileName}"> <!-- Project logs with logFileName set go here --> <Route> <RollingFile name="Rolling-${ctx:logFileName}" fileName="logs/${ctx:logFileName}" filePattern="logs/${ctx:logFileName}.%i.log.gz"> <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{10}.%method:%line-%msg%n"/> <SizeBasedTriggeringPolicy size="100 MB" /> </RollingFile> </Route> <!-- Project logs WITHOUT logFileName go to a dedicated project default file --> <Route key="$${ctx:logFileName}"> <RollingFile name="Rolling-ProjectDefault" fileName="logs/PROJECT-UNMARKED.log" filePattern="./logs/${date:yyyy-MM}/project-default-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{10}.%method:%line-%msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="6" modulate="true" /> <SizeBasedTriggeringPolicy size="100 MB" /> </Policies> </RollingFile> </Route> </Routes> </Routing> <!-- New appender exclusively for external library logs --> <RollingFile name="ExternalLibsAppender" fileName="logs/WITHOUT-THREAD-CONTEXT.log" filePattern="./logs/${date:yyyy-MM}/external-libs-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{10}.%method:%line-%msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="6" modulate="true" /> <SizeBasedTriggeringPolicy size="100 MB" /> </Policies> </RollingFile> </Appenders> <Loggers> <!-- Logger for YOUR PROJECT CODE: replace com.yourproject with your actual root package --> <Logger name="com.yourproject" level="INFO" additivity="false"> <AppenderRef ref="ProjectRoutingAppender"/> </Logger> <!-- Root Logger: catches ONLY external library logs (since project logs are handled above) --> <Root level="WARN"> <AppenderRef ref="ExternalLibsAppender"/> </Root> </Loggers> </Configuration>
Key Notes for This Setup:
- Project Logger: Replace
com.yourprojectwith your actual project’s root package (e.g.,com.yourapp.backend). Theadditivity="false"flag ensures project logs don’t leak into the Root Logger (so they won’t end up in the external libs file). - Project Unmarked Logs: Any logs from your code that don’t set
logFileNamego toPROJECT-UNMARKED.loginstead of mixing with external libs. - External Libs: The Root Logger now only processes logs from outside your project package, sending them to your original default log file.
Solution 2: Script-Based Routing (More Flexible)
If you prefer to keep everything in one Routing Appender without splitting loggers, you can use conditional routing with a script to check if the log comes from your project package:
<Configuration status="WARN"> <Appenders> <Routing name="RoutingAppender"> <Routes> <!-- Route 1: Project logs with logFileName set --> <Route condition="ctx:logFileName != null"> <RollingFile name="Rolling-${ctx:logFileName}" fileName="logs/${ctx:logFileName}" filePattern="logs/${ctx:logFileName}.%i.log.gz"> <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{10}.%method:%line-%msg%n"/> <SizeBasedTriggeringPolicy size="100 MB" /> </RollingFile> </Route> <!-- Route 2: Project logs WITHOUT logFileName --> <Route condition="ctx:logFileName == null && logger:name.startsWith('com.yourproject')"> <RollingFile name="Rolling-ProjectDefault" fileName="logs/PROJECT-UNMARKED.log" filePattern="./logs/${date:yyyy-MM}/project-default-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{10}.%method:%line-%msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="6" modulate="true" /> <SizeBasedTriggeringPolicy size="100 MB" /> </Policies> </RollingFile> </Route> <!-- Route 3: External library logs (all remaining logs) --> <Route> <RollingFile name="Rolling-ExternalLibs" fileName="logs/WITHOUT-THREAD-CONTEXT.log" filePattern="./logs/${date:yyyy-MM}/external-libs-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{10}.%method:%line-%msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="6" modulate="true" /> <SizeBasedTriggeringPolicy size="100 MB" /> </Policies> </RollingFile> </Route> </Routes> </Routing> </Appenders> <Loggers> <!-- Root Logger handles all logs, routing takes care of the rest --> <Root level="INFO"> <AppenderRef ref="RoutingAppender"/> </Root> </Loggers> </Configuration>
This setup uses Log4j2’s conditional routing to split logs into three buckets automatically, no extra loggers needed. Just make sure to update com.yourproject to your actual project package.
内容的提问来源于stack exchange,提问作者levangode

