统一日志的处理Slf4j,log4j,logback

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 统一日志的处理Slf4j,log4j,logback

1 日志

1.1 什么是日志

通过日志查看程序的运行过程,运行信息,异常信息等

1.2 配置日志级别

日志记录器(Logger)的行为是分等级的。如下表所示:

分为:FATAL、ERROR、WARN、INFO、DEBUG

默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别

# 设置日志级别
logging:
  level:
    root: ERROR

这种方式能将ERROR级别以及以上级别的日志打印在控制台上

2 log4j 简介

a) log4j 是 Apache 提供的一款记录日志的工具

b) log4j既可以将日志信息打印在控制台, 也可以打印输出

到一个日志文件中.

c) log4j 可以定制日志的输出格式

d) log4j 可以定制日志级别

2.1 日志级别

2.1.1 FATAL

致命的, 表示非常严重的错误, 一般是系统错误

2.1.2 ERROR

错误, 表示代码错误, 比较严重

2.1.3 WARN

警告, 不影响程序的运行, 但是可能存在风险.

2.1.4 INFO

信息, 表示一个普通的输出信息

2.1.5 DEBUG

调试, 表示程序员人为的一些调试信息

3 log4j 的使用

3.1 导包

log4j-1.2.17.jar

log4j-api-2.0-rc1.jar

log4j-core-2.0-rc1.jar

3.2 配置文件放到src目录下(方便加载,其他目录也可)

使用一个叫 log4j.properties 的配置文件, 会设定 log4j的设置信息, 例如日志级别, 日志输出方式, 日志格式等等.

# Set root category priority to INFO and its only appender to CONSOLE.
#设置根目录优先级为INFO和它的唯一附加到控制台。
log4j.rootCategory=INFO, CONSOLE
#log4j.rootCategory=INFO, CONSOLE, LOGFILE 
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
#将enterprise logger类别设置为FATAL及其惟一追加到控制台。
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
# CONSOLE被设置为使用PatternLayout的ConsoleAppender
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
# LOGFILE is set to be a File appender using a PatternLayout.
# LOGFILE被设置为使用PatternLayout的文件追加器。
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.Threshold=INFO
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

3.3 日志的输出格式

%c 输出日志信息所属的类的全名

%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy-MM-dd HH:mm:ss },输出类似:2020-5-4- 12:10:28

%f 输出日志信息所属的类的类名

%l 输出日志事件的发生位置,即输出日志信息的语句处于它所在的类的第几行

%m 输出代码中指定的信息,如log(message)中的message

%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”

%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL。如果是调用debug()输出的,则为DEBUG,依此类推

%r 输出自应用启动到输出该日志信息所耗费的毫秒数

%t 输出产生该日志事件的线程名

所以:

%5p [%t] (%F:%L) - %m%n 就表示宽度是5的优先等级 线程名称 (文件名:行号) - 信息 回车换行

3.4 测试代码

日志配置文件:

# Set root category priority to INFO and its only appender to CONSOLE.
# log4j.rootCategory=DEBUG, CONSOLE
log4j.rootCategory=DEBUG, CONSOLE, LOGFILE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m   %d{yyyy-MM-dd HH:mm:ss}%n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:/test.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=- %m %n

调用日志:

import org.apache.log4j.Logger;
public class TestLog4j {
  public static void main(String[] args) {
    // 获取日志对象
    Logger logger = Logger.getLogger(TestLog4j.class);
    // 五个日志级别分别对应五个输出方法, 方法名和级别名一致
    logger.fatal("系统崩溃了...");
    logger.error("发生了错误!");
    logger.warn("警告!");
    logger.info("消息~");
    logger.debug("调试...");
  }
}

运行效果:

4 MyBatis log4j 的支持

4.1 通过<settings>开启 log4j 的支持

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!-- properties加载外部文件 -->
  <properties resource="db.properties" />
  <!-- settings标签 -->
  <settings>
    <!-- 设置MyBatis使用log4j日志支持 -->
    <setting name="logImpl" value="LOG4J"/>
  </settings>
  <!-- typeAliases给类型起别名 -->
  <typeAliases>
    <!-- 给User类起别名为u -->
    <typeAlias type="com.csdn.pojo.User" alias="u" />
    <package name="com.csdn.pojo"/>
  </typeAliases>
  <environments default="dev">
    <environment id="dev">
      <transactionManager type="JDBC" />
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="com/csdn/mapper/UserMapper.xml" />
  </mappers>
</configuration>

4.2 日志配置文件区域选择:

<settings>用于设置 MyBatis 在运行时的行为方式, 例如:缓存, 延迟加载, 日志等.

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=ERROR, CONSOLE
# log4j.rootCategory=DEBUG, CONSOLE, LOGFILE
# 单独设置SQL语句的输出级别为DEBUG级别
# 方法级别
# log4j.logger.com.csdn.mapper.UserMapper.selAll=DEBUG
# 类级别
# log4j.logger.com.csdn.mapper.UserMapper=DEBUG
# 包级别
log4j.logger.com.csdn.mapper=DEBUG
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:/test.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=- %m %l%n

5 Logback日志

spring boot内部使用Logback作为日志实现的框架。

Logback和log4j非常相似,如果你对log4j很熟悉,那对logback很快就会得心应手。

logback相对于log4j的一些优点:https://blog.csdn.net/caisini_vc/article/details/48551287

5.1 配置logback日志

删除application.yml中的日志配置

安装idea彩色日志插件:grep console

resources 中创建 logback-spring.xml (默认日志的名字,必须是这个名字)

