Java如何合理打印日志(How to print logs reasonably in Java)

  • 一、应用程序日志的概述
  • 二、日志的作用
  • 三、记录日记的时机
  • 四、日志打印最佳实践1、日志变量定义2、参数占位符格式3、日志的基本格式4、日志文件5、日志配置6、日志使用规范
  • 1、日志变量定义
  • 2、参数占位符格式
  • 3、日志的基本格式
  • 4、日志文件
  • 5、日志配置
  • 6、日志使用规范

日志:记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。

日志:记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。

一、应用程序日志的概述

通常,Java程序员在开发项目时都是依赖Eclipse/IDEA等集成开发工具的Debug 调试功能来跟踪解决Bug,但项目发布到了测试、生产环境怎么办?你有可能会说可以使用远程调试,但实际并不能允许让你这么做。

所以,日志的作用就是在测试、生产环境没有 Debug 调试工具时开发和测试人员定位问题的手段。日志打得好,就能根据日志的轨迹快速定位并解决线上问题,反之,日志输出不好,不仅无法辅助定位问题反而可能会影响到程序的运行性能和稳定性。

很多介绍 AOP 的地方都采用日志来作为介绍,实际上日志要采用切面的话是极其不科学的!对于日志来说,只是在方法开始、结束、异常时输出一些什么,那是绝对不够的,这样的日志对于日志分析没有任何意义。

如果在方法的开始和结束整个日志,那方法中呢?如果方法中没有日志的话,那就完全失去了日志的意义!如果应用出现问题要查找由什么原因造成的,也没有什么作用。这样的日志还不如不用!

二、日志的作用

不管是使用何种编程语言,日志输出几乎无处不再。总结起来,日志大致有以下几种用途:

  • 「问题追踪」:辅助排查和定位线上问题,优化程序运行性能。
  • 「状态监控」:通过日志分析,可以监控系统的运行状态。
  • 「安全审计」:审计主要体现在安全上,可以发现非授权的操作。

三、记录日记的时机

在看线上日志的时候,我们可曾陷入到日志泥潭?该出现的日志没有,无用的日志一大堆,或者需要的信息分散在各个角落,特别是遇到紧急的在线bug时,有效的日志被大量无意义的日志信息淹没,焦急且无奈地浪费大量精力查询日志。那什么是记录日志的合适时机呢?

总结几个需要写日志的点:

  • 「编程语言提示异常」:如今各类主流的编程语言都包括异常机制,业务相关的流行框架有完整的异常模块。这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用warn或者error级别。
  • 「业务流程预期不符」:除开平台以及编程语言异常之外,项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。
  • 「系统核心角色,组件关键动作」:系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录INFO级别日志,比如电商系统用户从登录到下单的整个流程;微服务各服务节点交互;核心数据表增删改;核心组件运行等等,如果日志频度高或者打印量特别大,可以提炼关键点INFO记录,其余酌情考虑DEBUG级别。
  • 「系统初始化」:系统或者服务的启动参数。核心模块或者组件初始化过程中往往依赖一些关键配置,根据参数不同会提供不一样的服务。务必在这里记录INFO日志,打印出参数以及启动完成态服务表述。

四、日志打印最佳实践

1、日志变量定义

日志变量往往不变,最好定义成final static,变量名用大写。

private static final Logger log = LoggerFactory.getLogger({SimpleClassName}.getClass());

通常一个类只有一个 对象,如果有父类可以将 定义在父类中。

log
log

日志变量类型定义为门面接口(如 slf4j 的 Logger),实现类可以是 Log4j、Logback 等日志实现框架,不要把实现类定义为变量类型,否则日志切换不方便,也不符合抽象编程思想。

另外,推荐引入lombok的依赖,在类的头部加上的注解,之后便可以在程序的任意位置使用变量打印日志信息了,使用起来更加简洁一点,在重构代码尤其是修改类名的时候无需改动原有代码。

@Slf4j
log

2、参数占位符格式

使用参数化形式{}占位,[]进行参数隔离

String username = "linhuaming";
String password = "123456";
log.info("参数 username:[{}],参数 password:[{}]",username,password);

3、日志的基本格式

日志输出主要在文件中,应包括以下内容:

  • 日志时间
  • 日志级别主要使用
  • 调用链标识(可选)
  • 线程名称
  • 日志记录器名称
  • 日志内容
  • 异常堆栈(不一定有)

4、日志文件

日志文件放置于固定的目录中,按照一定的模板进行命名,推荐的日志文件名称:

