SpringBoot日志总结

文章目录

  • 一、日志系统简介
    • 1、简介
    • 2、slf4j 介绍
  • 二、Spring自带日志logback详解
    • 1、介绍
    • 2. application.yml 中对日志的配置
    • 3、日志关键文件节点介绍
      • 3.1 根节点 configuration
      • 3.2 logger子节点
      • 3.3 appender子节点
      • 3.4 root子节点
      • 3.5 常用appender介绍
    • 4、logback.xml 配置文件解析
      • 4.1 定义日志输出格式和存储路径
      • 4.2 定义控制台输出
      • 4.3 定义日志文件的相关参数
      • 4.4 定义日志输出级别
    • 5、logback配置文件举例
      • 5.1 简介
      • 5.2 logback.xml
      • 5.3 Logback.properties
      • 5.4 Appender.xml
      • 5.5 Logger.xml
  • 三、Springboot整合log4j2详解
    • 1、介绍
    • 2、log4j2日志简单介绍
      • 2.1 日志级别
      • 2.2 文件配置详解
    • 3、SpringBoot整合实战
      • 3.1 引入log4j2依赖,去除固有依赖
      • 3.2 配置log4j2.xml文件
      • 3.3 log4j2配置文件例子
      • 3.4 控制台显示颜色
  • 四、日志邮件告警与自定义Appender
    • 1、logback默认日志框架
      • 1.1 SMTPAppender邮件告警
      • 1.2 logback自定义Appender
    • 2、Log4j2日志框架
      • 2.1 SmtpAppender邮件告警
      • 2.2 log4j2自定义Appender
    • 3、日志告警拓展信息配置
    • 4、自定义日志邮件报警

一、日志系统简介

1、简介

在开发中,我们经常使用 System.out.println() 来打印一些信息,但是这样不好,因为大量的使用 System.out 会增加资源的消耗,而且部署在Linux上时只能通过日志来查看输出。我们实际项目中使用的是 slf4jlogback 来输出日志,slf4j+logback也是springboot的默认日志框架,当然logback也可以换成性能更好的log4j2框架。

下面是几个日志概念介绍

  • slf4j:slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback、log4j2)。
  • j.u.l:j.u.l是java.util.logging包的简称,是JDK在1.4版本中引入的Java原生日志框架。
  • log4j:log4j是apache实现的一个开源日志组件。
  • logback:logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现。
  • log4j2:Log4j2是log4j 1.x和logback的改进版,据说采用了一些新技术(无锁异步等),使得日志的吞吐量、性能比log4j 1.x提高了10倍,并解决了一些死锁的bug,而且配置更加简单灵活。

2、slf4j 介绍

SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。

slf4j要求我们只需要按统一的方式写记录日志的代码,而无需关心日志是通过哪个日志系统,以什么风格输出的。因为它们取决于部署项目时绑定的日志系统。

首先需要添加依赖







<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starterartifactId>
dependency>

日志的使用

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

public class Test {
    private static final Logger logger = LoggerFactory.getLogger(Test.class);
    //logger.info("test");
}

还有一种方法通过注解方式,需要安装好Lombok插件

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Test {
  
   // log.info("test");
}

二、Spring自带日志logback详解

1、介绍

logback官方手册

logback中文文档

目前logback默认与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能。

2. application.yml 中对日志的配置

application.yml 文件是 Spring Boot 中唯一一个需要配置的文件,一开始创建工程的时候是 application.properties 文件(注:同一优先级位置同时有 application.propertiesapplication.yml,那么 application.yml 里的属性会覆盖 application.properties里的属性)

这里我们对 application.yml文件中对日志的配置:

logging:
  config: classpath:logback.xml 
  level:
    com.test: trace

logging.config 是用来指定项目启动的时候,读取哪个配置文件,这里指定的是日志配置文件是根路径下的 logback.xml 文件,关于日志的相关配置信息,都放在 logback.xml 文件中了。logging.level 是用来指定具体的 mapper 中日志的输出级别,上面的配置表示 com.test 包下的所有日志输出级别为 trace,会打印出所以debug以上信息。在生产环境上,将这个日志级别再设置成 error 级别即可。当然在 application.yml 中还可以显示定义其他例如logging.pattern.console等配置信息。

常用的日志级别按照从高到低依次为:ERROR、WARN、INFO、DEBUG、TRACE 、 ALL

