Log4J API用于在临时文件中记录长消息

时间:2017-09-20 09:17:48

标签: java logging log4j2

我试图了解是否有任何最佳实践/实用程序/开箱即用的功能来记录很长的消息(通常是json文件响应/ xmls / CSV等)。

在作为我的应用程序的一部分进行日志记录时,我会执行类似

的操作
log.info("Incompatible order found {}", order.asJson())

问题是asJson()表示可能很长。在日志文件中,实际的json仅与1%的时间相关。因此,重要的是保留但足够大,以便在我浏览日志时让我失去焦点。

无论如何,我可以做一些像

这样的事情
log.info("Incompatible order found, file dumped at {}", SomeUtility.dumpString(order.asJson()));

实用程序将文件转储到与其他日志文件一致的位置,然后在我的日志文件中,我可以看到以下内容

Incompatible order found, file dumped at /abc/your/log/location/tmpfiles/xy23nmg

需要注意的关键事项

  1. 最好简单地使用现有的日志记录api以某种方式配置它,以便这些临时文件的位置与日志本身相同,这样它在N天之后经过清理周期等就像实际的日志文件一样
  2. 我显然可以写一些东西,但我热衷于现有的实用程序或功能,如果已经可以使用log4j
  3. 我知道当像这样的日志被导入Splunk之类的分析系统时,只有文件名才会出现而没有实际的文件,这没关系。

2 个答案:

答案 0 :(得分:1)

(建议是基于logback而不是log4j2。但我相信log4j2中存在类似的设施)

在Logback中,有一个名为SiftingAppender的工具,它允许根据某些鉴别器(例如在MDC中)创建单独的appender(例如文件追加器)。

因此,通过配置一个appender(例如orderDetailAppender),它根据一个鉴别器分离文件(例如通过在MDC中放置订单ID),并利用一个单独的记录器连接到appender,这应该给你结果你想:

伪代码:

logback config:

  <appender name="ORDER_DETAIL_APPENDER" class="ch.qos.logback.classic.sift.SiftingAppender">
    <!-- use MDC disciminator by default, you may choose/develop other appropriate discrimator -->
    <sift>
      <appender name="ORDER-DETAIL-${ORDER_ID}" class="ch.qos.logback.core.FileAppender">
          ....
      </appender>
    </sift>
  </appender>
  <logger name="ORDER_DETAIL_LOGGER">
     <appender-ref name="ORDER_DETAIL_APPENDER"/>
  </logger>

您的代码如下:

Logger logger = LoggerFactory.getLogger(YourClass.class); // logger you use normally
Logger orderDetailLogger = LoggerFactory.getLogger("ORDER_DETAIL_LOGGER");
.....

MDC.put("ORDER_ID", order.getId());
logger.warn("something wrong in order {}. Find the file in separate file", order.getId());
orderDetailLogger.warn("Order detail:\n{}", order.getJson());
// consider making order.getJson() an lambda, or wrap the line with logger 
// level checking, to avoid json construction even not required to log
MDC.remove("ORDER_ID");

答案 1 :(得分:0)

最简单的方法是为JSON转储使用单独的记录器,并配置该记录器将其输出放在不同的文件中。

举个例子:

private static final Logger log = LogManager.getLogger();
private static final Logger jsonLog = LogManager.getLogger("jsondump");

...

log.info("Incompatible order found, id = {}", order.getId());
jsonLog.info("Incompatible order found, id = {}, JSON = {}",
        order.getId(), order.asJson());

然后,将名为jsondump的记录器配置为单独的文件,可能还有单独的轮换计划。确保将additivity设置为false以获取JSON记录器,以便发送到该记录器的消息也不会被发送到根记录器。见Additivity  在Log4J文档中了解详细信息。

示例(XML配置,Log4J 2):

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
  <Appenders>
    <!-- By setting only filePattern, not fileName, all log files
         will use the pattern for naming. Files will be rotated once
         an hour, since that's the most specific unit in the
         pattern. -->
    <RollingFile name="LogFile" filePattern="app-%d{yyyy-MM-dd HH}00.log">
      <PatternLayout pattern="%d %p %c [%t] %m %ex%n"/>
      <Policies>
        <TimeBasedTriggeringPolicy/>
      </Policies>
    </RollingFile>
    <!-- See comment for appender named "LogFile" above. -->
    <RollingFile name="JsonFile" filePattern="json-%d{yyyy-MM-dd HH}00.log">
      <!-- Omitting logger name for this appender, as it's only used by
           one logger. -->
      <PatternLayout pattern="%d %p [%t] %m %ex%n"/>
      <Policies>
        <TimeBasedTriggeringPolicy/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <!-- Note that additivity="false" to prevent JSON dumps from being
         sent to the root logger as well. -->
    <Logger name="jsondump" level="debug" additivity="false">
      <appender-ref ref="JsonFile"/>
    </Logger>
    <Root level="debug">
      <appender-ref ref="LogFile"/>
    </Root>
  </Loggers>
</Configuration>