前提

当前(2022-02前后)日志框架logback的最新版本1.3.0已经更新到1.3.0-alpha14版本,此版本为非stable版本,相对于最新稳定版1.2.10来说,虽然slf4j-api版本升级了,但使用的API大体不变,对于XML配置来看提供了import标签对于多appender来说可以简化配置。鉴于软件最新版本强迫症,这里基于1.3.0-alpha14版本分析一下常用的logback配置项以及一些实践经验。

日志等级

日志等级的定义见Level类:

序号 日志级别 备注
1 OFF Integer.MAX_VALUE 关闭日志打印
2 TRACE 5000 -
3 DEBUG 10000 -
4 INFO 20000 -
5 WARN 30000 -
6 ERROR 40000 -
7 ALL Integer.MIN_VALUE 打印所有日志

日志等级的值越大,级别越高,级别由低到高(左到右)排列如下:

TRACE < DEBUG < INFO < WARN < ERROR

日志等级一般会作为日志事件的过滤条件或者查询条件,在一些特定组件中,可以通过配置项去决定丢弃低级别的日志事件或者忽略指定级别的日志事件。

依赖引入

因为当前的1.3.0-alpha14版本太过"新",大部分主流框架尚未集成,如果要尝鲜最好通过BOM全局指定对应依赖的版本:

<!-- BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.0-alpha14</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.3.0-alpha14</version>
</dependency>
</dependencies>
</dependencyManagement> <!-- 依赖集合 -->
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>

logback.xml基本配置示例

1.2.x1.3.x提供的API基本没有改变,并且1.3.x向前兼容了旧的配置方式,提供了import标签用于简化class的指定:

  • 1.2.x(旧的配置方式)前的配置方式:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] - %msg%n</pattern>
</encoder>
</appender> <root level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
  • 1.3.x可用的新配置方式:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"> <import class="ch.qos.logback.core.ConsoleAppender"/>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/> <appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] - %msg%n</pattern>
</encoder>
</appender> <root level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

对于单个Appender配置来看,import标签的引入看起来无法简化配置,但是对于多Appender配置来看可以相对简化class的指定,例如:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<property name="app" value="api-gateway"/>
<property name="filename" value="server"/> <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<import class="ch.qos.logback.classic.AsyncAppender"/>
<import class="ch.qos.logback.classic.filter.ThresholdFilter"/>
<import class="cn.vlts.logback.IncludeLevelSetFilter"/> <appender name="INFO" class="RollingFileAppender">
<file>/data/log-center/${app}/${filename}.log</file>
<rollingPolicy class="TimeBasedRollingPolicy">
<fileNamePattern>/data/log-center/${app}/${filename}.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>14</maxHistory>
</rollingPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] ${app} - %msg%n</pattern>
</encoder>
<filter class="IncludeLevelSetFilter">
<levels>INFO,WARN</levels>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <appender name="ERROR" class="RollingFileAppender">
<file>/data/log-center/${app}/${filename}-error.log</file>
<rollingPolicy class="TimeBasedRollingPolicy">
<fileNamePattern>/data/log-center/${app}/${filename}-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>14</maxHistory>
</rollingPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] ${app} - %msg%n</pattern>
</encoder>
<filter class="ThresholdFilter">
<level>ERROR</level>
</filter>
</appender> <appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] - %msg%n</pattern>
</encoder>
<filter class="ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender> <appender name="ASYNC_INFO" class="AsyncAppender">
<queueSize>1024</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="INFO"/>
</appender> <appender name="ASYNC_ERROR" class="AsyncAppender">
<queueSize>256</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="ERROR"/>
</appender> <logger name="sun.rmi" level="error"/>
<logger name="sun.net" level="error"/>
<logger name="javax.management" level="error"/>
<logger name="org.redisson" level="warn"/>
<logger name="com.zaxxer" level="warn"/> <root level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC_INFO"/>
<appender-ref ref="ASYNC_ERROR"/>
</root>
</configuration>