对于classpath路径,src 路径下的文件 在编译后都会放到 WEB-INF/classes 路径下。默认classpath 就是指这里。用maven构建 项目时,resources 目录就是默认的classpath

3、日志关键文件节点介绍

3.1 根节点 configuration

  • scan

    当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true

  • scanPeriod

    设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟

  • debug

    当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false


<configuration scan="true" scanPeriod="60 seconds" debug="false">

configuration>

3.2 logger子节点

logger用来设置某一个包或者具体的某一个类的日志打印级别、以及指定 。 仅有一个name属性,一个可选的level和一个可选的addtivity属性

  • name

    用来指定受此 loger 约束的某一个包或者具体的某一个类

  • level

    用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特殊值 INHERITED 或者同义词 NULL ,代表强制执行上级的级别。如果未设置此属性,那么当前 logger 将会继承上级的日志级别

  • addtivity

    是否向上级 logger 传递打印信息。默认是true

	
<logger name="org.mybatis" level="INFO"/>

3.3 appender子节点

appender是 的子节点,是负责写日志的组件。该标签负责以适当的格式将日志记录事件输出到适当的输出设备

  • name

    指定appender名称

  • class

    指定appender的全限定名



<appender name="ERROR-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
    
appender>

3.4 root子节点

root为根元素,只有一个level属性。 可以包含零个或多个 元素

  • level

    设置日志级别。


<root level="INFO">
    <appender-ref ref="ASYNC-INFO"/>
root>

3.5 常用appender介绍

RollingFileAppender,滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件,下面是其子标签

  • encoder 对日志进行格式化
  • target
  • rollingPolicy 当发生滚动时,决定RollingFileAppender 的行为,涉及文件移动和重命名(设置滚动策略)
    • class 为 rollingPolicy 的属性,设置 日志的滚动策略,最常用的滚动策略为TimeBasedRollingPolicy ,它根据时间来制定滚动策略,既负责滚动也负责出发滚动
    • fileNamePattern为一个必要的子节点,设置日志文件的名称 。一般包含文件名及“%d”转换符,“%d”可以包含一个java.text.SimpleDateFormat指定的时间格式,如:%d{yyyy-MM}。如果直接使用 %d,默认格式是 yyyy-MM-dd。
    • maxHistory 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。\

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    
    <fileNamePattern>${log.home}/%d{yyyy-MM,aux}/soi-%d{yyyy-MM-dd}.error.logfileNamePattern>
    
    <maxHistory>30maxHistory>
rollingPolicy>

filter为日志过滤器。执行一个过滤器会有返回一个枚举值,即 DENYNEUTRALACCEPT 其中之一,这也是appender下的子节点,其中classfilter 设置指定的过滤器 ,下面列举几个常见的 过滤器

  • LevelFilter

    级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志

    
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        
        <level>DEBUGlevel>
         
        <onMatch>ACCEPTonMatch>
        
        <onMismatch>DENYonMismatch>
    filter>
    
  • ThresholdFilter

    临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。

    
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>WARNlevel>
    filter>
    

4、logback.xml 配置文件解析

在上面 application.yml 文件中,我们指定了日志配置文件 logback.xmllogback.xml 文件中主要用来做日志的相关配置。在 logback.xml 中,我们可以定义日志输出的格式、路径、控制台输出格式、文件大小、保存时长等等。下面来分析一下:

4.1 定义日志输出格式和存储路径

<configuration>
  <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />
  <property name="FILE_PATH" value="D:/logs/demo.%d{yyyy-MM-dd}.%i.log" />
configuration>
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level :级别从左显示5个字符宽度
%logger{50}表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符

%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

对于“FILE_PATH” 文件路径,日志都会存储在该路径下。%i 表示第 i 个文件,当日志文件达到指定大小时,会将日志生成到新的文件里,这里的 i 就是文件索引,日志文件允许的大小可以设置。这里需要注意的是,不管是 windows 系统还是 Linux 系统,日志存储的路径必须要是绝对路径

如果想在控制台换成彩色打印输出,可以在里添加如下配置


    
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){green} %clr(${LOG_LEVEL_PATTERN:-%5p}){red} %clr(${PID:-}){magenta} %clr(---){blue} %clr([%30.30t]){yellow} %clr(%-40.40logger{39}){cyan} %clr(:){blue} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />

4.2 定义控制台输出

