优秀的编程知识分享平台

网站首页 > 技术文章 正文

为什么日志越多,系统却能跑得更快?

nanyue 2025-09-18 05:04:21 技术文章 1 ℃

在现代应用中,日志记录是不可或缺的组成部分,它为开发人员提供了关于应用运行状态的重要信息。尽管如此,很多开发者在配置日志时往往忽视了一个关键问题:日志记录的增加并不意味着系统性能的下降。相反,通过合理的日志配置,系统的性能和稳定性反而可以得到提升。那么,如何配置日志才能既确保不丢失重要信息,又不影响系统的效率呢?

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>

Tags:

最近发表
标签列表