logback自定义appender

1、定义appender

1.1 logback.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
    <!-- <property name="LOG_HOME" value="d:" /> -->

    <!-- ch.qos.logback.core.ConsoleAppender 控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>


    <!-- 自定义文件输出-错误日志 -->
    <appender name="ErrorFile" class="com.ybw.logback.demo.filter.LogFileAppender">
        <File>${LOG_HOME:-c:/}logs/error/error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_HOME:-c:/}logs/error/error-%d{yyyy-MM-dd}.%i.log
            </FileNamePattern>
            <MaxHistory>30</MaxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>100MB</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder charset="UTF-8">
            <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>


    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="ErrorFile"/>
    </root>
</configuration>

 1.2 自定义appender代码

package com.ybw.logback.demo.filter;

import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import ch.qos.logback.core.status.ErrorStatus;
import com.ybw.logback.demo.constant.fs.FSConstant;
import com.ybw.logback.demo.utils.FsUtils;

import java.io.IOException;


/**
 * 自定义的appender
 * 1.可以对接受到的event中的日志信息做任何操作处理
 * 2.可以在同步写日志文件的时候,存入MySQL或者es
 *
 * @author ybw
 * @version V1.0
 * @className LogFileAppender
 * @date 2022/9/16
 **/
public class LogFileAppender<E> extends RollingFileAppender<E> {

    public void setEncoder(Encoder encoder) {
        //自定义的appender的父类OutputStreamAppender在启动的时候会判断是否有encoder ,无的话自定义appender启动会失败 
        super.setEncoder(encoder);
    }

    /**
     * 覆盖父类OutputStreamAppender的日志记录功能,再这里面做日志切操作功能
     *
     * @param event
     */
    protected void subAppend(E event) {
        if (this.isStarted()) {
            try {
                if (event instanceof DeferredProcessingAware) {
                    ((DeferredProcessingAware) event).prepareForDeferredProcessing();
                }
                //根据标签规则拿到日志字符串,这里可以再次修改日志信息
                byte[] byteArray = super.encoder.encode(event);
                //做一些日志的特殊处理
                sendFs(byteArray);
//                writeMongo(...);
//                writeRedis(...);
//                writeMq(...);
                //写入日志文件
                writeBytes(byteArray);
            } catch (IOException var3) {
                this.started = false;
                this.addStatus(new ErrorStatus("IO failure in appender", this, var3));
            }
        }
    }

    /**
     * 飞书报警
     *
     * @param byteArray
     * @methodName: sendFs
     * @return: void
     * @author: ybw
     * @date: 2022/9/15
     **/
    private void sendFs(byte[] byteArray) {
        if (byteArray != null && byteArray.length != 0) {
            this.lock.lock();
            try {
                FsUtils.sendTextMessage(FSConstant.TextContentEnum.ERROR_LOG, new String(byteArray));
            } finally {
                this.lock.unlock();
            }
        }
    }

    private void writeBytes(byte[] byteArray) throws IOException {
        if (byteArray != null && byteArray.length != 0) {
            this.lock.lock();
            try {
                this.getOutputStream().write(byteArray);
                if (this.isImmediateFlush()) {
                    this.getOutputStream().flush();
                }
            } finally {
                this.lock.unlock();
            }
        }
    }
}

1.3 测试

package com.ybw.logback.demo.custom;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

/**
 * @className CustomLog
 * @author ybw
 * @date 2022/9/15 
 * @version V1.0
 **/
@Slf4j
public class CustomLog {

    /**
     *
     * @methodName: print
     * @return: void
     * @author: ybw
     * @date: 2022/9/15
     **/
    @Test
    public void print(){
        log.info("aa");
        log.error("bb");
    }
}

错误日志、报警内容如下:

[ERROR] 2022-09-17 18:07:51.577 [main] c.ybw.logback.demo.custom.CustomLog - bb

2、logback.xml变量使用-spring配置

