【Java基础】日志系统JDK Logging,Commons Logging,Log4j/Log4j2,Slf4j和Logback基本使用

一.使用JDK Logging

为什么要使用日志

  • 在编写程序的过程中,发现程序运行结果与预期不符,可以用System.out.println()打印出执行过程中的某些变量,观察每一步的结果与代码逻辑是否符合,然后有针对性地修改代码。

  • 代码改好了怎么办?当然是删除没有用的System.out.println()语句了。

  • 如果改代码又改出问题怎么办?再加上System.out.println()

  • 反复这么搞几次,很快大家就发现使用System.out.println()非常麻烦。

怎么办?解决方法是使用日志。

那什么是日志?
日志就是Logging,它的目的是为了取代System.out.println()

输出日志的好处

输出日志,而不是用System.out.println(),有以下几个好处:

  • 可以设置输出样式,避免自己每次都写"ERROR: " + var;
  • 可以设置输出级别,禁止某些级别输出。例如,只输出错误日志;
  • 可以被重定向到文件,这样可以在程序运行结束后查看日志;
  • 可以按包名控制日志级别,只输出某些包打的日志;
  • 日志可以存档,便于追踪问题;
  • 可以根据配置文件调整日志,无需修改代码;

那如何使用日志?

Java标准库内置了日志包java.util.logging,我们可以直接用

import java.util.logging.Level;
import java.util.logging.Logger;
public class Hello {
    public static void main(String[] args) {
        Logger logger = Logger.getGlobal();
        logger.info("start process...");
        logger.warning("memory is running out...");
        logger.fine("ignored.");
        logger.severe("process will be terminated...");
    }
}

在这里插入图片描述

使用日志最大的好处是,它自动打印了时间、调用类、调用方法等很多有用的信息。

再仔细观察发现,4条日志,只打印了3条,logger.fine()没有打印。这是因为,日志的输出可以设定级别。JDK的Logging定义了7个日志级别,从严重到普通:

  • SEVERE
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST

因为默认级别是INFO,因此,INFO级别以下的日志,不会被打印出来。 使用日志级别的好处在于,调整级别,就可以屏蔽掉很多调试相关的日志输出。

Java标准库内置的Logging的局限

  • Logging系统在·JVM启动时读取配置文件·并完成初始化,一旦开始运行main()方法,就无法修改配置;

  • 配置不太方便,需要在JVM启动时传递参数 -Djava.util.logging.config.file=<config-file-name>

    • 因此,Java标准库内置的Logging使用并不是非常广泛。更方便的日志系统下面介绍。

二.使用Commons Logging

什么是Commons Logging

  • 和Java标准库提供的日志不同,Commons Logging是一个第三方日志库,它是由Apache创建的日志模块
  • Commons Logging的特色是,它可以挂接不同的日志系统,并通过配置文件指定挂接的日志系统
  • 默认情况下,Commons Loggin自动搜索并使用Log4j(Log4j是另一个流行的日志系统),如果没有找到Log4j,再使用JDK Logging

Commons Logging 具体用法

