Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好。
Logback主要分为三个模块:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
@Test
void test(){
final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
SPI全称Service Provider Interface,是java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件
他是一种服务
发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
通过模仿logback我们自己就可以修改config
我们可以找到BasicConfigurator
的源码,这是默认的logback的配置,我们将其模仿自己也可以写一个
public class DefineConfigurator extends ContextAwareBase implements Configurator {
public DefineConfigurator() {
}
@Override
public void configure(LoggerContext lc) {
this.addInfo("Setting up default configuration.");
ConsoleAppender<ILoggingEvent> ca = new ConsoleAppender();
ca.setContext(lc);
ca.setName("console");
LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder();
encoder.setContext(lc);
//格式化处理
final PatternLayout layout = new PatternLayout();
layout.setPattern("%d{HH :mm :ss.sSS} [%thread] %-5level %logger{36} - %msg%n");
// TTLLLayout layout = new TTLLLayout();
layout.setContext(lc);
layout.start();
encoder.setLayout(layout);
ca.setEncoder(encoder);
ca.start();
Logger rootLogger = lc.getLogger("ROOT");
rootLogger.addAppender(ca);
}
}
com.example.jul.DefineConfigurator
logback会依次读取以下类型配置文件:
如果均不存在会采用默认配置
https://logback.qos.ch/manual/configuration.html
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg ok %n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@Test
void test(){
final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
加入name叫做FIle
的Appender,让他应用ch.qos.logback.core.FileAppender
类
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>D://myApp.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg -to file %n</pattern>
</encoder>
</appender>
找到root节点,使用appender-ref
进行引入
<root level="debug">
<appender-ref ref="FILE" />
</root>
我们还可以通过配置logger节点的方式,对单独的logger进行配置
如下在logback.xml中配置了叫test的logger直接在控制台进行输出,当然你还可以指定到文件,流中等这些都取决于你怎么配置
//name
final Logger logger = LoggerFactory.getLogger("test");
//logback.xml
<logger name="test" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<appender name="ROLL" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 设置按尺寸和时间(同时满足)分割 -->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>D://rolling.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
</encoder>
</appender>
我们需要将filter配置在appender中
<appender>
<filter class="chapters.filters.SampleFilter" />
</appender>
LevelFilter 根据精确的级别匹配过滤事件。 如果事件的级别等于配置的级别,则过滤器接受或拒绝该事件,具体取决于 onMatch 和 onMismatch 属性的配置。
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
通过配置属性我们可以进行复用增强移植性
<property name="p1" value="define value"></property>
然后我们就可以通过${}
来进行使用
主线程应该用于执行业务,而日志不应该占用主线程,所以我们应该采用异步的方式进行优化
AsyncAppender 异步记录 ILoggingEvents
。 它仅充当事件调度程序,因此必须引用另一个附加程序才能执行任何有用的操作
实际上我们是在外面配好appender然后到异步中进行ref引入的
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
属性名称 | 类型 | 描述 |
---|---|---|
queueSize | int | 阻塞队列的最大容量。 默认, queueSize 设置为 256。 |
discardingThreshold | int | 默认情况下,当阻塞队列有 20% 容量时 剩下的,它将丢弃 TRACE、DEBUG 和 INFO 级别的事件, 只保留 WARN 和 ERROR 级别的事件。 为了保持所有 事件,将 discardingThreshold 为 0。 |
includeCallerData | boolean | 提取呼叫者数据可能相当昂贵。 改善 性能,默认情况下,与事件关联的调用者数据 事件添加到事件队列时不提取。 经过 等“廉价”数据 MDC 复制 你可以指导这个 appender 通过将 includeCallerData 属性设置为 true 来包含调用者数据。 |
maxFlushTime | int | 根据引用的 appender 的队列深度和延迟, 这 AsyncAppender可能需要不可接受的数量 是时候完全刷新队列了。 当。。。的时候 LoggerContext是 停了下来, AsyncAppender stop方法等待 直到这个超时,工作线程才能完成。 利用 maxFlushTime 指定最大队列刷新 以毫秒为单位的超时。 无法在此范围内处理的事件 窗口被丢弃。 此值的语义与 Thread.join(long) 。 |
neverBlock | boolean | 如果 false(默认)appender 将阻塞 追加到一个完整的队列而不是丢失消息。 调成 trueappender 只会删除消息和 不会阻止您的应用程序。 |
Apache Log4]2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:
https://logging.apache.org/log4j/2.x/index.html
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
@Test
void test(){
final Logger logger = LogManager.getLogger(this.getClass().getName());
logger.fatal("fatal");
logger.error("error");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency>
@Test
void test(){
final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
//占位符输出方式
logger.warn("{}first,{}second","zhangsan",18);
}
在Configuration
节点上配置monitorInterval
属性
以下配置36000s重加载
<Configuration monitorInterval="30">
</Configuration>
<Appenders>
<File name="File1" fileName="output.log" bufferedIO="false" advertiseURI="file://path/to/output.log" advertise="true">
...
</File>
</Appenders>
//第一种
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
//第二种
<PatternLayout>
<Pattern>%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
</PatternLayout>
通过在loggers下配置子logger指定名称可以自定义单个logger
<Loggers>
<Logger name="name1">
<AppenderRef ref="name"/>
<filter ... />
</Logger>
</Loggers>
以下是官网的给出的建议配置
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" strict="true" name="XMLConfigTest"
packages="org.apache.logging.log4j.test">
<Properties>
<Property name="filename">target/test.log</Property>
</Properties>
<Filter type="ThresholdFilter" level="trace"/>
<Appenders>
<Appender type="Console" name="STDOUT">
<Layout type="PatternLayout" pattern="%m MDC%X%n"/>
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
</Filters>
</Appender>
<Appender type="Console" name="FLOW">
<Layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/><!-- class and line number -->
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</Appender>
<Appender type="File" name="File" fileName="${filename}">
<Layout type="PatternLayout">
<Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
</Layout>
</Appender>
</Appenders>
<Loggers>
<Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
<Filter type="ThreadContextMapFilter">
<KeyValuePair key="test" value="123"/>
</Filter>
<AppenderRef ref="STDOUT"/>
</Logger>
<Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<AppenderRef ref="File"/>
</Logger>
<Root level="trace">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
使用 JSON 进行配置
除了 XML,Log4j 还可以使用 JSON 进行配置。 JSON 格式与简洁的 XML 格式非常相似。 每个键代表一个插件的名称,与之关联的键/值对就是它的属性。 如果一个键包含的不仅仅是一个简单的值,它本身就是一个从属插件。 在下面的示例中,ThresholdFilter、Console 和 PatternLayout 都是插件,而 Console 插件的 name 属性将被分配一个 STDOUT 值,而 ThresholdFilter 将被分配一个调试级别。
{ "configuration": { "status": "error", "name": "RoutingTest",
"packages": "org.apache.logging.log4j.test",
"properties": {
"property": { "name": "filename",
"value" : "target/rolling1/rollingtest-$${sd:type}.log" }
},
"ThresholdFilter": { "level": "debug" },
"appenders": {
"Console": { "name": "STDOUT",
"PatternLayout": { "pattern": "%m%n" },
"ThresholdFilter": { "level": "debug" }
},
"Routing": { "name": "Routing",
"Routes": { "pattern": "$${sd:type}",
"Route": [
{
"RollingFile": {
"name": "Rolling-${sd:type}", "fileName": "${filename}",
"filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
"PatternLayout": {"pattern": "%d %p %c{1.} [%t] %m%n"},
"SizeBasedTriggeringPolicy": { "size": "500" }
}
},
{ "AppenderRef": "STDOUT", "key": "Audit"}
]
}
}
},
"loggers": {
"logger": { "name": "EventLogger", "level": "info", "additivity": "false",
"AppenderRef": { "ref": "Routing" }},
"root": { "level": "error", "AppenderRef": { "ref": "STDOUT" }}
}
}
}
Log4j 还支持将 YAML 用于配置文件。 该结构遵循与 XML 和 YAML 配置格式相同的模式。
Configuration:
status: warn
name: YAMLConfigTest
properties:
property:
name: filename
value: target/test-yaml.log
thresholdFilter:
level: debug
appenders:
Console:
name: STDOUT
target: SYSTEM_OUT
PatternLayout:
Pattern: "%m%n"
File:
name: File
fileName: ${filename}
PatternLayout:
Pattern: "%d %p %C{1.} [%t] %m%n"
Filters:
ThresholdFilter:
level: error
Loggers:
logger:
-
name: org.apache.logging.log4j.test1
level: debug
additivity: false
ThreadContextMapFilter:
KeyValuePair:
key: test
value: 123
AppenderRef:
ref: STDOUT
-
name: org.apache.logging.log4j.test2
level: debug
additivity: false
AppenderRef:
ref: File
Root:
level: error
AppenderRef:
ref: STDOUT
异步日志记录可以通过在单独的线程中执行 I/O 操作来提高应用程序的性能。 Log4j 2 在这方面做了很多改进。
Log4j-2.9 和更高版本需要在类路径上使用disruptor-3.3.4.jar 或更高版本。 在 Log4j-2.9 之前,需要disruptor-3.0.0.jar 或更高版本。
<!-- https://mvnrepository.com/artifact/com.lmax/disruptor -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
这个配置很简单,只要创建一个叫log4j2.component.properties
的文件然后加入Log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
或者Log4j2.contextSelector=org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector
借助AsyncLogger
即可设置异步日志,直接指明name这和普通的appender是一样的
<Loggers>
<AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
<AppenderRef ref="RandomAccessFile"/>
</AsyncLogger>
</Loggers>
即配置属性:includeLocation="false"
为了让性能进一步提升
includeLocation=false
,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。从版本2.6开始,默认情况下Log4j以“无垃圾”模式运行,其中重用对象和缓冲区,并且尽可能不分配临时对象。还有一个“低垃圾”模式,它不是完全无垃圾,但不使用ThreadLocal字段。
Log4j2.6中的无垃圾日志记录部分通过重用ThreadLocal字段中的对象来实现,部分通过在将文本转换为字节时重用缓冲区来实现。
这个模式是默认开启的,大大的提升了性能
https://logging.apache.org/log4j/2.x/manual/garbagefree.html