[J2EE:中间件]LOG4J+Slf4J快速入门 1 简述 2 LOG4J 3 Slf4J 4 问题集 5 实际项目开发中的日志应用 6 参考文献

1.1 简述

[J2EE:中间件]LOG4J+Slf4J快速入门
1 简述
2 LOG4J
3 Slf4J
4 问题集
5 实际项目开发中的日志应用
6 参考文献

commons-logging和slf4j(slf4j-api.jar)都是日志类库的接口,供客户端使用,而没有提供实现!
log4j,logback等等才是日志的真正实现。

  • 日志(类库)
    • 作用:可以调试程序,就像输出一样
  • Java语言的日志类库:
    • Log4J [Log For Java]
      • log4j 是Apache为java提供日志管理的工具
      • 真正实现日志功能的日志类库
    • Logback
    • java.util.logging
    • SLF4J [Simple Logging Facade(外观/表面) For Java]
      • 1个 日志标准/适配器
        • 通过调用slf4j的API统一打印日志,而可忽略其他日志的具体方法
  • slf4j只是1个日志标准,并不是日志系统的具体实现。如果项目只有slf4j的包,是没有办法实现日志功能的。
  • 而如果开发者已使用log4j,那么:开发者已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。

1.2 安装/依赖[slf4j+log4j]

方式1:手动导入JAR包(slf4j-api-1.7.5.jar、slf4j-log4j-1.7.5.jar、log4j-1.2.16.jar)或配置如下Maven依赖:

  • slf4j-api:提供接口
  • slf4j-log4j[xx]:提供Log具体的实现
[J2EE:中间件]LOG4J+Slf4J快速入门
1 简述
2 LOG4J
3 Slf4J
4 问题集
5 实际项目开发中的日志应用
6 参考文献
<!-- Log4j 依赖包 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.9.1</version>
</dependency>
<!-- slf4j 依赖包 -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.2</version>
</dependency>

1.3 配置文件

log4j.properties
log4j.xml
log4j2.xml(log4j/org.apache.logging.log4j:log4j在2.0.0及以后,被拆分为 log4j-api与log4j-core,对应的配置文件为 log4j2.xml)

2 LOG4J

2.0 Log4J的配置

如果没有调用BasicConfigurator.configure(),PropertyConfigurator.configure()或DOMConfigurator.configure()方法,Log4j会自动加载CLASSPATH下名为log4j.properties的配置文件。如果把此配置文件改为其他名字,例如my.properties,程序虽然仍能运行,但会报出不能正确初始化Log4j系统的提示。这时可以在程序中加上:PropertyConfigurator.configure("classes/my.properties");

配置Log4j环境就是指配置root Logger,包括把Logger为哪个级别,为它增加哪些Appender,以及为这些Appender设置Layout,等等。因为所有其他的Logger都是root Logger的后代,所以它们都继承了root Logger的性质。这些可以通过设置系统属性的方法来隐式地完成,也可以在程序中调用XXXConfigurator.configure()方法来显式地完成。有以下几种方式来配置Log4j。

  • A:配置放在文件里,通过环境变量传递文件名等信息,利用Log4j默认的初始化过程解析并配置。
  • B:配置放在文件里,通过应用服务器配置传递文件甸等信息,利用一个特定的Servlet来完成配置。
  • C:配置放在文件里,通过命令行org.apache.log4j.PropertyConfigurator.configure(args[])解析log4j.properties文件并配置Log4j
  • D:配置放在文件里,在程序中直接调用org.apache.log4j.BasicConfigor.configure()方法,或在程序中完成相关配置(↓)
    • step1:用默认的方式创建PatternLayout对象p:
      • PatternLayout p = new PatternLayout("%-4r[%t]%-5p%c%x-%m%n");
    • step2: 用p创建ConsoleAppender对象a,目标是System.out,标准输出设备:
      • ConsoleAppender a = new CpnsoleAppender(p,ConsoleAppender.SYSTEM_OUT);
    • step3: 为root Logger增加一个ConsoleAppender p:
      • rootLogger.addAppender(p);
    • step4: 把rootLogger的log level设置为DUBUG级别
      • rootLogger.setLevel(Level.DEBUG);
  • E:org.apache.log4j.DOMConfigurator.configure(String filename);

2.1 核心概念/3大组件