上面的配置是某个API网关的logback.xml配置示例,这里用到了一个自定义Filter实现IncludeLevelSetFilter

// cn.vlts.logback.IncludeLevelSetFilter
public class IncludeLevelSetFilter extends AbstractMatcherFilter<ILoggingEvent> { private String levels; private Set<Level> levelSet; @Override
public FilterReply decide(ILoggingEvent event) {
return levelSet.contains(event.getLevel()) ? onMatch : onMismatch;
} public void setLevels(String levels) {
this.levels = levels;
this.levelSet = Arrays.stream(levels.split(","))
.map(item -> Level.toLevel(item, Level.INFO)).collect(Collectors.toSet());
} @Override
public void start() {
if (Objects.nonNull(this.levels)) {
super.start();
}
}
}

IncludeLevelSetFilter用于接受指定日志级别集合的日志记录,如果有更加精细的日志过滤条件(内置常用的LevelFilterThresholdFilter等无法满足实际需求),可以自行实现ch.qos.logback.core.filter.Filter接口定制日志事件过滤策略。这份文件定义了五个appender,其中有2个用于异步增强,核心appender3个:

  • STDOUTConsoleAppender,标准输出同步日志打印,级别为DEBUG或以上
  • ASYNC_INFOINFO):RollingFileAppender,异步滚动文件追加日志打印,级别为INFO或者WARN,追加到文件/data/log-center/api-gateway/server.log,归档文件格式为/data/log-center/api-gateway/server-${yyyy-MM-dd}.log.${compression_suffix},归档文件最多保存14个副本
  • ASYNC_ERRORERROR):RollingFileAppender,异步滚动文件追加日志打印,级别为ERROR,追加到文件/data/log-center/api-gateway/server-error.log,归档文件格式为/data/log-center/api-gateway/server-error-${yyyy-MM-dd}.log.${compression_suffix},归档文件最多保存14个副本

常用的Appender及其参数

常用的Appender有:

  • ConsoleAppender
  • FileAppender
  • RollingFileAppender
  • AsyncAppender

其中,RollingFileAppenderFileAppender的扩展(子类),现实场景中ConsoleAppenderRollingFileAppender的适用范围更广。从类继承关系上看,ConsoleAppenderFileAppender都支持定义Encoder,最常用的Encoder实现就是PatternLayoutEncoder,用于定制日志事件的最终输出格式。关于Encoder,由于其参数格式太过灵活,参数众多,限于篇幅本文不会展开介绍

ConsoleAppender

ConsoleAppender用于追加日志到控制台,对于Java应用来说就是追加到System.out或者System.errConsoleAppender支持的参数如下:

参数 类型 默认值 描述
encoder ch.qos.logback.core.encoder.Encoder PatternLayoutEncoder 用于定义Encoder
target String System.out 定义输出目标,可选值System.outSystem.err
withJansi boolean false 是否支持Jansi,这是一个支持多彩ANSI编码的类库,用于输出彩色控制台字体

ConsoleAppender的使用例子如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"> <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.ConsoleAppender"/> <appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] - %msg%n</pattern>
</encoder>
</appender> <root level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

RollingFileAppender

RollingFileAppenderFileAppender的子类,支持输出日志到文件中,并且支持通过滚动规则(RollingPolicy)的设置,可以安装内置或者自定义规则去分割、归档日志文件。RollingFileAppender支持的参数如下:

参数 类型 默认值 描述
file String - 用于定义当前日志输出的目标文件
append boolean true 用于定义当前日志输出是否追加模式
rollingPolicy ch.qos.logback.core.rolling.RollingPolicy - 日志文件滚动策略
triggeringPolicy ch.qos.logback.core.rolling.TriggeringPolicy - 日志文件滚动时机触发策略
prudent boolean false 是否支持prudent模式(开启此模式会在FileLock保护下写入日志文件),FileAppender支持此模式