<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
            
      <pattern>${LOG_PATTERN}pattern>
    encoder>
  appender>
configuration>

使用 节点设置个控制台输出(class="ch.qos.logback.core.ConsoleAppender")的配置,定义为 “CONSOLE”。使用上面定义好的输出格式(LOG_PATTERN)来输出,使用 ${} 引用进来即可。

4.3 定义日志文件的相关参数

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      
      <fileNamePattern>${FILE_PATH}fileNamePattern>
      
      <maxHistory>30maxHistory>
      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        
        <maxFileSize>30MBmaxFileSize>
      timeBasedFileNamingAndTriggeringPolicy>
    rollingPolicy>

    <encoder>
      
      <pattern>${LOG_PATTERN}pattern>
    encoder>
  appender>
configuration>

使用 定义一个名为 “FILE” 的文件配置,主要是配置日志文件保存的时间、单个日志文件存储的大小、以及文件保存的路径和日志的输出格式。

4.4 定义日志输出级别

<configuration>
  
  <logger name="com.test" level="INFO" />
  <root level="INFO">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="FILE" />
         
  root>
configuration>

有了上面那些定义后,最后我们使用 来定义一下项目中默认的日志输出级别,这里定义级别为 INFO,然后针对 INFO 级别的日志,使用 引用上面定义好的控制台日志输出和日志文件的参数。这样 logback.xml 文件中的配置就设置完了。

5、logback配置文件举例

5.1 简介

这里演示另一种日志文件创建方法,更加抽象。首先需要在resources下创建logback文件夹,并存放以下三个文件,也可以都写在logback.xml中

  • logback.properties用于定义变量
  • appender.xml用于定义Appender对象
  • logger.xml用于定义logger
  • 创建logback.xml用于引入上面三个

5.2 logback.xml



<configuration debug="true">
    
    <contextName>logback_studycontextName>
    
    <property resource="logback/logback.properties" />
    
    <include resource="logback/appender.xml" />
    
    <include resource="logback/logger.xml" />
    
    <root level="info">
          <appender-ref ref="console" />
           <appender-ref ref="file" />
    root>
configuration>

5.3 Logback.properties

# 定义日志存放的目录
# 项目名称
app_name=logbackStudy
# 日志存放的根路径
file_root_dir=D:\\
# 当前项目日志存放的根路径
file_root_app_dir=${file_root_dir}\\${app_name}
# 历史日志文件存放的根路径
file_root_app_history_dir=${file_root_dir}\\${app_name}\\history
 
# 日志格式
# 默认的日志格式
default_pattern=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %n
# console的日志格式
console_pattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %logger %n[%thread] %7level: %message %n
# fileAppender的日志格式
file_pattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %logger %n[%thread] %7level: %message %n

5.4 Appender.xml

<included>

<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    
    <encoder>
        <pattern>${console_pattern}pattern>
    encoder>
    
    <target>System.outtarget>
    
    <withJansi>falsewithJansi>
appender>


<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    
    <file>${file_root_app_dir}/${app_name}_log.txtfile>
    
    <append>trueappend>
    
    <immediateFlush>trueimmediateFlush>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        
        <fileNamePattern>${file_root_app_history_dir}/%d{yyyy/MM,aux}/%d{yyyy-MM-dd}_log%i.zipfileNamePattern>
        
        <maxFileSize>5MBmaxFileSize>
        
        <maxHistory>365maxHistory>
        
        <totalSizeCap>20GBtotalSizeCap>
    rollingPolicy>
    
    <encoder>
        <pattern>${file_pattern}pattern>
    encoder>
appender>
included> 

5.5 Logger.xml

<included>
    
    <logger name="com.rain.test" level="debug" additivity="false">
        <appender-ref ref="console" />
        <appender-ref ref="file" />
    logger>
included>

三、Springboot整合log4j2详解

1、介绍

log4j2官方文档:https://logging.apache.org/log4j/2.x/index.html

Apache Log4j 2 是对 Log4j 的升级,它比其前身 Log4j 1.x 提供了显着改进,并提供了 Logback 中可用的许多改进,同时修复了 Logback 架构中的一些固有问题。

2、log4j2日志简单介绍

2.1 日志级别