common-logging组件:

  • Commons Logging (JCL)提供的是一个日志(Log)接口(interface),同时兼顾轻量级和不依赖于具体的日志实现工具。它提供给中间件/日志工具开发者一个简单的日志操作抽象,允许程序开发人员使用不同的具体日志实现工具。

  • 了解包里情况,可以查看它的API文档:http://www.oschina.net/uploads/doc/commons-logging-1.1.1/index.html, 其中Log(基本记录器)LogFactory(负责创建Log实例)是两个基类。

  • 该API直接提供对下列底层日志记录工具的支持:Jdk14Logger,Log4JLogger,LogKitLogger,NoOpLogger (直接丢弃所有日志信息),还有一个SimpleLog。 有必要详细说明一下调用LogFactory.getLog()时发生的事情。

  • 调用该函数会启动一个发现过程,即找出必需的底层日志记录功能的实现,具体的发现过程在下面列出: ( 换句话说就是, ·有这么多工具,common-logging该使用哪一个呢?· 这取决于系统的设置,common-logging将按以下顺序决定使用哪个日志记录工具:)

  1. 首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则
    使用其中定义的Log实现类;

  2. 如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量
    org.apache.commons.logging.Log,找到则使用其定义的Log实现类;

  3. 否则,查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;

  4. 否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类);

  5. 否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog;
    所以,在前面程序的基础上,只要导入log4j的包,则commons-logging会自动使用 log4j作为输出类,程序不需要任何改动,

    • 但是注意:log4j的配置文件 log4j.properties对Log4j来说是必须的。如果classpath中没有该配置文件,或者配置 不对,将会引发运行时异常。所以下面介绍log4j.properties的配置。
  • 根据不同的性质,日志信息通常被分成不同的级别,从低到高依次是:
    • “调试( DEBUG )” ,“信息( INFO )” ,“警告( WARN )”,“错误(ERROR )” ,“致命错误( FATAL )”
  1. 导入依赖
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 配置common-logging.properties文件
    只需要一行即可,放在classpath下,如果是Maven中就在src/resources
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
  1. 配置log4j.properties文件
    放在classpath下,如果是Maven中就在src/resources下
### set log levels ###
log4j.rootLogger = debug , stdout , D , E

### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
## 输出INFO级别以上的日志
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n

### 输出到日志文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E:/logs/log.log
log4j.appender.D.Append = true
## 输出DEBUG级别以上的日志
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} [ %t:%r ] - [ %p ] %m%n

### 保存异常信息到单独文件 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
## 异常日志文件名
log4j.appender.E.File = E:/logs/error.log
log4j.appender.E.Append = true
## 只输出ERROR级别以上的日志!!!
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} [ %t:%r ] - [ %p ] %m%n

测试

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Created by kun on 2016/5/13.
 */
public class LogTest {
    private static Log logger = LogFactory.getLog(LogTest.class);

    public static void main(String[] args) {
        logger.trace("我是trace信息");
        logger.debug("我是debug信息");
        logger.info("我是info信息");
        logger.warn("我是warn信息");
        logger.error("我是error信息");
        logger.fatal("我是fatal信息");
    }
}
  1. 基于common-logging的运行方式:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class TestCommLogger {
    public static Log log = LogFactory.getLog(TestCommLogger.class);

    public static void main(String[] args) {
        log.debug("111");
        log.info("125");
        log.warn("485");
        log.error("error");
    }
}   

在这里插入图片描述

  1. 基于log4j的运行方式
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class TestLog4j {

    static Logger logger = Logger.getLogger(TestLog4j.class);

    public static void main(String args[]) {
        PropertyConfigurator.configure("log4j.properties");
        logger.debug("Here is some DEBUG");
        logger.info("Here is some INFO");
        logger.warn("Here is some WARN");
        logger.error("Here is some ERROR");
        logger.fatal("Here is some FATAL");
    }
}  

在这里插入图片描述

三.使用Log4j

什么是log4j

  • Commons Logging,可以作为“日志接口”来使用。而真正的“日志实现”可以使用Log4j

  • Log4j的1.x版本虽然已经被广泛使用于很多应用程序中,但由于出现内存泄漏等bug,代码难以维护,以及需要使用老版本的jdk等劣势,在2015年8月已经玩完。目前log4j的升级版本为重构后的log4j2

  • Log4j是一个组件化设计的日志系统,它的架构大致如下:
    在这里插入图片描述
    当我们使用Log4j输出一条日志时,Log4j自动通过不同的Appender把同一条日志输出到不同的目的地。例如:

    • console:输出到屏幕;
    • file:输出到文件;
    • socket:通过网络输出到远程计算机;
    • jdbc:输出到数据库

在输出日志的过程中,通过Filter来过滤哪些log需要被输出哪些log不需要被输出

  • 例如,仅输出ERROR级别的日志。