常用的RollingPolicy内置实现有:

  • TimeBasedRollingPolicy:最常用的日志滚动策略,基于日期时间进行滚动分割和归档
参数 类型 默认值 描述
fileNamePattern String - 文件名格式,例如/var/log/app/server.%d{yyyy-MM-dd, UTC}.log.gz
maxHistory int - 最大归档文件数量
totalSizeCap FileSize - 所有归档文件总大小的上限
cleanHistoryOnStart boolean false 标记为trueAppender启动时候清理(不合法的)归档日志文件
  • SizeAndTimeBasedRollingPolicy:基于日志文件大小或者日期时间进行滚动分割和归档
参数 类型 默认值 描述
fileNamePattern String - 文件名格式,例如/var/log/app/server.%d{yyyy-MM-dd, UTC}.%i.log.gz
maxHistory int - 最大归档文件数量
totalSizeCap FileSize - 所有归档文件总大小的上限
cleanHistoryOnStart boolean false 标记为trueAppender启动时候清理(不合法的)归档日志文件
  • FixedWindowRollingPolicy:基于日志文件大小或者日期时间进行滚动分割和归档
参数 类型 默认值 描述
fileNamePattern String - 文件名格式,例如/var/log/app/server.%d{yyyy-MM-dd, UTC}.log.gz
minIndex int - 窗口索引下界
maxIndex int - 窗口索引上界

常用的TriggeringPolicy内置实现有:

  • SizeBasedTriggeringPolicy:基于文件大小的触发策略
  • DefaultTimeBasedFileNamingAndTriggeringPolicylogback内部使用):基于日期时间和文件名通过判断系统日期时间触发

这里值得注意的几点:

  • TimeBasedRollingPolicy自身也实现了TriggeringPolicy接口(委托到DefaultTimeBasedFileNamingAndTriggeringPolicy中执行),提供了兜底的日志文件滚动时机触发策略,所以在使用TimeBasedRollingPolicy的时候可以不需要指定具体的triggeringPolicy实例
  • SizeAndTimeBasedRollingPolicy使用了子组件SizeAndTimeBasedFNATP实现,旧版本一般使用SizeAndTimeBasedFNATP实现基于文件大小或者日期时间进行日志滚动归档功能,此组件在新版本中建议使用SizeAndTimeBasedRollingPolicy替代
  • logback会基于参数fileNamePattern中定义的文件名后缀去选择对应的归档日志文件压缩算法,例如.zip会选用ZIP压缩算法,.gz会选用GZIP压缩算法
  • SizeAndTimeBasedRollingPolicyFixedWindowRollingPolicyfileNamePattern参数都支持%i占位符,用于定义归档文件的索引值,其实索引为0
  • FixedWindowRollingPolicySizeBasedTriggeringPolicy组合使用可以实现基于文件大小进行日志滚动的功能(TimeBasedRollingPolicy的对标功能)

AsyncAppender

AsyncAppender用于异步记录日志,需要搭配其他类型的Appender使用,直观上看就是把"异步"功能赋予其他Appender实例。AsyncAppender支持的参数如下:

参数 类型 默认值 描述
queueSize int 256 存放日志事件的阻塞队列的最大容量
discardingThreshold int queueSize / 5 日志事件丢弃阈值,阻塞队列剩余容量小于此阈值,会丢弃除了WARNERROR级别的其他所有级别的日志事件,此阈值设置为0相当于不会丢弃任意日志事件
includeCallerData boolean false 日志事件中是否包含调用者数据,设置为true会添加调用线程信息、MDC中的数据等
maxFlushTime int 1000 异步日志写入工作线程退出的最大等待时间,单位为毫秒
neverBlock boolean false 是否永不阻塞(当前应用的调用线程),设置为true的时候队列满了会直接丢弃当前新添加的日志事件