Log4j2中日志有六个级别(level),当日志级别设置为某个值的时候,低于它的日志信息将不会被记录,只有高于设置的级别的信息会被记录。

  • trace:追踪,是最低的日志级别,相当于追踪程序的执行,一般不怎么使用
  • debug:调试,一般在开发中,都将其设置为最低的日志级别
  • info:信息,输出重要的信息,使用较多
  • warn:警告,有些时候,虽然程序不会报错,但是还是需要告诉程序员的
  • error:错误,这个在开发中也挺常用的
  • fatal:严重错误,这个一旦发生,程序基本上也要停止了

2.2 文件配置详解

  • Configuration

    • properties
      • property
    • Appenders
      • Console
        • PatternLayout
      • File
      • RollingRandomAccessFile
      • Async
    • Loggers
      • Logger
      • Root
        • AppenderRef

1、Configuration

Configuration为根节点,有statusmonitorInterval等多个属性。

  • status的值有trace、debug、info、warn、error和 fatal,用于控制log4j2日志框架本身的日志级别,如果将status设置为较低的级别(如trace)就会看到很多关于log4j2本身的日志,如加载log4j2配置文件的路径等信息,一般不用设置。
  • monitorInterval,含义是每隔多少秒重新读取配置文件,可以不重启应用的情况下修改配置。
  • name:配置名称
  • strict: 是否使用严格的XML格式,推荐使用,规范开发者的配置编写。

2、properties

  • properties:配置文件全局的参数变量,用于减少自定义配置信息的重复编码,该配置是可选的,例如定义日志的存放位置D:/logs

3、Appenders

