天天看点

Log日志(一)

日志

1.1常见日志框架

在项目开发过程中,我们可以通过 debug 查找问题。而在线上环境我们查找问题只能通过打印日志的方式查找问题。因此对于一个项目而言,日志记录是一个非常重要的问题。因此,如何选择一个合适的日志记录框架也非常重要。

在Java开发中,目前常用的日志框架包括Log4j 1,Log4j 2,Commons Logging,Slf4j,Logback,Jul。这些日志记录框架各有各的特点,各有各的应用场景。了解这些框架的特点及应用场景,有利于我们做技术选型的时候做出正确的判断。

框架 说明
Log4j Apache Log4j是一个基于Java的日志记录工具。它是由Ceki首创的,现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。
Log4j 2 Apache Log4j 2是apache开发的一款Log4j的升级产品。
Commons Logging Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging。
Slf4j 类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。
Logback 一套日志组件的实现(Slf4j阵营)
Jul (Java Util Logging),自Java1.4以来的官方日志实现。

1.2日志框架关系

  1. Log4j 2与Log4j 1发生了很大的变化,Log4j 2不兼容Log4j1。
  2. Commons Logging和Slf4j是【日志门面】(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用)。

    Log4j和Logback则是具体的日志实现方案。可以简单的理解为接口与接口的实现,调用者只需要关注接口而无需关注具体的实现,做到解耦。

  3. 比较常用的组合使用方式是Slf4j与Logback组合使用,Commons Logging与Log4j组合使用。

未来使用Slf4j与Logback组合会越来越多

(1)Slf4j实现机制决定Slf4j限制较少,使用范围更广

(2)Logback拥有更好的性能。

(3)Logback文档免费。

1.3 log4j的使用

显示级别【重要】

共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF。

log4j 1.2.x版本的使用

依赖信息

<!--门面commons-logging-->
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency> 

<!--log4j-->
<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
 </dependency>
           

第一步:配置文件:【log4j.properties】

### set log levels - for more verbose logging change 'info' to 'debug' , 'off' ###
###info打印部分信息,trace打印所有信息,包含跟踪信息,off关闭不打印###
log4j.rootLogger=info, stdout,file

###1 direct log messages to stdout 打印到控制台###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

###2 direct messages to file mylog.log 打印到路径文件###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d:\\mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

###3每天生成一个文件【选用】
log4j.appender.daily=org.apache.log4j.DailyRollingFileAppender
log4j.appender.daily.File=d:\\logs\\soso.log
log4j.appender.daily.Append=true
log4j.appender.daily.Threshold=INFO
log4j.appender.daily.ImmediateFlush=true
#生成一个配置文件 命名  soso.8888-88-88-88 时间
log4j.appender.daily.DatePattern='_'yyyy-MM-dd'.txt'
log4j.appender.daily.layout=org.apache.log4j.PatternLayout
log4j.appender.daily.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n

###4控制每个滚动日志文件大小和个数,要在log4j.rootLogger后添加【R】
###log文件个数最多5个,每个log文件1kb限制【选用】
log4j.appender.R=org.apache.log4j.RollingFileAppender  
log4j.appender.R.Threshold=DEBUG  
log4j.appender.R.File=d:\\logs\\test.log  
log4j.appender.R.layout=org.apache.log4j.PatternLayout  
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.R.MaxFileSize=1kb
log4j.appender.R.MaxBackupIndex=5
           

log4jPatternLayout代码

