说明:公司系统采取的是log4j12结合slj4f,需要升级为log4j2结合slj4j,特地学习log4j的日志操作。
1.log4j12的升级
升级前,使用Apache Log4j和1.7.7版本的slf4j,说明:引用的是Apache Log4j,而不是Apache Log4j Core 或者 Apache Log4j API。
第一步:删除pom.xml中的log4j和slf4j的引入;
第二步:添加以下jar包引入:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${
slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${
log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${
log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${
log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>${
log4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>${
log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.1.8.1</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.2.0</version>
</dependency>
<log4j.version>2.7</log4j.version>
<slf4j.version>1.7.26</slf4j.version>
第三步:web.xml中去除log4jConfigLocation的context-param
log4j2在使用上,不需要再在web.xml引入log4jConfigLocation进行启动,而是自动在/src/resources目录下找到log4j2.xml文件,进行加载。
第四步:添加log4j2.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<Appenders>
<Console name="console_appender" target="SYSTEM_OUT">
<PatternLayout pattern="%d{MM/dd/yyyy HH:mm:ss.SSS} %c %-5p %m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<appender-ref ref="console_appender"/>
</Root>
</Loggers>
</configuration>
2.实现日志控制台输出
使用的是Console的Appender。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<Appenders>
<Console name="console_appender" target="SYSTEM_OUT">
<PatternLayout pattern="%d{MM/dd/yyyy HH:mm:ss.SSS} %c %-5p %m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<appender-ref ref="console_appender"/>
</Root>
</Loggers>
</configuration>
相当于properties中以下配置:
log4j.rootLogger=INFO, console_appender
log4j.appender.console_appender=org.apache.log4j.ConsoleAppender
log4j.appender.console_appender.layout=org.apache.log4j.PatternLayout
log4j.appender.console_appender.layout.ConversionPattern=%d{
MM/dd/yyyy HH:mm:ss.SSS} %c %-5p %m%n
3.实现日志文件输出
3.1.自定义的Appender
(以下代码基本来源于网络):
package com.fracong.test.log4j;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
@Plugin(name = "FileAppender", category = "Core", elementType = "appender", printObject = true)
public class Log4jCustomizeFileAppender extends AbstractAppender {
private String fileName;
public Log4jCustomizeFileAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String fileName) {
super(name, filter, layout, ignoreExceptions);
this.fileName = fileName;
}
@Override
public void append(LogEvent event) {
final byte[] bytes = getLayout().toByteArray(event);
writerFile(bytes);
}
@PluginFactory
public static Log4jCustomizeFileAppender createAppender(@PluginAttribute("name") String name,
@PluginAttribute("fileName") String fileName,
@PluginElement("Filter") final Filter filter,
@PluginElement("Layout") Layout<? extends Serializable> layout,
@PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
if (name == null) {
LOGGER.error("no name defined in conf.");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
if (!createFile(fileName)) {
return null;
}
return new Log4jCustomizeFileAppender(name, filter, layout, ignoreExceptions, fileName);
}
private static boolean createFile(String fileName) {
Path filePath = Paths.get(fileName);
try {
if (Files.exists(filePath)) {
Files.delete(filePath);
}
Files.createFile(filePath);
} catch (IOException e) {
LOGGER.error("create file exception", e);
return false;
}
return true;
}
private void writerFile(byte[] log) {
try {
Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);
} catch (IOException e) {
LOGGER.error("write file exception", e);
}
}
}
说明:@Plugin注解中的name,会作为log4j2.xml中的一个标签。
3.2.使用自定义的Appender进行输出
<?xml version="1.0" encoding="UTF-8"?>
<configuration packages="com.fracong.test.log4j">
<Properties>
<Property name="LOG_HOME">/logs/test</Property>
<Property name="LOG_FILE">info</Property>
</Properties>
<Appenders>
<FileAppender name="file_appender" fileName="${LOG_HOME}/${LOG_FILE}.log">
<PatternLayout pattern="%d{MM/dd/yyyy HH:mm:ss.SSS} %c %-5p %m%n" />
</FileAppender>
</Appenders>
<Loggers>
<Root level="INFO">
<appender-ref ref="file_appender"/>
</Root>
</Loggers>
</configuration>
说明:
1.configuration中的packages是将该目录下的Appender引入到标签中,Appender中定义的name需要和标签名保持一致。
2.Properties定义的属性,引用时使用格式:${Xxxx}
3.如上XML定义相当于properties中如下定义:
log4j.rootLogger=INFO,file_appender
log4j.appender.file_appender=com.fracong.test.log4j.Log4jCustomizeFileAppender
log4j.appender.file_appender.File=/logs/test/info.log
log4j.appender.file_appender.layout=org.apache.log4j.PatternLayout
log4j.appender.file_appender.layout.ConversionPattern=%d{
MM/dd/yyyy HH:mm:ss.SSS} %c %-5p %m%n
4.实现日志写入数据库
4.1.数据库结构
CREATE TABLE public.log_test (
log_id bigserial NOT NULL, //主键,且实现自增长
user_id varchar NULL,
log_type varchar NULL,
log_date timestampt NULL,
"log_level" varchar NULL,
"exception" text NULL,
CONSTRAINT log_test_pk PRIMARY KEY (log_id)
);
4.2.数据库连接
package com.fracong.test.log4j;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;
public class Log4j2ConnectionFactory {
private static BasicDataSource dataSource;
private Log4j2ConnectionFactory() {
}
public static Connection getConnection() throws SQLException {
if (dataSource == null) {
dataSource = new BasicDataSource();
//postgresql
/*dataSource.setUrl("jdbc:postgresql://localhost:5432/fracong-test?useSSL=false");
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUsername("postgres");
dataSource.setPassword("123456");*/
//mysql
dataSource.setUrl("jdbc:mysql://localhost:3306/test_demo?useSSL=false&serverTimezone=UTC");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");//8.0版本以下
//dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");//8.0版本以上
dataSource.setUsername("root");
dataSource.setPassword("root");
}
return dataSource.getConnection();
}
}
4.3.log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<Appenders>
<JDBC name="jdbcDatadase_appender" tableName="log_test">
<ConnectionFactory class="com.fracong.test.log4j.Log4j2ConnectionFactory"
method="getConnection" />
<Column name="user_id" pattern="%X{userId}" isUnicode="false"/>
<Column name="log_type" pattern="%logger" isUnicode="false"/>
<Column name="log_date" isEventTimestamp="true" />
<Column name="log_level" pattern="%level" isUnicode="false"/>
<Column name="exception" pattern="%message %ex{full}" isUnicode="false"/>
</JDBC>
</Appenders>
<Loggers>
<logger name="log_type_one" level="DEBUG">
<appender-ref ref="jdbcDatadase_appender"/>
</logger>
<logger name="log_type_two" level="DEBUG">
<appender-ref ref="jdbcDatadase_appender"/>
</logger>
</Loggers>
</configuration>
说明:
1.Appenders下的JDBC是log4j2自带的JDBCAppender.
2.JDBC的tableName是指在连接的数据库下的数据库表.
3.ConnectionFactory是连接数据库的连接,其中class是调用的类,method为调用的方法.
4.Column为数据库的字段,name的值和数据库一一对应,pattern为从logEvent中获取的数据的格式,如果值为string类型,那么使用 isUnicode="false"进行转换.
5.%logger获取的是LogFactory.getLog("log_type_one");
中的log_type_one;%level为获取的日志等级如ERROR/DEBUG等;%message为获取的是logger.error("test");
中的test;%ex{full}获取的logger.error("test",new Exception("Error one"));
中的new Exception(“Error one”)打印出来的日志异常;
4.4.java程序使用
//log_type_one的值和xml定义的保持一致,才能被日志绑定
private static Log logger = LogFactory.getLog("log_type_one");
@POST
@Path("/newTest")
public String newTest(@QueryParam("userId") String userId) throws IOException {
if(StringUtils.isNotBlank(plant_code)) MDC.put("userId", userId);
logger.error("test");
return "ok";
}
说明:如果想将java数据中的param传递到log4j2.xml中,可以使用MDC.put(key, value),其中value不能为空,而log4j2.xml使用**%X{key}**进行接收数据。