Java中的日志框架

Posted 滴水穿石

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的日志框架相关的知识,希望对你有一定的参考价值。

需求

使用日志框架有三点需求:

1、期望日志能保存在文件中,方便时候排错。

2、开发环境的日志记录会更多方便调试。

3、生产环境需要记录重要的信息。

 

Log4j

可用于普通maven项目,也可以用于springboot项目。

Log4j提供了简单的API调用,强大的日志格式定义以及灵活的扩展性。

可以自定义Appender来满足日志输出的需求。

 

日志级别

从低到高

DEBUG INFO WARN ERROR FATAL

 

pom依赖

<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.8.2</version>
</dependency>

 

目录结构

- src
 - main
  - java
  - resources
     log4j.properties

 

配置文件

log4j.properties

log4j.rootLogger=debug, stdout, A, D, E
 
### Output to the console ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %m%n
 
### Output to the log file ###
log4j.appender.A=org.apache.log4j.RollingFileAppender
log4j.appender.A.File=D:/logs/firestorm.log
log4j.appender.A.MaxFileSize=1000KB
log4j.appender.A.MaxBackupIndex=1
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %m%n
 
### 输出DEBUG 级别以上的日志 ###
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=D://logs/debug/debug.log
log4j.appender.D.Append=true
log4j.appender.D.Threshold=DEBUG
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %m%n
 
### 输出ERROR 级别以上的日志 ###
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File=D://logs/error/error.log
log4j.appender.E.Append=true
log4j.appender.E.Threshold=ERROR
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %m%n

 

Appender

指定日志输出目的地,可以是控制台也可以是文件。

  • 输出到控制台的org.apache.log4j.ConsoleAppender

  • 输出到文件的org.apache.log4j.DailyRollingFileAppender

 

ConversionPattern

指定日志的输出格式

%p:输出日志信息的优先级,即DEBUG,INFO,WARN,ERROR,FATAL。
%d:输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,如:%d{yyyy/MM/dd HH:mm:ss,SSS}。
%r:输出自应用程序启动到输出该log信息耗费的毫秒数。
%t:输出产生该日志事件的线程名。
%l:输出日志事件的发生位置,相当于%c.%M(%F:%L)的组合,包括类全名、方法、文件名以及在代码中的行数。例如:test.TestLog4j.main(TestLog4j.java:10)。
%c:输出日志信息所属的类目,通常就是所在类的全名。
%M:输出产生日志信息的方法名。
%F:输出日志消息产生时所在的文件名称。
%L::输出代码中的行号。
%m::输出代码中指定的具体日志信息。
%n:输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"%x:输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%:输出一个"%"字符。
另外,还可以在%与格式字符之间加上修饰符来控制其最小长度、最大长度、和文本的对齐方式。如:
%20c:指定输出category的名称,最小的长度是20,如果category的名称长度小于20的话,默认的情况下右对齐。
%-20c:"-"号表示左对齐。
%.30c:指定输出category的名称,最大的长度是30,如果category的名称长度大于30的话,就会将左边多出的字符截掉,但小于30的话也不会补空格。

 

性能问题

Java的IO是阻塞式的,频繁的往文件中写入日志,对系统性能有较大的影响。

 

性能最差的是ImmediateFlush=true的时候(关闭了缓存),而性能最好的就是开启日志异步AsyncAppender处理的时候

 

开启缓存

1、好处:提升系统响应性能;

2、不足:当系统因为异常而崩溃,又或者jvm被强行关闭,从而导致缓存中的数据丢失,日志不存在,无法及时确定异常原因。

 

具体实施

  • 当ImmediateFlush=true时候,表示每一条打印日志请求都会被立即输出,也就是立刻同步到磁盘中去。在高并发下,系统性能受到很大的影响,IO和磁盘读写数大大提升。

  • 当ImmediateFlush=false时候,与上面正好相反,表示每一条打印日志请求不会被立即输出,会使用java.io.OutputStreamWriter的缓存,缓存大小为1024字节。

  • 当ImmediateFlush=false、BufferedIO=true、BufferSize=8192时候,表示使用java.io.BufferedWriter缓存,缓存大小为默认8192字节,每一条打印请求不会立即输出,当缓存达到8192字节后才会输出到文件。这样一来,大大减少了IO和磁盘读写操作,提升了系统的性能。

 

log4j.appender.HOOKFILE=com.***.***.HookFileAppender
log4j.appender.HOOKFILE.File=D://logs/debug/debug.log
log4j.appender.HOOKFILE.Append=true
log4j.appender.HOOKFILE.Threshold=DEBUG
### 开启缓存 ###
log4j.appender.D.BufferedIO=true
log4j.appender.D.ImmediateFlush=false
log4j.appender.D.BufferSize=8192
### 开启缓存 ###
log4j.appender.HOOKFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.HOOKFILE.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %m%n

 

钩子

jvm运行结束,日志信息没有保存到磁盘中,还存在于缓存中

以下的程序可以保证数据保存到文件。

public class HookFileAppender extends FileAppender {
    public HookFileAppender(){
        super();
        //添加钩子程序
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }
    public HookFileAppender(Layout layout, String filename) throws IOException {
        super(layout,filename);
        //添加钩子程序
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }
    public HookFileAppender(Layout layout, String filename, boolean append) throws IOException {
        super(layout,filename,append);
        //添加钩子程序
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }
    public HookFileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,
                 int bufferSize) throws IOException {
        super(layout,filename,append,bufferedIO,bufferSize);
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }
 
    class Log4jHockThread extends Thread{
        @Override
        public void run() {
            //jvm结束之前,运行flush操作,将日志写入磁盘;
            if(qw != null){
                qw.flush();
            }
        }
    }
}

 

 

具体使用

import fabric.edu.sdk.ca.CaManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
 
/**
 * @author wzm
 * @version 1.0.0
 * @date 2020/1/24 15:25
 **/
public class MyTest {
 
    private static final Log log = LogFactory.getLog(CaManager.class);
 
    public static void main(String[] args) {
        log.info("hello");
        log.debug("hello");
        log.error("hello");
        log.warn("hello");
    }
}

 

打印

2020/01/26 15:01:43.005 main [INFO] CaManager (MyTest.java:17) hello
2020/01/26 15:01:43.006 main [DEBUG] CaManager (MyTest.java:18) hello
2020/01/26 15:01:43.006 main [ERROR] CaManager (MyTest.java:19) hello
2020/01/26 15:01:43.007 main [WARN] CaManager (MyTest.java:20) hello

 

logback

xxx

 

以上是关于Java中的日志框架的主要内容,如果未能解决你的问题,请参考以下文章

Java日志框架 -- SpringBoot中的日志使用

Java中的日志框架

LockSupport.java 中的 FIFO 互斥代码片段

argparse 代码片段只打印部分日志

日志片段,类中的Logfactory声明

常用python日期日志获取内容循环的代码片段