Skip to content
0

Log4j - 配置

NOTE

日志能记录项目运行的一点一滴,生产上,我们也能通过日志快速定位和处理问题。本内容介绍 log4j 的一些基本使用,以及在 JS、SpringBoot 下的配置模板。

2021-12-24 @Young Kbt

什么是日志框架

我们在系统中对于记录日志的需求并不单纯。首先,我们希望日志要能持久化到磁盘,最基本的就是要能够保存到文件中;其次,我们希望在开发和生产环境中记录的日志并不相同,明显开发环境的日志记录会更多方便调试,但放到生产环境下大量的日志很容易会撑爆服务器,因此在生产环境我们希望只记录重要信息。

基于不单纯的目的,System.out.println 是直接不能满足我们的要求,因此抛弃它,选择功能更强的日志框架。而 log4j 是 apache 下一款著名的开源日志框架,log4j 满足上面的一切需求。

记录日志的框架并不仅仅只有 log4j,比较有名的还有 logback 等,现在比较火的 Spring Boot 默认集成的日志就是 logback。不管哪种日志框架,一般都能够实现日志的持久化功能。

日志框架介绍

Log4j 是 Apache 下的一款开源的日志框架,能够满足我们在项目中对于日志记录的需求。一般来讲,在项目中,我们会结合 slf4j 和 log4j 一起使用。Log4j 提供了简单的 API 调用,强大的日志格式定义以及灵活的扩展性。我们可以自己定义 Appender 来满足我们对于日志输出的需求。

Log4j 由三个重要的组件构成:日志信息的优先级(Logger),日志信息的输出目的地(Appender),日志信息的输出格式(Layout)。

  • 日志信息的优先级别分为四级,优先级从低到高有 DEBUG、INFO、WARN、ERROR,用来指定这条日志信息的重要程度

  • 日志信息的输出目的地指定了日志将打印到控制台还是文件中,官方已经默认给我们提供了大量的 Appender,基本上可以满足我们的日常需求,当然如果你的需求比较特殊,可以自己实现 Appender,也是非常简单的

  • 日志信息的输出格式则控制了日志信息的显示内容

注意:从源码知道,Log4j 日志级别是定义在 org.apache.log4j.Level 类中。Log4j 只建议使用 4 个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。

  • off 最高等级,用于关闭所有日志记录。

  • fatal 指出每个严重的错误事件将会导致应用程序的退出。

  • error 指出虽然发生错误事件,但仍然不影响系统的继续运行。

  • warm 表明会出现潜在的错误情形。

  • info 一般和在粗粒度级别上,强调应用程序的运行全程。

  • debug 一般用于细粒度级别上,对调试应用程序非常有帮助。

  • TRACE 一般用于更细粒度级别上,比 debug 级别更低

  • all 最低等级,用于打开所有日志记录。

日志的输出格式配置如下:

ini
%p:输出日志信息的优先级,即 DEBUG,INFO,WARN,ERROR,FATAL。
%d:输出日志时间点的日期或时间,默认格式为 ISO8601,也可以在其后指定格式,如:%d{yyyy/MM/dd HH:mm:ss,SSS}。
%r:输出自应用程序启动到输出该 log 信息耗费的毫秒数。
%t:输出产生该日志事件的线程名。
%l:输出日志事件的发生位置,相当于 %c.%M(%F:%L) 的组合,包括类全名、方法、文件名以及在代码中的行数。例如:test.TestLog4j.main(TestLog4j.java:10)。
%c:输出日志信息所属的类目,通常就是所在类的全名。
%M:输出产生日志信息的方法名。
%F:输出日志消息产生时所在的文件名称。
%L::输出代码中的行号。
%m::输出代码中指定的具体日志信息。
%n:输出一个回车换行符,Windows平台为 "/r/n" ,Unix平台为 "/n"
%x:输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像 java servlets 这样的多客户多线程的应用中。
%%:输出一个 "%" 字符。
另外,还可以在%与格式字符之间加上修饰符来控制其最小长度、最大长度、和文本的对齐方式。如:
%20c:指定输出 category 的名称,最小的长度是 20,如果 category 的名称长度小于 20 的话,默认的情况下右对齐。
%-20c:"-" 号表示左对齐。
%.30c:指定输出 category 的名称,最大的长度是 30,如果 category 的名称长度大于 30 的话,就会将左边多出的字符截掉,但小于 30 的话也不会补空格。