最后,通过Layout来格式化日志信息

  • 例如,自动添加日期、时间、方法名称等信息。

上述结构虽然复杂,但我们在实际使用的时候,并不需要关心Log4j的API,而是通过配置文件来配置它

Log4j的架构

Log4j系统的三大板块:日志写入器日志输出终端日志布局模式

Logger类

Logger类是日志包的核心,Logger的名称是大小写敏感的,并且名称之间有继承关系。子名由父名做前缀,用点号“.”分隔,如x.y是x.y.z的父亲Logger。

  • Logger系统中有个根logger,是所有logger的祖先,它总是存在的,并且不可以通过名字获取,可以通过Logger.getRootLogger()来获取。
  • 获取Logger对象的方法很多,可以参考API文档,在某对象中,用该对象所属的类作为参数,调用Logger.getLogger(Class clazz)以获取logger对象被认为是目前所知最理智的命名logger方法。

Log4j的日志级别(Level)

每个logger都有一个日志级别,用来控制日志的输出。未分配级别的logger将自动继承它最近的父logger的日志级别。Logger的由低到高级别如下:
ALL<DEBUG<INFO<WARN<ERROR<FATAL<OFF

Log4j的输出终端(Appender接口)

Log4j提供了以下几个实现

org.apache.log4j.ConsoleAppender(控制台)     
org.apache.log4j.FileAppender(文件)     
org.apache.log4j.DailyRollingFileAppender(每天都产生一个日志文件)     
org.apache.log4j.RollingFileAppender(文件大小达到指定尺寸时产生一个新的日志文件,文件名称上会自动添加数字序号。)     
org.apache.log4j.WriterAppender(将日志信息以流的格式发送到任意指定的地方)    

Log4j的输出布局模式(Layout接口)

Log4j提供Layout有以下几种

org.apache.log4j.HTMLLayout(以HTML表格形式布局)     
org.apache.log4j.PatternLayout(可以灵活地指定布局模式)     
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)     
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)    

Log4j采用类似C语言中的printf函数的打印格式格式化日志信息。打印参数如下:

%r:输入自应用启动到输出该log信息耗费的毫秒数。     
%c:输出所属的类目,通常就是所在类的全名。     
%n:输出一个回车换行符。Windows平台为“\r\n”,UNIX为“\n”。        
%l:输出日志事件发生的位置,包括类名、线程名,以及所在代码的行数。    
%c:输出所属的类目,通常就是所在类的全名。  
%t:输出产生该日志线程的线程名。  
%d:输出日志时间点的日期或时间,默认格式为ISO8601,推荐使用“%d{ABSOLUTE}”,这个输出格式形如:“2007-05-07 18:23:23,500”,符合中国人习惯。   
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy-MM-dd HH:mm:ss },输出类似:2002-10-18- 221028  
%f 输出日志信息所属的类的类名   
%m 输出代码中指定的信息,如log(message)中的message  
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL。如果是调用debug()输出的,则为DEBUG,依此类推   

Log4j的properties配置

  • 在实际使用中,Log4j一般是通过配置文件配置使用的。配置文件有两种,Java properties和XML文件一般都选用properties文件来配置,因为简洁易读。下面只介绍Java properties的配置方式。
  • 对Log4j的配置就是对rootLogger子Logger的配置。主要的配置项为:rootLogger输出终端输出布局模式,所有的配置项都必须以log4j开头。
##Log4J的配置之简单使它遍及于越来越多的应用中了     
    
##Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了。     
    
##此文件(log4j.properties)内容来自网络,非本文作者liigo原创。     
log4j.rootLogger = DEBUG, CONSOLE,A1     
log4j.addivity.org.apache = true    
    
# 应用于控制台     
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender     
log4j.appender.Threshold = DEBUG     
log4j.appender.CONSOLE.Target = System.out     
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout     
log4j.appender.CONSOLE.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n     
#log4j.appender.CONSOLE.layout.ConversionPattern = [start] % d {DATE} [DATE] % n % p[PRIORITY] % n % x[NDC] % n % t[THREAD] n % c[CATEGORY] % n % m[MESSAGE] % n % n     
    