Appenders是输出源,用于定义日志输出的地方,log4j2支持的输出源有很多,有控制台Console、文件File、RollingRandomAccessFile、MongoDB、Flume等。

  • Console:控制台输出源是将日志打印到控制台上,开发的时候一般都会配置,以便调试

  • File:文件输出源,用于将日志写入到指定的文件,需要配置输入到哪个位置(例如:D:/logs/mylog.log)

  • RollingRandomAccessFile: 该输出源也是写入到文件,不同的是比File更加强大,可以指定当文件达到一定大小(如20MB)时,另起一个文件继续写入日志,另起一个文件就涉及到新文件的名字命名规则,因此需要配置文件命名规则 这种方式更加实用,因为你不可能一直往一个文件中写,如果一直写,文件过大,打开就会卡死,也不便于查找日志。

    • fileName:指定当前日志文件的位置和文件名称
    • filePattern:指定当发生Rolling时,文件的转移和重命名规则
    • SizeBasedTriggeringPolicy:指定当文件体积大于size指定的值时,触发Rolling
    • DefaultRolloverStrategy:指定最多保存的文件个数
    • TimeBasedTriggeringPolicy:这个配置需要和filePattern结合使用。假如filePattern中配置的文件重命名规则是${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i,最小的时间粒度是mm,即分钟,若TimeBasedTriggeringPolicy指定的interval是1,结合起来就是每1分钟生成一个新文件。如果改成%d{yyyy-MM-dd HH},最小粒度为小时,则每一个小时生成一个文件。
  • RollingFile:同上,与RollingRandomAccessFile不同的是,RollingRandomAccessFile默认日志文件写入策略为异步刷盘,RollingRandomAccessFile会将日志信息先写入到缓冲区,然后缓冲区满后刷到磁盘,并清空缓冲区,默认缓冲区的大小在8-256kb,具体大小需要自己设置。

  • NoSql:MongoDb, 输出到MongDb数据库中
    Flume:输出到Apache Flume(Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。)

  • Async:异步,需要通过AppenderRef来指定要对哪种输出源进行异步(一般用于配置RollingRandomAccessFile)

  • PatternLayout:控制台或文件输出源(Console、File、RollingRandomAccessFile)都必须包含一个PatternLayout节点,用于指定输出文件的格式(如日志输出的时间、文件、方法、行数等格式),例如pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n",各标记符详细含义如下:

    %d 输出时间 
        %d{HH:mm:ss.SSS} 表示输出到毫秒的时间 
    %t 输出当前线程名称 
    %p 输出日志级别 
        %-5p -5表示左对齐并且固定输出5个字符,如果不足在右边补0
    %c 日志消息所在类名 
    %m 消息内容 
    %n 换行 
    %F 输出所在的类文件名,如Log4j2Test.java 
    %L 输出行号 
    %M 输出所在方法名 
    %l 输出语句所在的行数, 包括类名、方法名、文件名、行数 
    %logger 输出logger名称,如果没有名称,就不输出 
    

4、Loggers

日志器分根日志器Root和自定义日志器,当根据日志名字获取不到指定的日志器时就使用Root作为默认的日志器,自定义时需要指定每个Logger的名称name(对于命名可以以包名作为日志的名字,不同的包配置不同的级别等),日志级别level,相加性additivity(是否继承下面配置的日志器), 对于一般的日志器(如Console、File、RollingRandomAccessFile)一般需要配置一个或多个输出源AppenderRef。

每个logger可以指定一个level(TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF),不指定时level默认为ERROR。additivity指定是否同时输出log到父类的appender,缺省为true。

3、SpringBoot整合实战

3.1 引入log4j2依赖,去除固有依赖

首先 ,引入log4j2的starter,将logback.xml改为log4j2的配置文件

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-log4j2artifactId>
dependency>

然后需要排除spring自带的日志框架logback依赖,如果项目中只引入了web的starter,则可以在web starter中排除掉logging

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-loggingartifactId>
        exclusion>
    exclusions>
dependency>

如果项目中引入的不止web starter,此时我们需要在spring-boot-starter中排除logging的依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starterartifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-loggingartifactId>
        exclusion>
    exclusions>
dependency>

3.2 配置log4j2.xml文件

若自定义文件名,则需要在application.yml进行修改,配置文件也logback也有区别

logging:
  config: classpath:log4j2_dev.xml




<configuration monitorInterval="30">
    
    <Properties>
        
        <property name="LOG_CONSOLE" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level} [%t] %highlight{%c{1.}.%M(%L)}: %msg%n" />
        <Property name="LOG_HOME" value="logs"/>
        
        <property name="LOG_FILE" value="%d{yyyy-MM-dd HH:mm:ss.SSS} {%-5level} [%t] {%c{1.}.%M(%L)}: %msg%n" />
        
        <property name="FILE_PATH" value="lamp.%d{yyyy-MM-dd}.%i.log" />
    Properties>

    <Appenders>
        <console name="CONSOLE" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_CONSOLE}"/>
        console>

        
        <RollingFile name="FILE" fileName="${LOG_HOME}/log.log" filePattern="${LOG_HOME}/${FILE_PATH}">
            
            <PatternLayout pattern="${LOG_FILE}"/>
            <Policies>
                
                
                <TimeBasedTriggeringPolicy modulate="true" interval="1"/>
                <SizeBasedTriggeringPolicy size="20 MB" />
            Policies>

            <DefaultRolloverStrategy>
                
                <Delete basePath="${LOG_HOME}" maxDepth="2">
                    <IfFileName glob="${LOG_HOME}/*.log">
                        
                        <IfLastModified age="30d"/>
                    IfFileName>
                Delete>
            DefaultRolloverStrategy>
        RollingFile>

    Appenders>
    <Loggers>
        
        <logger name="org.springframework" level="INFO">logger>
        <logger name="org.mybatis" level="INFO">logger>
        
        
        <root level="info">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="FILE" />
        
        root>
    Loggers>

configuration>

3.3 log4j2配置文件例子

这里可以和之前logback那样分文件,这里我全部放在一起了






<configuration status="warn" monitorInterval="30">
    <properties>
        
        <property name="MAIL_LEVEL">ERRORproperty>
		
        <property name="PROJECT_NAME">prod-projectproperty>
        
        <property name="LOG_BACK_HOME">./${PROJECT_NAME}-logproperty>
    properties>
    
    <appenders>
        
        <console name="Console" target="SYSTEM_OUT">
            
            
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level} [%t] %highlight{%l}: %msg%n"/>
        console>
        
        <RollingFile name="RollingFileInfo" fileName="${LOG_BACK_HOME}/info.log"
                     filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                
                <ThresholdFilter level="WARN" onMatch="NEUTRAL" onMismatch="DENY"/>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            Filters>
            <PatternLayout pattern="[%d{yyyy/MM/dd HH:mm:ss,SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            Policies>
        RollingFile>

        <RollingFile name="RollingFileWarn" fileName="${LOG_BACK_HOME}/warn.log"
                     filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
            Filters>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="20"/>
        RollingFile>

        <RollingFile name="RollingFileError" fileName="${LOG_BACK_HOME}/error.log"
                     filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="ERROR"/>
            <PatternLayout pattern="[%d{yyyy/MM/dd HH:mm:ss,SSS}] [%t] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="20"/>
        RollingFile>
    appenders>
    
    <loggers>
        
        <logger name="org.springframework" level="INFO">logger>
        <logger name="org.mybatis" level="INFO">logger>
        
        <logger name="org.springframework.core" level="info" />
        <logger name="org.springframework.beans" level="info" />
        <logger name="org.springframework.context" level="info" />
        <logger name="org.springframework.web" level="info" />
        <logger name="org.jboss.netty" level="warn" />
        <logger name="org.apache.http" level="warn" />
        <logger name="org.hibernate" level="info" />
        <logger name="com.alibaba.druid" level="info" />
        <logger name="org.thymeleaf" level="info" />

        
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        root>
    loggers>