SpringBoot 配置

log4j 的配置文件分为两种,log4j.xmllog4j.properties,推荐使用 log4j.xml。

引入依赖

xml
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>

依赖、版本不唯一,请自行选择。

如何进行测试呢?首先在类上面加上 @Slf4j 注解,然后使用 log 对象即可,因为该注解自动创建了 log 全局对象,如下:

java
@Slf4j
public class Log4jTest {
    public void test() {
        log.debug("现在是早上六点,开始自动签到");
        log.info("现在是早上六点,开始自动签到");
        log.error("现在是早上六点,开始自动签到");
        log.warn("现在是早上六点,开始自动签到");
    }
}

这样运行项目后,就可以输出到控制台,如果您不单单想要输出到控制台,也想要输出到文件里,请看下面。

log4j.xml 详解

首先配置在 application.properties 或 application.yml 引用 log4j.xml,路径在 resource 下。

yml
logging:
  config: classpath:log4j.xml

log4j.xml 内容:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为 TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为 WARN,则低于 WARN 的信息都不会输出 -->
<!-- scan:当此属性设置为 true 时,配置文档如果发生改变,将会被重新加载,默认值为 true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
                 当 scan 为 true 时,此属性生效。默认的时间间隔为 1 分钟。 -->
<!-- debug:当此属性设置为 true 时,将打印出 logback 内部日志信息,实时查看 logback 运行状态。默认值为 false -->
<configuration  scan="true" scanPeriod="10 seconds">
    <contextName>logback-spring</contextName>

    <!-- name 的值是变量的名称,value 的值时变量定义的值。通过定义的值会被插入到 logger 上下文中。定义后,可以使 ${} 来使用变量。 -->
    <!-- 如果是 Windows,则 value="D:/logs" 类似 -->
    <property name="logging.path" value="/ROOT/logs" />
    <!-- 当天日志的文件名 -->
    <property name="logFile.name" value="bx" />
    <!-- 时间归档日志的文件名 -->
    <property name="logDateFile.name" value="bx" />

    <!-- 日志格式和颜色渲染 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.CTT}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <!--1. 输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志 appender 是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--2. 输出到文档-->
    <!-- 2.1 level 为 DEBUG 日志,按时间滚动输出  -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${logging.path}/${logFile.name}_debug.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.CTT} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志归档 -->
            <fileNamePattern>${logging.path}/${logDateFile.name}_debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是 10MB -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录 DEBUG 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.2 level 为 INFO 日志,时间滚动输出  -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${logging.path}/${logFile.name}_info.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.CTT} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${logging.path}/${logDateFile.name}_info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是 10MB -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录 INFO 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.3 level为 WARN 日志,时间滚动输出  -->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${logging.path}/${logFile.name}_warn.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.CTT} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logging.path}/${logDateFile.name}_warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是 10MB -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录 WARN 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.4 level为 ERROR 日志,时间滚动输出  -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${logging.path}/${logFile.name}_error.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.CTT} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logging.path}/${logDateFile.name}_error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是 10MB -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录 ERROR 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--
        File 标签是日志文件的输出地址:必须要为 ${LOG_PATH}/${LOG_FILE}。
        level 标签:如果设置了 level 为 info,只会输出 info 的日志信息,其他日志级别的日志就会过滤掉,
                   appender 的 level 标签优先级最高,如果指定了,则其他标签的 level 将会失效。
    -->

    <!--
        <logger> 用来设置某一个包或者具体的某一个类的日志打印级别,以及指定 <appender>。
        <logger> 仅有一个 name 属性,一个可选的 level 和一个可选的 addtivity 属性。
        name:用来指定受此 logger 约束的某一个包或者具体的某一个类。
        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
              还有一个特俗值 INHERITED 或者同义词 NULL,代表强制执行上级的级别。
              如果未设置此属性,那么当前 logger 将会继承上级的级别(appender 里指定的),
              优先级高于 root 的 level,低于 appender 的 level。
        addtivity:是否传递打印信息给上级的其他的 logger 或者 root 节点。默认是 true。
                   为 ture,意思是日志级别高的其他 logger 或 root 都会捕获到该 looger 的日志信息,并「给自己」进行日志输出。
            例子:
            <logger name="org.springframework.web" level="info"/>
            <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
            <logger name="你的 controller 包路径" level="debug"/>

        注意 logger 里的 level 是一个下限,比其日志级别高的日志信息也会输出。
    -->

    <!--
        使用 mybatis 的时候,sql 语句是 debug 下才会打印,而这里我们只配置了 info,所以想要查看 sql 语句的话,有以下两种操作:
        第一种把 <root level="info"> 改成 <root level="DEBUG">,这样就会打印 sql,不过这样日志那边会出现很多其他消息。
        第二种就是单独给 dao 层下目录配置 debug 模式,这样配置 sql 语句会打印,其他还是正常 info 级别:
        XML 文件配置:
            全局配置(可选)
                <logger name="com.apache.ibatis" level="DEBUG"/>
                <logger name="java.sql.Connection" level="DEBUG"/>
                <logger name="java.sql.Statement" level="DEBUG"/>
                <logger name="java.sql.PreparedStatement" level="DEBUG"/>
            包名配置(建议)
                <logger name="com.xxx.dao" level="DEBUG"/>

        properties 文件配置:
            全局配置(可选)
                logging.level.org.apache.ibatis=DEBUG
                logging.level.org.mybatis=DEBUG
                logging.level.java.sql.Connection=DEBUG
                logging.level.java.sql.Statement=DEBUG
            包名配置(建议)
                logging.level.com.xxx.dao=debug

        注意:com.xxx.dao 是你的包名。
     -->

    <!--
        root 节点是必选节点,用来指定最基础的日志输出级别,只有一个 level 属性。
        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
               不能设置为 INHERITED 或者同义词 NULL。默认是 DEBUG,不设置 level 不会输出到文件。
               可以包含零个或多个元素,标识这个 appender 将会添加到这个 logger。
               其输出会受到 logger 的影响,即注意 logger 中的 additivity 属性,如果为 false,则指定的包名下的日志不会输出。
    -->

    <!-- 4. 最终的策略 -->
    <!-- 4.1 开发环境:打印控制台-->
    <springProfile name="dev">
        <!-- 修改此处的扫描包名,指定这个包的日志级别最低下限 -->
        <logger name="com.glyxybxhtxt.controller" level="debug"/>
        <!-- logger 的 level 是一个下限,所有大于这个下限的日志级别都会输出 -->
    </springProfile>

    <!--
        因为 logger 指定了 level 的下限和 addtivity 默认为 true ,所以 root 的 level 可以不写。
        root 能捕获 level 低的 logger 的日志信息,是因为 addtivity 为 true,从而导致 logger 的 level 优先级比 root 高。
     -->
    <root level="info">
        <!--
            appender-ref 是 appender 的引用。
            如果 appender 里指定了 level,则只按照 appender 里的 level 进行输出。
         -->
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="DEBUG_FILE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="WARN_FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>

    <!-- 4.2 生产环境:输出到文档
    <springProfile name="pro">
        <root level="info">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="DEBUG_FILE" />
            <appender-ref ref="INFO_FILE" />
            <appender-ref ref="ERROR_FILE" />
            <appender-ref ref="WARN_FILE" />
        </root>
    </springProfile> -->