#应用于文件     
log4j.appender.FILE = org.apache.log4j.FileAppender     
log4j.appender.FILE.File = file.log     
log4j.appender.FILE.Append = false    
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout     
log4j.appender.FILE.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n     
# Use this layout for LogFactor 5 analysis     
    
# 应用于文件回滚     
log4j.appender.ROLLING_FILE = org.apache.log4j.RollingFileAppender     
log4j.appender.ROLLING_FILE.Threshold = ERROR     
log4j.appender.ROLLING_FILE.File = rolling.log     
log4j.appender.ROLLING_FILE.Append = true    
log4j.appender.ROLLING_FILE.MaxFileSize = 10KB     
log4j.appender.ROLLING_FILE.MaxBackupIndex = 1    
log4j.appender.ROLLING_FILE.layout = org.apache.log4j.PatternLayout     
log4j.appender.ROLLING_FILE.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n     
    
#应用于socket     
log4j.appender.SOCKET = org.apache.log4j.RollingFileAppender     
log4j.appender.SOCKET.RemoteHost = localhost     
log4j.appender.SOCKET.Port = 5001    
log4j.appender.SOCKET.LocationInfo = true    
# Set up for Log Facter 5    
log4j.appender.SOCKET.layout = org.apache.log4j.PatternLayout     
log4j.appender.SOCET.layout.ConversionPattern = [start] % d {DATE} [DATE] % n % p[PRIORITY] % n % x[NDC] % n % t[THREAD] % n % c[CATEGORY] % n % m[MESSAGE] % n % n     
    
# Log Factor 5 Appender     
log4j.appender.LF5_APPENDER = org.apache.log4j.lf5.LF5Appender     
log4j.appender.LF5_APPENDER.MaxNumberOfRecords = 2000    
    
# 发送日志给邮件     
log4j.appender.MAIL = org.apache.log4j.net.SMTPAppender     
log4j.appender.MAIL.Threshold = FATA     
log4j.appender.MAIL.BufferSize = 10    
log4j.appender.MAIL.From = web@www.wuset.com     
log4j.appender.MAIL.SMTPHost = www.wusetu.com     
log4j.appender.MAIL.Subject = Log4J Message     
log4j.appender.MAIL.To = web@www.wusetu.com     
log4j.appender.MAIL.layout = org.apache.log4j.PatternLayout     
log4j.appender.MAIL.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n     
    
    
# 用于数据库     
log4j.appender.DATABASE = org.apache.log4j.jdbc.JDBCAppender     
log4j.appender.DATABASE.URL = jdbc:mysql: // localhost:3306/test     
log4j.appender.DATABASE.driver = com.mysql.jdbc.Driver     
log4j.appender.DATABASE.user = root     
log4j.appender.DATABASE.password =     
log4j.appender.DATABASE.sql = INSERT INTO LOG4J (Message) VALUES ( ' [framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n ' )     
log4j.appender.DATABASE.layout = org.apache.log4j.PatternLayout     
log4j.appender.DATABASE.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n     
log4j.appender.A1 = org.apache.log4j.DailyRollingFileAppender     
log4j.appender.A1.File = SampleMessages.log4j     
log4j.appender.A1.DatePattern = yyyyMMdd - HH ' .log4j '    
log4j.appender.A1.layout = org.apache.log4j.xml.XMLLayout     
    
#自定义Appender     
log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender     
log4j.appender.im.host = mail.cybercorlin.net     
log4j.appender.im.username = username     
log4j.appender.im.password = password     
log4j.appender.im.recipient = corlin@cybercorlin.net     
log4j.appender.im.layout = org.apache.log4j.PatternLayout     
log4j.appender.im.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n     
    
# 结束    

log4j使用

  1. 引入依赖
<dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.2.17</version>
</dependency>
  1. 在classPath下面log4j.properties编写配置文件