configuration>

3.4 控制台显示颜色

在Log4j 2.10以前的版本,pattern中配置%highlight属性是可以正常打印彩色日志的。但是是更新到2.10版本以后,控制台中就无法显示彩色日志了,各种级别的日志混杂在一起,难以阅读。Log4j2默认关闭了Jansi(一个支持输出ANSI颜色的类库)。

操作

IDEA中,点击右上角->Edit Configurations,在VM options中添加-Dlog4j.skipJansi=false

四、日志邮件告警与自定义Appender

首先引入邮件依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-mailartifactId>
dependency>

1、logback默认日志框架

1.1 SMTPAppender邮件告警

这里每出现一次定义的异常,都会发送一次邮件。






<configuration status="warn" monitorInterval="30">
    
    
    
    <springProperty scope="context" name="smtpHost" source="spring.mail.host" />
    <springProperty scope="context" name="username" source="spring.mail.username" />
    <springProperty scope="context" name="password" source="spring.mail.password" />
    <springProperty scope="context" name="mailSubject" source="spring.mail.error.subject" />
    <springProperty scope="context" name="mailTo" source="spring.mail.error.to" />
    <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            
            <pattern>${LOG_PATTERN}pattern>
        encoder>
    appender>

    <appender name="MAIL" class="ch.qos.logback.classic.net.SMTPAppender">
        <smtpHost>${smtpHost}smtpHost>
        <smtpPort>465smtpPort>
        <username>${username}username>
        <password>${password}password>
        <SSL>trueSSL>
        <asynchronousSending>trueasynchronousSending>
        <from>${username}from>
        <to>${mailTo}to>
        <subject>${mailSubject}: %logger{0} subject>
        <charsetEncoding>UTF-8charsetEncoding>
        <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
            
            <bufferSize>10bufferSize>
        cyclicBufferTracker>
        
        <layout class="ch.qos.logback.classic.html.HTMLLayout"/>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            
            <level>ERRORlevel>
        filter>
    appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE">appender-ref>
        <appender-ref ref="MAIL"/>
    root>
configuration>

1.2 logback自定义Appender

自定义文件Appender

public class MyAppender extends AppenderBase<ILoggingEvent> {
    

    @Override
    public void start() {
        //这里可以做些初始化判断 比如layout不能为null ,
        //或者写入数据库、redis时的初始化连接等等
        super.start();
    }

    @Override
    public void stop() {
        //释放相关资源,如数据库连接,redis线程池等等
        if (!isStarted()) {
            return;
        }
        super.stop();
    }

    @Override
    protected void append(ILoggingEvent event) {
        System.out.println("这是我自定义的Appender");
    }
}

其次添加logback.xml配置


<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d [%t] %-5level %logger{36}.%M\(%file:%line\) - %msg%npattern>
            
            <charset>UTF-8charset>
        encoder>
    appender>

    <appender name="MyAppender" class="com.example.mx80.log.MyAppender">
        
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFOlevel>
        filter>
    appender>


    
    
    
    <logger name="com.example.mx80." level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="MyAppender"/>
    logger>

    
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="MyAppender"/>
    root>
configuration>

2、Log4j2日志框架

2.1 SmtpAppender邮件告警

这里每出现一次定义的异常,都会发送一次邮件。如果要修改为策略形式,可以参考https://cxymm.net/article/qq_29693653/100564296

SMTP相关参数介绍

参数 说明
name appender的名称
subject 主题
to 邮件的接收方邮箱,多个账户使用“,”分割
from 邮件发送所使用的账户
smtpProtocol stmp协议
smtpHost 邮件发送服务器域名
smtpPort 邮件发送服务器端口
smtpPassword 账户密码
smtpUsername 账户名称





