Jetty12中多WebAppContext的RequestLog分日志配置方案咨询
Jetty 12 按WebAppContext分离请求日志的解决方案
Jetty 12 移除了RequestLogHandler,统一通过Server.setRequestLog配置请求日志,但默认实现会将所有WebAppContext的请求记录到同一个日志文件。你之前尝试在WebAppContext配置中创建新Server实例的方式是错误的——WebAppContext本身隶属于已启动的Server实例,这种配置不仅无法生效,还会创建冗余的Server对象。
以下是几种可行的按WebAppContext分离请求日志的方案:
方案1:自定义RequestLog实现按上下文分流日志
实现org.eclipse.jetty.server.RequestLog接口,在日志记录逻辑中根据请求对应的WebAppContext选择不同的日志写入器。
示例代码(Java)
public class ContextAwareRequestLog implements RequestLog { private final Map<String, RequestLog> contextLoggers = new ConcurrentHashMap<>(); public void addContextLogger(String contextPath, RequestLog logger) { contextLoggers.put(contextPath, logger); } @Override public void log(Request request, Response response) { Handler handler = request.getHandler(); if (handler instanceof WebAppContext webAppContext) { String contextPath = webAppContext.getContextPath(); RequestLog logger = contextLoggers.get(contextPath); if (logger != null) { logger.log(request, response); return; } } // 处理未匹配到上下文的请求(可选) RequestLog defaultLogger = contextLoggers.get("/"); if (defaultLogger != null) { defaultLogger.log(request, response); } } }
对应的XML配置(在Server层面配置)
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <!-- 为每个WebAppContext创建独立的RequestLog --> <New id="App1RequestLogWriter" class="org.eclipse.jetty.server.RequestLogWriter"> <Set name="filename">/tmp/app1-yyyy_mm_dd.request.log</Set> <Set name="filenameDateFormat">yyyy_MM_dd</Set> </New> <New id="App1RequestLog" class="org.eclipse.jetty.server.CustomRequestLog"> <Arg><Ref refid="App1RequestLogWriter"/></Arg> <Arg>%{client}a - %u %t "%r" %s %O "%{Referer}i" "%{User-Agent}i"</Arg> </New> <New id="App2RequestLogWriter" class="org.eclipse.jetty.server.RequestLogWriter"> <Set name="filename">/tmp/app2-yyyy_mm_dd.request.log</Set> <Set name="filenameDateFormat">yyyy_MM_dd</Set> </New> <New id="App2RequestLog" class="org.eclipse.jetty.server.CustomRequestLog"> <Arg><Ref refid="App2RequestLogWriter"/></Arg> <Arg>%{client}a - %u %t "%r" %s %O "%{Referer}i" "%{User-Agent}i"</Arg> </New> <!-- 创建上下文感知的RequestLog --> <New id="ContextAwareLog" class="com.example.ContextAwareRequestLog"> <Call name="addContextLogger"> <Arg>/app1</Arg> <Arg><Ref refid="App1RequestLog"/></Arg> </Call> <Call name="addContextLogger"> <Arg>/app2</Arg> <Arg><Ref refid="App2RequestLog"/></Arg> </Call> <!-- 默认日志(可选) --> <Call name="addContextLogger"> <Arg>/</Arg> <Arg><Ref refid="DefaultRequestLog"/></Arg> </Call> </New> <!-- 给Server设置自定义的RequestLog --> <Set name="requestLog"><Ref refid="ContextAwareLog"/></Set> </Configure>
方案2:给每个WebAppContext添加HandlerWrapper
通过HandlerWrapper为每个WebAppContext包裹一层日志记录逻辑,相当于在WebAppContext的处理链中插入日志记录步骤。
示例XML配置(针对单个WebAppContext)
<Configure id="App1WebApp" class="org.eclipse.jetty.ee8.webapp.WebAppContext"> <Set name="contextPath">/app1</Set> <Set name="war">./webapps/app1.war</Set> <!-- 创建当前WebAppContext专属的RequestLog --> <New id="App1RequestLogWriter" class="org.eclipse.jetty.server.RequestLogWriter"> <Set name="filename">/tmp/app1-yyyy_mm_dd.request.log</Set> <Set name="filenameDateFormat">yyyy_MM_dd</Set> </New> <New id="App1RequestLog" class="org.eclipse.jetty.server.CustomRequestLog"> <Arg><Ref refid="App1RequestLogWriter"/></Arg> <Arg>%{client}a - %u %t "%r" %s %O "%{Referer}i" "%{User-Agent}i"</Arg> </New> <!-- 创建HandlerWrapper包裹WebAppContext的处理逻辑 --> <New id="App1LogWrapper" class="org.eclipse.jetty.server.handler.HandlerWrapper"> <Set name="handler"><Ref refid="App1WebApp"/></Set> <Call name="setHandler"> <Arg> <New class="org.eclipse.jetty.server.handler.RequestLogHandler"> <Set name="requestLog"><Ref refid="App1RequestLog"/></Set> </New> </Arg> </Call> </New> <!-- 将Wrapper设置为WebAppContext的处理链 --> <Set name="handler"><Ref refid="App1LogWrapper"/></Set> </Configure>
注意:此方案需要确保每个WebAppContext的处理链中包含独立的RequestLogHandler,虽然Jetty12移除了直接通过insertHandler添加的方式,但可以通过HandlerWrapper手动构建处理链来实现类似效果。
方案3:利用日志框架的MDC(映射诊断上下文)分流
如果使用SLF4J等支持MDC的日志框架,可以在请求进入WebAppContext时将上下文路径放入MDC,然后通过日志框架的配置(如Logback、Log4j2)按MDC值拆分日志文件。
步骤:
- 实现一个
Handler,在请求处理前将上下文路径放入MDC:
public class ContextMdcHandler extends Handler.Wrapper { @Override public void handle(String target, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException { Handler handler = request.getHandler(); if (handler instanceof WebAppContext webAppContext) { MDC.put("contextPath", webAppContext.getContextPath()); } try { super.handle(target, request, servletRequest, servletResponse); } finally { MDC.remove("contextPath"); } } }
- 在Server层面添加这个Handler到全局处理链:
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Set name="handler"> <New class="org.eclipse.jetty.server.handler.HandlerCollection"> <Item> <New class="com.example.ContextMdcHandler"> <Set name="handler"> <!-- 这里是原有的Handler集合,包含所有WebAppContext --> <Ref refid="Handlers"/> </Set> </New> </Item> </New> </Set> <!-- 使用SLF4JRequestLog将日志输出到日志框架 --> <Set name="requestLog"> <New class="org.eclipse.jetty.server.Slf4jRequestLog"> <Set name="loggerName">jetty.requestlog</Set> <Set name="format">%{client}a - %u %t "%r" %s %O "%{Referer}i" "%{User-Agent}i"</Set> </New> </Set> </Configure>
- 配置日志框架(以Logback为例)按
contextPath拆分日志:
<appender name="CONTEXT_APPENDER" class="ch.qos.logback.classic.sift.SiftingAppender"> <discriminator> <key>contextPath</key> <defaultValue>default</defaultValue> </discriminator> <sift> <appender name="FILE-${contextPath}" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>/tmp/jetty-${contextPath}.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>/tmp/jetty-${contextPath}-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <maxFileSize>100MB</maxFileSize> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> </sift> </appender> <logger name="jetty.requestlog" level="INFO" additivity="false"> <appender-ref ref="CONTEXT_APPENDER"/> </logger>
内容的提问来源于stack exchange,提问作者Hemanth Karniyana
相关产品推荐
相关产品推荐