参数 说明 例子
%c 列出logger名字空间的全称,如果加上{<层数>}表示列出从最内层算起的指定层数的名字空间 log4j配置文件参数举例 输出显示媒介
假设当前logger名字空间是“a.b.c”
%c a.b.c
%c{2} b.c
%20c (若名字空间长度小于20,则左边用空格填充)
%-20c (若名字空间长度小于20,则右边用空格填充)
%.30c (若名字空间长度超过30,截去多余字符)
%20.30c (若名字空间长度小于20,则左边用空格填充;若名字空间长度超过30,截去多余字符)
%-20.30c (若名字空间长度小于20,则右边用空格填充;若名字空间长度超过30,截去多余字符)
%C 列出调用logger的类的全名(包含包路径) 假设当前类是“org.apache.xyz.SomeClass”
%C org.apache.xyz.SomeClass
%C{2} xyz.SomeClass
%d 显示日志记录时间,{<日期格式>}使用ISO8601定义的日期格式 %d{yyyy/MM/dd HH:mm:ss,SSS} 2005/10/12 22:23:30,117
%d{ABSOLUTE} 22:23:30,117
%d{DATE} 12 Oct 2005 22:23:30,117
%d{ISO8601} 2005-10-12 22:23:30,117
%F 显示调用logger的源文件名 %F MyClass.java
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 %l MyClass.main(MyClass.java:129)
%L 显示调用logger的代码行 %L 129
%m 显示输出消息 %m This is a message for debug.
%M 显示调用logger的方法名 %M main
%n 当前平台下的换行符 %n Windows平台下表示rn UNIX平台下表示n
%p 显示该条日志的优先级 %p INFO
%r 显示从程序启动时到记录该条日志时已经经过的毫秒数 %r 1215
%t 输出产生该日志事件的线程名 %t MyClass
%x 按NDC(Nested Diagnostic Context,线程堆栈)顺序输出日志 假设某程序调用顺序是MyApp调用com.foo.Bar
%c %x - %m%n MyApp - Call com.foo.Bar. com.foo.Bar - Log in Bar MyApp - Return to MyApp.
%X 按MDC(Mapped Diagnostic Context,线程映射表)输出日志。通常用于多个客户端连接同一台服务器,方便服务器区分是那个客户端访问留下来的日志。 %X{5} (记录代号为5的客户端的日志)
%% 显示一个百分号 %% %

第二步 编写java类测试:

import org.apache.log4j.Logger;

public class Demo1 {
	public static void main(String[] args) {
		//不用日志框架
		System.out.println("xxxx");
	
         Logger logger = LogManager.getLogger(LogDemo2.class.getName());
      	//门面使用
      	 //Log log = LogFactory.getLog(Demo2.class.getName());
		//使用日志
		logger.trace("跟踪信息");
		logger.debug("调试信息");
		logger.info("信息");
		logger.warn("警告信息");
		logger.error("错误信息");
		logger.fatal("致命错误");
	}
}
           

1.4 log4j 2.x的使用

依赖信息

<!--门面commons-logging-->
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency> 
<!-- log4j -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.1</version>
</dependency>
<!--如果和commons-logging配合使用需要添加log4j-jcl依赖-->
<dependency>
	<groupId>org.apache.logging.log4j</groupId>
	<artifactId>log4j-jcl</artifactId>
	<version>2.11.1</version>
</dependency>

           

第一步:配置文件:log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="warn"><!--警告以上打印-->
            <AppenderRef ref="STDOUT"/>
        </Root>
    </Loggers>
</Configuration>
           

第二步 编写java类测试:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * @author wgy 2018/12/10 21:52
 * @version 1.0
 */
public class LogDemo2 {
    public static void main(String[] args){
        Logger logger = LogManager.getLogger(LogDemo2.class.getName());
       
        //门面使用
      	//Log log = LogFactory.getLog(Demo2.class.getName());
      
        logger.trace("entry");	//等同于logger.entry();但此方法在新版本好像已经废弃

        logger.error("Did it again!");

        logger.info("这是info级信息");

        logger.debug("这是debug级信息");

        logger.warn("这是warn级信息");

        logger.fatal("严重错误");

        logger.trace("exit");
    }
}

//打印结果
2019-09-18 19:10:13,378 WARN  [main] log.Demo1 (Demo1.java:31) - 警告信息
2019-09-18 19:10:13,381 ERROR [main] log.Demo1 (Demo1.java:32) - 错误信息
2019-09-18 19:10:13,381 FATAL [main] log.Demo1 (Demo1.java:33) - 致命错误
           

1.5 logback和slf4j的使用【重要】

