文章目录

  • 一、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):

high level es 打印日志 logger日志打印规范_slf4j

上图包含了很多信息,简要介绍下:

  • 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 的使用方式,关系如下图:

high level es 打印日志 logger日志打印规范_日志打印_02

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 为例:

high level es 打印日志 logger日志打印规范_slf4j_03

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");
    }
}
参考
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");
    }
}
参考
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");
    }
}
参考
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");
    }
}
参考

三、日志打印规范

在工程中经常会出现日志随意打印的现象,比如:

log.info("[TestServiceImpl-apply] request:{}", JSON.toJSONString(request));
log.info("success")
//......

如果要使用日志做数据分析,则很难提取到规整的数据,以下提供一种思路来规范日志打印,达到格式化输出。

核心 UML 设计

high level es 打印日志 logger日志打印规范_日志打印_04

上述是日志规范的一个 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 实例来打印。

日志打印示例

high level es 打印日志 logger日志打印规范_日志规范_05

四、总结

上述几种日志框架中使用比较广泛的是 log4j 和 logback,而 logback 整体性能比 log4j 更好,建议选择 slf4j 和 logback 结合使用。另外为了规范日志打印,简述了一种日志打印规范设计方式,有兴趣同学可以实现下做一个简单小工具。