在Javaservlet中按用户/请求对日志进行分组

在Javaservlet中按用户/请求对日志进行分组,java,servlets,logging,slf4j,Java,Servlets,Logging,Slf4j,我有一个简单的servlet,它执行一些计算,并使用slf4j作为外观将中间结果记录到一个文件中,还有一个简单的日志绑定器(也来自slf4j)记录到该文件中。这很好,但是当多个用户同时发送请求时,他们的日志会相互交织。下面是一个虚拟示例来说明问题:来自用户alice和bob的日志记录混淆了: log: phase1 of user alice log: phase2 of user alice log: phase1 of user bob log: phase3 of user alice l

我有一个简单的servlet,它执行一些计算,并使用slf4j作为外观将中间结果记录到一个文件中,还有一个简单的日志绑定器(也来自slf4j)记录到该文件中。这很好,但是当多个用户同时发送请求时,他们的日志会相互交织。下面是一个虚拟示例来说明问题:来自用户alice和bob的日志记录混淆了:

log: phase1 of user alice
log: phase2 of user alice
log: phase1 of user bob
log: phase3 of user alice
log: phase2 of user bob
log: phase3 of user bob
...
如果涉及的用户数量和日志语句的复杂性增加,那么这确实很麻烦


一个可能的解决方案是为每个用户创建一个日志文件,当用户X输入一个请求时(这可以通过我的代码检测到),将其附加到日志文件X。问题是我不知道如何使用简单的日志来实现这一点。是可能的,如果没有,还有其他方法吗?

您可以在某些结构中存储每个用户阶段的条目,然后在最后一个阶段结束时,或在发生错误时立即将所有阶段记录为单个日志条目。

您可能希望查看SLF4J中的“MDC”(映射诊断上下文)类(其他日志框架也支持这一点)

它允许您设置自定义日志上下文数据,然后您可以将该数据显示在消息中

例如,你可以做

import org.slf4j.MDC;

...
    private static final USER_NAME_MDC_KEY = "USRKEY";

    servlet method {
        MDC.put(USER_NAME_MDC_KEY, user.getUserId());

        ...rest of logic...

    }
如果你有一个这样的模式设置

        <PatternLayout pattern="[UID=%X{USRKEY}] %d{HH:mm} [%t] %-level %c{1} - %m%n" />

…那么日志中的每一行都将包含

[UID=<whatever has been set in the MDC>]
[UID=]
然后,您可以为特定的用户Id对日志进行GREP,以便只为他们记录消息


(我认为这是您想要的,我可能错了!)

您可以设置一个“过滤器”来完成这项工作,而不是将此代码直接放在servlet中(如果您可以从请求中访问用户ID)太好了!所以基本上你是说,把它都放在一个文件中,但是在查看日志时根据id过滤文件。不过有两个小问题:如何设置模式(在哪里,在哪个文件中?)以及如何在windows机器上的logfile.txt上“GREP”?@user1884155我在本例中使用的模式在SLF4J使用Log4J2时有效“引擎盖下”。您使用的是什么日志框架?如果它是log4j,那么您可以提供一个log4j.xml配置文件来实现这一点(网络上有很多这样的示例)。在Windows上,您可以使用“findstr”代替grepAtm。我使用一个简单的日志,因为不需要任何其他东西,但log4j它将变成:)网络上确实有大量的示例可用于设置和配置它,谢谢!尽管这会产生预期的结果(即,在日志文件中分组日志块),但创建某种“缓存”并不是一件小事“每个类都可以存储每个用户的日志条目,直到最后一个阶段出现。对于Nonitrival,我的意思是它使源代码变得复杂/复杂,而不仅仅是编写“logger.log(text)”。如果没有其他选择,我会调查的,谢谢!这只是一个如何实现的想法。您可以使用简单的StringBuilder,它可以与一些自定义日志生成器类打包在一起。每个阶段调用这个LogBuilder.Append(字符串logEntry,可选阶段名称),然后最后一个阶段调用LogBuilder.log(),它记录StringBuilder的内容。