如何覆盖Jetty 11.0.6中的请求日志记录机制
我正在将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)
,使用它。
在Request
和Response
中进行了内部更改,以便在每个组件提交时保留这些组件的状态,因此您必须使用和Request
和Response
中的新方法来获取有关这些对象在网络层实际处理时的状态的准确信息。由于当日志记录发生在http交换和生命周期期间,使用原始方法很容易导致数据错误/不准确。
RequestLog
Jetty发布的实现由两部分组成。
RequestLog
本身是接受Request
和Response
并将信息格式化为日志行字符串的组件。
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);
}
}
}
相关文章