Log4j有3大组件:

  • 【日志器(Logger)】:用来输出消息的类,可以输出不同级别的,比如错误消息,警告消息等
    • 创建日志器
      • Logger log = Logger.getLogger(calssNameX.class); log.info(); log.debug(); [log4J方式]
      • Logger logger = LoggerFactory.getLogger(calssNameX.class); log.info(); log.debug();[slf方式(推荐)]
    • 根日志器(rootLogger)
      • 若需要输出到多个位置的时候可用逗号隔开: log4j.rootLogger=info, A, B
在配置文件里,需要为log4j.properties配置一个根日志器:
log4j.rootLogger=DEBUG,AA
log4j.rootLogger=WARN
log4j.APPENDER.AA=org.apache.log4j.ConsoleAppender
  • 【输出源(Appender)】:日志输出的目标。日志输出到哪里去(文件、控制台)
    • org.apache.log4j.ConsoleAppender: 向控制台输出日志
    • org.apache.log4j.FileAppender: 向文件输出日志
    • org.apache.log4j.DailyRollingFileAppender
    • org.apache.log4j.RollingFileAppender
log4j.appender.AA.File=../log.txt
log4j.appender.AA.LAYOUT=org.apache.log4j.SimpleLayout
log4j.appender.AA.DatePatten='yyyy-MM-dd'
  • 【格式化器(Layout)】:对输出消息进行格式化,比如添加日期
    • org.apache.log4j.PatternLayout
    • org.apache.log4j.SimpleLayout
    • org.apache.log4j.HTMLLayout
    • org.apache.log4j.TTCCLayout

2.2 日志级别(从高到低)

  • OFF:最高等级,用于关闭所有日志记录
  • FATAL:重大错误级(系统有问题)
  • ERROR:错误级(一个模块有问题)
  • WARN:警告级
  • INFO:信息级。可以查看程序执行的流程
  • DEBUG:调试。用来调试程序的bug及显示
  • ALL:最低等级。用于打开所有日志记录

debug,info,error等日志级别的方法都可传入多个可变参数:(以debug为例)

[J2EE:中间件]LOG4J+Slf4J快速入门
1 简述
2 LOG4J
3 Slf4J
4 问题集
5 实际项目开发中的日志应用
6 参考文献

2.3.1 日志器[Logger]

  • 特点
    • Logger(log4j)记录的是当前类的日志,不是每个实例的日志
      • 使用static修饰的属性
      • 只要有一个记录就可以了
    • private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class.getName());// slf4j日志记录器
    • 每个Logger都配有1个日志级别(Log Level):用来控制日志信息的输出。
  • rootLogger(根日志器)
    • root Logger(根Logger)是所有Logger的祖先
    • 它总是存在的
    • 它不可以通过名字获得
      • public static Logger Logger.getRootLogger();
      • public static Logger Logger.getLogger(Class clazz)

2.3.2 输出源[Appender]

org.apache.log4j.ConsoleAppender

Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认值是true
Target=System.err:默认值是System.out

org.apache.log4j.FileAppender

Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认true
Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认true
File=D:/logs/logging.log4j:指定消息输出到logging.log4j文件

org.apache.log4j.DailyRollingFileAppender(常用)

Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认true
Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认true
File=D:/logs/logging.log4j:指定当前消息输出到logging.log4j文件
DatePattern='.'yyyy-MM:每月滚动一次日志文件,即每月产生一个新的日志文件。
                        当前月的日志文件名为logging.log4j,前一个月的日志文件名为logging.log4j.yyyy-MM
另外,也可以指定按周、天、时、分等来滚动日志文件,对应的格式如下:
1)'.'yyyy-MM:每月
2)'.'yyyy-ww:每周
3)'.'yyyy-MM-dd:每天
4)'.'yyyy-MM-dd-a:每天两次
5)'.'yyyy-MM-dd-HH:每小时
6)'.'yyyy-MM-dd-HH-mm:每分钟

org.apache.log4j.RollingFileAppender

Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认true
Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认true
File=D:/logs/logging.log4j:指定消息输出到logging.log4j文件
MaxFileSize=100KB:后缀可以是KB,MB或者GB。在日志文件到达该大小时,将会自动滚动,即将原来的内容移到logging.log4j.1文件
MaxBackupIndex=2:指定可以产生的滚动文件的最大数,例如,设为2则可以产生logging.log4j.1,logging.log4j.2两个滚动文件和一个logging.log4j文件