</configuration>

<!-- level 优先级:appender > logger > root -->

<!-- 修改日志输出目录,输出日志大小,项目指定扫描包即可使用 -->

我已经配置好了这个模板,你只需要修改的是 12 - 16 行的日志路径以及名字。

File 标签是日志文件的输出地址:必须要为 ${LOG_PATH}/${LOG_FILE}。

level 标签:如果设置了 level 为 info,只会输出 info 的日志信息,其他日志级别的日志就会过滤掉,appender 的 level 标签优先级最高,如果指定了,则其他标签的 level 将会失效。

logger 用来设置某一个包或者具体的某一个类的日志打印级别,以及指定 appenderlogger 仅有一个 name 属性,一个可选的 level 和一个可选的 addtivity 属性。

  • name:用来指定受此 logger 约束的某一个包或者具体的某一个类。

  • level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值 INHERITED 或者同义词 NULL,代表强制执行上级的级别。如果未设置此属性,那么当前 logger 将会继承上级的级别(appender 里指定的),优先级高于 root 的 level,低于 appender 的 level。

  • addtivity:是否传递打印信息给上级的其他的 logger 或者 root 节点。默认是 true。如果为 ture,意思是日志级别高的其他 logger 或 root 都会捕获到该 looger 的日志信息,并「给自己」进行日志输出。 例子:

    xml
    <logger name="org.springframework.web" level="info"/>
    <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
    <logger name="你的 controller 包路径" level="debug"/>

    注意 logger 里的 level 是一个下限,比其日志级别高的日志信息也会输出。

