日志处理
日志框架 logback+slf4j
logback:
-
logback分为三个模块:logback-core,logback-classic和logback-access
-
logback-core:为其他两个模块奠定基础
-
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API。这里面包含了core模块。
-
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。
-
相关组件
在logback中包含三个组件Logger、Appender以及Layout:
- Logger:日志记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
- Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。
- Layout:负责把事件转换成字符串,格式化的日志信息的输出,在Logback中Layout对象被封装在encoder中。
-
使用logback.xml文件进行自定义配置,日志框架会默认读取以下类型的配置文件:
- logback.groovy
- logback-test.xml
- logback.xml
配置文件详细信息:
-
结构
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <property name="键" value="值"/> <appender name="输出地方" class="输出实体类"> <target>控制输出流对象,默认System.out</target> <encoder> <pattern>格式化</pattern> <charset>日志字符编码 </charset> </encoder> <file>文件路径</file> <filter class="过滤实体类"> <level>过滤的级别</level> <onMatch>匹配时的操作</onMatch> <onMismatch>不匹配时的操作</onMismatch> </filter> <rollingPolicy class="日志滚动方式实体类"> <fileNamePattern>文件名称格式</fileNamePattern> <maxHistory>日志文件保留的个数</maxHistory> <maxFileSize>单个日志的最大大小</maxFileSize> <totalSizeCap>全部日志的总大小</totalSizeCap> </rollingPolicy> </appender> <logger name="目标包路径" level="日志输出水平,优先级高于root"> <appender-ref ref="引用appender,通过name属性"/> </logger> <root level="根日志输出水平"> <appender-ref ref="引用appender,通过name属性"/> </root> </configuration>-
日志输出格式
%level:日志等级
%d{yyyy-MM-dd HH:mm:ss.SSS}:日期
%c:类的完整名称
%M:method
%L:行号
%thread:线程名称
%m或者%msg:信息
%n:换行
-
property用于设置可以复用的变量,引用方式:${键}
-
Appender
ConsoleAppender:输出到屏幕。FileAppender:输出到指定文件。RollingFileAppender:可定时创建文件以及按文件大小进行切割。
-
自定义appender附加器
- 继承
UnsynchronizedAppenderBase类,重写abstract protected voidappend(E eventObject)方法,这个类没有线程同步 - 继承
AppenderBase<E>类,也是重写append方法。这个类有线程同步
- 继承
-
Filter用来进行过滤指定日志等级的日志记录。可设置在任意的
appender中EvaluatorFilter:临界过滤器,滤掉低于指定临界值的日志。LevelFilter:级别过滤器,对特定某个级别的日志进行过滤。- 还有其他的
- 自定义Filter
继承Filter<E>类,重写public abstract FilterReply
decide(E event)方法, 方法解释:如果决策为 FilterReply.DENY,则事件将被丢弃。如果决策为 FilterReply.NEUTRAL,则将调用下一个过滤器(如果有)。如果决定是, FilterReply.ACCEPT 则将记录事件,而无需咨询链中的其他过滤器。 形参: event – 要决定的事件。
-
在日志管理框架中,"滚动"(Rolling)是指按照某种策略自动地将当前的日志文件关闭,并创建一个新的日志文件继续记录日志的过程。这样做可以防止单个日志文件无限制地增长,便于管理和维护。以下是两种常见的滚动策略:
TimeBasedRollingPolicy:这种策略是基于时间来触发的。你可以设定一个时间单位(比如每天、每小时或每分钟),当达到这个时间点时,日志文件就会滚动。例如,如果设置为每天滚动,那么每天结束时,当前的日志文件就会被关闭,并且一个新的日志文件会在第二天开始时创建。这种策略通常用于长期存储日志,使得日志文件可以按照日期进行归档。- fileNamePattern中不能使用**%i**
- maxHistory表示保留日志文件个数【x个月,x个分钟,x个秒】,与fileNamePattern的日期格式有关,最后一个格式符号和个数对应,即:【mm=>x个月,MM=>x个分钟,ss=>x个秒】
SizeBasedTriggeringPolicy:这种策略是基于日志文件的大小来触发的。你可以设定一个文件大小阈值(比如 10MB 或 100MB),当日志文件达到这个大小时,就会触发滚动。当日志文件达到指定的大小时,当前的日志文件会被关闭,并创建一个新的日志文件继续记录。这种策略适用于限制日志文件大小的场景,防止日志文件占用过多磁盘空间。- maxFileSize大小文件限制
-
-
-
异步日志,Logback中,可以使用异步日志来提高日志的性能和吞吐量。异步日志将日志的写入操作放在一个独立的线程中进行,不会阻塞主线程的执行。
<appender name="附加器名称" class="ch.qos.logback.classic.AsyncAppender"> <!-- 使用 AsyncAppender 包装了这个xxxAppender,将其转换为异步的 --> <appender-ref ref="其他附加器引用名称" /> <!-- 指定了异步队列的大小 --> <queueSize>512</queueSize> <!-- 指定了当队列满时是否丢弃日志 0,表示不丢弃任何日志事件。新的日志事件将被阻塞,直到队列中有空闲位置 大于0,表示当队列已满时,丢弃最早的<discardingThreshold>个日志事件,然后将新的日志事件添加到队列中 如果对日志的实时性要求较高,可以将 <discardingThreshold> 的值设置为一个较小的数值,以避免队列过大导致的性能问题--> <discardingThreshold>0</discardingThreshold> </appender>
slf4j:即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。允许最终用户在部署其应用时使用其所希望的日志系统。就是适配器模式里的适配器。
日志级别排序为:TRACE< DEBUG<INFO< WARN< ERROR。默认日志等级为Debug,其中OFF与ALL作为日志开关。
简单的演示
-
依赖pom.xml
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.5.11</version> </dependency> <!-- logback-classic包含了logback-core,logback-core可以不用引入,但是不建议 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.5.11</version> </dependency> <!--日志门面--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.1.0-alpha1</version> </dependency> -
logback.xml配置
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 格式化日志 --> <pattern>%d{yyyy-MM-dd HH:mm:ss} %thread %level %logger %msg \n</pattern> <!-- 日志字符编码 --> <charset>UTF-8</charset> </encoder> </appender> <root level="INFO"> <appender-ref ref="stdout"/> </root> </configuration> -
代码
package log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WaterLog { Logger logger = LoggerFactory.getLogger(WaterLog.class); public void discovery1(){ logger.debug("debug"); logger.info("info"); logger.error("error"); } }@Test void discoveryTest(){ WaterLog waterLog = new WaterLog(); waterLog.discovery1(); }运行结果
运行截图里少了一个输出,1处的debug,因为2设置了INfO,debug<info,所以不输出。
springboot3 默认日志框架
springboot采用logback+slf4j日志框架
-
pom.xml依赖
<!-- 这个里有日志框架 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> -
application.yaml配置
logging: # 设置配置文件的位置路径 config: classpath:logconfig/logback.xml -
logback.xml配置
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <!--用于设置可复用的变量--> <property name="file.path" value="D:\\biancheng\\JAVA_IDEA\\springboot-explore\\src\\main\\resources"/> </configuration> -
代码
package wnan.explore.log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class DemoLog { public void fun1() { Logger log = LoggerFactory.getLogger(DemoLog.class); log.debug("这是一个 DemoLog debug日志"); log.info("这是一个 DemoLog info日志"); log.warn("这是一个 DemoLog warning日志"); log.error("这是一个 DemoLog error日志"); } }@Autowired DemoLog log1; @Test void t2(){ log1.fun1(); } @Test void t3(){ while(true){ log1.fun1(); } }- 控制台输出
<!-- 设置日志输出位置 --> <!-- 输出到控制台--> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <!-- 输出对象--> <!-- <target>System.err</target>--> <!-- 日志信息格式 --> <encoder> <!-- 格式化日志 --> <pattern>%d{yyyy-MM-dd HH:mm:ss} %thread %level %logger %msg \n</pattern> <!-- 日志字符编码 --> <charset>UTF-8</charset> </encoder> </appender> <!-- 用于指定包下,日志的输出等级--> <logger name="wnan.explore.log" level="info"> <appender-ref ref="stdout"/> </logger> <!--日志输出等级--> <root level="debug"> <!-- 日志具体附加器 --> <appender-ref ref="stdout"/> </root>运行结果:
重复输出了,因为logger和root都引用了stdout
删除掉root里的appender的引用,还可以在logger里设置additivity="false",这样日志就不会传播给父logger。
- 输出到文件
<!-- 输出到文件--> <appender name="file" class="ch.qos.logback.core.FileAppender"> <file>${file.path}/log_info/demo.log</file> <encoder> <!-- 格式化日志 --> <pattern>%d{yyyy-MM-dd HH:mm:ss} %thread %level %logger %msg \n</pattern> <!-- 日志字符编码 --> <charset>UTF-8</charset> </encoder> </appender> <logger name="wnan.explore.log" level="info"> <appender-ref ref="file"/> </logger>- 输出到文件,高级设置日期和文件大小滚动
<!-- 文件大小和时间滚动--> <appender name="roll_size_time" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${file.path}/log_info/log_tys.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${file.path}/log_info/log_%d{yyyy-MM-dd-HH-mm-ss}_%i.log</fileNamePattern> <maxFileSize>10MB</maxFileSize> <maxHistory>3</maxHistory> <totalSizeCap>20GB</totalSizeCap> </rollingPolicy> <encoder> <!-- 格式化日志 --> <pattern>%d{yyyy-MM-dd HH:mm:ss} %thread %level %logger %msg \n</pattern> <!-- 日志字符编码 --> <charset>UTF-8</charset> </encoder> <!-- 配置日志等级过滤器:LevelFilter --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 指定日志等级 --> <level>INFO</level> <!-- 若是>=info(匹配)直接通过不过滤 --> <onMatch>ACCEPT</onMatch> <!-- 若是<info(不匹配)过滤掉 --> <onMismatch>DENY</onMismatch> </filter> </appender> <logger name="wnan.explore.log" level="debug"> <appender-ref ref="roll_size_time"/> </logger>运行结果:
日志文件的数量会保持三个。单个文件10MB
- 文件输出到html
<appender name="file_html" class="ch.qos.logback.core.FileAppender"> <file>${file.path}/log_info/demo.html</file> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <!-- 格式化日志 --> <pattern>%d{yyyy-MM-dd HH:mm:ss} %thread %level %logger %msg \n</pattern> <!-- 日志字符编码 --> <charset>UTF-8</charset> </layout> </encoder> </appender> <!-- 用于指定包下,日志的输出等级--> <logger name="wnan.explore.log" level="info"> <appender-ref ref="file_html"/> </logger> <!--日志输出等级--> <root level="debug"> </root>输出