在Java和一般情况下的日志。最佳实践?[英] Logging in Java and in general: Best Practices?

本文是小编为大家收集整理的关于在Java和一般情况下的日志。最佳实践?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

有时候,当我看到记录代码时,我想知道我是否做对了.可能没有明确的答案,但是我有以下问题:

图书馆类

我有几个可以记录一些INFO消息的库类.致命错误报告为例外.目前,我的课程中有一个静态记录器实例,其中类名称为记录名称. (log4j's:Logger.getLogger(MyClass.class))

这是正确的方法吗?也许该库类的用户不希望我的实现中的任何消息,也不希望将其重定向到特定于应用程序的日志.我应该允许用户从"外界"设置记录仪?您如何处理这种情况?

一般日志

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

推荐答案

您的惯例非常标准且很好(IMHO).

要观看的一件事是从过度未经验证的调试呼叫中的内存碎片,因此使用log4j(以及大多数其他Java记录框架),您最终会得到类似的东西:

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

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

您的信息级记录不应该太" chatty"(从您所说的,听起来好像不是).信息消息通常应该是有意义的且重要的,例如启动和停止应用程序.如果您遇到问题,您可能想知道的事情.当您实际诊断出问题时,调试/罚款级记录更常用.通常仅在需要时打开调试/罚款.信息通常一直在.

如果某人不希望您的课程中的特定信息消息,那么他们当然可以自由更改您的log4j配置以不获取它们. log4j在该部门非常简单(与Java 1.4记录相对).

.

至于您的HTTP事物,我通常没有发现Java Logging是一个问题,因为通常一个类负责您感兴趣的内容,因此您只需要将其放在一个地方即可.在(在我的经验中很少见)时,当您想要跨看似无关的类中的通用日志消息时,只需放入一些很容易被抓取的令牌.

其他推荐答案

以下是我在所有项目中遵循的一组准则,以确保良好的性能.我是根据Internet中各种来源的输入来构成这套准则的.

迄今为止,我相信log4j 2是迄今为止登录Java的最佳选择.

基准可用在这里.我遵循的最佳性能的做法如下:

  1. 由于以下原因,我避免使用SLF4J:
  2. 使用异步记录仪进行所有常规记录以获得更好的性能
  3. 使用同步记录器在单独文件中的日志错误消息,因为我们想在发生错误消息时立即查看
  4. 请勿使用位置信息,例如文件名,类名称,方法名称,常规日志记录的行号,因为为了获取这些信息,该框架会拍摄堆栈的快照并浏览其.这会影响性能.因此,仅在错误日志中使用位置信息,而不是在常规日志中使用
  5. 中的位置信息
  6. 为了跟踪由单独线程处理的单个请求,请考虑使用线程上下文和随机uuid,如在这里
  7. 由于我们在单独的文件中记录错误,因此在错误日志中也登录上下文信息非常重要.例如如果应用程序在处理文件时遇到错误,请打印文件名,以及在错误日志文件中处理的文件记录以及stackTrace
  8. 日志文件应易于理解且易于理解.例如如果应用程序处理多个文件中的客户记录,则每个日志消息应如下所示:
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>

其他推荐答案

@cletus的答案,他写了

的问题
if (log.isDebugEnabled()) {
  log.debug("val is " + value);
}

可以通过使用 slf4j .它提供了格式的帮助

log.debug("val is {}", value);

仅在调试级别时才构造消息的地方.

所以,如今,使用sl4j及其同伴记录仪,logback,是建议性能和稳定原因.

本文地址:https://www.itbaoku.cn/post/1574757.html

问题描述

Sometimes when I see my logging code I wonder if I am doing it right. There might be no definitive answer to that, but I have the following concerns:

Library Classes

I have several library classes which might log some INFO messages. Fatal Errors are reported as exceptions. Currently I have a static logger instance in my classes with the class name as the logging name. (Log4j's: Logger.getLogger(MyClass.class))

Is this the right way? Maybe the user of this library class doesn't want any messages from my implementation or wants to redirect them to an application specific log. Should I allow the user to set a logger from the "outside world"? How do you handle such cases?

General logs

In some applications my classes might want to write a log message to a specific log not identified by the class' name. (I.e.: HTTP Request log) What is the best way to do such a thing? A lookup service comes to mind...

推荐答案

Your conventions are pretty standard and quite fine (imho).

The one thing to watch is memory fragmentation from excessive unnedded debug calls so, with Log4J (and most other Java logging frameworks), you end up with something like this:

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

because constructing that log message (that you probably aren't using) could be expensive, especially if done thousands or millions of times.

Your INFO level logging shouldn't be too "chatty" (and from what you say, it sounds like it isn't). INFO messages should be generally meaningful and significant, like the application being started and stopped. Things that you might want to know if you encounter a problem. Debug/fine level logging is more used for when you actually have a problem you're trying to diagnose. Debug/fine logging is typically only turned on when needed. Info is typically on all the time.

If someone doesn't want specific INFO messages from your classes, they are of course free to change your log4j configuration to not get them. Log4j is beautifully straightforward in this department (as opposed to Java 1.4 logging).

As for your HTTP thing, I've generally not found that to be an issue with Java logging because typically a single class is responsible for what you're interested in so you only need to put it in one place. In the (rare in my experience) when you want common log messages across seemingly unrelated classes, just put in some token that can easily be grepped for.

其他推荐答案

The following is the set of guidelines I follow in all my projects to ensure good performance. I have come to form this set of guidelines based on the inputs from various sources in the internet.

As on today, I believe Log4j 2 is by far the best option for logging in Java.

The benchmarks are available here. The practice that I follow to get the best performance is as follows:

  1. I avoid using SLF4J at the moment for the following reasons:
  2. Do all regular logging using asynchronous logger for better performance
  3. Log error messages in a separate file using synchronous logger because we want to see the error messages as soon as it occurs
  4. Do not use location information, such as filename, class name, method name, line number in regular logging because in order to derive those information, the framework takes a snapshot of the stack and walk through it. This impacts the performance. Hence use the location information only in the error log and not in the regular log
  5. For the purpose of tracking individual requests handled by separate threads, consider using thread context and random UUID as explained here
  6. Since we are logging errors in a separate file, it is very important that we log the context information also in the error log. For e.g. if the application encountered an error while processing a file, print the filename and the file record being processed in the error log file along with the stacktrace
  7. Log file should be grep-able and easy to understand. For e.g. if an application processes customer records in multiple files, each log message should be like below:
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. Log all SQL statements using an SQL marker as shown below and use a filter to enable or disable it:
private static final Marker sqlMarker = 
  MarkerManager.getMarker("SQL");

private void method1() {
    logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE");
}
  1. Log all parameters using Java 8 Lambdas. This will save the application from formatting message when the given log level is disabled:
int i=5, j=10;
logger.info("Sample output {}, {}", ()->i, ()->j);
  1. Do not use String concatenation. Use parameterized message as shown above

  2. Use dynamic reloading of logging configuration so that the application automatically reloads the changes in the logging configuration without the need of application restart

  3. Do not use printStackTrace() or System.out.println()

  4. The application should shut down the logger before exiting:

LogManager.shutdown();
  1. Finally, for everybody’s reference, I use the following configuration:
<?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. The required Maven dependencies are here:
<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>

其他推荐答案

In @cletus' answer, he wrote of the problem of

if (log.isDebugEnabled()) {
  log.debug("val is " + value);
}

which might be overcome by using SLF4J. It provides a formatting help

log.debug("val is {}", value);

where the message is only constructed if the level is debug.

So, nowaday, using SL4J and its companion logger, Logback, is advised for performance and stability reasons.