Jetty 10/11使用Custom Logging调用request方法触发NPE问题求解
问题原因
- 空指针的直接原因:
setCustomAttributesToLog方法是在JettyCustomLogger的构造函数中执行的,构造阶段还没有任何请求进入,request静态变量还没有被赋值,调用request.getMethod()必然抛出空指针。你对request变量的赋值逻辑写在log方法中,这个方法只有实际请求到达时才会被触发,执行时机远晚于构造函数。 - 额外隐藏问题:把
Request对象设为静态变量存在严重线程安全风险,Jetty 是多线程处理请求的,多个请求会同时修改这个静态变量,最终日志记录的请求属性会出现错乱,完全不符合业务预期。 - 设计逻辑错误:你要拼接的请求相关属性每个请求都不一样,不能在日志组件初始化阶段就把日志格式拼死,必须在每次请求处理完成、触发日志记录时动态拼接当前请求的属性。
解决方案
调整实现逻辑,把请求属性的获取、自定义字段的拼接逻辑放到 log 方法中执行,同时去掉静态 request 变量,避免线程安全问题。示例实现代码如下:
import org.eclipse.jetty.server.CustomRequestLog; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import java.io.Writer; import java.io.IOException; public class JettyCustomLogger extends CustomRequestLog { // 存储基础日志格式字符串,不要在构造阶段就拼接请求相关内容 private final String baseLogStr; @Override public void log(Request request, Response response) { // 每次请求都动态获取当前请求的属性拼接日志 String finalLogContent = setCustomAttributesToLog(request, baseLogStr); // 可选择直接输出自定义拼接好的日志,不走父类原有格式解析逻辑,更灵活 try (Writer writer = getWriter()) { writer.write(finalLogContent + System.lineSeparator()); } catch (IOException e) { // 自行处理日志写入异常 e.printStackTrace(); } // 如果需要保留父类原有格式解析能力,也可以调用super.log()实现 // super.log(request, response); } public JettyCustomLogger(Writer writer, String logStr) { super(writer, logStr); this.baseLogStr = logStr; } // 方法增加Request入参,每次调用传入当前请求的对象,不需要用静态变量存储 private String setCustomAttributesToLog(Request request, String logStr) { String method = request.getMethod(); StringBuilder logBuffer = new StringBuilder(logStr); logBuffer.append(method); // 你自己的其他int、string类型自定义属性拼接逻辑都可以在这里实现 logBuffer.append(" Ashish"); logBuffer.append(" "); logBuffer.append("Goyanka"); logBuffer.append(" "); logBuffer.append("absgdh"); logBuffer.append(" "); return logBuffer.toString(); } }
如果需要保留父类 CustomRequestLog 原有格式解析能力,还可以通过自定义 RequestLog.Writer 或者扩展 Jetty 的日志格式字段实现,上述方案是改动最小、最容易适配你现有自定义属性拼接需求的实现。
内容的提问来源于stack exchange,提问作者Ashish Goyanka
相关产品推荐
相关产品推荐