logback的变量作用于有三种:local,context,system

  •  local 作用域在配置文件内有效;
  •  context 作用域的有效范围延伸至 logger context;
  •  system 作用域的范围最广,整个 JVM 内都有效;

logback 在替换变量时,首先搜索 local 变量,然后搜索 context,然后搜索 system,在spring项目中,应将变量的作用域设置为context,并交给spring控制。

2.1 spring的配置作为变量

application.yml

server:
  port: 8080
spring:
  application:
    name: logback-online-alarm
  profiles:
    active: dev

application-dev.yml

#飞书
fs:
  bot:
    webhook:
      suf: /aa #测试

logback.xml配置如下,PROFILES-ACTIVE、FS-BOT-WEBHOOK-SUF为spring配置文件中的属性

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--获取spring配置文件的变量-->
    <springProperty scope="context" name="PROFILES-ACTIVE" source="spring.profiles.active" defaultValue=""/>
    <springProperty scope="context" name="FS-BOT-WEBHOOK-SUF" source="fs.bot.webhook.suf" defaultValue=""/>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
    <!-- <property name="LOG_HOME" value="d:" /> -->

    <!-- ch.qos.logback.core.ConsoleAppender 控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>



    <!-- 所有日志输出 -->
    <appender name="AllFile"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${LOG_HOME:-c:/}logs/all/all.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_HOME:-c:/}logs/all/all-%d{yyyy-MM-dd}.%i.log
            </FileNamePattern>
            <MaxHistory>30</MaxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>100MB</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n
            </pattern>
        </layout>
    </appender>

    <!-- 自定义文件输出-错误日志 -->
    <appender name="ErrorFile" class="com.ybw.logback.demo.config.LogFileAppender">
        <File>${LOG_HOME:-c:/}logs/error/error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_HOME:-c:/}logs/error/error-%d{yyyy-MM-dd}.%i.log
            </FileNamePattern>
            <MaxHistory>30</MaxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>100MB</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder charset="UTF-8">
            <pattern>[${PROFILES-ACTIVE}] [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
        <fsSuf>${FS-BOT-WEBHOOK-SUF}</fsSuf>
    </appender>


    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="ErrorFile"/>
        <appender-ref ref="AllFile"/>
    </root>
</configuration>

2.2 自定义appender代码

package com.ybw.logback.demo.config;

import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import ch.qos.logback.core.status.ErrorStatus;
import com.ybw.logback.demo.constant.fs.FSConstant;
import com.ybw.logback.demo.utils.FsUtils;
import lombok.Data;

import java.io.IOException;


/**
 * 自定义的appender
 * 1.可以对接受到的event中的日志信息做任何操作处理
 * 2.可以在同步写日志文件的时候,存入mysql或者es
 *
 * @author ybw
 * @version V1.0
 * @className LogFileAppender
 * @date 2022/9/16
 **/
@Data
public class LogFileAppender<E> extends RollingFileAppender<E> {

    private String fsSuf;

    @Override
    public void setEncoder(Encoder encoder) {
        //自定义的appender的父类OutputStreamAppender在启动的时候会判断是否有encoder ,无的话自定义appender启动会失败 
        super.setEncoder(encoder);
    }

    /**
     * 覆盖父类OutputStreamAppender的日志记录功能,再这里面做日志切操作功能
     *
     * @param event
     */
    @Override
    protected void subAppend(E event) {
        if (this.isStarted()) {
            System.out.println("fsSuf:"+fsSuf);
            try {
                if (event instanceof DeferredProcessingAware) {
                    ((DeferredProcessingAware) event).prepareForDeferredProcessing();
                }
                //根据标签规则拿到日志字符串,这里可以再次修改日志信息
                byte[] byteArray = super.encoder.encode(event);
                //做一些日志的特殊处理
                sendFs(byteArray);
//                writeMongo(...);
//                writeRedis(...);
//                writeMq(...);
                //写入日志文件
                writeBytes(byteArray);
            } catch (IOException var3) {
                this.started = false;
                this.addStatus(new ErrorStatus("IO failure in appender", this, var3));
            }
        }
    }

    /**
     * 飞书报警
     *
     * @param byteArray
     * @methodName: sendFs
     * @return: void
     * @author: ybw
     * @date: 2022/9/15
     **/
    private void sendFs(byte[] byteArray) {
        if (byteArray != null && byteArray.length != 0) {
            this.lock.lock();
            try {
                FsUtils.sendTextMessage(FSConstant.TextContentEnum.ERROR_LOG, new String(byteArray));
            } finally {
                this.lock.unlock();
            }
        }
    }

    private void writeBytes(byte[] byteArray) throws IOException {
        if (byteArray != null && byteArray.length != 0) {
            this.lock.lock();
            try {
                this.getOutputStream().write(byteArray);
                if (this.isImmediateFlush()) {
                    this.getOutputStream().flush();
                }
            } finally {
                this.lock.unlock();
            }
        }
    }
}
fsSuf为logback.xml配置的属性。

2.3 测试

package com.ybw.logback.demo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author ybw
 * @version V1.0
 * @className LogController
 * @date 2022/9/16
 **/
@RestController
@Slf4j
public class LogController {

    /**
     *
     * @methodName: getLog
     * @return: java.lang.String
     * @author: ybw
     * @date: 2022/9/16
     **/
    @GetMapping("/getLog")
    public String getLog() {
        log.info("aa");
        log.error("bb");
        return "OK";
    }
}

访问接口:http://localhost:8080/getLog

错误日志、报警内容如下:

[dev] [ERROR] 2022-09-17 18:30:31.513 [http-nio-8080-exec-5] c.y.l.demo.controller.LogController - bb

 [dev]为spring.profiles.active。

3、logback.xml变量使用-配置中心

以nacos为例

3.1 配置文件

bootstrap.yml配置

server:
  port: 8080
spring:
  application:
    name: logback-nacos
  profiles:
    active: dev
logging:
  config: classpath:logback.xml #日志配置要放在nacos配置之后

bootstrap-dev.yml配置

spring:
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        username: nacos
        password: nacos
        namespace: nacos-config #命名空间
        #        group: DEFAULT_GROUP #默认DEFAULT_GROUP
        file-extension: yml #配置中心的格式,默认为properties

配置中心

 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--获取spring配置文件的变量-->
    <springProperty scope="context" name="PROFILES-ACTIVE" source="spring.profiles.active" defaultValue=""/>
    <springProperty scope="context" name="FS-BOT-WEBHOOK-SUF" source="fs.bot.webhook.suf" defaultValue=""/>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
    <!-- <property name="LOG_HOME" value="d:" /> -->

    <!-- ch.qos.logback.core.ConsoleAppender 控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>



    <!-- 所有日志输出 -->
    <appender name="AllFile"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${LOG_HOME:-c:/}logs/all/all.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_HOME:-c:/}logs/all/all-%d{yyyy-MM-dd}.%i.log
            </FileNamePattern>
            <MaxHistory>30</MaxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>100MB</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n
            </pattern>
        </layout>
    </appender>

    <!-- 自定义文件输出-错误日志 -->
    <appender name="ErrorFile" class="com.ybw.logback.demo.config.LogFileAppender">
        <File>${LOG_HOME:-c:/}logs/error/error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_HOME:-c:/}logs/error/error-%d{yyyy-MM-dd}.%i.log
            </FileNamePattern>
            <MaxHistory>30</MaxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>100MB</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder charset="UTF-8">
            <pattern>[${PROFILES-ACTIVE}] [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
        <fsSuf>${FS-BOT-WEBHOOK-SUF}</fsSuf>
    </appender>


    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="ErrorFile"/>
        <appender-ref ref="AllFile"/>
    </root>
</configuration>

3.2 自定义appender代码

与2.2一致。

3.3 测试

与2.3一致。

fsSuf即配置中心的属性,会打印在控制台。

fsSuf:/aa


版权声明:本文为xixingzhe2原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。