### set log levels ###
log4j.rootLogger = debug , stdout , D , E

### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
## 输出INFO级别以上的日志
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n

### 输出到日志文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E:/logs/log.log
log4j.appender.D.Append = true
## 输出DEBUG级别以上的日志
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} [ %t:%r ] - [ %p ] %m%n

### 保存异常信息到单独文件 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
## 异常日志文件名
log4j.appender.E.File = E:/logs/error.log
log4j.appender.E.Append = true
## 只输出ERROR级别以上的日志!!!
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} [ %t:%r ] - [ %p ] %m%n
  1. web.xml中进行配置
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
</context-param>
<context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>60000</param-value>
</context-param>
  1. 测试
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class TestLog4j {
    static Logger logger = Logger.getLogger(TestLog4j.class);
    public static void main(String args[]) {
        PropertyConfigurator.configure("log4j.properties");
        logger.debug("Here is some DEBUG");
        logger.info("Here is some INFO");
        logger.warn("Here is some WARN");
        logger.error("Here is some ERROR");
        logger.fatal("Here is some FATAL");
    }
}  

在这里插入图片描述
在这里插入图片描述

什么是log4j2

Log4j和Log4j2的区别

  • log4j2是log4j 1.x 的升级版,2015年5月,Apache宣布log4j1.x 停止更新。最新版为1.2.17

  • log4j是Apache的一个开源项目,log4j2和log4j是一个作者,只不过log4j2是重新架构的一款日志组件,他抛弃了之前log4j的不足,以及吸取了优秀的logback的设计重新推出的一款新组件log4j2的社区活跃很频繁而且更新的也很快。

  • Log4j2虽然在各个方面都与logback非常相似,但是却提供了更强的性能和并发性,下一代异步logger,易于拓展自定义需求的架构,是目前使用十分广泛的日志框架

  1. 配置文件类型

log4j是通过一个.properties的文件作为主配置文件的,而现在的log4j 2则已经弃用了这种方式,采用的是.xml,.json或者.jsn这种方式来做,可能这也是技术发展的一个必然性,毕竟properties文件的可阅读性真的是有点差。

  1. 核心JAR包

log4j只需要引入一个jar包即可,

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

而log4j 2则是需要2个核心

<dependency>
 <groupId>org.apache.logging.log4j</groupId>
 <artifactId>log4j-core</artifactId>
 <version>2.5</version>
</dependency>
<dependency>
 <groupId>org.apache.logging.log4j</groupId>
 <artifactId>log4j-api</artifactId>
 <version>2.5</version>
</dependency>

log4j和log4j 2的包路径是不同的,Apache为了区分,包路径都更新了

  1. 文件渲染

log4j想要生效,我们需要在web.xml中进行配置,

<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:config/log4j.properties</param-value>
</context-param>
<context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>60000</param-value>
</context-param>
  • 这段配置目的在于告诉工程去哪加载log4j的配置文件和定义一个扫描器,这样可以随心所欲的放置log4j配置文件。
  • log4j2就比较简单,以maven工程为例,我们只需要把log4j2.xml放到工程resource目录下就行了。大家记住一个细节点,是log4j2.xml,而不是log4j.xml,xml名字少个2都不行!!
  1. Log调用

log4j

import org.apache.log4j.Logger;
private final Logger LOGGER = Logger.getLogger(Test.class.getName());

log4j2:

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private static Logger logger = LogManager.getLogger(Test.class.getName());
  1. 配置文件方式
<?xml version="1.0" encoding="UTF-8"?> 
<configuration status="error"> 
<!--  先定义所有的appender --> 
 <appenders> 
<!--   这个输出控制台的配置 --> 
  <Console name="Console" target="SYSTEM_OUT"> 
<!--    控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> 
   <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/> 
<!--    这个都知道是输出日志的格式 --> 
   <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> 
  </Console> 
 