<configuration status="warn" monitorInterval="30">
    <properties>
        
        <property name="MAIL_LEVEL">ERRORproperty>
        
        <property name="PROJECT_NAME">prod-projectproperty>
        
        <property name="LOG_BACK_HOME">./${PROJECT_NAME}-logproperty>
    properties>
    
    <appenders>
        
        <console name="Console" target="SYSTEM_OUT">
            
            
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level} [%t] %highlight{%l}: %msg%n"/>
        console>
        
        <SMTP name="Mail" subject="${PROJECT_NAME} Error Log" to="要发送的邮箱@qq.com(可以多个,用逗号分隔)" from="你的邮箱名@qq.com"
              smtpHost="smtp.qq.com" smtpPort="465"  smtpProtocol="smtps" smtpPassword="你的密码" 
              smtpUsername="你的邮箱名@qq.com"
              bufferSize="1024" smtpDebug="false">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
        SMTP>
        
        <Async name="AsyncMail" >
            <appender-ref ref="Mail"/>
        Async>
    appenders>
    
    <loggers>
        <root level="all">
            <appender-ref ref="Console"/>
            
            <appender-ref ref="AsyncMail" level="${MAIL_LEVEL}"/>
        root>
    loggers>
configuration>

2.2 log4j2自定义Appender

自定义文件Appender,这里需要注意几个点

  • @Plugin..注解:这个注解,是为了在之后配置log4j2.xml时,指定的Appender Tag。

  • 构造函数:除了使用父类的以外,也可以增加一些自己的配置。

  • 重写append()方法:这里面需要实现具体的逻辑,日志的去向。

  • createAppender()方法:主要是接收log4j2.xml中的配置项。

/**
 * 自定义日志Appender
 */
@Plugin(name = "MyAppender", category = "Core", elementType = "appender", printObject = true)
public class MyAppender extends AbstractAppender {

    private String fileName;

    protected MyAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, Property[] properties, String fileName) {
        super(name, filter, layout, ignoreExceptions, properties);
        this.fileName=fileName;
    }

    @Override
    public void start(){
        //这里可以做些初始化判断,即系统启动时
        System.out.println("日志开始");
        super.start();
    }

    @Override
    public void stop(){
        System.out.println("日志结束");
        if (!isStarted()) {
            return;
        }
        super.stop();
    }


    @Override
    public void append(LogEvent event) {
        // 这里就可以通过log日志做很多事情,比如接入邮件等日志报警通知,或者自己的文件写入磁盘
        System.out.println("这是我的自定义appender");
        //final byte[] bytes = getLayout().toByteArray(event);
        //writerFile(bytes);
    }

    /**
     *  接收配置文件中的参数
     */
    @PluginFactory
    public static MyAppender createAppender(@PluginAttribute("name") String name,
                                            @PluginAttribute("fileName") String fileName,
                                            @PluginElement("Filter") final Filter filter,
                                            @PluginElement("Layout") Layout<? extends Serializable> layout,
                                            @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {

        if (name == null) {
            LOGGER.error("no name defined in conf.");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }

        // 创建文件
//        if (!createFile(fileName)) {
//            return null;
//        }

        return new MyAppender(name, filter, layout, ignoreExceptions, null,fileName);
    }

//    private static boolean createFile(String fileName) {
//        Path filePath = Paths.get(fileName);
//        try {
//            // 每次都重新写文件,不追加
//            if (Files.exists(filePath)) {
//                Files.delete(filePath);
//            }
//            Files.createFile(filePath);
//        } catch (IOException e) {
//            LOGGER.error("create file exception", e);
//            return false;
//        }
//        return true;
//    }
//
//    private void writerFile(byte[] log) {
//        try {
//            Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);
//        } catch (IOException e) {
//            LOGGER.error("write file exception", e);
//        }
//    }

}

其次添加log4j2.xml配置,需要注意一下几点

  • 下面的log配置,一共配了2个输出。一个是终端输出,一个是采用自定义的MyAppender 输出,即append()方法输出

  • 标签要与自定义Appender中的类注解保持一致。



