Logback由来
Logback意在替代log4j。
Logback is intended as a successor to the popular log4j project.
Log4j:
import org.apache.log4j.Logger
import org.apache.log4j.LoggerFactory
Logback:
import org.slf4j.Logger
import org.slf4j.LoggerFactory
与log4j相比,logback的优点非常多,总之用就对了。并且因为logback完美实现了log4j,所以从log4j迁移到logback非常容易,只要换个jar包就好了。
在Spring boot中使用logback
项目中使用了任意Spring boot的starter,就可以直接使用logback了。例如spring-boot-starter-web
的依赖关系如下:
spring-boot-starter-web -> spring-boot-starter-logging -> spring-jcl
从org.slf4j.LoggerFactory
创建org.slf4j.Logger
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication
{
private static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
logger.info("ffish.net demo log with inputs {}, {}, {}", 1, 2, "abc");
}
}
执行输出
2021-03-15 12:23:14.295 INFO 2011 --- [ main] net.ffish.demo.DemoApplication : ffish.net demo log with inputs: 1 2 abc
如果你急需一份差不多的配置
如果你急需,下面是一份差不多的配置文件,将它保存在logback.xml中,放在application.properties平级目录,修改一下日志文件的存储路径(value="/var/log"
这里),即可使用。
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="60 seconds" debug="true">
<property name="LOG_LOCATION" value="/var/log" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} - %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_LOCATION}/mylog.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} - %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_LOCATION}/archived/mylog-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
接下来我会展开介绍一些主要的语法,如果你英文阅读不错,建议读一读官网说明,更具体。
Logback配置文件读取顺序
Logback可识别的配置文件列表如下:
- 最高优先级:
classpath
中的logback-test.xml。 - 第二优先级:
classpath
中的logback.groovy。 - 第三优先级:
classpath
中的logback.xml。 - 第四优先级:实现了
com.qos.logback.classic.spi.Configurator
接口的配置类。 - 第五优先级:logback的默认最小配置
BasicConfigurator
,log会直接输出到控制台。
配置文件的结构
一个logback.xml应该包含:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!-- 0个、1个或多个“appender”元素 -->
<appender>
</appender>
<!-- 0个、1个或多个“logger”元素 -->
<logger>
</logger>
<!-- 最多1个“root”元素 -->
<root>
</root>
</configuration>
最基础的配置
下面这份是最基础的配置,将log输出到控制台,先不用理解其中的含义,做一个demo程序,将配置文件粘贴到logback.xml试试。
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="CONSOLE" 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%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
打印logstack的内部状态
ch.qos.logback.core.util.StatusPrinter.print(ch.qos.logback.classic.LoggerContext context)
方法,可将logback
的内部状态打印出来。
public static void main(String[] args) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
}
你会看到输出为
10:24:22.704 [main] INFO net.ffish.demo.DemoApplication - Started DemoApplication in 1.426 seconds (JVM running for 1.762)
10:24:21,520 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
10:24:21,521 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
10:24:21,521 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [CONSOLE]
10:24:21,521 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
10:24:21,522 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
10:24:21,522 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [CONSOLE] to Logger[ROOT]
10:24:21,522 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
10:24:21,522 |-INFO in org.springframework.boot.logging.logback.SpringBootJoranConfigurator@44536de4 - Registering current configuration as safe fallback point
如果不想在代码中通过StatusPrinter.print()
来打印logback内部状态,也可以通过配置文件来实现:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true">
...
</configuration>
上面这个
debug="true"
,表示打印logback内部状态。我看到有些人错误地认为这是设置日志级别为“debug”,实际上没有那个功效:)
官网强烈建议打印logback的内部状态,有助于诊断日志系统的问题。
Enabling output of status data usually goes a long way in the diagnosis of issues with logback. As such, it is highly recommanded and should be considered as a resource of first resort.
配置文件变更自动生效
在<configuration>
元素如下属性配置,可自动扫描配置文件的修改,并生效新的配置。
<configuration scan="true" scanPeriod="30 seconds">
...
</configuration>
- 如果不设置,默认的
scanPeriod
是60秒 scanPeriod
支持的单位有milliseconds
,seconds
,minutes
,hours
scanPeriod
的默认单位是milliseconds
,如果你没有指定的话- 如果新的配置文件有xml语法错误,扫描程序会自动回滚到前一个无错的配置
在日志中添加包信息
Logback可以在日志的每一行添加包信息,包含jar包名称和版本号,报错的文件和行号,如下:
14:28:48.835 [btpool0-7] INFO c.q.l.demo.prime.PrimeAction - 99 is not a valid value
java.lang.Exception: 99 is invalid
at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]
设置方法如下:
<configuration packagingData="true">
...
</configuration>
然而,打印包信息非常耗费计算资源,尤其是抛异常较多的程序。
关闭logback
只创建不关闭是程序员的大忌,如何优雅地关闭logback?
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...
// assume SLF4J is bound to logback-classic in the current environment
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();
或者利用Shutdown hook机制。
<confiuration debug="true">
<shutdownHook />
...
</configuration>
当然你也可以为<shutdownHook>
添加自定义class
,否则系统会调用DefaultShutdownHook
飞鱼原创 https://ffish.net,未经允许,严禁转载!