如何轻松上手与设置Logback
日志在项目开发过程的作用不言而喻,项目上线后,我们需要根据日志文件定位问题发生的位置以及产生的原因。以前在项目开发中,已经有前人在工程中配置过日志,所以完全不需要自己再进行日志的配置。这次在新的项目中自己来配置日志时,还是遇到了一些波折,下面慢慢道来。
1 为什么选用Logback?
目前,在Springboot工程体系中,使用较多的日志组件是Slf4j、Logback以及Log4j2。而Slf4j本身只是一个接口类,具体的实现还是由Logback或Log4j2来完成的。由于Springboot自身就集成了Logback,默认使用的日志组件是Logback,Logback的体系结构具有足够的通用性,可以在不同的场景下使用。因此,在我们的DRS工程中,使用了Logback进行日志的打印。
目前,Logback包含三个部分:logback-core, logback-classic, logback-access。 Logback-core: 为其他两个模块奠定了基础 Logback-classic: 可以被同化为log4j的一个显著改进的版本,此外, logback-classic实现了slf4japi,因此您可以方便地在logback和其他日志框架(如log4j或log4j)之间来回切换java.util.logging文件 Logback-access: 集成了Servlet容器(如Tomcat和Jetty),以提供HTTP访问日志功能。
如果使用的是Springboot是2.4版本及以上,则不需要另外引入logback-core, logback-classic, logback-access和slf2j-api的依赖包的。
2 Logback的配置
首先,看一下我们在DRS工程中使用的Logback配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.path" value="/duan/drs-server/logs" />
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统debug日志输出 -->
<appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/drs-server-debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/drs-server-debug.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统info日志输出 -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/drs-server-info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/drs-server-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统warn日志输出 -->
<appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/drs-server-warn.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/drs-server-warn.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统error日志输出 -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/drs-server-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/drs-server-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<logger name="com.nuonuo.middleware.drsserver" level="INFO" />
<logger name="org.springframework" level="warn" />
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE_DEBUG" />
<appender-ref ref="FILE_INFO" />
<appender-ref ref="FILE_WARN" />
<appender-ref ref="FILE_ERROR" />
</root>
</configuration>
说明:
属性 | 说明 |
---|---|
log.path | 定义日志文件的输出路径 |
log.pattern | 定义日志文件输出格式 |
appender | 定义了日志的输出文件,日志文件保留时长,使用何种模式输出,以及过滤规则。在上述文件中,分别针对DEBUG、INFO、WARN和ERROR日志进行了定义了四个Appender |
logger | 用来设置某一个类或某个包的日志打印级别。在上述配置中,我们将DRS工程的日志输出级别定义为INFO,那么,如果只想打印出DRS工程中的日志,则只需将打印级别level设置为WARN |
root | 是一个根logger,root下可以包含零个或多个appender。如果一个appender没有在root下引入,则不会生成对应的日志文件 |
3 Logback的输出格式
针对以上述配置中的格式进行说明:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n
字段 | 说明 |
---|---|
%d{HH:mm:ss.SSS} | 用于输出日志事件的日期,其中%d也可以替换成%date,该设置对应下图中的红色标记部分 |
[%thread] | 输出对应的线程名。在上面的截图中可以看到,输出线程是主线程main |
%-5level | 输出日志级别,其中-5表示如果记录器名称长度小于5个字符,则右键填充空格 |
%logger{20} | 输出日志事件起源处记录器的名称。其中20表示输出记录的名称长度限制,超过这个长度,记录器名将会进行缩写。但是,记录器名称中最右边的段从不缩写,即使其长度比“长度”选项长。其他段可以缩短到最多一个字符,但永远不会删除 |
[%method,%line] | %method 输出发出日志记录请求的方法名称,%method也可替换成%M; %line——输出发出日志记录请求的行号,%line也可替换成%L |
%msg | 输出应用程序提供的与日志事件关联的消息。其中%msg也可替换成%m或%message |
%n | 输出平台相关的行分隔符字符 |
4 Appender
Appender子类包含Filter、Encoder、ConsoleAppender、FileAppender、以及RollingFileAppender。其类图关系如下所示。
顾名思义目标文件由file选项指定。如果该文件已存在,则根据append属性的值将其追加或截断。RollingFileAppender 扩展了FileAppender,具有滚动更新日志文件的功能。
类名 | encoder | immediateFlush | target | withJansi | append | file | prudent | rollingPolicy | triggeringPolicy |
---|---|---|---|---|---|---|---|---|---|
OutputStreamAppender | √ | √ | |||||||
ConsoleAppender | √ | √ | √ | √ | |||||
FileAppender | √ | √ | - | - | √ | √ | √ | ||
RollingFileAppender | √ | √ | - | - | √ | √ | √ | √ | √ |
OutputStreamAppender 这个类提供了其他附加程序构建的基本服务。该类包含两个属性设置: encoder—— 确定日志写入的编码格式。 immediateFlush——布尔值,默认为true, 立即刷新输出流可确保立即写出日志事件,并且在应用程序退出而未正确关闭附加器时不会丢失这些事件。 另一方面,将此属性设置为“false”可能会使日志吞吐量增加四倍。同样,如果immediateFlush设置为“false”,并且应用程序退出时appender未正确关闭,则尚未写入磁盘的日志事件可能会丢失。
ConsoleAppender 将日志附加在控制台上,其包含三个属性: encoder——略 target—— System.out或者 System.err字符串值之一 默认目标是 System.out withJansi——布尔值,默认false。 将withJansi设置为true将激活Jansi库,该库为Windows机器上的ANSI颜色代码提供支持。在Windows主机上,如果此属性设置为true,则应将网址:org.fusesource.jansi:jansi:1.17“在类路径上进行设置。请注意,基于Unix的操作系统(如Linux和macosx)默认支持ANSI颜色代码。一般我们很少用到,使用默认值即可。
FileAppender 它将日志事件附加到文件中,其包含四个属性: append——布尔值,默认为true, 表示事件附加在现有文件的末尾。如果append为false,则任何现有文件都将被截断。 encoder——略 file—— 要写入的文件的名称, 如果文件不存在,则创建该文件。 如果文件的父目录不存在,FileAppender将自动创建它,包括任何必要但不存在的父目录。 prudent——布尔值,默认值为false。如果设置为true, FileAppender将安全地写入指定的文件,即使存在运行在不同jvm(可能运行在不同主机上)中的其他FileAppender实例。
RollingFileAppender 扩展了FileAppender,具有滚动更新日志文件的功能,其包含以下几个属性: file——略 append——略 encoder——略 rollingPolicy—— 此选项是在发生滚动时指定RollingFileAppender行为的组件,包含SizeAndTimeBasedRollingPolicy、FixedWindowRollingPolicy、TimeBasedRollingPolicy。在上述配置中我们使用的是最为广泛的TimeBasedRollingPolicy。 triggeringPolicy—— 此选项是通知RollingFileAppender何时激活滚动更新过程的组件 prudent——布尔值,当为true时,不支持FixedWindowRollingPolicy。
5 Logback使用过程中遇到的问题
起初,在配置完logback.xml文件后,始终无法将日志输出到指定目录。 第一步,调试确认使用的是logback组件; 第二步,引入logback-core、logback-classic、logback-access和slf4j-api对应的jar包,验证是否可以将日志输出到指定目录。验证后发现,仍然无法解决问题,于是将引入的jar包删除。 第三步,排除springboot中包含的log4j2相关的jar,验证后仍无法解决问题。 第四步,在网上查到可能跟配置文件的命名有关,有说logback-spring.xml加载的优先级最高,于是将文件名由logback.xml改为logback-spring.xml,但是仍然无法解决问题。官网上看到的加载顺序是: logback-test.xml> logback.groovy> logback.xml。具体可见官方文档logback.qos.ch/manual/conf… 第五步,怀疑可能没加载logback.xml文件,于是在application.properties中加入logging.config=classpath:logback.xml,启动工程时报错,错误信息是: class path resource [logback.xml] cannot be resolved to URL because it does not exist。
根据错误信息可以看到,系统中根本没有加载到logback.xml文件。 第六步,由于第五步验证系统根本没有加载到logback.xml文件,于是检查了一下pom.xml文件,发现在build时,没有引入资源文件,于是修改pom.xml文件,添加如下内容,成功将日志输出到指定目录。
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
推荐阅读
-
正负偏差变量 即 d2+、d2- 分别表示决策值中超出和未达到目标值的部分。而 di+、di- 均大于 0 刚性约束和目标约束(柔性目标约束有偏差) 在多目标规划中,>=/<= 在刚性约束中保持不变。当需要将约束条件转换为柔性约束条件时,需要将 >=/<= 更改为 =(因为已经有 d2+、d2- 用来表示正负偏差),并附加上 (+dii-di+) 注意这里是 +di、-di+!之所以是 +di,-di+,是因为需要将目标还原为最接近的原始刚性约束条件 优先级因素和权重因素 对多个目标进行优先排序和优先排序 目标规划的目标函数 是所有偏差变量的加权和。值得注意的是,这个加权和都取最小值。而 di+ 和 dii- 并不一定要出现在每个不同的需求层次中。具体分析需要具体问题具体分析 下面是一个例子: 题目中说设备 B 既要求充分利用,又要求尽可能不加班,那么列出的时间计量表达式即为:min z = P3 (d3- + d3 +) 使用 + 而不是 -d3 + 的原因是:正负偏差不可能同时存在,必须有 di+di=0 (因为判定值不可能同时大于目标值和小于目标值),而前面是 min,所以只要取 + 并让 di+ 和 dii- 都为正值即可。因此,得出以下规则: 最后,给出示例和相应的解法: 问题:某企业生产 A 和 B 两种产品,需要使用 A、B、C 三种设备。下表显示了与工时和设备使用限制有关的产品利润率。问该企业应如何组织生产以实现下列目标? (1) 力争利润目标不低于 1 500 美元; (2) 考虑到市场需求,A、B 两种产品的生产比例应尽量保持在 1:2; (3)设备 A 是贵重设备,严禁超时使用; (4)设备 C 可以适当加班,但要控制;设备 B 要求充分利用,但尽量不加班。 从重要性来看,设备 B 的重要性是设备 C 的三倍。 建立相应的目标规划模型并求解。 解:设企业生产 A、B 两种产品的件数分别为 x1、x2,并建立相应的目标计划模型: 以下为顺序求解法,利用 LINGO 求解: 1 级目标: 模型。 设置。 variable/1..2/:x;! s_con_num/1...4/:g,dplus,dminus;!所需软约束数量(g=dplus=dminus 数量)及相关参数; s_con(s_con_num);! s_con(s_con_num,variable):c;!软约束系数; 结束集 数据。 g=1500 0 16 15. c=200 300 2 -1 4 0 0 5; 结束数据 min=dminus(1);!第一个目标函数;!对应于 min=z 的第一小部分;! 2*x(1)+2*x(2)<12;!硬约束 @for(s_con_num(i):@sum(variable(j):c(i,j)*x(j))+dminus(i)-dplus(i)=g(i)); !使用设置完成的数据构建软约束表达式; ! !软约束表达式 @for(variable:@gin(x)); !将变量约束为整数; ! 结束 此时,第一级目标的最优值为 0,第一级偏差为 0: 第二级目标: !求 dminus(1)=0,然后求解第二级目标。 模型。 设置。 变量/1..2/:x;!设置:变量/1..2/:x; ! s_con_num/1...4/:g,dplus,dminus;!软约束数量及相关参数; s_con(s_con_num(s_con_num));! s_con(s_con_num,variable):c;! 软约束系数; s_con(s_con_num,variable):c;! 结束集 数据。 g=1500 0 16 15; c=200 300 2 -1 4 0 0 5; 结束数据 min=dminus(2)+dplus(2);!第二个目标函数 2*x(1)+2*x(2)<12;!硬约束 @for(s_con_num(i):@sum(variable(j):c(i,j)*x(j))+dminus(i)-dplus(i)=g(i)); ! 软约束表达式;! dminus(1)=0; !第一个目标结果 @for(variable:@gin(x)); ! 结束 此时,第二个目标的最优值为 0,偏差为 0: 第三目标 !求 dminus(2)=0,然后求解第三个目标。 模型。 设置。 变量/1..2/:x;!设置:变量/1..2/:x; ! s_con_num/1...4/:g,dplus,dminus;!软约束数量及相关参数; s_con(s_con_num(s_con_num));! s_con(s_con_num,variable):c;! 软约束系数; s_con(s_con_num,variable):c;! 结束集 数据。 g=1500 0 16 15; c=200 300 2 -1 4 0 0 5; 结束数据 min=3*dminus(3)+3*dplus(3)+dminus(4);!第三个目标函数。 2*x(1)+2*x(2)<12;!硬约束 @for(s_con_num(i):@sum(variable(j):c(i,j)*x(j))+dminus(i)-dplus(i)=g(i)); ! 软约束表达式;! dminus(1)=0; !第一个目标约束条件; ! dminus(2)+dplus(2)=0; !第二个目标约束条件 @for(variable:@gin(x));! 结束 最终结果为 x1=2,x2=4,dplus(1)=100,最优利润为
-
如何在群晖中轻松设置 PHP 环境
-
新手如何使用 JavaSDK 轻松上手腾讯云 COS?在 Java 中调用对象存储桶
-
教您一键设置本地服务器,轻松与 4 人以上联机玩《幻兽帕鲁》!
-
如何轻松设置腾讯云 CDN
-
如何在GVIM中进行个性化设置与操作
-
如何在Sublime Text 3中轻松设置C++环境
-
如何轻松地设置和安装GVim
-
如何为Windows新手轻松安装与配置GVim的最新版本指南
-
在Windows系统中轻松安装与设置GVim