Log4j2 快速入门
log4j 2.x 版本提供了更多的功能和更加简便的使用方式,和1.X版本相比使用过程有些不同,其中比较大的改动是
- 取消了 log4j.propelog配置文件,使用 xml,json,jsn 格式作为配置文件,
- 修改了部分对象的调用过程,如 Logger 对象等
系统选择配置文件的优先级(从先到后)如下:
classpath
下的名为log4j2-test.json
或者log4j2-test.jsn
的文件.classpath
下的名为log4j2-test.xml
的文件.classpath
下名为log4j2.json
或者log4j2.jsn
的文件.-
使用示例
在官方下载log4j2,下载地址:https://logging.apache.org/log4j/2.x/download.html
将以下的 jar 包导入到项目依赖 lib 中: log4j-core-xx.jar
- log4j-api-xx.jar
也可以通过 在 Maven 项目的 pom.xml 引入:
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.x</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.x</version>
</dependency>
</dependencies>
之后在项目工程的 src 目录下创建一个 log4j2.xml ,里面写入:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
之后在工程里就可以使用 log4j 记录日志了,以下是简单的测试代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TestLog4j {
private static final Logger log = LogManager.getLogger();
public static void main(String[] args){
log.debug("test debug message"); //Logger的各种记录方法;
log.info("test info message");
log.warn("test warn message");
log.error("test error message");
log.fatal("test fatal message");
}
}
以上代码在控制台输出
20:35:28.181 [main] INFO demo.TestLog4j - test info message
20:35:28.181 [main] WARN demo.TestLog4j - test warn message
20:35:28.181 [main] ERROR demo.TestLog4j - test error message
20:35:28.181 [main] FATAL demo.TestLog4j - test fatal message
绑定Slf4j 与 Log4j2
<!--用于与slf4j保持桥接-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.9.1</version>
</dependency>
<!-- slf4j核心包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.api.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.core.version}</version>
</dependency>
<!-- lombok for slf4j -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
配置文件引用POM文件变量
pom.xml
<!-- 配置不同环境的变量 -->
<profiles>
<!-- 本地环境 -->
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<profileActive>local</profileActive>
<logLocation>LocalFile</logLocation>
</properties>
</profile>
<!-- 生产环境 -->
<profile>
<id>prod</id>
<properties>
<profileActive>prod</profileActive>
<logLocation>File</logLocation>
</properties>
</profile>
<!-- 沙箱测试环境 -->
<profile>
<id>sandbox</id>
<properties>
<profileActive>sandbox</profileActive>
<logLocation>File</logLocation>
</properties>
</profile>
</profiles>
<!-- 打包所有的配置文件,并进行配置文件中的变量替代 -->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 启用过滤,即该资源中的变量将会被过滤器中的值替换 -->
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources/${env}/</directory>
<filtering>true</filtering>
<includes>
<include>commons.cfg</include>
</includes>
</resource>
</resources>
</build>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<!-- 日志输出配置 -->
<Appenders>
<!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<!-- 文件输出 生产环境输出到公司统一规定的位置,测试输出到D:/logs/... -->
<File name="File" fileName="@user.home@/logs/ds-phoenix-radar-script-hh-kol/ds-phoenix-radar-script-hh-kol-@profileActive@.log">
<PatternLayout>
<pattern>%d{yyyy-MM-dd_HH:mm:ss} [%t] %p %c{2}:%L: %m%n</pattern>
</PatternLayout>
</File>
<File name="LocalFile" fileName="D:/logs/ds-phoenix-radar-kol-type-titan/ds-phoenix-radar-kol-type-titan-@profileActive@.log">
<PatternLayout>
<pattern>%d{yyyy-MM-dd_HH:mm:ss} [%t] %p %c{2}:%L: %m%n</pattern>
</PatternLayout>
</File>
</Appenders>
<!-- 日志采集 -->
<Loggers>
<!-- 默认采集器 -->
<Root level="info">
<!-- 日志输出 -->
<AppenderRef ref="Console" />
<AppenderRef ref="@logLocation@"/>
</Root>
</Loggers>
</Configuration>
日志记录的级别
log4j 的日志记录级别由低到高如下表:
trace | 栈追踪级别,细粒度最低的级别,一般很少使用; |
---|---|
debug | 调试级别,在调试过程中很最经常使用,一般在工程中会以debug最为最低级别,主要用于代替Eclipse和IDEA的debug功能,用于输出调试信息; |
info | 用于输出程序运行中重要或感兴趣的信息,在生产环境中最经常使用; |
warn | 指定潜在的警告信息,类似于java中的@warn注解 |
error | 指定错误信息,但仍然允许程序继续运行,常用在记录catch捕获到的异常信息; |
fatal | 指定重大错误信息,这种等级的错误可能会导致程序中止; |
off | 最高等级,这个参数只是为了关闭日志记录 |
这些级别的等级排布:trace < debug < info < warn < error < fatal
这些级别之间的包含的关系,比如将日志级别设置为 debug,那么大于等于这个级别的日志都会输出,即输出 debug,info,warn,error,fatal 等级的日志;
配置文件说明
一般会把配置文件命名为 log4j2.xml ,同时放置在项目src的目录下,关于log4j2调用配置文件的顺序,可以参考官方说明:
https://logging.apache.org/log4j/2.x/manual/configuration.html#AutomaticConfiguration
一个典型的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="myConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="myConsole" />
</Root>
</Loggers>
</Configuration>
这里补充Log4j的3个主要的对象介绍:
- Logger: 负责捕获记录信息;
- Appender : 负责发布日志信息,同时决定日志的发送目的地;
-
<Configuration>
log4j2.xml 的根节点,内部包含、等多种节点;
status
属性指定默认的优先级(即在内部logger没有指定优先级的情况下,为其指定的优先级);monitorInterval
指定每隔多少秒重新读取配置文件,可以在不重启的情况下读取该配置文件;<Appenders>
定义各种 Appender 输出器,包含各个appender,有各种种类的appender标签
其中有2种比较常用的标签:<Console>
、<File>
,分别代表控制台输出、文件输出<Console>
控制台输出类型 appender,其中
name
属性为该appender的标识名,traget
指定输出目的地<PatternLayout>
用于appender输出的格式化,pattern属性为一个输出格式的字符串;
如%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
中的含义如下: %d{HH:mm:ss.SSS}
:表示输出到毫秒的时间%t
:输出当前线程名称(t / thread)p / le / level
:输出日志级别。%-5level
:输出日志级别,-5表示左对齐并且固定输出5个字符%logger
:输出logger名称,如果Logger没有名称,则不输出%logger{36}
:表示logger名字最长36个字符,否则按照句点分割%msg
:输出日志文本(m / msg / message)%n
:换行%F
:输出所在的类文件名,如Client.java%L
:输出行号%M
:输出所在方法名
输出以下格式的文本:
20:35:28.181 [main] INFO demo.TestLog4j - test info message
以下是一个在debug常用的格式字符串,用于输出debug的详细信息:%d{yyyy.MM.dd HH:mm:ss.SSS} [%t] %-5level %logger{36} %F:%M(line:%L) - %msg%n
输出格式如下
2017.07.27 14:30:21.889 [main] DEBUG demo.TestLog4j TestLog4j.java:main(line:17) - test debug messagelogger{length}
输出日志的logger名,可有一个整形参数,功能是缩短logger名,设置为0表示只输入logger最右边
Conversion specifier | Logger name | Result |
---|---|---|
%logger | mainPackage.sub.sample.Bar | mainPackage.sub.sample.Bar |
%logger{0} | mainPackage.sub.sample.Bar | Bar |
%logger{5} | mainPackage.sub.sample.Bar | m.s.s.Bar |
%logger{10} | mainPackage.sub.sample.Bar | m.s.s.Bar |
%logger{15} | mainPackage.sub.sample.Bar | m.s.sample.Bar |
%logger{16} | mainPackage.sub.sample.Bar | m.sub.sample.Bar |
%logger{26} | mainPackage.sub.sample.Bar | mainPackage.sub.sample.Bar |
%d{HH:mm:ss.SSS}
输出日志的打印日志,模式语法与 java.text.SimpleDateFormat
兼容。看上去 %d 就已经够好了
Conversion Pattern | Result |
---|---|
%d | 2006-10-20 14:06:49,812 |
%date | 2006-10-20 14:06:49,812 |
%date{ISO8601} | 2006-10-20 14:06:49,812 |
%date{HH:mm:ss.SSS} | 14:06:49.812 |
%date{dd MMM yyyy ;HH:mm:ss.SSS} | 20 oct. 2006;14:06:49.812 |
可选的格式修饰符位于“%”和转换符之间。第一个可选修饰符是左对齐 标志,符号是减号“-”;
接着是可选的最小宽度 修饰符,用十进制数表示。如果字符小于最小宽度,则左填充或右填充,默认是左填充(即右对齐),填充符为空格。
如果字符大于最小宽度,字符永远不会被截断。
<Loggers>
定义各种loggers,包含 <Root>
和 <Logger>
2种标签<Root>
为根logger,即在代码中 LogManager.getlogger() 没有指定参数的情况下委派的默认 logger,这些logger会包含一个 <AppenderRef>
用于指定该 logger 的 appender,在其 ref 属性表示该 append的 name 标识;
指定Logger
在代码Logger logger = LogManager.getLogger()
中,如果不指定Logger name,那么lg4j会直接调用<Root>
的Logger,在使用 %logger 会输出所调用类的 class 名
如以上示例例子会输出“demo.Log4jTest”
如果要指定一个 Logger 来处理该日志收集,可以如下设置:
- 调用 ```java private static final Logger log = LogManager.getLogger(“mylogger”);
public static void main(String[] args){ log.debug(“test debug message”); log.info(“test info message”); log.warn(“test warn message”); log.error(“test error message”); log.fatal(“test fatal message”); }
- log4j2.xml
```xml
<Configuration status="WARN" monitorInterval="300">
<Appenders>
<Console name="myConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="mylogger" level="debug" additivity="false">
<AppenderRef ref="myConsole" />
</Logger>
<Root level="debug">
<AppenderRef ref="myConsole" />
</Root>
</Loggers>
</Configuration>
<Logger>
中 additivity=”false” 表示在该logger中输出的日志不会再延伸到父层 logger,这里如果改为true,则会延伸到Root Logger,即Root Logger的配置也会输出一次。
指定Appender
通过Appender可以指定输出的目的地和格式,常用的有输出到控制台和文件,即 <Console>
和 <File>
以下示例者两种输出形式
<Configuration status="WARN" monitorInterval="300">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<File name="myfile" fileName="D:/logs/app.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
</Appenders>
<Loggers>
<Logger name="mylog" level="debug" additivity="false">
<AppenderRef ref="myfile" />
</Logger>
<Root level="error">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
参考文献
- https://logging.apache.org/log4j/2.x/index.html
- Spring Boot - log4j 2 configuration example
- Log4j 2 - JDBCAppender example
- Log4j 2 - XML configuration example
- Log4j 2 - JSON configuration example
- Log4j 2 - YAML configuration example
- Hibernate 5 + Log4j 2 configuration example
- Log4j 2 - RollingFileAppender example
Log4j2 日志按级别输出到不同文件
在生产环境中,可以按日志信息级别,输出到不同的文件中,如生成 info.log,warn.log,error.log 等;也可以按照自定义的功能方式进行分类。比如接口API调用日志,定时任务日志等等。
这样的功能可以通过在 log4j2.xml 中配置 Filter 来实现;
示例代码(一)
log4j2.xml
- 默认 Logger 对象的日志信息输出到 4 个
AppenderRef
然后每个
AppenderRef
对传过来的日志信息进行过滤, 只留下指定等级的信息<Configuration status="WARN" monitorInterval="500">
<!--定义日志储存文件目录-->
<properties>
<property name="LOG_HOME">user/logs</property>
</properties>
<Appenders>
<!--控制台输出所有日志-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<!--Info级别日志输出-->
<RollingRandomAccessFile name="InfoFile"
fileName="${LOG_HOME}/info/info.log"
filePattern="${LOG_HOME}/info-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL" />
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="20" />
</RollingRandomAccessFile>
<!--Error级别日志输出-->
<RollingRandomAccessFile name="ErrorFile"
fileName="${LOG_HOME}/error/error.log"
filePattern="${LOG_HOME}/error-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="fatal" onMatch="DENY" onMismatch="NEUTRAL" />
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="20" />
</RollingRandomAccessFile>
<!--Fatal级别日志输出-->
<RollingRandomAccessFile name="FatalFile"
fileName="${LOG_HOME}/fatal/fatal.log"
filePattern="${LOG_HOME}/fatal-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="fatal" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="20" />
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="Debug">
<AppenderRef ref="Console" />
<AppenderRef ref="InfoFile" />
<AppenderRef ref="ErrorFile" />
<AppenderRef ref="FatalFile" />
</Root>
</Loggers>
</Configuration>
示例二
```java <?xml version=”1.0” encoding=”UTF-8”?>
<!--这个输出控制台的配置-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout charset="UTF-8" pattern="[%t] %d{yyyy-MM-dd HH:mm:ss,SSS a} [%p] %l - %msg%n"/>
</Console>
<!-- 日常info输出-->
<RollingFile name="XmasLogFile" fileName="${sys:catalina.home}/logs/xmas.log" filePattern="${sys:catalina.home}/logs/xmas-%d{yyyy-MM-dd}.log" append="true">
<!--日志格式-->
<PatternLayout charset="UTF-8" pattern="[%t] %d{yyyy-MM-dd HH:mm:ss,SSS a} [%p] %l - %msg%n"/>
<!-- 设置间隔时间 -->
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<!-- 日常error输出-->
<RollingFile name="XmasErrorFile" fileName="${sys:catalina.home}/logs/xmas-error.log" filePattern="${sys:catalina.home}/logs/xmas-error-%d{yyyy-MM-dd}.log" append="true">
<PatternLayout charset="UTF-8" pattern="[%t] %d{yyyy-MM-dd HH:mm:ss,SSS a} [%p] %l - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<!--Quartz定时任务日志-->
<RollingFile name="XmasQuartzLogFile" fileName="${sys:catalina.home}/logs/xmas-quartz.log" filePattern="${sys:catalina.home}/logs/xmas-quartz-%d{yyyy-MM-dd}.log" append="true">
<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS z} [%t] %-5level - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
<!--api接口请求-->
<RollingFile name="XmasApiLogFile" fileName="${sys:catalina.home}/logs/xmas-api.log" filePattern="${sys:catalina.home}/logs/xmas-api-%d{yyyy-MM-dd}.log" append="true">
<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS z} [%t] %-5level - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
<!--日志统计-->
<RollingFile name="XmasAnalyzeLogFile" fileName="${sys:catalina.home}/logs/analyze-xmas.log" filePattern="${sys:catalina.home}/logs/analyze-xmas-%d{yyyy-MM-dd}.log" append="true">
<PatternLayout charset="UTF-8" pattern="%msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<logger name="com.chengxumiao.xmas.interceptor.TimeInterceptor" level="info" additivity="false">
<appender-ref ref="XmasApiLogFile"/>
</logger>
<logger name="com.chengxumiao.xmas.logAnalyzer.TimelogAnalyzer" level="info" additivity="false">
<appender-ref ref="XmasAnalyzeLogFile"/>
</logger>
<logger name="com.chengxumiao.xmas.quartz" level="info" additivity="false">
<appender-ref ref="XmasQuartzLogFile"/>
</logger>
<Logger name="com.chengxumiao.xmas" level="trace" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="XmasLogFile"/>
<appender-ref ref="xmasErrorFile"/>
</Logger>
<Root level="warn"/>
</Loggers>
一般项目中其他 jar 包引用的是 log4j.properties 文件,比如tomcat启动时候等一些日志,所以可以配置 log4j2.xml 和 log4j.properties 两个文件共存,这样所有的日志都能记录下来了,我习惯于 log4j2.xml 按照功能,类别拆分,log4j.properties只输出一些控制台打印
```bash
log4j.rootLogger=info, stdout
# Console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%t] %d{yyyy-MM-dd HH:mm:ss,SSS} [%p]:%l - %m%n
定期生成日志文件
Log4j2 对于生成日志文件时,可以通过对中的进行设置,来完整日志文件各种生成方案
log4j2.xml
<Configuration status="WARN" monitorInterval="300">
<properties>
<property name="LOG_HOME">user/logs</property>
</properties>
<Appenders>
<RollingRandomAccessFile name="logfile" fileName="${LOG_HOME}/app.log"
filePattern="${LOG_HOME}/app-%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
</RollingRandomAccessFile>
<DefaultRolloverStrategy max="20"/>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="logfile" />
</Root>
</Loggers>
</Configuration>
此时日志按天数产生,及每天产生一个新的日志文件
关键的地方在于 <RollingRandomAccessFile>
filepattern属性和 <TimeBasedTriggeringPolicy>
的设置
<RollingRandomAccessFile >
filepattern 中的日期格式精确位数决定了生成日志的日期单位,<TimeBasedTriggeringPolicy>
interval 决定该单位下的间隔数,如果在上面例子中 interval 设置为 5,那么就是5天生成一个日志
如果按月生成日志,那么 filePath 修改为 ${LOG_HOME}/app-%d{yyyy-MM}.log
按小时生成日志,filePath = ${LOG_HOME}/app-%d{yyyy-MM-dd-HH-mm}.log
上面例子中的<DefaultRolloverStrategy max="20"/>
用于设置符合同个filePath的日志文件的最大数量,超过这个数量时,新产生的日志文件会覆盖旧的日志文件,没有设置时,默认值为7;
定大小生成日志文件
log4j2.xml
<Configuration status="WARN" monitorInterval="300">
<properties>
<property name="LOG_HOME">user/logs</property>
</properties>
<Appenders>
<RollingRandomAccessFile name="logfile" fileName="${LOG_HOME}/app.log"
filePattern="${LOG_HOME}/error-%i.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
</RollingRandomAccessFile>
<DefaultRolloverStrategy max="20"/>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="logfile" />
</Root>
</Loggers>
</Configuration>
<SizeBasedTriggeringPolicy size="10 MB" />
用于设置单个日志文件的大小上限<RollingRandomAccessFile>
filepattern 中的 “%i” 输出文件的次序<DefaultRolloverStrategy>
设置设置符合同个filePath的日志文件的最大数量不同线程写入不同文件
https://www.haoyizebo.com/posts/a29ac0/
- 新建一个实现类, 实现log4j2的 StrLookup 接口
- log4j2.xml文件中配置多线程的路由输出路径
之后可实现代码无入侵实现该需求
@Plugin(name = "thread", category = StrLookup.CATEGORY)
public class ThreadLookup implements StrLookup {
@Override
public String lookup(String s) {
return Thread.currentThread().getName();
}
@Override
public String lookup(LogEvent logEvent, String s) {
return logEvent.getThreadName() == null ? Thread.currentThread().getName()
: logEvent.getThreadName();
}
}
配置文件示例
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF">
<Appenders>
<Routing name="Routing">
<Routes pattern="$${thread:threadName}">
<Route>
<RollingFile name="logFile-${thread:threadName}"
fileName="logs/concurrent-${thread:threadName}.log"
filePattern="logs/concurrent-${thread:threadName}-%d{MM-dd-yyyy}-%i.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} - %m%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="50 MB"/>
</Policies>
<DefaultRolloverStrategy max="100"/>
</RollingFile>
</Route>
</Routes>
</Routing>
<Async name="async" bufferSize="1000" includeLocation="true">
<AppenderRef ref="Routing"/>
</Async>
<!-- 很直白,Console 指定了结果输出到控制台 -->
<Console name="ConsolePrint" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %t %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info" includeLocation="true">
<AppenderRef ref="async"/>
<AppenderRef ref="ConsolePrint"/>
</Root>
</Loggers>
</Configuration>
示例 ```java @Log4j2 public class TestThreadLog { // private static final Logger log = LogManager.getLogger(TestThreadLog.class);
@Test void test001ThreadLog() {
new Thread(() -> {
Thread.currentThread().setName("线程1-1");
log.info("info");
log.debug("debug");
log.error("error");
}).start();
new Thread(() -> {
Thread.currentThread().setName("线程1-2");
log.info("info");
log.debug("debug");
log.error("error");
}).start();
}
}
<a name="fTgQi"></a>
## 日志写入数据库
新建一张表
```sql
CREATE TABLE `t_bde_etl_log` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT "唯一标识",
`log_level` varchar(100) NOT NULL COMMENT "日志等级",
`log_location` varchar(1000) DEFAULT NULL COMMENT "日志位置",
`log_date` varchar(1000) DEFAULT NULL COMMENT "日志日期",
`log_message` varchar(1000) NOT NULL COMMENT "日志信息",
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4;
配置文件中配置一个写入数据库的Appender
, 注意指定连接池的类
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<!--定义日志储存文件目录-->
<properties>
<property name="LOG_HOME">${user.home}/logs</property>
</properties>
<!-- 设定输出的地方, 可以设定多个, 然后让Logger去挑选 -->
<Appenders>
<!-- 输出到控制台 -->
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<!-- 输出到文件 -->
<File name="file" fileName="D:/logs/app.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
<!-- 输出到数据库, 表名为: t_bde_etl_log, 需获取数据库连接池 -->
<JDBC name="databaseAppender" tableName="t_bde_etl_log">
<ConnectionFactory class="com.datastory.radar.serv.base.ConnectionFactory" method="getDatabaseConnection"/>
<!-- pattern中填写标准的log4j2参数即可 %msg %d 等 -->
<Column name="log_level" pattern="%level"/>
<Column name="log_location" pattern="%ogger{36}"/>
<Column name="log_date" pattern="%d{yyyy-MM-dd hh:mm:ss}"/>
<Column name="log_message" pattern="%m"/>
</JDBC>
</Appenders>
<!-- 日志样式, 这里有两个, 一个是默认的, 一个是DBLogger -->
<Loggers>
<Logger name="DBLogger" level="Debug" additivity="false">
<!-- 指定使用的追加器 -->
<AppenderRef ref="console" />
<AppenderRef ref="file" />
<AppenderRef ref="databaseAppender" />
</Logger>
<Root level="debug">
<AppenderRef ref="console" />
</Root>
</Loggers>
</Configuration>
获取druid连接池
package com.datastory.radar.serv.base;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class ConnectionFactory{
private DruidDataSource dataSource;
private static ConnectionFactory connectionFactory;
private Connection getConnection() throws SQLException {
Properties properties = new Properties();
String user = "mondelez";
String password = "akT9d9FhSvtBTbuN";
String url = "jdbc:mysql://dev.mysql.proxy.hdp:3001/db_datastory_mondelez?useUnicode=true&characterEncoding=utf-8";
String driverClassName = "com.mysql.jdbc.Driver";
properties.put("driverClassName",driverClassName);
properties.put("url",url);
properties.put("username",user);
properties.put("password",password);
try {
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
try {
init();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return dataSource.getConnection();
}
public static Connection getDatabaseConnection() throws SQLException {
if(connectionFactory == null){
connectionFactory = new ConnectionFactory();
}
return connectionFactory.getConnection();
}
public void init(){
try {
if (dataSource != null)
dataSource.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用
// 输出到数据库的日志对象
private static final Logger dbLogger = LogManager.getLogger("DBLogger");