之前写过通过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:换行