如何通过事件监听器拦截Jetty服务器的请求与响应并记录日志?
解决Jetty全请求/响应日志覆盖(含解析失败请求)的方案
要捕获所有传入请求(包括那些因解析错误无法到达业务Handler的请求)和响应,Jetty提供了几种可靠的方案,完全不需要手动在所有Handler里加日志:
1. 用Jetty内置的RequestLogHandler(最省心)
这是官方推荐的全请求日志方案,能自动记录所有请求——哪怕是像你遇到的URI空段导致的400错误请求,因为它在连接初始化阶段就开始追踪请求生命周期了。
配置示例(Java代码)
Server server = new Server(); // 创建RequestLogHandler RequestLogHandler requestLogHandler = new RequestLogHandler(); NCSARequestLog requestLog = new NCSARequestLog("./logs/yyyy_mm_dd.request.log"); requestLog.setRetainDays(90); requestLog.setAppend(true); requestLog.setExtended(true); requestLog.setLogTimeZone("GMT"); requestLogHandler.setRequestLog(requestLog); // 将RequestLogHandler作为最外层Handler,包裹你的业务Handler链 HandlerCollection handlers = new HandlerCollection(); handlers.setHandlers(new Handler[]{yourBusinessHandler, requestLogHandler}); server.setHandler(handlers);
这个日志会自动记录请求方法、URI、状态码、响应大小、耗时等核心信息,包括解析失败返回的400状态。
2. 自定义底层监听器捕获解析阶段的请求
如果需要更灵活的自定义逻辑(比如记录原始请求头、错误栈),可以通过Jetty的底层连接监听器,捕获HttpParser解析失败的事件:
代码示例:监听HttpParser解析失败
server.addConnector(new ServerConnector(server) { @Override protected Connection newConnection(EndPoint endPoint, HttpConfiguration config) { HttpConnection connection = (HttpConnection) super.newConnection(endPoint, config); // 给HttpParser添加解析失败监听器 connection.getHttpChannel().getHttpParser().addListener(new HttpParser.Listener() { @Override public void onBadMessage(BadMessageException failure) { // 记录解析失败的请求信息,比如从EndPoint获取远程地址 String remoteAddr = endPoint.getRemoteAddress().toString(); // 这里可以尝试读取原始请求数据(注意:解析失败时可能只能拿到部分数据) System.err.printf("解析失败请求 - 远程地址: %s, 错误: %s%n", remoteAddr, failure.getMessage()); } @Override public void headerComplete() { // 请求头解析完成,此时可以拿到Request对象,提前记录 Request request = connection.getHttpChannel().getRequest(); System.out.printf("收到请求 - 方法: %s, URI: %s%n", request.getMethod(), request.getURI()); } }); return connection; } });
这个方式能在请求解析出错的第一时间捕获到事件,哪怕Request对象还没完全初始化,也能记录错误原因和客户端信息。
3. 调整URI解析严格性(可选,谨慎使用)
如果你的业务允许空段URI(比如//quests/195),可以通过修改HttpConfiguration关闭严格模式,让请求能到达业务Handler:
HttpConfiguration httpConfig = new HttpConfiguration(); // 允许URI中的空段 httpConfig.setAllowEmptySegments(true); // 或者关闭整体严格模式 // httpConfig.setStrictMode(false); ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); server.addConnector(connector);
但注意:关闭严格模式可能引入安全风险,只有在明确业务需求且做过安全评估后再使用。
为什么你之前的日志没捕获到?
你遇到的BadMessageException是在HttpParser解析请求头阶段抛出的,此时请求还没进入业务Handler链,所以你在业务Handler里加的日志根本不会执行。必须用Jetty底层的监听器或RequestLogHandler才能覆盖这类场景。
内容的提问来源于stack exchange,提问作者Gili
相关产品推荐
相关产品推荐