当前正在写入的日志文件名:<应用名>[-<功能名>].log
已经滚入历史的日志文件名:<应用名>[-<功能名>].log.<yyyy-MM-dd>

5、日志配置

根据不同的环境配置不同的日志输出方式:

  • 本地调试可以将日志输出到控制台上
  • 测试环境或者生产环境输出到文件中,每天产生一个文件,如果日志量庞大可以每个小时产生一个日志文件
  • 生产环境中的文件输出,可以考虑使用异步文件输出,该种方式日志并不会马上刷新到文件中去,会产生日志延时,在停止应用时可能会导致一些还在内存中的日志未能及时刷新到文件中去而产生丢失,如果对于应用的要求并不是非常高的话,可暂不考虑异步日志

logback 日志工具可以在日志文件滚动后将前一文件进行压缩,以减少磁盘空间占用,若使用 logback 对于日志量庞大的应用建议开启该功能。

6、日志使用规范

1、输出Exceptions的全部Throwable信息。

void foo(){
    try{
       //do somehing 
    }catch(Exception e){
        log.error(e.getMessage());//错误示范
        log.erroe("Bad Things",e.getMessage());//错误示范
        log.error("Bad Things",e);//正确演示
    }
}

2、不允许记录日志后又抛出异常。

void foo() throws LogException{
    try{
        //do somehing 
    }catch(Exception e){
        log.error("Bad Things",e);//正确
        throw new LogException("Bad Things",e);
    }
}

3、不允许使用标准输出 Syste.out.println。

void foo(){
    try{
       //do somehing 
    }catch(Exception e){
        Syste.out.println(e.getMessage());//错误
        System.error.println(e.getMessage());//错误
        log.error("Bad Things",e);//正确
    }
}

4、不允许出现printStackTrace。

void foo(){
    try{
       //do somehing 
    }catch(Exception e){
        e.printStacktrace();//错误
        log.error("Bad Things",e);//正确
    }
}

public void printStackTrace() {
    printStackTrace(System.err);
}

5、不要在大循环中打印日志。

for(int i=0; i<2000; i++){
    log.info("XX");
}

参考:

https://www.cnblogs.com/javastack/p/15548907.html

————————
  • 1、 Overview of application logs
  • 2、 Role of log
  • 3、 Time to keep a diary
  • 4、 Log printing Best Practices 1. Log variable definition 2. Parameter placeholder format 3. Basic format of log 4. Log file 5. Log configuration 6. Log usage specification
  • 1. Log variable definition
  • 2. Parameter placeholder format
  • 3. Basic format of log
  • 4. Log file
  • 5. Log configuration
  • 6. Log usage specification

Log: record the running track of the program, which is convenient to find key information and quickly locate and solve problems.

Log: record the running track of the program, which is convenient to find key information and quickly locate and solve problems.

1、 Overview of application logs

Usually, when developing a project, Java programmers rely on the debug debugging function of integrated development tools such as eclipse / idea to track and solve bugs, but what if the project is released to the test and production environment? You may say that you can use remote debugging, but you are not allowed to do so.

Therefore, the function of log is to locate problems for developers and testers when there is no debug debugging tool in the testing and production environment. If the log is well typed, it can quickly locate and solve online problems according to the log track. On the contrary, if the log output is not good, it can not assist in positioning problems, but may affect the operation performance and stability of the program.

Many places that introduce AOP use logs as an introduction. In fact, it is extremely unscientific to use sections in logs! For logs, it is absolutely not enough to output something at the beginning, end and exception of the method. Such logs have no significance for log analysis.

If the whole log is at the beginning and end of the method, what about in the method? If there is no log in the method, the meaning of the log will be completely lost! If there is a problem in the application, it is useless to find out what caused it. It’s better not to use such a log!

2、 Role of log

No matter what programming language is used, log output is almost everywhere. To sum up, logs can be used for the following purposes:

  • “Problem tracking”: assist in troubleshooting and locating online problems and optimize program performance.
  • “Status monitoring”: the running status of the system can be monitored through log analysis.
  • “Security audit”: the audit is mainly reflected in security, and unauthorized operations can be found.

3、 Time to keep a diary

When reading the online log, have we ever fallen into the log quagmire? There are no logs, there are a lot of useless logs, or the information needed is scattered in every corner. Especially in the event of an urgent online bug, the effective logs are flooded by a large number of meaningless log information, and they anxiously and helplessly waste a lot of energy querying the logs. So what is the right time to log?

