之前写过通过logback.xml配置将日志写入数据库的文章,本章中公司项目中使用的则是log4j2.xml,本来以为很容易实现,结果费了一下午时间才搞定,记录一下。
一、在本地新建库auge_log,在该库中新建表error_log
DROP TABLE IF EXISTS `error_log`;
CREATE TABLE `error_log` (
`log_id` int(20) NOT NULL AUTO_INCREMENT,
`class` varchar(200) DEFAULT NULL,
`method` varchar(100) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`log_level` varchar(50) DEFAULT NULL,
`msg` varchar(4000) DEFAULT NULL,
PRIMARY KEY (`log_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
二、在log4j2.xml配置写入数据库的信息:JDBC部分
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
</Console>
<!-- 按天第天备份一个日志 -->
<RollingFile name="saveFile" fileName="${sys:catalina.home}/logs/beanspark/auge.log"
filePattern="${sys:catalina.home}/logs/beanspark/auge_%d{yyyy-MM-dd}_%i.log">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
<Policies>
<!-- 每天更新一次 每个文件最大活跃大小 512MB 最多备份20个文件 -->
<TimeBasedTriggeringPolicy modulate="true" interval="1" />
<SizeBasedTriggeringPolicy size="512MB" />
</Policies>
<!-- 最多备份20个 -->
<DefaultRolloverStrategy max="20" />
</RollingFile>
<!-- 按天第天备份一个日志 -->
<RollingFile name="XdyjError" fileName="${sys:catalina.home}/logs/beanspark/auge_error.log"
filePattern="${sys:catalina.home}/logs/beanspark/auge_error_%d{yyyy-MM-dd}_%i.log">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
<Policies>
<!-- 每天更新一次 每个文件最大活跃大小 128MB 最多备份20个文件 -->
<TimeBasedTriggeringPolicy modulate="true" interval="1" />
<SizeBasedTriggeringPolicy size="128MB" />
</Policies>
<!-- 最多备份20个 -->
<DefaultRolloverStrategy max="20" />
</RollingFile>
<!--写入数据库配置,在Logger中设置日志级别为error-->
<JDBC name="databaseAppender" tableName="error_log">
<ConnectionFactory class="com.xiaodou.park.util.ConnectionFactory" method="getDataSourceConnection" />
<Column name="class" pattern="%c" />
<Column name="method" pattern="%M" />
<Column name="log_level" pattern="%p" />
<Column name="msg" pattern="%msg"/>
<Column name="create_time" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}"/>
</JDBC>
</Appenders>
<Loggers>
<logger name="com.xiaodou.park" level="trace" additivity="false">
<appender-ref ref="XdyjError" level="error"/>
<appender-ref ref="saveFile" level="info"/>
<appender-ref ref="Console" level="debug"/>
<appender-ref ref="databaseAppender" level="error"/>
</logger>
<Root level="error">
<!-- Only events at DIAG level or more specific are sent to the console. -->
<appenderRef ref="XdyjError"/>
<appenderRef ref="saveFile"/>
<appender-ref ref="Console"/>
</Root>
</Loggers>
</Configuration>
三、新建数据库连接类,使用的是druid数据源(主要是项目中与数据库的连接使用的就是druid数据源)
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import com.google.common.collect.MapConstraints;
import org.apache.log4j.jdbc.JDBCAppender;
import org.apache.log4j.spi.ErrorCode;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class ConnectionFactory extends JDBCAppender {
/* Druid数据源 */
private DruidDataSource dataSource;
private static ConnectionFactory connectionFactory;
public ConnectionFactory() {
super();
}
@Override
protected void closeConnection(Connection con) {
try {
/* 如果数据库连接对象不为空和没有被关闭的话,关闭数据库连接 */
if (con != null && !con.isClosed())
con.close();
} catch (SQLException e) {
errorHandler.error("Error closing MyJDBCAppender.closeConnection() 's connection", e, ErrorCode.GENERIC_FAILURE);
}
}
@Override
protected Connection getConnection() throws SQLException {
String className = "com.mysql.jdbc.Driver";
String connUrl = "jdbc:mysql://localhost:3306/auge_log?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true";
String uname = "root";
String psw = "123456";
Properties result = new Properties();
result.put("driverClassName", className);
result.put("url", connUrl);
result.put("username", uname);
result.put("password", psw);
/* 其他配置 自然你也可以自己写property 然后获取set */
result.put("maxActive", "30");
result.put("minIdle", "3");
try {
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(result);
} catch (Exception e) {
/* Druid数据库源对象产生失败后,取消初始化 */
try {
uninitialize();
} catch (Exception e2) {
}
}
return dataSource.getConnection();
}
/* 取消初始化 */
public void uninitialize() {
try {
if (dataSource != null)
dataSource.close();
} catch (Exception e) {
} finally {
super.close();
}
}
public static Connection getDataSourceConnection() throws SQLException {
if (connectionFactory==null){
connectionFactory = new ConnectionFactory();
}
return connectionFactory.getConnection();
}
}
四、在需要记录的方法里添加日志记录。
代码部分省略,若录入数据库为error级别,则只有logger.error的日志会被存储到数据库。
@Controller
@RequestMapping("/admin")
public class AdminController {
private static Logger logger = LogManager.getLogger(AdminController.class);
@RequestMapping(value = "/login", method = {RequestMethod.POST}, produces = MEDIATYPE_CHARSET_JSON_UTF8)
@ResponseBody
public String login(HttpServletRequest request,String name,String password) {
logger.info("管理员尝试登陆: name = " + name);
try {
if (Strings.isNullOrEmpty(name) || Strings.isNullOrEmpty(password)) {
logger.info("用户名或密码不能为空!");
}
AugeAdmin admin = adminService.loginByUesrnameAndPassword(name, md5Pwd);
if (admin == null) {
logger.error("用户名或密码错误");
}
} catch (Exception e) {
logger.error(e.getMessage());
e.printStackTrace();
}
return "";
}
}
五、运行代码,密码故意输错,查看数据库。
附:log4j2.xml配置解析
配置文件节点解析:
1.根节点Configuration有两个属性:status和monitorinterval,有两个子节点:Appenders和Loggers(表明可以定义多个Appender和Logger).
status用来指定log4j本身的打印日志的级别。monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s。
2.Appenders节点,常见的有三种子节点:Console、RollingFile、File,本章中还使用了JDBC节点。
Console节点用来定义输出到控制台的Appender。
属性
name:指定Appender的名字。
target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT。
子节点
PatternLayout:输出格式,不设置默认为:%m%n。
格式举例:<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
File节点用来定义输出到指定位置的文件的Appender。
属性
name:指定Appender的名字。
fileName:指定输出日志的目的文件带全路径的文件名。
子节点
PatternLayout:输出格式,不设置默认为:%m%n。
RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender。
属性
name:指定Appender的名字。
fileName:指定输出日志的目的文件带全路径的文件名。
filePattern:指定新建日志文件的名称格式。
子节点
PatternLayout:输出格式,不设置默认为:%m%n。
Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志。基于时间的触发策略。该策略主要是完成周期性的log文件封存工作。
有两个参数:interval,integer型,指定两次封存动作之间的时间间隔。单位:以日志的命名精度来确定单位,比如yyyy-MM-dd-HH 单位为小时,yyyy-MM-dd-HH-mm 单位为分钟
modulate,boolean型,说明是否对封存时间进行调制。若modulate=true,则封存时间将以0点为边界进行偏移计算。
比如,modulate=true,interval=4hours,那么假设上次封存日志的时间为03:00,则下次封存日志的时间为04:00,之后的封存时间依次为08:00,12:00,16:00
Policies的子节点
TimeBasedTriggeringPolicy:基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am.
SizeBasedTriggeringPolicy:基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.
DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。
JDBC节点用来配置日志输出到数据库
属性
name:指定Appender的名字。
tableName:日志存储表的名称。
子节点
ConnectionFactory:两个属性class:连接库的类,method:类中的连接方法。
Column:数据库表字段。 name:字段名称。pattern:写入值。isEventTimestamp:日期格式是否使用时间戳,不和pattern一起出现。
3.Loggers节点,常见的有两种:Root和Logger.
Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.
AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender.
Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。
level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.
name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.
AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。
日志level级别定义:
All:最低等级的,用于打开所有日志记录.
Trace:是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出.
Debug:指出细粒度信息事件对调试应用程序是非常有帮助的.
Info:消息在粗粒度级别上突出强调应用程序的运行过程.
Warn:输出警告及warn以下级别的日志.
Error:输出错误信息日志.
Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志.
OFF:最高等级的,用于关闭所有日志记录.
自定义格式:
%t:线程名称
%p:日志级别
%c:日志消息所在类名
%m:消息内容
%M:输出执行方法
%d:发生时间,%d{yyyy-MM-dd HH:mm:ss.SSS}
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%L:代码中的行数
%n:换行