<configuration status="INFO" monitorInterval="30">
    <appenders>
        
        <console name="Console" target="SYSTEM_OUT">
            
            <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
        console>

        
        <MyAppender name="MyAppender" fileName="log.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
        MyAppender>

    appenders>

    <loggers>
        
        <logger name="org.springframework" level="INFO">logger>
        <logger name="org.mybatis" level="INFO">logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="MyAppender"/>
        root>
    loggers>
configuration>

3、日志告警拓展信息配置

https://howtodoinjava.com/log4j2/threadcontext-fish-tagging/

线程上下文堆栈会唯一地标记每个请求,用户可以在请求刚进入时将上下文信息压入堆栈(通过ThreadContext.put()),当发送错误邮件时,日志框架会获取压入的上下文数据进行发送。此时再看邮件效果会多出来一行MDC

@Slf4j
@Component
public class RequestLogFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request =  (HttpServletRequest) servletRequest;
        ThreadContext.put("URL", request.getRequestURL().toString());
        ThreadContext.put("HttpMethod", request.getMethod());
        ThreadContext.put("IPAddr", request.getRemoteAddr());
        ThreadContext.put("ContentType", request.getContentType());

        // 获取所有参数名
        Map<String, String> paramNames = new HashMap<>();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            paramNames.put(name, request.getParameter(name));
        }
        ThreadContext.put("ParamNames", CollectionUtils.isEmpty(paramNames) ? "{}" : JSON.toJSONString(paramNames));

        // TODO 需要时打开注释
        // log.info("URL: " + request.getRequestURL().toString());
        // log.info("Http Method: " + request.getMethod());
        // log.info("IP Addr: " + request.getRemoteAddr());
        // log.info("Content Type: " + request.getContentType());
        // log.info("ParamNames: " + (CollectionUtils.isEmpty(paramNames) ? "{}" : JSON.toJSONString(paramNames)));

        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        //请求结束后清除信息
        ThreadContext.clearAll();
    }
}

4、自定义日志邮件报警

首先参考SpringBoot异步、邮件、定时任务搭建好基本邮件框架,并按照上面自定义appender配置好,在自定义的appender进行修改

private static final long INTERVAL = 1 * 1000 * 60;
private long lastAlarmTime = 0;

private static final String TO = "[email protected]";
private static final String SUBJECT = "测试邮件";
private static final String CONTENT = "test content";
private static final String SENDER = "[email protected]";

public static void sendMail(String context) {
    SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
    //发件人
    simpleMailMessage.setFrom(SENDER);
    //邮件接收人,可以是多个
    simpleMailMessage.setTo(TO);
    //邮件主题
    simpleMailMessage.setSubject(SUBJECT);
    //邮件内容
    simpleMailMessage.setText(context);

    JavaMailSender javaMailSender = ContextUtil.getApplicationContext().getBean(JavaMailSender.class);
    javaMailSender.send(simpleMailMessage);
}


//这里按照不同的日志框架自己修改
@Override
public void append(LogEvent event) {
    // 到达级别才报警
    if(event.getLevel().equals(Level.ERROR) || event.getLevel().equals(Level.FATAL)){
        if(canAlarm()){
            sendMail(String.valueOf(event.getMessage()));
        }
    }

}

private boolean canAlarm() {
    // 做一个简略的频率过滤
    long now = System.currentTimeMillis();
    if (now - lastAlarmTime >= INTERVAL) {
        lastAlarmTime = now;
        return true;
    } else {
        return false;
    }
}

上面的邮件发送中,需要使用JavaMailSender,因为MailUtil这个不是Spring bean对象,而是普通的java对象,由日志框架实例化,不能直接注入获取JavaMailSender,所以借助Spring的ApplicationContext来获取对象。

@Component
public class ContextUtil implements ApplicationContextAware, EnvironmentAware {

    private static ApplicationContext applicationContext;

    private static Environment environment;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ContextUtil.applicationContext = applicationContext;
    }

    @Override
    public void setEnvironment(Environment environment) {
        ContextUtil.environment = environment;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static Environment getEnvironment() {
        return environment;
    }
}

当然了,还可以在全局异常那块进行日志发送告警


参考:

https://blog.csdn.net/java821643/article/details/88753898

http://logback.qos.ch/manual/index.html

https://blog.csdn.net/weixin_39370859/article/details/105039787

log4j2自定义Appender(输出到文件/RPC服务中)

SpringBoot实战基于异常日志的邮件报警

你可能感兴趣的:(#,SpringBoot,log4j2,spring,boot)