使用 mybatis 的时候,sql 语句是 debug 下才会打印,而这里我们只配置了 info,所以想要查看 sql 语句的话,有以下两种操作:

  • 第一种把 <root level="info"> 改成 <root level="DEBUG">,这样就会打印 sql,不过这样日志那边会出现很多其他消息。

  • 第二种就是单独给 dao 层下目录配置 debug 模式,这样配置 sql 语句会打印,其他还是正常 info 级别,如下: XML 文件配置:

    • 全局配置(可选)

      xml
      <logger name="com.apache.ibatis" level="DEBUG"/>
      <logger name="java.sql.Connection" level="DEBUG"/>
      <logger name="java.sql.Statement" level="DEBUG"/>
      <logger name="java.sql.PreparedStatement" level="DEBUG"/>
    • 包名配置(建议)

      xml
      <logger name="com.xxx.dao" level="DEBUG"/>

    properties 文件配置:

    • 全局配置(可选)

      properties
      logging.level.org.apache.ibatis=DEBUG
      logging.level.org.mybatis=DEBUG
      logging.level.java.sql.Connection=DEBUG
      logging.level.java.sql.Statement=DEBUG
    • 包名配置(建议)

      properties
      logging.level.com.xxx.dao=debug

      注意:com.xxx.dao 是你的包名。

root 节点是必选节点,用来指定最基础的日志输出级别,只有一个 level 属性。

  • level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为 INHERITED 或者同义词 NULL。默认是 DEBUG,不设置 level 不会输出到文件。可以包含零个或多个元素,标识这个 appender 将会添加到这个 logger。其输出会受到 logger 的影响,即注意 logger 中的 additivity 属性,如果为 false,则指定的包名下的日志不会输出。

对比 12 - 16 行的日志路径以及名字。路径以及名字的效果如图:

image-20211224143543115

INFO 日志内容的效果如图:

image-20211224143623158

ERROR 日志内容的效果如图:

image-20211224143712417

log4j.properties 详解

如果不喜欢在 log4j.xml 配置,那么也可以在 log4j.properties 配置,但是我建议在 xml 配置。

properties
## 设置全局日志 Logger
log4j.rootLogger=DEBUG,CONSOLE,FILEOUT
# DEBUG、CONSOLE、FILE、ROLLING_FILE、MAIL、DATABASE
log4j.addivity.org.apache=true

## 输出控制台 Appender ##
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=DEBUG
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c] \:%m%n
## 另外两种输出格式,可选
# log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n\u00A0
# log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n

## 输出 DEBUG 级别以上的日志到 E://logs/debug.log
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=E://logs/log.log
log4j.appender.D.Append=true
log4j.appender.D.Threshold=DEBUG
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

## 输出 ERROR 级别以上的日志到 E://logs/error.log
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File=E://logs/error.log
log4j.appender.E.Append=true
log4j.appender.E.Threshold=ERROR
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

## ...... 其他级别的日志同理


