You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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值拆分日志文件。

步骤:

  1. 实现一个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");
        }
    }
}
  1. 在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>
  1. 配置日志框架(以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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:57:04