网站首页 > 技术文章 正文
在现代应用中,日志记录是不可或缺的组成部分,它为开发人员提供了关于应用运行状态的重要信息。尽管如此,很多开发者在配置日志时往往忽视了一个关键问题:日志记录的增加并不意味着系统性能的下降。相反,通过合理的日志配置,系统的性能和稳定性反而可以得到提升。那么,如何配置日志才能既确保不丢失重要信息,又不影响系统的效率呢?
1.日志记录的两面性
日志记录在软件开发中的作用不言而喻。从错误追踪到性能分析,再到用户行为跟踪,它为我们提供了宝贵的数据。然而,日志记录本身也会带来性能上的消耗。尤其是在生产环境中,日志过多可能导致磁盘空间不足,I/O瓶颈,甚至影响系统的响应速度。如何在这些矛盾之间找到平衡,是每个开发者必须面对的问题。
2.日志配置的核心目标
为了使日志既能提供足够的调试信息,又不成为系统性能的瓶颈,合理的日志配置至关重要。基于 Logback 的配置文件示例,我们可以看到以下几个关键配置点:
a. 日志滚动策略(Rolling Policy)
日志滚动策略的配置决定了日志文件的管理方式。在上面的配置中,RollingFileAppender 被用来实现日志滚动,并且结合了
SizeAndTimeBasedRollingPolicy。这种配置可以确保日志文件按时间和大小同时进行滚动。具体而言:
- 每个日志文件的最大大小为 100MB。
- 日志文件会按天生成,并且保留最多 30 天的日志文件。
这样做的好处是:
- 保证日志不会无限制增长,占用过多磁盘空间。
- 及时回收历史日志,减少系统负担。
b. 异步日志(AsyncAppender)
通过 AsyncAppender,我们可以将日志写入操作异步化,即不再阻塞主线程。尤其是在高并发的应用场景中,异步日志可以有效避免日志写入带来的性能瓶颈。配置中的 discardingThreshold 参数确保即使日志队列满了,重要日志也不会丢失,达到最佳的性能和数据完整性平衡。
c. 控制输出级别
合理设置日志级别是优化日志配置的另一重要步骤。在生产环境中,我们通常设置日志级别为 INFO,只记录关键的操作和状态信息,而将调试信息(DEBUG)和跟踪信息(TRACE)的日志禁用或降低级别,以减少日志的冗余。在配置中,日志输出的级别通过 <root level="INFO"> 来指定,这意味着只有 INFO 及以上级别的日志会被记录,减少了无用信息的存储。
d. 控制输出格式
日志的输出格式不仅仅是为了可读性,也有助于后期的分析。配置中的 PatternLayoutEncoder 控制了日志格式,例如:
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
这种格式确保每条日志都有清晰的时间戳、线程信息、日志级别以及具体消息,有助于开发者快速定位问题。
3.开发与生产环境的配置差异
一个常见的误区是,开发环境和生产环境的日志配置是一样的。事实上,开发环境的日志配置可以相对宽松,允许记录更多的调试信息。然而,生产环境则需要严格控制日志的级别和内容,以避免对系统性能造成影响。配置中的 <springProfile name="dev"> 和 <springProfile name="prd"> 就是为此目的服务的。它们分别针对开发和生产环境进行了不同的日志配置:
- 开发环境可能更倾向于输出调试级别的信息,方便开发人员调试。
- 生产环境则通常只记录 INFO 级别以上的日志,避免过多的日志信息对系统性能造成影响。
4.性能优化:日志不再成为瓶颈
合理配置日志系统不仅有助于避免过多日志占用磁盘空间,更能提升整体性能。通过以下方式可以进一步优化性能:
- 异步日志记录:确保日志记录不会阻塞业务线程。
- 日志滚动和压缩:自动按日期和大小滚动日志文件,避免过大文件导致性能问题,同时压缩历史日志节省存储空间。
- 控制日志级别:减少不必要的 DEBUG 或 TRACE 级别日志,特别是在生产环境中。
5.结语
日志记录是软件系统中不可或缺的一部分,但只有合理的配置才能让日志记录为系统的稳定性和性能加分,而不是拖后腿。通过合理的日志滚动策略、异步处理和输出格式的调整,开发者可以确保日志记录既不丢失重要信息,又不会影响系统的运行效率。
在技术上,我们已经找到了让“日志更多”与“性能更好”并行的解决方案。在这个信息化、数据化的时代,正确的日志配置是提升系统可维护性和可扩展性的关键步骤。
参考日志配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<property name="LOG_HOME" value="/var/logs/as" />
<property name="APP_NAME" value="app" />
<!-- 本地环境 -->
<springProfile name="dev">
<!-- 按照每天生成日志文件 -->
<appender name="rolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_HOME}/${APP_NAME}.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/${APP_NAME}-%d{yyyy-MM-dd}.%i.log.zip</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
<!--日志文件大小-->
<maxFileSize>100MB</maxFileSize>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<appender name="async_rolling" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="rolling"/>
<includeCallerData>true</includeCallerData>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="async_rolling" />
</root>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prd">
<!-- 按照每天生成日志文件 -->
<appender name="rolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_HOME}/${APP_NAME}.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/${APP_NAME}-%d{yyyy-MM-dd}.%i.log.zip</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
<!--日志文件大小-->
<maxFileSize>100MB</maxFileSize>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<appender name="async_rolling" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="rolling"/>
<includeCallerData>true</includeCallerData>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="async_rolling" />
</root>
</springProfile>
<logger name="com.sr.gf" level="INFO" />
<logger name="org.apache.ibatis" level="ERROR"/>
<logger name="org.springframework" level="ERROR"/>
<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="ERROR"/>
<logger name="org.apache.commons" level="ERROR"/>
<logger name="com.alibaba" level="ERROR"/>
<logger name="com.netflix" level="ERROR"/>
<logger name="catalia" level="ERROR"/>
<logger name="org.apache" level="ERROR"/>
<logger name="com.ibatis.common.jdbc.SimpleDataSource" level="ERROR"/>
<logger name="java.sql.ResultSet" level="ERROR"/>
<logger name="com.ibatis.common.jdbc.ScriptRunner" level="ERROR"/>
<logger name="java.sql.Connection" level="ERROR"/>
<logger name="java.sql.PreparedStatement" level="ERROR"/>
</configuration>
猜你喜欢
- 2025-09-18 GPU集群扩展:Ray Serve与Celery的技术选型与应用场景分析
- 2025-09-18 【不背八股】2.操作系统-进程、线程、协程的基本理解
- 2025-09-18 两张图看透Android Handler使用与机制
- 2025-09-18 Spring Boot 3.x 日志配置与 Logback 集成指南
- 2025-09-18 解锁C++异步之力:高效并发编程指南
- 2025-09-18 Flutter框架分析(八)-Platform Channel
- 2025-09-18 原来你是这样打印日志的,怪不得天天背锅……
- 2025-09-18 .NET Aspire 9.4 发布了 CLI GA、交互式仪表板和高级部署功能
- 2025-09-18 27.8K!一把梭 LLM:LiteLLM 带你用一套接口召唤 100+ 大模型
- 2025-09-18 Rust异步编程神器:用Tokio轻松创建定时任务
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)