<!--   文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 --> 
<!--   append为TRUE表示消息增加到指定文件中,false表示消息覆盖指定的文件内容,默认值是true --> 
  <File name="log" fileName="log/test.log" append="false"> 
   <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> 
  </File> 
 
<!--   添加过滤器ThresholdFilter,可以有选择的输出某个级别以上的类别 onMatch="ACCEPT" onMismatch="DENY"意思是匹配就接受,否则直接拒绝 --> 
  <File name="ERROR" fileName="logs/error.log"> 
   <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> 
   <PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/> 
  </File> 
 
<!--   这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --> 
  <RollingFile name="RollingFile" fileName="logs/web.log"
      filePattern="logs/$${date:yyyy-MM}/web-%d{MM-dd-yyyy}-%i.log.gz"> 
   <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/> 
   <SizeBasedTriggeringPolicy size="2MB"/> 
  </RollingFile> 
 </appenders> 
 
<!--  然后定义logger,只有定义了logger并引入的appender,appender才会生效 --> 
 <loggers> 
<!--   建立一个默认的root的logger --> 
  <root level="trace"> 
   <appender-ref ref="RollingFile"/> 
   <appender-ref ref="Console"/> 
   <appender-ref ref="ERROR" /> 
   <appender-ref ref="log"/> 
  </root> 
 
 </loggers> 
</configuration>

log4j2的输出终端(Appender接口)

Appender负责将LogEvents传递到目的地。 每个Appender都必须实现Appender接口。 大多数Appender继承自AbstractAppender,它增加了Lifecycle和Filterable支持。 生命周期允许组件在配置完成后完成初始化并在关闭期间执行清理。 Filterable接口允许组件附加过滤器,在事件处理期间对其进行筛选。Appender通常只负责将事件数据写入目标目标。 在大多数情况下,他们将格式化事件的责任委托给布局。 一些appender包装其他appender,以便他们可以修改LogEvent,处理Appender中的故障,根据高级Filter条件将事件路由到从属Appender

1)AsyncAppender:引用其他Appender,被引用的Appender可以做到异步输出日志。
(2)CassandraAppender:可以将消息写入Cassandra数据库。
(3)ConsoleAppender:日志写入到标准输出,如System.out或System.error
(4)FailoverAppender:引用一组Appender,如果主的Appender失败则备用Appender开始起作用,直到主Appender恢复正常。
(5)FileAppender:将日志写入文件,比较常用。
(6)FlumeAppender:将日志以event的形式写入flume。

有三种模式:
a.远程客户端模式:模拟flume远程客户端,以avro_event的方式向agent发送消息。
b.内置flume agent模式:直接将event写入flume channel。
c.persist模式:将event写入本地BerkeleyDB,然后通过异步的方式将event发送到flume。

(7)JDBCAppender:使用JDBC连接将数据写入传统数据库。
(8)JMS Appender:将格式化后的日志写入JMS Destination。
(9)HttpAppender:发送日志到一个Http服务,必须使用Layout来格式化日志。
(10)KafkaAppender:将数据发送到kafka的topic,log4j的event对应kafka的record。
(11)MemoryMappedFileAppender:是一种特殊的日志写入方式,将日志写入内存以减少读写磁盘带来的IO开销,提升性能。
(12)NoSQLAppender:可以将数据写入nosql数据库,目前支持MongoDB和CouchDb。
(13)RandomAccessFileAppender:和FileAppender类似,但是使用了ByteBuffer+RandomAccessFile的方式来代替BufferedOutputStream
(14)RewriteAppender:允许LogEvent在其他appender处理之前先由RewriteAppender处理。
(15)RollingFileAppender:配置文件滚动生成策略,按照策略生成新的日志文件。
(16)RollingRandomAccessFileAppender:和RollingFileAppender类似,使用了ByteBuffer+RandomAccessFile的方式代替BufferedOutputStream。
(17)RoutingAppender:路由appender,可以分发Logevent到多个子Appender。
(18)SMTPAppender:将日志以邮件的形式发送,用在错误监控或者报警上。
(19)SocketAppender:将logevent发送到远程机器上,可以使用TCP或者UDP协议。