## 日志发生到服务器 Appender
#log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
#log4j.appender.SOCKET.RemoteHost=localhost
#log4j.appender.SOCKET.Port=7272
#log4j.appender.SOCKET.LocationInfo=true
## Set up for Log Facter 5
#log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
#log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n

## Log Factor 5 Appender
#log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
#log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000

## 日志发生到邮箱 Appender
#log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
#log4j.appender.MAIL.Threshold=FATAL
#log4j.appender.MAIL.BufferSize=10
#log4j.appender.MAIL.From=openwolfl@163.com
#log4j.appender.MAIL.SMTPHost=mail.openwolf.com
#log4j.appender.MAIL.Subject=Log4J Message
#log4j.appender.MAIL.To=openwolfl@163.com
#log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
#log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

## JDBC Appender
#log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
#log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
#log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
#log4j.appender.DATABASE.user=root
#log4j.appender.DATABASE.password=123456
#log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
#log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
#log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.A1.File=SampleMessages.log4j
#log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
#log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout

### 自定义 Appender
#log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender
#log4j.appender.im.host = mail.cybercorlin.net
#log4j.appender.im.username = username
#log4j.appender.im.password = password
#log4j.appender.im.recipient = 2456019588@qq.com
#log4j.appender.im.layout=org.apache.log4j.PatternLayout
#log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

log4js 配置

我使用 JS 来配置 log4j 日志,是因为我的邮箱项目是基于 node 构建。但是其原理 Java 的 log4j 类似。

本内容基于 node 进行 log4js 配置,其他框架的同理,只需要引入 log4js 即可使用日志功能。

安装

在 package.json 添加 log4js 依赖

json
"dependencies": {
    "log4js": "^6.3.0",
}

我的完整 package.json 如下:

json
{
  "name": "email",
  "version": "1.0",
  "private": true,
  "scripts": {
    "dev": "node app.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "log4js": "^6.3.0",
    "nodemailer": "^6.7.2"
  }
}

安装依赖:

sh
npm install

# 如果使用的是 yarn
yarn install

配置文件

安装完 log4js 依赖,我们就可以引用这个依赖,并且配置日志的级别、输出内容、输出路径。

js
// 引入插件 log4js
var log4js = require("log4js");
log4js.configure({
  appenders: {
    access: {
      // info 日志的 name
      type: "file",
      filename: "logs/access.log",
      layout: {
        type: "pattern",
        pattern: "[%d{yyyy-MM-dd hh:mm:ss SSS}] [%p] %c - %m",
      },
    },
    error: {
      // 错误日志的 name
      type: "file",
      filename: "logs/error.log",
      layout: {
        // 定义日志输出的样式
        type: "pattern",
        pattern: "[%d{yyyy MM dd hh:mm:ss SSS}] [%p] %c - %m%n",
      },
    },
  },
  // 引用 appenders
  categories: {
    default: { appenders: ["access"], level: "info" }, // 上方 appenders 的 name
    error: { appenders: ["error"], level: "error" },
  },
});

exports.logger = function (name) {
  // name 取 categories 的 name
  return log4js.getLogger(name || "default"); // name 为undefined 时,取 default
};
exports.use = function (app, logger) {
  app.use(
    log4js.connectLogger(logger || log4js.getLogger("default"), {
      level: "info",
      format: "请求类型/URI:「 :method:url 」",
    })
  );
};

可能无法理解 29 行的 name,这个是引用 logger 时,传入的参数,根据这个参数,获取不同的日志输出格式,如下:

js
// 引入 info 日志
const logger = require("./log").logger();
// 引入 error 日志,传入错误日志的 name,对应 categories
const errLogger = require("./log").logger("error");

// 以下仅仅是测试,实际根据需求输出
logger.info("info 信息");
errLogger.error("error 信息");

日志文件路径以及名字如图:

image-20211224151935916

access 日志内容如图:

image-20211224152016383

图片内容和我给的代码不一样,因为这是我实际使用的日志输出。不一样的地方仅仅是 default - 后面的内容,前面的内容都是在配置文件配置好了格式,你输出的内容就会放到 default - 后面。

最近更新