Summarize several points that need to be logged:

  • “Programming languages prompt exceptions”: Nowadays, all kinds of mainstream programming languages include exception mechanisms, and business-related popular frameworks have complete exception modules. This kind of exception is an error report of high quality that the system tells developers to pay attention to. The log should be properly recorded, and the warn or error level should be used according to the actual situation of the business.
  • “Business process does not meet expectations”: in addition to platform and programming language exceptions, when the results in the project code do not meet expectations is also one of the log scenarios. In short, all process branches can be considered. It is up to the developer to judge whether the situation can be tolerated. Common suitable scenarios include incorrect external parameters, data processing problems that cause the return code to be out of a reasonable range, and so on.
  • “System core role, component key action”: the business action triggered by the core role in the system needs more attention and is an important indicator to measure the normal operation of the system. It is recommended to record the info level log, such as the whole process of e-commerce system users from login to order; Microservices interact with each service node; Addition, deletion and modification of core data sheet; Core component operation, etc. if the log frequency is high or the printing volume is particularly large, you can refine the info records of key points, and consider the debug level as appropriate.
  • “System initialization”: startup parameters of the system or service. The initialization process of core modules or components often depends on some key configurations, and different services will be provided according to different parameters. Be sure to record the info Log here, print out the parameters and start the service description in completion status.

4、 Log printing best practices

1. Log variable definition

The log variable is often unchanged. It is best to define it as final static, and the variable name is capitalized.

private static final Logger log = LoggerFactory.getLogger({SimpleClassName}.getClass());

Usually, a class has only one object. If there is a parent class, it can be defined in the parent class.

log
log

The log variable type is defined as a facade interface (such as the logger of slf4j). The implementation class can be a log implementation framework such as log4j and logback. Do not define the implementation class as a variable type, otherwise the log switching is inconvenient and does not conform to the abstract programming idea.

In addition, it is recommended to introduce Lombok dependency and add comments on the head of the class. Then you can use variables to print log information at any position of the program. It is more concise to use. There is no need to change the original code when reconstructing the code, especially when modifying the class name.

@Slf4j
log

2. Parameter placeholder format

Use the parameterized form {} to occupy, [] for parameter isolation

String username = "linhuaming";
String password = "123456";
log.info("参数 username:[{}],参数 password:[{}]",username,password);

3. Basic format of log

The log output is mainly in the file and should include the following contents:

  • Log time
  • Log level mainly uses
  • Call chain ID (optional)
  • Thread name
  • Logger name
  • Log content
  • Exception stack (not necessarily)

4. Log file

The log file is placed in a fixed directory and named according to a certain template. The recommended log file name is:

当前正在写入的日志文件名:<应用名>[-<功能名>].log
已经滚入历史的日志文件名:<应用名>[-<功能名>].log.<yyyy-MM-dd>

5. Log configuration

Configure different log output methods according to different environments:

  • Local debugging can output logs to the console
  • The test environment or production environment is output to files, and one file is generated every day. If the log volume is large, one log file can be generated every hour
  • Asynchronous file output can be considered for file output in the production environment. In this way, the log will not be refreshed to the file immediately, resulting in log delay. When stopping the application, some logs still in memory may not be refreshed to the file in time, resulting in loss. If the requirements for the application are not very high, asynchronous logs can be ignored temporarily

Logback logging tool can compress the previous file after the log file is rolled to reduce the disk space occupation. If logback is used for applications with a large amount of logs, it is recommended to enable this function.

6. Log usage specification

< strong > 1. Output all throwable information of exceptions

void foo(){
    try{
       //do somehing 
    }catch(Exception e){
        log.error(e.getMessage());//错误示范
        log.erroe("Bad Things",e.getMessage());//错误示范
        log.error("Bad Things",e);//正确演示
    }
}

< strong > 2. It is not allowed to throw an exception after logging

void foo() throws LogException{
    try{
        //do somehing 
    }catch(Exception e){
        log.error("Bad Things",e);//正确
        throw new LogException("Bad Things",e);
    }
}

< strong > 3. Standard output syste is not allowed out. println。

void foo(){
    try{
       //do somehing 
    }catch(Exception e){
        Syste.out.println(e.getMessage());//错误
        System.error.println(e.getMessage());//错误
        log.error("Bad Things",e);//正确
    }
}

4、不允许出现printStackTrace。

void foo(){
    try{
       //do somehing 
    }catch(Exception e){
        e.printStacktrace();//错误
        log.error("Bad Things",e);//正确
    }
}

public void printStackTrace() {
    printStackTrace(System.err);
}

< strong > 5. Do not print the log in a large cycle

for(int i=0; i<2000; i++){
    log.info("XX");
}

reference resources:

https://www.cnblogs.com/javastack/p/15548907.html