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!