使用 Java 进行日志记录和一般情况:最佳实践?

新手上路,请多包涵

有时当我看到我的日志记录代码时,我想知道我是否做对了。可能没有明确的答案,但我有以下担忧:

图书馆课程

我有几个库类可能会记录一些 INFO 消息。致命错误被报告为异常。目前我的类中有一个静态记录器实例,类名作为日志名称。 (Log4j 的: Logger.getLogger(MyClass.class)

这是正确的方法吗?也许这个库类的用户不想要来自我的实现的任何消息,或者想要将它们重定向到特定于应用程序的日志。我应该允许用户从“外部世界”设置记录器吗?您如何处理此类情况?

一般日志

在某些应用程序中,我的类可能希望将日志消息写入未由类名标识的特定日志。 (即: HTTP Request log )做这样的事情最好的方法是什么?我想到了一个查找服务……

原文由 Malax 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 340
2 个回答

您的约定非常标准且非常好(恕我直言)。

需要注意的一件事是过多的未调试调用造成的内存碎片,因此,使用 Log4J(和大多数其他 Java 日志记录框架),您最终会得到如下结果:

 if (log.isDebugEnabled()) {
  log.debug("...");
}

因为构建该日志消息(您可能没有使用)可能很昂贵,尤其是在完成数千或数百万次的情况下。

您的 INFO 级别日志记录不应该太“健谈”(从您所说的来看,这听起来不是)。 INFO 消息通常应该有意义和重要,例如正在启动和停止的应用程序。如果您遇到问题,您可能想知道的事情。调试/精细级别日志记录更多地用于当您实际遇到要尝试诊断的问题时。调试/精细日志记录通常只在需要时打开。信息通常一直在线。

如果有人不想要来自您的类的特定 INFO 消息,他们当然可以自由更改您的 log4j 配置以不获取它们。 Log4j 在这方面非常简单(与 Java 1.4 日志记录相反)。

至于你的 HTTP 事情,我通常不认为这是 Java 日志记录的问题,因为通常一个类负责你感兴趣的内容,所以你只需要将它放在一个地方。在(我的经验中很少见)当你想要跨看似不相关的类的公共日志消息时,只需放入一些可以很容易地被 grepped 的标记。

原文由 cletus 发布,翻译遵循 CC BY-SA 2.5 许可协议

以下是我在所有项目中遵循的一套准则,以确保良好的性能。我根据互联网上各种来源的输入形成了这套指南。

与今天一样,我相信 Log4j 2 是迄今为止用 Java 进行日志记录的最佳选择。

基准可 在此处 获得。为了获得最佳性能,我遵循的做法如下:

  1. 我目前避免使用 SLF4J,原因如下:
  2. 使用异步记录器进行所有常规记录以获得更好的性能
  3. 使用同步记录器将错误消息记录在单独的文件中,因为我们希望在错误发生时立即看到错误消息
  4. 不要在常规日志记录中使用位置信息,例如文件名、类名、方法名、行号,因为为了派生这些信息,框架会拍摄堆栈快照并遍历它。这会影响性能。因此,仅在错误日志中使用位置信息,而不在常规日志中使用
  5. 为了跟踪由单独线程处理的单个请求,请考虑使用线程上下文和随机 UUID,如此 所述
  6. 由于我们在单独的文件中记录错误,因此将上下文信息也记录在错误日志中非常重要。例如,如果应用程序在处理文件时遇到错误,则在错误日志文件中打印文件名和正在处理的文件记录以及堆栈跟踪
  7. 日志文件应该是 grep-able 并且易于理解。例如,如果应用程序处理多个文件中的客户记录,则每个日志消息应如下所示:
 12:01:00,127 INFO FILE_NAME=file1.txt - Processing starts
12:01:00,127 DEBUG FILE_NAME=file1.txt, CUSTOMER_ID=756
12:01:00,129 INFO FILE_NAME=file1.txt - Processing ends

  1. 使用 SQL 标记记录所有 SQL 语句,如下所示,并使用过滤器启用或禁用它:
 private static final Marker sqlMarker =
  MarkerManager.getMarker("SQL");

private void method1() {
    logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE");
}

  1. 使用 Java 8 Lambdas 记录所有参数。当给定的日志级别被禁用时,这将使应用程序免于格式化消息:
 int i=5, j=10;
logger.info("Sample output {}, {}", ()->i, ()->j);

  1. 不要使用字符串连接。使用参数化消息,如上所示

  2. 使用日志记录配置的动态重新加载,以便应用程序自动重新加载日志记录配置中的更改,而无需重新启动应用程序

  3. 不要使用 printStackTrace()System.out.println()

  4. 应用程序应在退出前关闭记录器:

 LogManager.shutdown();

  1. 最后,为了大家参考,我使用了如下配置:
 <?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorinterval="300" status="info" strict="true">
    <Properties>
        <Property name="filePath">${env:LOG_ROOT}/SAMPLE</Property>
        <Property name="filename">${env:LOG_ROOT}/SAMPLE/sample
        </Property>
        <property name="logSize">10 MB</property>
    </Properties>
    <Appenders>
        <RollingFile name="RollingFileRegular" fileName="${filename}.log"
            filePattern="${filePath}/sample-%d{yyyy-dd-MM}-%i.log">
            <Filters>
                <MarkerFilter marker="SQL" onMatch="DENY"
                    onMismatch="NEUTRAL" />
            </Filters>
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />

            </Policies>
        </RollingFile>
        <RollingFile name="RollingFileError"
            fileName="${filename}_error.log"
            filePattern="${filePath}/sample_error-%d{yyyy-dd-MM}-%i.log"
            immediateFlush="true">
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <AsyncLogger name="com"
            level="trace">
            <AppenderRef ref="RollingFileRegular"/>
        </AsyncLogger>
        <Root includeLocation="true" level="trace">
            <AppenderRef ref="RollingFileError" level="error" />
        </Root>
    </Loggers>
</Configuration>

  1. 所需的 Maven 依赖项在这里:
 <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.6</version>
</dependency>
<!-- (Optional)To be used when working
with the applications using Log4j 1.x -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-1.2-api</artifactId>
    <version>2.8.1</version>
</dependency>

原文由 Saptarshi Basu 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题