如何覆盖Jetty 11.0.6中的请求日志记录机制

2022-05-14 00:00:00 java eclipse jetty slf4j
我正在将jetty 9更新为jetty11,我看到org.eclipse.jetty.server.AsyncNCSARequestLog已过时,为此引入了另外两个类-org.eclipse.jetty.server.CustomRequestLog和org.eclipse.jetty.server.AsyncRequestLogWriter.

但问题是,我过去习惯于扩展AsyncNCSARequestLog类并重写日志和写入方法(有关详细信息,请参阅下面的代码),但找不到在Jetty11中执行此操作的方法。

public class JettyRequestLogger extends AsyncNCSARequestLog
    {
        private Request request;
    
        @Override
        public void log(Request request, Response response)
        {
            this.request = request;
            super.log(request, response);
        }
    
        @Override
        public void write(String logStr) throws IOException
        {
            logStr = setCustomAttributesToLog(logStr);
            super.write(logStr);
        }
       private String setCustomAttributesToLog(String logStr)
       {
         // logic here
       }

    }

更新: 我的用例是在打印Jetty日志时添加用户ID、用户名和自定义属性等自定义属性


解决方案

自Jetty 9.x以来,RequestLog整体上发生了一些重要变化

RequestLog实现已添加到Server,请不要使用旧的RequestLogHandler

// Modern version (for all system loads and speeds)
Slf4jRequestLogWriter slfjRequestLogWriter = new Slf4jRequestLogWriter();
slfjRequestLogWriter.setLoggerName("com.company.request.log");
String format = "%{client}a - %u %t '%r' %s %O '%{Referer}i' '%{User-Agent}i' '%C'";
CustomRequestLog customRequestLog = new CustomRequestLog(slfjRequestLogWriter, format);
server.setRequestLog(customRequestLog);
// Old school version (not for busy systems)
Path outputPattern = logsDir.resolve("yyyy_mm_dd.request.log");
AsyncRequestLogWriter logWriter = new AsyncRequestLogWriter(outputPattern.toString());
logWriter.setFilenameDateFormat("yyyy_MM_dd");
logWriter.setRetainDays(90);
logWriter.setTimeZone("GMT");
String format = "%{client}a - %u %t '%r' %s %O '%{Referer}i' '%{User-Agent}i' '%C'";
CustomRequestLog requestLog = new CustomRequestLog(logWriter, format);
server.setRequestLog(requestLog);
虽然RequestLogHandler仍然存在,但它有一些根本的缺陷,因为它不记录在解析步骤中失败的请求、在合规性验证步骤中失败的请求、在SSL/TLS验证步骤中失败的请求、不属于已知上下文的请求、在响应生成期间失败的交换、或者通过错误处理层返回到应用程序但在应用层失败的交换,等等。如您所见,有许多RequestLogHandler无法处理的边缘情况,因此创建了基本Server.setRequestLog(RequestLog),使用它。

RequestResponse中进行了内部更改,以便在每个组件提交时保留这些组件的状态,因此您必须使用和RequestResponse中的新方法来获取有关这些对象在网络层实际处理时的状态的准确信息。由于当日志记录发生在http交换和生命周期期间,使用原始方法很容易导致数据错误/不准确。

RequestLogJetty发布的实现由两部分组成。

RequestLog本身是接受RequestResponse并将信息格式化为日志行字符串的组件。

RequestLog.Writer组件由RequestLog用于将原始字符串写入任何需要的内容。

  • Slf4jRequestLogWriter(默认实现,推荐)-将字符串写入名为logger的slf4j。这是设置唯一日志文件、滚动行为(按大小、日期、任意持续时间或其他触发器)、旧日志存档(带压缩)、警报、支持扩展访问日志分析工具等的最佳选择。
  • RequestLogWriter(老派,很多问题)--同步给任意OutputStream写信(您可以在这里使用Jetty的老派RolloverFileOutputStream)。如果网络事件多于磁盘I/O可以处理的网络事件(这是一种令人惊讶的常见情况),这将在收到条目后写入每个条目。使用异步版本切换为猝发写入。
  • AsyncRequestLogWriter-异步版本的RequestLogWriter,如果您的网络事件仍然超过您的磁盘I/O可以处理的范围,请切换到Slf4jRequestLogWriter并使用各种slf4j实现的高级异步日志追加,这比Jetty本身在AsyncRequestLogWriter中使用的简单的java.io行为要可靠得多。

最常见的自定义请求日志记录形式是更改记录的内容,而不是自定义输出格式(这已经由CustomRequestLog本身处理)。

例如,仅记录错误案例,而不记录其余情况。

请注意,这显示了上面提到的已提交状态信息的示例。

package jetty;

import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;

public class ErrorOnlyRequestLog extends CustomRequestLog
{
    public ErrorOnlyRequestLog(Writer writer, String formatString)
    {
        super(writer, formatString);
    }

    public ErrorOnlyRequestLog(String file)
    {
        super(file);
    }

    public ErrorOnlyRequestLog(String file, String format)
    {
        super(file, format);
    }

    @Override
    public void log(Request request, Response response)
    {
        // Get the response status actually sent (as you cannot rely on
        // the Response.getStatus() at this point in time, which can change
        // due to a number of factors, including error handling, dispatch
        // completion, recycling, etc)
        int committedStatus = response.getCommittedMetaData().getStatus();

        // only interested in error cases - bad request & server errors
        if ((committedStatus >= 500) || (committedStatus == 400))
        {
            super.log(request, response);
        }
        else
        {
            System.err.println("### Ignored request (response.committed.status=" + committedStatus + "): " + request);
        }
    }
}

相关文章