登录 Java 和一般情况:最佳实践?

2022-08-31 12:39:41

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

库类

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

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

常规日志

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


答案 1

你的惯例非常标准,很好(恕我直言)。

需要注意的一件事是过多的无声调试调用造成的内存碎片,因此,使用Log4J(以及大多数其他Java日志记录框架),您最终会得到这样的东西:

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

因为构建该日志消息(您可能没有使用)可能很昂贵,特别是如果完成数千或数百万次。

您的 INFO 级别日志记录不应该太“健谈”(从您所说的内容来看,听起来似乎不是)。INFO 消息通常应该有意义且意义重大,就像正在启动和停止的应用程序一样。遇到问题时可能想知道的事项。调试/精细级别日志记录更适用于实际遇到尝试诊断的问题。调试/精细日志记录通常仅在需要时打开。信息通常始终在线。

如果有人不希望您的课程提供特定的 INFO 消息,他们当然可以自由地更改您的 log4j 配置,使其不获取它们。Log4j在这个部门中非常简单(与Java 1.4日志记录相反)。

至于你的HTTP,我通常不会发现这是Java日志记录的问题,因为通常单个类负责你感兴趣的内容,所以你只需要把它放在一个地方。在(根据我的经验很少见)当你想要跨看似不相关的类的常见日志消息时,只需放入一些很容易被抓取的令牌。


答案 2

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

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

此处提供了基准测试。为了获得最佳性能,我遵循的实践如下:

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

推荐