logback进阶


参考:

以上是logback的官方文档的翻译,值得一看。

在一般的服务中,我们调试的时候将日志打印到控制台console,生产环境中将日志输出到日志文件

1.对日志分级别level进行打印,一般至少分三个文件error.log,warn.log和info.log

2.每天的日志写入以当天命名的文件夹中,比如20220525,

3.日志文件指定一个阈值大小,比如10M,或者100M,超过阈值对日志文件进行切分,新日志写入新文件。

分级别、分日期、滚动写入。以上的配置对于一般的服务就足够了。

但有一种特殊的服务,比如调度系统,它会执行非常多的异构任务,这些任务的日志为了方便查看,是不应该写入一个文件中的。

需要将每个任务写入特定的文件中。那么logback能实现吗?

答案是肯定的!

首先我们贴出logback.xml配置文件。

<?xml version="1.0" encoding="UTF-8"?>




<configuration>
    <property name="logDir" value="logs" />
    <property name="maxHistory" value="30" />
    <property name="log.base" value="logs"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%npattern>
        encoder>
    appender>



    
    <appender name="ERROR"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logDir}/error.logfile>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERRORlevel>
            <onMatch>ACCEPTonMatch>
            <onMismatch>DENYonMismatch>
        filter>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/%d{yyyyMMdd}/error.%i.logfileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MBmaxFileSize>
            timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>${maxHistory}maxHistory>
        rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger -
                %msg%npattern>
            <charset class="java.nio.charset.Charset">UTF-8charset>
        encoder>
        <append>falseappend>
        <prudent>falseprudent>
    appender>

    
    <appender name="WARN"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logDir}/warn.logfile>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARNlevel>
            <onMatch>ACCEPTonMatch>
            <onMismatch>DENYonMismatch>
        filter>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/%d{yyyyMMdd}/warn.%i.logfileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MBmaxFileSize>
            timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>${maxHistory}maxHistory>
        rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%npattern>
            <charset class="java.nio.charset.Charset">UTF-8charset>
        encoder>
        <append>falseappend>
        <prudent>falseprudent>
    appender>

    
    <appender name="INFO"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logDir}/info.logfile>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFOlevel>
            <onMatch>ACCEPTonMatch>
            <onMismatch>DENYonMismatch>
        filter>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/%d{yyyyMMdd}/info.%i.logfileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MBmaxFileSize>
            timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>${maxHistory}maxHistory>
        rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%npattern>
            <charset class="java.nio.charset.Charset">UTF-8charset>
        encoder>
        <append>falseappend>
        <prudent>falseprudent>
    appender>

    <appender name="TASKLOGFILE" class="ch.qos.logback.classic.sift.SiftingAppender">
        <filter class="cc.eslink.metadata.scheduler.log.TaskLogFilter"/>
        <Discriminator class="cc.eslink.metadata.scheduler.log.TaskLogDiscriminator">
            <key>taskAppIdkey>
            <logBase>${log.base}logBase>
        Discriminator>
        <sift>
            <appender name="FILE-${taskAppId}" class="ch.qos.logback.core.FileAppender">
                <file>${log.base}/${taskAppId}.logfile>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%npattern>
                    <charset class="java.nio.charset.Charset">UTF-8charset>
                encoder>
                <append>trueappend>
            appender>
        sift>
    appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="ERROR" />
        <appender-ref ref="WARN" />
        <appender-ref ref="INFO" />
        <appender-ref ref="TASKLOGFILE" />
    root>
configuration>

name等于ERROR、WARN、INFO的appender都是一般的配置,我们指定任务写入指定文件的配置主要在TASKLOGFILE的appender中。

为了实现行级日志的指定文件写入,我们自定义了两个配置类。分别是TaskLogFilter和TaskLogDiscriminator

//TaskLogFilter 决定哪些日志会被筛选到名为TASKLOGFILE的appender中
public class TaskLogFilter extends Filter {
    private Level level;

    public void setLevel(String level) {
        this.level = Level.toLevel(level);
    }

    @Override
    public FilterReply decide(ILoggingEvent event) {
        FilterReply filterReply = FilterReply.DENY;
        if ((event.getThreadName().startsWith("taskAppId")
                && event.getLoggerName().startsWith("TaskLogLogger"))
                || event.getLevel().isGreaterOrEqual(level)) {
            filterReply = FilterReply.ACCEPT;
        }
        return filterReply;
    }
}

日志的筛选我们已经完成了,下一步就是把对应任务的日志写入对应的文件中,这个实现在TaskLogDiscriminator中

//通过一系列操作,我们将groupName=group,jobName=job的调度任务的日志写入到group/job.log文件中
public class TaskLogDiscriminator extends AbstractDiscriminator {
    /**
     * key
     */
    private String key;

    /**
     * log base
     */
    private String logBase;

    @Override
    public String getDiscriminatingValue(ILoggingEvent event) {
        String key = "unknown_task";
        if (event.getLoggerName().startsWith("TaskLogLogger")) {
            String threadName = event.getThreadName();

            String part1 = threadName.split("=")[1];
            String prefix = "TASK-";
            if (part1.startsWith(prefix)) {
                key = part1.substring(prefix.length()).replaceFirst("-", "/");
            }
        }
        return key;
    }

    @Override
    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getLogBase() {
        return logBase;
    }

    public void setLogBase(String logBase) {
        this.logBase = logBase;
    }
}

大功告成,感谢logback!