logback是java的日志开源组件,是log4j创始人写的,性能比log4j要好,目前主要分为3个模块

  1. logback-core:核心代码模块
  2. logback-classic:log4j的一个改良版本,同时实现了

    slf4j

    的接口,这样你如果之后要切换其他日志组件也是一件很容易的事
  3. logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能

依赖如下

<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>

           

logback.xml

简单模式

<?xml version="1.0" encoding="UTF-8"?>
<configuration >
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="D:/" /><!-- 日志文件的家目录 -->
    <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" /><!-- 生成指定格式的时间戳,用于日志文件的唯一命名 -->
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <immediateFlush>true</immediateFlush><!-- 立即刷新输出流,设置为 false 可以提高日志吞吐量 -->
        <file>${LOG_HOME}${bySecond}.log</file><!-- 日志文件路径 -->
        <encoder><!-- 日志输出格式,必须指定,否则不会往文件输出内容 -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>
        </encoder>
        <append>true</append><!-- 追加 -->
        <prudent>false</prudent><!-- 关闭严格模式,也提高日志吞吐量(3倍),默认false -->
    </appender>

    <!-- 日志输出级别 -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT" /><!--打印到控制台-->
        <appender-ref ref="fileAppender" /><!--输出到文本文件-->
    </root>
</configuration>
           

高级模式

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">

    <!--<property name="log.base" value="${log_dir}/" />-->
    <property name="log.base" value="D:/" /><!--配置日志输出路径(测试配置)-->

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </encoder>
    </appender>

    <!-- 按照每天生成错误日志文件【选用】 -->
    <appender name="ERRORFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名 -->
            <FileNamePattern>${log.base}TEST_ERROR-%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数 -->
            <MaxHistory>5</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->  
            <level>ERROR</level>  
            <onMatch>ACCEPT</onMatch>  
            <onMismatch>DENY</onMismatch>  
        </filter>
    </appender>

    <!-- 按照每天生成警告日志文件 【选用】 -->
    <appender name="WARNFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名 -->
            <FileNamePattern>${log.base}TEST_WARN-%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数 -->
            <MaxHistory>5</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印警告日志 -->  
            <level>WARN</level>  
            <onMatch>ACCEPT</onMatch>  
            <onMismatch>DENY</onMismatch>  
        </filter>
    </appender>

    <!-- 按照每天生成INFO日志文件 【选用】 -->
    <appender name="INFOFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名 -->
            <FileNamePattern>${log.base}TEST_INFO-%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数 -->
            <MaxHistory>5</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印INFO日志 -->  
            <level>INFO</level>  
            <onMatch>ACCEPT</onMatch>  
            <onMismatch>DENY</onMismatch>  
        </filter>
    </appender>

    <!-- 按照每天生成DEBUG日志文件 【选用】 -->
    <appender name="DEBUGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名 -->
            <FileNamePattern>${log.base}TEST_DEBUG-%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数 -->
            <MaxHistory>5</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印DEBUG日志 -->  
            <level>DEBUG</level>  
            <onMatch>ACCEPT</onMatch>  
            <onMismatch>DENY</onMismatch>  
        </filter>
    </appender>

    <!-- 日志输出级别【必填】 -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="ERRORFILE" />
        <appender-ref ref="WARNFILE" />
        <appender-ref ref="INFOFILE" />
        <appender-ref ref="DEBUGFILE" />
    </root>

</configuration>
           

代码如下

Logger logger = LoggerFactory.getLogger(Demo3.class.getName());
logger.trace("跟踪信息_logback");
logger.debug("调试信息_logback");
logger.info("基本信息_logback");
logger.warn("警告信息_logback");
logger.error("错误信息_logback");

//打印结果
2019-09-18 19:21:33.067 [main] DEBUG com.rj.log.Demo1 - 调试信息
2019-09-18 19:21:33.069 [main] INFO  com.rj.log.Demo1 - 信息
2019-09-18 19:21:33.069 [main] WARN  com.rj.log.Demo1 - 警告信息
2019-09-18 19:21:33.069 [main] ERROR com.rj.log.Demo1 - 错误信息
           

继续阅读