文章目录
- 一、slf4j 简介
- 二、常用日志框架
- 1)log4j
- pom 依赖
- log4j.properties 文件配置
- 测试
- 参考
- 2)logback
- pom 依赖
- logback.xml 配置
- 测试
- 参考
- 3) java.util.logging
- 4)commons logging
- pom 依赖
- 配置
- 测试
- 参考
- 5)slf4j-simple
- pom 依赖
- simplelogger.properties
- 测试
- 参考
- 三、日志打印规范
- 核心 UML 设计
- 1)Logger 模块
- 2)业务打印内容 Schema 模块
- 3)日志打印器模块
- 日志打印示例
- 四、总结
一、slf4j 简介
slf4j,全称 Simple Logging Facade for Java,是一个开源项目,对各种日志框架的进行 facade 抽象,允许最终用户在部署时插入所需的日志框架。slf4j 为不同的日志框架提供了不同的绑定,下图所示(图来自于 slf4j 官网:https://www.slf4j.org/manual.html):
上图包含了很多信息,简要介绍下:
- SLF4J unbound: slf4j 提供了未绑定任何日志框架 slf4j-api.jar,该 jar 单纯定义了抽象日志 api
- SLF4J bound to logback-classic: logback 项目提供了slf4j API 的实现 logback-classic.jar,使用 logback 还需要logback-core.jar
- SLF4J bound to log4j: 图中 slf4j-log412.jar 是绑定了 1.2 版本的 log4j 日志框架,核心实现在 log4j.jar 中,所以使用 log4j 框架要引入 log4j.jar
- SLF4J bound to java.util.logging: 用来绑定 jdk 提供的 logger 日志框架 java.util.logging(常被称为jdk1.4 logging),不需要额外依赖其他 jar 包
- SLF4J bound to simple:绑定的日志框架 slf4j-simple,slf4j 提供的简单实现
- SLF4J bound to no-operation: 绑定的日志框架为不执行任何操作
二、常用日志框架
以下简单介绍一下 log4j、logback、 java.util.logging、commons logging 和 slf4j-simple 的使用方式,关系如下图:
slf4j 是各种日志框架的 facade 抽象,log4j、logback、java.util.logging 是 slf4j 不同的实现方式,另外 slf4j-simple 是 slf4j 提供的简单实现。commons-logging 和 slf4j 类似,是 apache 最早提供的日志的门面接口,log4j 和 java.util.logging 也提供了相应的实现方式。
1)log4j
log4j 是 Apache 的一个开源项目,代码中有两个示例模块(配置类似)供参考:slf4j-log4j12 和单纯使用 log4j。下面示例代码以 slf4j-log4j12 为例:
pom 依赖
<!-- slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
log4j.properties 文件配置
# 日志级别配置
log4j.rootLogger=DEBUG,Console
# 控制台输出
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
测试
import org.apache.log4j.Logger;
public class TestSlf4jLog4J {
public static void main(String[] args) {
Logger logger = Logger.getLogger(TestSlf4jLog4J.class);
logger.info("this is info");
logger.warn("this is warn");
logger.error("this is error");
logger.fatal("this is fatal");
}
}
参考
- Log4J 官网:https://logging.apache.org/
- 示例代码 github:https://github.com/zhuqiuhui/log-demo
2)logback
logback 也是由 log4j 的作者设计完成的,拥有更好的特性,用来取代 log4j 的一个日志框架,实现了简单日志门面 slf4j。其中 logback-core 是其它模块的基础设施,其它模块基于它构建。使用示例工程如下:
pom 依赖
<!--注意:这里需要注意要选择合适的版本:jdk、slf4j-api、logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
logback.xml 配置
<!-- 输出到控制台 -->
<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>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
测试
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestLogback {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(TestLogback.class);
logger.trace("this is trace");
logger.debug("this is debug");
logger.info("this is info");
logger.warn("this is warn");
logger.error("this is error");
}
}
参考
- 官网:https://logback.qos.ch/
- logback github 开源代码:https://github.com/qos-ch/logback
- 示例代码 github:https://github.com/zhuqiuhui/log-demo
3) java.util.logging
java.util.logging 是 jdk 1.4 自带的 logger,使用直接按 api 介绍来即可,易上手(使用 slf4j-jdk 框架的详细参考 github 示例代码):
import java.util.logging.Logger;
public class TestLogging {
public static void main(String[] args) {
Logger logger = Logger.getLogger("TestLogging");
logger.severe("this is severe"); // 严重
logger.info("this is info");
logger.warning("this is warn");
logger.config("this is config");
logger.fine("this is fine");
//......
}
}
4)commons logging
Jakarta Commons-logging(JCL)是 apache 最早提供的日志的门面接口,提供简单的日志实现以及日志解耦功能。JCL 能够选择使用 Log4j 还是 JDK Logging,以使用 Log4j 示例如下:
pom 依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
配置
- commons-logging.properties
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
- log4j.properties
# 日志级别配置
log4j.rootLogger=DEBUG,Console
# 控制台输出
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
测试
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class TestCommonLogging {
public static void main(String[] args) {
Log logger = LogFactory.getLog(TestCommonLogging.class);
logger.info("this is info");
logger.warn("this is warn");
logger.error("this is error");
logger.fatal("this is fatal");
}
}
参考
- commons-logging github 开源代码:https://github.com/apache/commons-logging
- 示例代码 github:https://github.com/zhuqiuhui/log-demo
5)slf4j-simple
slf4j-simple 是 slf4j 提供的简单实现,使用示例如下:
pom 依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
simplelogger.properties
org.slf4j.simpleLogger.showDateTime = true
org.slf4j.simpleLogger.dateTimeFormat = HH:mm:ss:SSSS
org.slf4j.simpleLogger.defaultLogLevel = warn
测试
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestSlf4jSimple {
public static void main(String[] args) {
// 生成 SimpleLogger 实例
Logger logger = LoggerFactory.getLogger(TestSlf4jSimple.class);
logger.info("this is info");
logger.warn("this is warn");
logger.error("this is error");
logger.debug("this is debug");
}
}
参考
- 示例代码 github:https://github.com/zhuqiuhui/log-demo
三、日志打印规范
在工程中经常会出现日志随意打印的现象,比如:
log.info("[TestServiceImpl-apply] request:{}", JSON.toJSONString(request));
log.info("success")
//......
如果要使用日志做数据分析,则很难提取到规整的数据,以下提供一种思路来规范日志打印,达到格式化输出。
核心 UML 设计
上述是日志规范的一个 uml 图示例,可围绕以下几点来设计:
1)Logger 模块
- 日志按业务 module 和 subModule 进行分类
日志打印中可按照 moduleName 和 subModuleName 拆分不同的业务子文件夹做日志分类,其中模块日志属性配置(如模块名称、日志级别、是否需要结构化等)可使用 Spring 注解 @EnableConfigurationProperties 做成文件配置形式,达到 Spring 启动时自动生成各模块日志文件夹,配置简要示例如下:
logging.business[0].moduleName=student
logging.business[0].subModuleName=biz,error
logging.business[0].level=info
- Logger 实例创建
Spring Boot 启动时可使用 logback api 来自动创建 Logger 实例存放到 LoggerContext 中,同时并创建各 module 下的文件夹,代码可通过如下方式来获取 Logger 实例:
BusinessLogger businessLogger = BusinessLogger.getLogger("student", "query");
而 BusinessLogger 是从 LoggerContext 来获取的:
public static BusinessLogger getLogger(String moduleName, String subModuleName) {
return new BusinessLogger(LoggerFactory.getLogger(PREFIX + moduleName + "-" + subModuleName));
}
2)业务打印内容 Schema 模块
业务打印内容通常包括以下内容:
- bizCode
- 异常
- message
- 自定义 key,如 traceId、rt、userId 等
- …
3)日志打印器模块
日志打印器的职责是负责将业务打印内容 Schema 按指定分隔符拼接成一个长的字符串,最终将能 Logger 实例来打印。
日志打印示例
四、总结
上述几种日志框架中使用比较广泛的是 log4j 和 logback,而 logback 整体性能比 log4j 更好,建议选择 slf4j 和 logback 结合使用。另外为了规范日志打印,简述了一种日志打印规范设计方式,有兴趣同学可以实现下做一个简单小工具。