需要通过<appender-ref>标签关联一个已经存在的Appender实例到一个全新的AsyncAppender实例中,并且一个AsyncAppender实例是可以基于多个<appender-ref>标签添加多个Appender实例,例如:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"> <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<import class="ch.qos.logback.classic.AsyncAppender"/> <appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] - %msg%n</pattern>
</encoder>
</appender> <appender name="ASYNC_STDOUT" class="AsyncAppender">
<queueSize>1024</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="STDOUT"/>
<!-- <appender-ref ref="OTHER_APPENDER"/> -->
</appender> <root level="DEBUG" additivity="false">
<appender-ref ref="ASYNC_STDOUT"/>
</root>
</configuration>

指定配置文件进行初始化

logback内置的初始化策略(按照优先级顺序)如下:

  • 通过ClassPath中的logback-test.xml文件初始化
  • 通过ClassPath中的logback.xml文件初始化
  • 通过SPI的方式由ClassPath中的META-INF\services\ch.qos.logback.classic.spi.Configurator进行初始化
  • 如果前面三步都没有配置,则通过BasicConfigurator初始化,提供最基础的日志处理功能

可以通过命令行参数logback.configurationFile直接指定外部的logback配置文件(后缀必须为.xml或者.groovy),这种初始化方式会忽略内置的初始化策略,例如:

java -Dlogback.configurationFile=/path/conf/config.xml app.jar

或者设置系统参数(下面的Demo来自官方例子):

public class ServerMain {
public static void main(String args[]) throws IOException, InterruptedException {
// must be set before the first call to LoggerFactory.getLogger();
// ContextInitializer.CONFIG_FILE_PROPERTY is set to "logback.configurationFile"
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, "/path/to/config.xml");
...
}
}

这种方式要求尽量不能存在静态成员变量调用了LoggerFactory.getLogger()方法,因为有可能会导致提前使用内置的初始化策略进行初始化。

编程式初始化

为了完全控制logback的初始化,可以使用纯编程式进行设置(下面的编程式配置按照"最佳实践"中的配置文件进行编写):

import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.ThresholdFilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import org.slf4j.LoggerFactory; /**
* @author throwable
* @version v1
* @description
* @since 2022/2/13 13:09
*/
public class LogbackLauncher { public static void main(String[] args) throws Exception {
LoggerContext loggerContext = (LoggerContext) org.slf4j.LoggerFactory.getILoggerFactory();
loggerContext.reset();
Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
// 移除所有Appender
rootLogger.detachAndStopAllAppenders();
// RollingFileAppender
PatternLayoutEncoder fileEncoder = new PatternLayoutEncoder();
fileEncoder.setContext(loggerContext);
fileEncoder.setPattern("[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] ${app} - %msg%n");
fileEncoder.start();
RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<>();
fileAppender.setContext(loggerContext);
fileAppender.setName("FILE");
fileAppender.setFile("/data/log-center/api-gateway/server.log");
fileAppender.setAppend(true);
fileAppender.setEncoder(fileEncoder);
ThresholdFilter fileFilter = new ThresholdFilter();
fileFilter.setLevel("INFO");
fileAppender.addFilter(fileFilter);
TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setParent(fileAppender);
rollingPolicy.setContext(loggerContext);
rollingPolicy.setFileNamePattern("/data/log-center/api-gateway/server.%d{yyyy-MM-dd}.log.gz");
rollingPolicy.setMaxHistory(14);
rollingPolicy.start();
fileAppender.setRollingPolicy(rollingPolicy);
fileAppender.start();
AsyncAppender asyncAppender = new AsyncAppender();
asyncAppender.setName("ASYNC_FILE");
asyncAppender.setContext(loggerContext);
asyncAppender.setDiscardingThreshold(0);
asyncAppender.setQueueSize(1024);
asyncAppender.addAppender(fileAppender);
asyncAppender.start();
// ConsoleAppender
PatternLayoutEncoder consoleEncoder = new PatternLayoutEncoder();
consoleEncoder.setContext(loggerContext);
consoleEncoder.setPattern("[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] - %msg%n");
consoleEncoder.start();
ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<>();
consoleAppender.setContext(loggerContext);
consoleAppender.setEncoder(consoleEncoder);
ThresholdFilter consoleFilter = new ThresholdFilter();
consoleFilter.setLevel("DEBUG");
consoleAppender.addFilter(consoleFilter);
consoleAppender.start();
rootLogger.setLevel(Level.DEBUG);
rootLogger.setAdditive(false);
rootLogger.addAppender(consoleAppender);
rootLogger.addAppender(asyncAppender); org.slf4j.Logger logger = LoggerFactory.getLogger(LogbackDemo1.class);
logger.debug("debug nano => {}", System.nanoTime());
logger.info("info nano => {}", System.nanoTime());
logger.warn("warn nano => {}", System.nanoTime());
logger.error("error nano => {}", System.nanoTime());
}
}

