之前写过通过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部分
三、新建数据库连接类,使用的是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。
格式举例:
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:换行