logback是java的日志开源组件,是log4j创始人写的,性能比log4j要好,目前主要分为3个模块
- logback-core:核心代码模块
- logback-classic:log4j的一个改良版本,同时实现了slf4j的接口,这样你如果之后要切换其他日志组件也是一件很容易的事
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能
一、logback的使用
依赖(这个依赖直接包含了 logback-core 以及 slf4j-api的依赖)
ch.qos.logback
logback-classic
1.2.11
然后就可以直接在代码中使用slf4j的接口获取Logger输出日志了。(配置在下面的章节介绍)
//这是slf4j的接口,由于我们引入了logback-classic依赖,所以底层实现是logback
private static final Logger LOGGER = LoggerFactory.getLogger(Test.class);
public static void main(String[] args) throws InterruptedException {
LOGGER.info("hello world");
}
二、logback的配置
logback配置获取顺序
logback在启动的时候,会按照下面的顺序加载配置文件
- 如果java程序启动时指定了logback.configurationFile属性,就用该属性指定的配置文件。如java -Dlogback.configurationFile=/path/to/mylogback.xml Test ,这样执行Test类的时候就会加载/path/to/mylogback.xml配置
- 在classpath中查找 logback.groovy 文件
- 在classpath中查找 logback-test.xml 文件
- 在classpath中查找 logback.xml 文件
- 如果是 jdk6+,那么会调用ServiceLoader 查找 com.qos.logback.classic.spi.Configurator接口的第一个实现类
- 自动使用ch.qos.logback.classic.BasicConfigurator,在控制台输出日志
上面的顺序表示优先级,使用java -D配置的优先级最高,只要获取到配置后就不会再执行下面的流程。相关代码可以看ContextInitializer#autoConfig()方法。
关于SLF4j的日志输出级别
在slf4j中,从小到大的日志级别依旧是trace、debug、info、warn、error。
logback.xml 配置样例1
logback
DEBUG
%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n
${scheduler.manager.server.home}/logs/${app.name}.log
${scheduler.manager.server.home}/logs/${app.name}.%d{yyyy-MM-dd.HH}.log.gz
60
20GB
100MB
%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n
logback.xml 配置样例2
${log_pattern}
utf8
${defaultLogDir:-/qy/qy-doctorservice/logs}/log/%d{yyyy-MM-dd, aux}/credit.%d.%i.zip
${logMaxHistory:-15}
${logMaxSize:-100MB}
1GB
true
false
${log_pattern}
utf8
配置详解
configuration节点相关属性
属性名称 | 默认值 | 介绍 |
---|---|---|
debug | false | 要不要打印 logback内部日志信息,true则表示要打印。 |
scan | true | 配置发送改变时,要不要重新加载 |
scanPeriod | 1 seconds | 检测配置发生变化的时间间隔。如果没给出时间单位,默认时间单位是毫秒 |
configuration子节点介绍
1. contextName节点
设置日志上下文名称,后面输出格式中可以通过定义 %contextName 来打印日志上下文名称
2.property节点
用来设置相关变量,通过key-value的方式配置,然后在后面的配置文件中通过 ${key}来访问
3.appender 节点
日志输出组件,主要负责日志的输出以及格式化日志。常用的属性有name和class
属性名称 | 默认值 | 介绍 |
---|---|---|
name | 无默认值 | appender组件的名称,后面给logger指定appender使用 |
class | 无默认值 | appender的具体实现类。常用的有 ConsoleAppender、FileAppender、RollingFileAppender |
3.1 ConsoleAppender:向控制台输出日志内容的组件,只要定义好encoder节点就可以使用。
3.2 FileAppender:向文件输出日志内容的组件,用法也很简单,不过由于没有日志滚动策略,一般很少使用
FileAppender
是 OutputStreamAppender
的子类,将日志事件输出到文件中。通过 file
来指定目标文件。如果该文件存在,根据 append
的值,要么将日志追加到文件中,要么该文件被截断。
3.3 RollingFileAppender:向文件输出日志内容的组件,同时可以配置日志文件滚动策略,在日志达到一定条件后生成一个新的日志文件。
appender节点中有一个子节点filter,配置具体的过滤器,比如上面的例子配置了一个内置的过滤器ThresholdFilter,然后设置了level的值为DEBUG。这样用这个appender输出日志的时候都会经过这个过滤器,日志级别低于DEBUG的都不会输出来。
RollingFileAppender
的属性如下所示:
属性名 | 类型 | 描述 |
---|---|---|
file | String | 参见 FileAppender |
append | boolean | 参见 FileAppender |
encoder | Encoder |
参见 OutputStreamAppender |
rollingPolicy | RollingPolicy | 当轮转发生时,指定 RollingFileAppender 的行为。下面将会详细说明 |
triggeringPolicy | TriggeringPolicy | 告诉 RollingFileAppender 什么时候发生轮转行为。下面将会详细说明 |
prudent | boolean | FixedWindowRollingPolicy 不支持该属性。RollingFileAppender 在使用严格模式时要与 TimeBasedRollingPolicy 结合使用,但是有两个限制:1. 在严格模式下,也不支持也不允许文件压缩(我们不能让一个 JVM 在写入文件时,另一个 JVM 在压缩该文件) 2. 不能对 FileAppender 的 file 属性进行设置。实际上,大多数的操作系统不允许在有进程操作文件的情况下对文件改名。其它的参考 FileAppender |
RollingFileAppender的两种策略:
RollingPolicy
负责轮转的方式为:移动文件以及对文件改名。
1.SizeAndTimeBasedRollingPolicy
基于大小以及时间的轮转策略。
有时你希望按时轮转,但同时又想限制每个日志文件的大小。特别是如果后期处理工具需要对日志进行大小限制。为了满足这个需求,logback 配备了 SizeAndTimeBasedRollingPolicy
。
注意,TimeBasedRollingPolicy
可以限制归档文件总的大小。所以如果你想要这个限制,你可以通过设置 totalSizeCap
来达到这个目的。
2.TimeBasedRollingPolicy
是最常用的轮转策略,它是基于时间来定义轮转策略。
TimeBasedRollingPolicy
是最常用的轮转策略。它是基于时间来定义轮转策略。例如按天或者按月。TimeBasedRollingPolicy
既负责轮转的行为,也负责触发轮转。实际上,TimeBasedRollingPolicy
同时实现了 RollingPolicy
与 TriggeringPolicy
接口。
TimeBasedRollingPolicy
的配置需要一个强制的属性 fileNamePattern
以及其它的可选属性。
例如按天或者按月。TimeBasedRollingPolicy
既负责轮转的行为,也负责触发轮转。实际上,TimeBasedRollingPolicy
同时实现了 RollingPolicy
与 TriggeringPolicy
接口。
属性名 | 类型 | 描述 |
---|---|---|
fileNamePattern | String | 该属性定义了轮转时的属性名。它的值应该由文件名加上一个 %d 的占位符。%d 应该包含 java.text.SimpleDateFormat 中规定的日期格式。如果省略掉这个日期格式,那么就默认为 yyyy-MM-dd。轮转周期是通过 fileNamePattern 推断出来的。 注意:可以选择对 RollingFileAppender (TimeBasedRollingPolicy 的父类)中的 file 属性进行设置,也可以忽略。通过设置 FileAppender 的 file 属性,你可以将当前活动日志的路径与归档日志的路径分隔开来。当前日志永远会是通过 file 指定的文件。它的名字不会随着时间的推移而发生变化。但是,如果你选择忽略 file 属性,当前活动日志在每个周期内将会根据 fileNamePattern 的值变化。稍后的例子将会说明这一点。%d{} 中的日期格式将会遵循 java.text.SimpleDateFormat 中的约定。斜杆 '/' 或者反斜杠 '' 都会被解析成目录分隔符。 指定多个 %d 可以指定多个 %d,但是只能有一个是主要的,用于推断轮转周期。其它的 %d 占位符必须通过 'aux' 标记为辅助的。见下面的示例: 多个 %d 占位符允许你在文件夹中去管理归档文件,这个跟轮转周期不同。如下所示:通过年月来管理日志文件夹,但是轮转周期是在每天晚上零点。 /var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log TimeZone 在某些情况下,你可能想要根据时区而不是主机的时钟来轮转日志。你可以通过如下方式来指定一个时区,例如: aFloder/test.%d{yyyy-MM-dd-HH, UTC}.log 如果指定的 timezone 不能被识别或者拼写错误,将会根据 TimeZone.getTimeZone(String) 方法指定为 GMT。 |
maxHistory | int | 这个可选的属性用来控制最多保留多少数量的归档文件,将会异步删除旧的文件。比如,你指定按月轮转,指定 maxHistory = 6,那么 6 个月内的归档文件将会保留在文件夹内,大于 6 个月的将会被删除。注意:当旧的归档文件被移除时,当初用来保存这些日志归档文件的文件夹也会在适当的时候被移除。 |
totalSizeCap | int | 这个可选属性用来控制所有归档文件总的大小。当达到这个大小后,旧的归档文件将会被异步的删除。使用这个属性时还需要设置 maxHistory 属性。而且,maxHistory 将会被作为第一条件,该属性作为第二条件。 |
cleanHistoryOnStart | boolean | 如果设置为 true,那么在 appender 启动的时候,归档文件将会被删除。默认的值为 false。 归档文件的删除通常在轮转期间执行。但是,有些应用的存活时间可能等不到轮转触发。对于这种短期应用,可以通过设置该属性为 true,在 appender 启动的时候执行删除操作。 |
3.4 SMTPAppender
SMTPAppender
收集日志事件到一个或多个固定大小的缓冲区,当用户指定的事件发生时,将从缓冲区中取出适当的内容进行发送。SMTP 邮件是异步发送的。默认情况下,当日志的级别为 ERROR 时,邮件发送将会被触发。而且默认的情况下,所有事件都使用同一个缓冲区。
SMTPAppender
的属性如下表所示:
属性名 | 类型 | 描述 |
---|---|---|
smtpHost | String | SMTP 服务器的主机名。强制性的。 |
smtpPort | int | SMPT 服务监听的端口。默认为 25. |
to | String | 接收者的邮件地址。触发事件发送给接收者。多个收件人可以使用逗号(,)分隔,或者使用多个 元素来指定。 |
from | String | SMTPAppender 使用的发件人,格式遵循邮件通用格式,如果你想要包含发送者的名字,使用这种格式 " Adam Smith |
subject | String | 邮件的主题。它可以是通过 PatternLayout 转换后的有效值。关于 Layout 将在接下来的章节讨论。 邮件应该有一个主题行,对应触发的邮件信息。 假设 subject 的值为:"Log: %Logger - %msg",触发事件的 logger 名为 "com.foo.Bar",并且日志信息为 "Hello world"。那么发出的邮件信息将会有一个名为 "Log: com.foo.Bar - Hello World" 的主题行。默认情况下,这个属性的值为 "%logger{20} - %m" |
discriminator | Discriminator | 在 Discriminator 的帮助下,SMTPAppender 根据 discriminator 返回的值可以将不同日志事件分散到不同的缓冲区中。默认的 discriminator 将返回同一个值,所以所有的事件都使用同一个缓冲区。 |
evaluator | IEvaluator | 通过创建一个新的 class 属性指定 class 的名字表示用户希望通过哪个类来满足 SMTPAppender 的 Evaluator 的需要。如果没有指定此选项,当触发一个大于等于 ERROR 级别的事件时, SMTPAppender 将会被分配一个 OnErrorEvaluator 的实例。logback 配备了几个其它的 evaluator,分别叫 OnMarkerEvaluator (将在下面讨论),一个相对强大的 evaluator 叫 JaninoEventEvaluator (在其它章节讨论) 以及最近版本才有的一个更加强大的 evaluator 叫 GEventEvaluator 。 |
cyclicBufferTracker | CyclicBufferTracker |
从名字可以看出,是一个 CyclicBufferTracker 的实例追踪循环缓冲区。它基于 discriminator 返回的 key (见上)。如果你不想指定一个 cyclicBufferTracker,那么将会自动创建一个 CyclicBufferTracker 的实例。默认的,这个实例用来保留事件的循环缓冲区的大小为 256。你需要改变 bufferSize 选项的大小(见下面) |
username | String | 默认为 null |
password | String | 默认为 null |
STARTTLS | boolean | 如果为 true,那么 appender 将会发送 STARTTLS 命令(如果服务器支持)将连接变成 SSL 连接。注意,连接初始的时候是为加密的。默认为 false。 |
SSL | boolean | 如果为 true,将通过 SSL 连接服务器。默认为 false。 |
charsetEncoding | String | 邮件信息将会通过 charset 进行编码。默认编码为 "UTF-8" |
localhost | String | 一旦 SMTP 客户端的主机名没有配置正确,例如客户端的 hostname 不是全限定的,那么服务端会拒绝客户端发送的 HELO/EHLO 命令。为了解决这个问题,你可以将 localhost 的值设置为客户端主机的全限定名。详情见 com.sun.mail.smtp 包文档中的 "mail.smtp.localhost" 属性。(这个网站已经关闭了...) |
asynchronousSending | boolean | 决定邮件传输是否是异步进行。默认为 'true'。但是,在某些特定的情况下,异步发送不怎么合适。例如,当发生一个严重错误时,你的应用使用 SMTPAppender 去发送一个警告,然后退出。但是相关线程可能没有时间去发送警告邮件。在这种情况下,你可以设置该属性的值为 'false'。 |
includeCallerData | boolean | 默认为 false。如果 asynchronousSending 的值为 true,并且你希望在日志中看到调用者的信息,你可以设置该属性的值为 true |
sessionViaJNDI | boolean | SMTPAppender 基于 javax.mail.Session 来发送邮件信息。默认情况下,该属性的值为 false,所以需要用户指定相关属性通过 SMTPAppender 来构建 javax.mail.Session 实例。如果设置为 true,javax.mail.Session 实例将会通过 JNDI 来获取。参见 jndiLocation 属性。通过 JNDI 获取 Session 实例可以减少需要配置的数量,使你的应用减少重复(dryer)的工作。更多关于在 Tomcat 配置 JNDI 的信息请参考 JNDI Resources How-to。注意 :通过 JNDI 获取 Session 的时候请移除 web 应用下 WEB-INF/lib 文件夹下的 mail.jar 与 activation.jar。 |
jndiLocation | String | JNDI 中放置 javax.mail.Session 的地方。默认为:" java:comp/env/mail/Session " |
SMTPAppender
仅仅只在它的循环缓存区中保留最后 256 个日志事件,当缓存区快要满的时候丢掉旧的日志事件。因此,通过 SMTPAppender 发送任何邮件包含的日志事件都不会超过 256 个。这在保留内存需求的限制,还提供了数量可观的应用上下文。
SMTPAppender
基于 JavaMail API。在 JavaMail 1.4 版本做过测试。JavaMail 需要 JavaBeans Activation Framework 包。你可以去它们各自的网站下载 JavaMail API 与 JavaBeans Activation Framework。在运行下面的示例之前先确保将这两个 jar 包放在 classpath 下。
chapters.appenders.mail.EMail
应用会生成多个日志信息,随后再生成一个错误日志信息。它接收两个参数,第一参数是整形,表示需要生成多少个日志事件。第二个参数表示 logback 的配置文件。Email 最后生成一个错误日志,将会触发发送邮件信息。
下面是一个 Email
应用的简单配置信息:
4. logger以及root节点
root节点和logger节点其实都是表示Logger组件。个人觉的可以把他们之间的关系可以理解为父子关系,root是最顶层的logger,正常情况getLogger("name/class")没有找到对应logger的情况下,都是使用root节点配置的logger。
如果配置了logger,并且通过getLogger("name/class")获取到这个logger,输出日志的时候,就会使用这个logger配置的appender输出,同时还会使用rootLogger配置的appender。我们可以使用logger节点的additivity="false"属性来屏蔽rootLogger的appender。这样就可以不使用rootLogger的appender输出日志了。
关于logger的获取,一般logger是配置name的。我们再代码中经常通过指定的CLass来获取Logger,比如这样LoggerFactory.getLogger(Test.class);,其实这个最后也是转成对应的包名+类名的字符串com.kongtrio.Test.class。假设有一个logger配置的那么是com.kongtrio,那么通过LoggerFactory.getLogger(Test.class)获取到的logger就是这个logger。
也就是说,name可以配置包名,也可以配置自定义名称。
上面说的logger和root节点的父子关系只是为了方便理解,具体的底层实现本人并没有看,他们之间真正的关系读者有兴趣的话可以去看logback的源码
三、SpringBoot中logback日志配置文件加载顺序
springboot加载日志配置文件有两种,一种是加载logback自身的配置文件,另一种是加载具有spring特性的logback配置文件:
// @see org.springframework.boot.logging.AbstractLoggingSystem
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
String config = getSelfInitializationConfig();
if (config != null && logFile == null) {
// self initialization has occurred, reinitialize in case of property changes
reinitialize(initializationContext);
return;
}
if (config == null) {
config = getSpringInitializationConfig();
}
if (config != null) {
loadConfiguration(initializationContext, config, logFile);
return;
}
loadDefaults(initializationContext, logFile);
}
其中config 指logback自身的配置文件,logFile 指logging.file.name或者logging.file.path指定的文件
// @See org.springframework.boot.logging.LogFile
public static LogFile get(PropertyResolver propertyResolver) {
// public static final String FILE_NAME_PROPERTY = "logging.file.name"
String file = propertyResolver.getProperty(FILE_NAME_PROPERTY);
// public static final String FILE_PATH_PROPERTY = "logging.file.path"
String path = propertyResolver.getProperty(FILE_PATH_PROPERTY);
if (StringUtils.hasLength(file) || StringUtils.hasLength(path)) {
return new LogFile(file, path);
}
return null;
}
1.加载logback自身的配置文件按(二、logback的配置--logback配置获取顺序)顺序加载;
2.如果上述配置文件都不存在,则加载springboot自身具有spring特性的logback配置文件,加载顺序和logback自身配置文件一致。只是在每种配置文件的末尾加上“-spring”。
Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),因为logback.xml加载早于application.properties,所以如果你在logback.xml使用了变量时,而恰好这个变量是写在application.properties时,那么就会获取不到,只要改成logback-spring.xml就可以解决。
- logging.file.name:设置具体输出的日志名称,可以是绝对路径或者基于当前运行目录的相对路径,例如:logging.file.name=app.log、logging.file.name=/var/log/app.log
- logging.file.path:设置输出的日志被写入到的目录,默认文件名为 spring.log,例如:logging.file.path=/var/log/
如果你两个都同时设置,则以 logging.file.name 为准,建议直接使用 logging.file.name。
具有spring特性的logback的配置文件--logback-spring.xml
- springProfile
标签允许我们更加灵活配置文件,可选地包含或排除配置部分。使用该name属性指定哪个配置文件接受配置。可以使用逗号分隔列表指定多个配置文件。
例子
- springProperty
标签允许我们从Spring中显示属性,Environment 以便在Logback中使用。如果你想将 application.properties在回读配置中访问文件中的值,这将非常有用。
属性名字 | 描述 |
---|---|
scope | 作用域(设置属性的范围,例local(默认)、context、system) |
name | 变量名字 |
source | 需要引用的yml文件中配置名称 |
local--从配置文件中定义其属性的位置到该配置文件的解释/执行结束为止,都存在具有本地范围的属性。因此,每次解析和执行配置文件时,都会重新定义本地作用域中的变量。
context--具有上下文范围的属性被插入到上下文中,并且持续时间与上下文一样长,直到被清除为止。一旦定义,上下文范围内的属性就是上下文的一部分。这样,它在所有日志记录事件中都可用,包括那些通过序列化发送到远程主机的事件。
system--具有系统范围的属性被插入 JVM 的系统属性中,并且持续时间与 JVM 一样长,或者直到被清除为止。
例子
${FILE_LOG_PATTERN}
${FILE_LOG_CHARSET}
${LOG_ERROR_FILE}
ERROR
ACCEPT
DENY
${LOG_ERROR_FILE}.%d{yyyy-MM-dd}.%i.gz
false
10MB
0
7
参考:26. Logging (spring.io)