最佳实践

实践中建议使用logback文档中提到的最常用的:RollingFileAppender + TimeBasedRollingPolicy + ConsoleAppender(这个是为了方便本地开发调试)组合。一般来说,日志文件最终会通过Filebeat等日志收集组件上传到ELK体系,在合理定义日志的输出格式(例如在输出格式中指定Level参数)前提下,其实可以不拆分不同级别的日志文件并且输出所有INFO或者以上级别的日志,最终在Kibana中也可以轻易通过参数level: ${LEVEL}进行不同级别的日志查询。而对于性能要求比较高的服务例如API网关,建议把RollingFileAppender关联到AsyncAppender实例中,在内存足够的前提下调大queueSize参数并且设置discardingThreshold = 0(队列满了不丢弃日志事件,有可能会阻塞调用线程,无法忍受可以自行扩展异步日志功能)。在服务器磁盘充足的前提下,一般对于归档日志的文件大小不设置上限,只设置最大归档文件数量,建议数量为14 ~ 30(也就是2周到1个月之间)。下面是一个模板:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<property name="app" value="应用名,例如api-gateway"/>
<property name="filename" value="文件名前缀,例如server"/> <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<import class="ch.qos.logback.classic.AsyncAppender"/>
<import class="ch.qos.logback.classic.filter.ThresholdFilter"/> <appender name="FILE" class="RollingFileAppender">
<file>/data/log-center/${app}/${filename}.log</file>
<rollingPolicy class="TimeBasedRollingPolicy">
<fileNamePattern>/data/log-center/${app}/${filename}.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>14</maxHistory>
</rollingPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] ${app} - %msg%n</pattern>
</encoder>
<filter class="ThresholdFilter">
<level>INFO</level>
</filter>
</appender> <appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>[%date{ISO8601}] [%level] %logger{80} [%thread] [%X{TRACE_ID}] - %msg%n</pattern>
</encoder>
<filter class="ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender> <appender name="ASYNC_FILE" class="AsyncAppender">
<queueSize>1024</queueSize>
<!-- 队列满了不丢弃任一日志事件 -->
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="FILE"/>
</appender> <!-- 需要覆盖日志级别,减少不关注的日志输出 -->
<logger name="sun.rmi" level="error"/>
<logger name="sun.net" level="error"/>
<logger name="javax.management" level="error"/>
<logger name="org.redisson" level="warn"/>
<logger name="com.zaxxer" level="warn"/> <root level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC_FILE"/>
</root>
</configuration>

小结

这篇文章仅仅介绍logback最新版本的一些基本配置和实践经验,也作为一篇日后随时可以拿起使用的流水账笔记存档。

参考资料:

(本文完 c-2-d e-a-20220212 这一两个月的基金有点可怕)