<?xml version="1.0" encoding="UTF-8"?>
<configuration  scan="true" scanPeriod="10 seconds">
    <contextName>logback</contextName>
    <property name="log.path" value="D:/project/helen/guli_log/edu" />
    <!--控制台日志格式:彩色日志-->
    <!-- magenta:洋红 -->
    <!-- boldMagenta:粗红-->
    <!-- cyan:青色 -->
    <!-- white:白色 -->
    <!-- magenta:洋红 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
    <!--文件日志格式-->
    <property name="FILE_LOG_PATTERN"
              value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n" />
    <!--编码-->
    <property name="ENCODING"
              value="UTF-8" />
    <!--输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!--日志级别-->
            <level>DEBUG</level>
        </filter>
        <encoder>
            <!--日志格式-->
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!--日志字符集-->
            <charset>${ENCODING}</charset>
        </encoder>
    </appender>
    <!--输出到文件-->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志过滤器:此日志文件只记录INFO级别的-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_info.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${ENCODING}</charset>
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志过滤器:此日志文件只记录WARN级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_warn.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志过滤器:此日志文件只记录ERROR级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_error.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>
    <!--开发环境-->
    <springProfile name="dev">
        <!--可以灵活设置此处,从而控制日志的输出-->
        <root level="DEBUG">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="INFO_FILE" />
            <appender-ref ref="WARN_FILE" />
            <appender-ref ref="ERROR_FILE" />
        </root>
    </springProfile>
    <!--生产环境-->
    <springProfile name="pro">
        <root level="ERROR">
            <appender-ref ref="ERROR_FILE" />
        </root>
    </springProfile>
</configuration>

5.2 节点说明

  • <property>:定义变量
  • <appender>:定义日志记录器
  • <filter>:定义日志过滤器
  • <rollingPolicy>:定义滚动策略
  • <springProfile>:定义日志适配的环境
  • <root>:根日志记录器

5.3 控制日志级别

通过在开发环境设置以下<root>节点的 level 属性的值,调节日志的级别

<!--开发环境-->
<springProfile name="dev">
    <!--可以灵活设置此处,从而控制日志的输出-->
    <root level="DEBUG">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="WARN_FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>
</springProfile>

在controller的listAll方法中输出如下日志,调节日志级别查看日志开启和关闭的效果

log.info("所有讲师列表....................");

6 错误日志处理

6.1 用日志记录器记录错误日志

GlobalExceptionHandler.java 中

类上添加注解 @Slf4j

修改异常输出语句

 //e.printStackTrace();
log.error(e.getMessage());

6.2 输出日志堆栈信息

为了保证日志的堆栈信息能够被输出,我们需要定义工具类

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
public class ExceptionUtils {
    public static String getMessage(Exception e) {
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            // 将出错的栈信息输出到printWriter中
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
        } finally {
            if (sw != null) {
                try {
                    sw.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (pw != null) {
                pw.close();
            }
        }
        return sw.toString();
    }
}

common_util中创建ExceptionUtil.java工具类

修改异常输出语句

 //e.printStackTrace();
//log.error(e.getMessage());
log.error(ExceptionUtils.getMessage(e));

7 日志实例模板

public void info(String format, Object... arguments);

支持多个参数的参数化log方法,对比上面的俩个方法来说,会多增加构造一个Object[]的开销。例子:


logger.info("在配置文件[{}]中读取到配置项[{}]的值为[{}]","/somePath/config.properties","maxSize", 5);


输出


2021-08-11 23:36:17,789  [ main ] INFO  c.j.training.logging.service.UserService - 在配置文件 [ /somePath/config.properties ]中读取到配置项 [ maxSize ]的值为 [ 5 ]

相关实践学习
日志服务之数据清洗与入湖
本教程介绍如何使用日志服务接入NGINX模拟数据,通过数据加工对数据进行清洗并归档至OSS中进行存储。
目录
相关文章
|
5天前
|
XML 安全 Java
必知的技术知识:Java日志框架:logback详解
必知的技术知识:Java日志框架:logback详解
|
12天前
|
Java API
SpringBoot系列之切换log4j日志框架
SpringBoot系列之切换log4j日志框架
|
20天前
|
消息中间件 存储 数据可视化
【JAVA日志】关于日志系统的架构讨论
【JAVA日志】关于日志系统的架构讨论
22 0
|
20天前
|
Java API Apache
【JAVA日志框架大全】一文快速讲透JAVA日志体系
【JAVA日志框架大全】一文快速讲透JAVA日志体系
30 0
|
20天前
|
XML Java 数据格式
【JAVA日志框架】JUL,JDK原生日志框架详解。
【JAVA日志框架】JUL,JDK原生日志框架详解。
14 0
|
25天前
|
设计模式 Java
Log4j 输出日志到 TextArea & JavaFX、Swing
Log4j 输出日志到 TextArea & JavaFX、Swing
|
25天前
|
XML 移动开发 Java
|
2月前
|
Java 数据库
log4j:WARN Please initialize the log4j system prop
log4j:WARN Please initialize the log4j system prop
18 1
|
2月前
|
运维 监控 前端开发
[SpringAop + Logback +MDC] 现网必备全链路日志追踪
[SpringAop + Logback +MDC] 现网必备全链路日志追踪
206 1
|
2月前
|
运维 监控 安全
Java一分钟之-Log4j与日志记录的重要性
【5月更文挑战第16天】Log4j是Java常用的日志框架,用于灵活地记录程序状态和调试问题。通过设置日志级别和过滤器,可避免日志输出混乱。为防止日志文件过大,可配置滚动策略。关注日志安全性,如Log4j 2.x的CVE-2021-44228漏洞,及时更新至安全版本。合理使用日志能提升故障排查和系统监控效率。
153 0