Log4j2的输出布局模式(Layout接口)

layout是指输出Logevent的布局,常见的比如输出日志的级别、时间、类名、线程等信息。Log4j2支持的Layout有如下几种:

1)CSV Layouts。日志输出为csv文件,如log.info(a,b,c)会输出到csv文件对应的三列。
(2)HTML Layout。将日志输出为html页面,每个Logevent对应table里面的一行。
(3)JSON Layout。将日志输出为json格式。
(4)Pattern Layout。较为常用,通过使用一些匹配规则来确定日志输出格式。
(5)RFC5424 Layout。消息型的Appender经常用这种layout。
(6)Serialized Layout。使用java自身的序列化工具将Logevent序列化成byte array,但因为java固有的安全性问题,这种方式不再被推荐。
(7)Syslog Layout。将日志格式化为BSD syslog格式。
(8)XML Layout。格式化为xml。

Log4j2配置文件

如果没有配置文件,log4j会使用自身的最小化配置,但是只能输出error级别的日志,并且在运行过程中会提示没有找到配置文件。log4j2支持xml,json,yaml,properties这几种格式的配置文件。配置文件应该放到项目的classpath下面,一般都放在resources目录下。

读取配置文件的优先级由高到低依次为:

log4j2.properties>log4j2.yaml>log4j2.json>log4j2.xml>defaultConfiguration。

Log4j2使用

  1. 引入pom依赖
   <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.11.0</version>
       </dependency>
  1. 配置log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>
  1. 测试代码
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class MyApp {

    private static final Logger logger = LogManager.getLogger(MyApp.class);
    public static void main(final String... args) {
        logger.info("log4j test.");
        logger.error("this is error message .");
    }
}

在这里插入图片描述
Log4j2最佳实践
Log4j2使用教程

四.使用SLF4J和Logback

SLF4J和Logback简介

  • Commons Logging和Log4j这一对好基友,它们一个负责充当日志API,一个负责实现日志底层,搭配使用非常便于开发。

  • SLF4J类似于Commons Logging,也是一个日志接口,而Logback类似于Log4,是一个日志的实现

  • slf4j是接口抽象层,logback是slf4j的具体实现,在程序中使用slf4j的api进行日志的记录。使用logback来配置日志的输出规则

    • SLF4J是众多日志系统的内核,提供统一的接口,不提供具体实现,不是具体可使用可配置的日志系统。
    • Logback提供了对SLF4J具体实现的日志系统,相比Log4j1.x,Logback的性能,使用场景,内存使用等方面的优化要远远强于Log4j1.x。

为什么有了Commons Logging和Log4j,又会蹦出来SLF4J和Logback?

  • Java有着非常悠久的开源历史,不但OpenJDK本身是开源的,而且我们用到的第三方库,几乎全部都是开源的。开源生态丰富的一个特定就是,同一个功能,可以找到若干种互相竞争的开源库。
  • 因为对Commons Logging的接口不满意,有人就搞了SLF4J。因为对Log4j的性能不满意,有人就搞了Logback。

SLF4J对Commons Logging的接口有何改进。在Commons Logging中,我们要打印日志,有时候得这么写

int score = 99;
p.setScore(score);
log.info("Set score " + score + " for Person " + p.getName() + " ok.");

拼字符串是一个非常麻烦的事情,所以SLF4J的日志接口改进成这样了:

int score = 99;
p.setScore(score);
logger.info("Set score {} for Person {} ok.", score, p.getName());

SLF4J的日志接口传入的是一个带占位符的字符串,用后面的变量自动替换占位符,所以看起来更加自然。

如何使用SLF4J?
它的接口实际上和Commons Logging几乎一模一样

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class Main {
    final Logger logger = LoggerFactory.getLogger(getClass());
}

对比一下Commons Logging和SLF4J的接口:

Commons Logging SLF4J
org.apache.commons.logging.Log org.slf4j.Logger
org.apache.commons.logging.LogFactory org.slf4j.LoggerFactory

不同之处就是Log变成了LoggerLogFactory变成了LoggerFactory

slf4j和logback的使用

使用SLF4J和Logback和前面讲到的使用Commons Logging加Log4j是类似的

  1. 增加对slf4j和logback的依赖,此处如果是引用了spring boot则不需要再引一下的jar包了,spring boot中已集成了这些包
       <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <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>

  1. logback.xml放到classpath的下,配置如下
    1. logback 会在类路径classpath下寻找名为logback-test.xml的文件。
    2. 如果没有找到,logback 会继续寻找名为 logback.groovy 的文件。
    3. 如果没有找到,logback 会继续寻找名为 logback.xml 的文件。
    4. 如果没有找到,将会通过 JDK 提供的 ServiceLoader 工具在类路径下寻找文件META-INFO/services/ch.qos.logback.classic.spi.Configurator,该文件的内容为实现了 Configurator 接口的实现类的全限定类名。
    5. 如果以上都没有成功,logback 会通过 BasicConfigurator 为自己进行配置,并且日志将会全部在控制台打印出来。
<?xml version="1.0" encoding="utf-8" ?>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则  根据当前ROOT 级别,日志输出时,级别高于root默认的级别时  会输出 -->
<!-- 以下  每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->

<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,
默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志文件 输入位置 -->
    <property name="logPath" value="D:/test_log" />
    <!-- 日志最大的历史 30-->
    <property name="maxHistory" value="30"/>

    <!-- 配置项, 通过此节点配置日志输出位置(控制台、文件、数据库)、输出格式等-->
    <!-- ConsoleAppender代表输出到控制台 -->
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <!-- layout代表输出格式 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
        </layout>
    </appender>
    <!-- 日志输出文件 -->
    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
        </encoder>
        <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender-->
        <!-- 滚动策略,它根据时间来制定滚动策略.既负责滚动也负责触发滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 输出路径 -->
            <fileNamePattern>${logPath}/info/%d.log</fileNamePattern>
            <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>6,
            则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除-->
            <maxHistory>${maxHistory}</maxHistory>
        </rollingPolicy>
        <!-- 按照固定窗口模式生成日志文件,当文件大于20MB时,生成新的日志文件。窗口大小是13,当保存了3个归档文件后,将覆盖最早的日志。
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
          <fileNamePattern>${logPath}/%d{yyyy-MM-dd}/.log.zip</fileNamePattern>
          <minIndex>1</minIndex>
          <maxIndex>3</maxIndex>
        </rollingPolicy>   -->
        <!-- 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>5MB</maxFileSize>
        </triggeringPolicy>   -->
    </appender>
    <!-- 特殊记录Error日志 -->
    <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 只记录ERROR级别日志,添加范围过滤,可以将该类型的日志特殊记录到某个位置 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logPath}/error/%d.log</fileNamePattern>
            <!-- 日志最大的历史 60-->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- 根节点,表名基本的日志级别,里面可以由多个appender规则 -->
    <!-- level="info"代表基础日志级别为info -->
    <root level="info">
        <!-- 引入控制台输出规则 -->
        <appender-ref ref="consoleLog" />
        <appender-ref ref="fileInfoLog" />
        <appender-ref ref="fileErrorLog" />
    </root>
</configuration>
  1. 测试
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestLog {
    static Logger logger = LoggerFactory.getLogger(TestLog.class);
    public static void main(String[] arge){
        logger.debug("taiyonghai debug");
        logger.info("taiyonghai info");
        logger.error("taiyonghai error");
        logger.warn("taiyonghai warn");
    }
}

在这里插入图片描述

在这里插入图片描述

发现没有了debug日志的输出,那是因为root节点中我们记录的日志级别是info,他就只会记录等于或高于info级别的日志,其他的都忽略避免生产环境产生过多日志


SpringBoot(四)之优雅地日志处理

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/104376958