logback1.3.x配置详解与实践的更多相关文章

  1. kafka原理和实践(五)spring-kafka配置详解

    系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...

  2. JSHint配置详解

    Also available on Github JSHint配置详解 增强参数(Enforcing Options) 本类参数设为true,JSHint会产生更多告警. bitwise 禁用位运算符 ...

  3. maven常用插件配置详解

    常用插件配置详解Java代码    <!-- 全局属性配置 --> <properties> <project.build.name>tools</proje ...

  4. 使用LVS实现负载均衡原理及安装配置详解

    负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均衡设备F5.Netscale.这里主要是学 ...

  5. JAVA环境变量配置详解(Windows)

    JAVA环境变量配置详解(Windows)   JAVA环境变量JAVA_HOME.CLASSPATH.PATH设置详解  Windows下JAVA用到的环境变量主要有3个,JAVA_HOME.CLA ...

  6. MapReduce On Yarn的配置详解和日常维护

    MapReduce On Yarn的配置详解和日常维护 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MapReduce运维概述 MapReduce on YARN的运维主要是 ...

  7. Web.xml配置详解(转)

    Web.xml配置详解 Posted on 2010-09-02 14:09 chinaifne 阅读(295105) 评论(16) 编辑 收藏 1 定义头和根元素 部署描述符文件就像所有XML文件一 ...

  8. Log4j配置详解(转)

    一.Log4j简介 Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合使 ...

  9. logback 常用配置详解<appender>

    logback 常用配置详解 <appender> <appender>: <appender>是<configuration>的子节点,是负责写日志的 ...

随机推荐

  1. 关于一类容斥原理设计 dp 状态的探讨

    写在前面 为什么要写?因为自己学不明白希望日后能掌握. 大体思路大概是 设计一个容斥的方案,并使其贡献可以便于计算. 得出 dp 状态,然后优化以得出答案. 下列所有类似 \([l,r]\) 这样的都 ...

  2. 如何在 CentOS 上安装 dos2unix 和 unix2dos 命令

    yum install -y dos2unix 注意:以上安装包既包含 dos2unix 命令,又包含 unix2dos 命令.

  3. linux tomcat【9.0.12】 使用 ssl证书 配置 https 的具体操作 【使用 域名 】

    1.前言 根据上一个随笔,已经可以正式在 阿里云服务器发布 工程了 ,但是用的协议默认是 http ,端口80 但是 http不安全 ,容易被拦截抓包 ,于是出来了个 https tomcat发布 对 ...

  4. Spark词频前十的统计练习

    注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6815390070254600712/ 承接上一个文档<Spark本地环境实现wordCount单词计数> ...

  5. 基于windows环境VsCode的ESP32开发环境搭建

    1. 基于windows环境VsCode的ESP32开发环境搭建,网上有各类教程,但是我实测却不行. 例如我在vscode内安装的乐鑫插件,扩展配置项是下图这样: 而百度的各类博文却都是这样: 经过网 ...

  6. 51 Nod 1006 最长公共子序列(LCS & DP)

    原题链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1006 题目分析: 首先先知道LCS问题,这有两种: Long ...

  7. test_3 简单密码破解

    题目描述:密码是我们生活中非常重要的东东,我们的那么一点不能说的秘密就全靠它了.哇哈哈. 接下来渊子要在密码之上再加一套密码,虽然简单但也安全. 假设渊子原来一个BBS上的密码为zvbo9441987 ...

  8. 阿里神器 Seata 实现 TCC模式 解决分布式事务,真香!

    今天这篇文章介绍一下Seata如何实现TCC事务模式,文章目录如下: 什么是TCC模式? TCC(Try Confirm Cancel)方案是一种应用层面侵入业务的两阶段提交.是目前最火的一种柔性事务 ...

  9. 深入浅出 CSS 动画

    本文将比较全面细致的梳理一下 CSS 动画的方方面面,针对每个属性用法的讲解及进阶用法的示意,希望能成为一个比较好的从入门到进阶的教程. CSS 动画介绍及语法 首先,我们来简单介绍一下 CSS 动画 ...

  10. Unity打包安卓项目问题汇总(持续更新)

    1.V1,v2签名问题 安卓11以上--v1签名无法使用: 安卓7以下--v2无法使用: 应用宝不支持没有v1签名的包: AndroidStudio版本2020打签名包时无法勾选v1,v2选项,4.2 ...