2.3.3 日志格式化器[Layout]

指定logger输出内容及格式: log4j.appender.appenderName.layout=className

  • org.apache.log4j.PatternLayout(最常用,可以灵活地指定布局模式)根据指定的转换模式格式化日志输出,或若未指定任何转换模式,就用默认的转化模式格式
    • ConversionPattern=%m%n:设定以怎样的格式显示消息
  • org.apache.log4j.HTMLLayout:(以HTML表格形式布局)格式化日志输出为HTML表格形式
    • LocationInfo=true:输出java文件名称和行号,默认false
    • Title=My Logging: 默认值是Log4J Log Messages
  • org.apache.log4j.SimpleLayout:(包含日志信息的级别和信息字符串)以一种非常简单的方式格式化日志输出,它打印三项内容:级别-信息
  • org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
【patterm参数】
%p: 日志级别<从高到低>(OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL)
%c: 输出日志信息所属的类目,通常就是所在类的全名。可写为%c{num},表示取完整类名的层数,从后向前取,比如%c{2}取 "cn.qlq.exam"类为"qlq.exam"。
%r: 输出自应用启动到输出该日志耗费的毫秒数
%d: 输出日志时间点的日期或时间。默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},SSS为毫秒数(也可以写为SS,只不过SSS如果不足三位会补0),输出类似:2011-10-18 22:10:28,021
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows平台为"
",Unix平台为"
"输出日志信息换行
%t: 输出当前产生日志的线程名称
%F: 输出日志消息产生时所在的文件名称 
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中 
%%: 输出1个"%"字符
%l: 输出日志事件的位置,相当于%c.%M(%F:L)的组合,包括类全名、方法、文件名以及在代码中行数。例如:cn.xm.test.PlainTest.main(PlanTest.java:12)

3 Slf4J

[J2EE:中间件]LOG4J+Slf4J快速入门
1 简述
2 LOG4J
3 Slf4J
4 问题集
5 实际项目开发中的日志应用
6 参考文献

3.1 特点

  • 不同于其他日志类库,与其它日志类库有很大的不同
    • 不是1个真正的日志实现,而是1个抽象层
    • 它允许你在后台使用任意一个日志类库
  • 使代码独立于任一特定的日志API;
  • 无需忍受加载和维护新的日志框架的痛苦
  • 占位符(place holder):"{}"
    • 占位符非常类似于在String的format()方法中的%s
      • 因为它会在运行时被某个提供的实际字符串所替换
    • 不仅降低了代码中字符串连接次数,而且还节省了新建的String对象
    • 可以在运行时延迟字符串的建立。
      • 这意味着只有需要的String对象才被建立

4 问题集

1: log4j.properties 放置在/src/log4j.properties
2: org.apache.log4j.BasicConfigurator.configure(); //自动快速地使用缺省Log4j环境

5 实际项目开发中的日志应用

1 一般是将捕捉到的Exception对象(e)作为日志记录的最后一个参数(会显示具体的出错信息以及出错位置),而且要放在{}可以格式化的参数之外。防止被{}转为e.toString()
2 可使用e.toString(),而尽量不使用e.getMessage()。因为有的异常不一定有message,可以使用e.toString只会显示信息,不会显示出错的位置信息(不建议这种)

//link - blog: https://www.cnblogs.com/qlqwjy/p/9275415.html
package cn.johnnyzen.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

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

public class Slf4jTest {

    private static Logger log = LoggerFactory.getLogger(Slf4jTest.class);

    public static void main(String[] args) {
        openFile("xxxxxx");
    }

    public static void openFile(String filePath) {
        File file = new File(filePath);
        try {
            InputStream in = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            //Exception对象(e)作为日志记录的最后一个参数(会显示具体的出错信息以及出错位置),而且要放在{}可以格式化的参数之外
            log.error("can found file [{}]", filePath, e);
        }
    }

}
2020-06-04 00:30:03 [cn.johnnyzen.test.Slf4jTest]-[ERROR] can found file [xxxxxx]
java.io.FileNotFoundException: xxxxxx (系统找不到指定的文件。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:146)
    at cn.xm.exam.test.Slf4jTest.openFile(Slf4jTest.java:22)
    at cn.xm.exam.test.Slf4jTest.main(Slf4jTest.java:16)

6 参考文献