Logback 日志持久化
Logback是log4j的增强版,比log4j更具灵活,其提供了将日志输出到数据库的功能,本文将介绍如何将指定的日志输出到mysql中。
一、自定义log标志
由于Logback原生的配置会将所有的日志信息输出到mysql数据表中,故需要自定义标志,继承AbstractMatcherFilter,过滤掉无标志的日志:
1、自定义标志过滤器
- public class LogbackMarkerFilter extends AbstractMatcherFilter<ILoggingEvent> {
- private Marker markerToMatch = null;
- @Override
- public void start() {
- if (null != this.markerToMatch) {
- super.start();
- } else {
- addError(" no MARKER yet !");
- }
- }
- @Override
- public FilterReply decide(ILoggingEvent event) {
- Marker marker = event.getMarker();
- if (!isStarted()) {
- return FilterReply.NEUTRAL;
- }
- if (null == marker) {
- return onMismatch;
- }
- if (markerToMatch.contains(marker)) {
- return onMatch;
- }
- return onMismatch;
- }
- public void setMarker(String markerStr) {
- if (null != markerStr) {
- markerToMatch = MarkerFactory.getMarker(markerStr);
- }
- }
- }
2、logback-spring.xml 相关配置文件
- <!-- 读取配置文件中参数 -->
- <springProperty scope="context" name="driverClass" source="spring.datasource.driver-class-name"/>
- <springProperty scope="context" name="url" source="spring.datasource.url"/>
- <springProperty scope="context" name="username" source="spring.datasource.username"/>
- <springProperty scope="context" name="password" source="spring.datasource.password"/>
- <springProperty scope="context" name="logFilePath" source="logging.path"/>
- <springProperty scope="context" name="maxHistory" source="logging.maxHistory"/>
- <!-- 数据库日志记录 -->
- <appender name="DB_APPENDER" class="ch.qos.logback.classic.db.DBAppender">
- <filter class="com.cenobitor.logging.filter.LogbackMarkerFilter">
- <!-- 自定义标志 -->
- <marker>DB</marker>
- <onMatch>ACCEPT</onMatch>
- <onMismatch>DENY</onMismatch>
- </filter>
- <!-- 配置数据源 springboot默认情况会开启光连接池 -->
- <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
- <driverClass>${driverClass}</driverClass>
- <url>${url}</url>
- <user>${username}</user>
- <password>${password}</password>
- </connectionSource>
- </appender>
- <!-- 异步日志记录 -->
- <appender name="ASYNC_APPENDER" class="ch.qos.logback.classic.AsyncAppender">
- <appender-ref ref="DB_APPENDER" />
- <includeCallerData>true</includeCallerData>
- </appender>
- <!-- 日志输出级别 -->
- <root level="${LOG_LEVEL}">
- <appender-ref ref="ASYNC_APPENDER" />
- </root>
经配置,其需要建三张数据表,分别为日志信息、异常信息、属性信息,其建表语句如下:
- BEGIN;
- DROP TABLE IF EXISTS logging_event_property;
- DROP TABLE IF EXISTS logging_event_exception;
- DROP TABLE IF EXISTS logging_event;
- COMMIT;
- BEGIN;
- CREATE TABLE logging_event
- (
- timestmp BIGINT NOT NULL,
- formatted_message TEXT NOT NULL,
- logger_name VARCHAR(254) NOT NULL,
- level_string VARCHAR(254) NOT NULL,
- thread_name VARCHAR(254),
- reference_flag SMALLINT,
- arg0 VARCHAR(254),
- arg1 VARCHAR(254),
- arg2 VARCHAR(254),
- arg3 VARCHAR(254),
- caller_filename VARCHAR(254) NOT NULL,
- caller_class VARCHAR(254) NOT NULL,
- caller_method VARCHAR(254) NOT NULL,
- caller_line CHAR(4) NOT NULL,
- event_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
- );
- COMMIT;
- BEGIN;
- CREATE TABLE logging_event_property
- (
- event_id BIGINT NOT NULL,
- mapped_key VARCHAR(254) NOT NULL,
- mapped_value TEXT,
- PRIMARY KEY(event_id, mapped_key),
- FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
- );
- COMMIT;
- BEGIN;
- CREATE TABLE logging_event_exception
- (
- event_id BIGINT NOT NULL,
- i SMALLINT NOT NULL,
- trace_line VARCHAR(254) NOT NULL,
- PRIMARY KEY(event_id, i),
- FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
- );
- COMMIT;
二、自定义输出日志
由于Logback原生要求建三张表,如何指定指输出一种信息,及自定义日志内容,而异常、属性信息不输出?
通过查看DBAppender发现,插入数据方法,此处只需重写DBAppender,即继承DBAppenderBase<ILoggingEvent>,删除掉异常、属性信息插入的相关方法即可实现只输出指定日志到指定表,而其它信息将不会输出到数据库中,代码如下:
- public class LogDBAppender extends DBAppenderBase<ILoggingEvent> {
- protected String insertSQL;
- protected static final Method GET_GENERATED_KEYS_METHOD;
- private DBNameResolver dbNameResolver;
- static final int TIMESTMP_INDEX = 1;
- static final int FORMATTED_MESSAGE_INDEX = 2;
- static final int LOGGER_NAME_INDEX = 3;
- static final int LEVEL_STRING_INDEX = 4;
- static final int THREAD_NAME_INDEX = 5;
- static final int REFERENCE_FLAG_INDEX = 6;
- static final int ARG0_INDEX = 7;
- static final int ARG1_INDEX = 8;
- static final int ARG2_INDEX = 9;
- static final int ARG3_INDEX = 10;
- static final int CALLER_FILENAME_INDEX = 11;
- static final int CALLER_CLASS_INDEX = 12;
- static final int CALLER_METHOD_INDEX = 13;
- static final int CALLER_LINE_INDEX = 14;
- static final int EVENT_ID_INDEX = 15;
- static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();
- static {
- // PreparedStatement.getGeneratedKeys() method was added in JDK 1.4
- Method getGeneratedKeysMethod;
- try {
- // the
- getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[]) null);
- } catch (Exception ex) {
- getGeneratedKeysMethod = null;
- }
- GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
- }
- public void setDbNameResolver(DBNameResolver dbNameResolver) {
- this.dbNameResolver = dbNameResolver;
- }
- @Override
- public void start() {
- if (dbNameResolver == null)
- dbNameResolver = new DefaultDBNameResolver();
- insertSQL = buildInsertSQL(dbNameResolver);
- super.start();
- }
- @Override
- protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {
- bindLoggingEventWithInsertStatement(insertStatement, event);
- bindLoggingEventArgumentsWithPreparedStatement(insertStatement, event.getArgumentArray());
- // This is expensive... should we do it every time?
- bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData());
- int updateCount = insertStatement.executeUpdate();
- if (updateCount != 1) {
- addWarn("Failed to insert loggingEvent");
- }
- }
- protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId) throws Throwable {
- Map<String, String> mergedMap = mergePropertyMaps(event);
- //insertProperties(mergedMap, connection, eventId);
- // if (event.getThrowableProxy() != null) {
- // insertThrowable(event.getThrowableProxy(), connection, eventId);
- // }
- }
- void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException {
- stmt.setLong(TIMESTMP_INDEX, event.getTimeStamp());
- stmt.setString(FORMATTED_MESSAGE_INDEX, event.getFormattedMessage());
- stmt.setString(LOGGER_NAME_INDEX, event.getLoggerName());
- stmt.setString(LEVEL_STRING_INDEX, event.getLevel().toString());
- stmt.setString(THREAD_NAME_INDEX, event.getThreadName());
- stmt.setShort(REFERENCE_FLAG_INDEX, DBHelper.computeReferenceMask(event));
- }
- void bindLoggingEventArgumentsWithPreparedStatement(PreparedStatement stmt, Object[] argArray) throws SQLException {
- int arrayLen = argArray != null ? argArray.length : 0;
- for (int i = 0; i < arrayLen && i < 4; i++) {
- stmt.setString(ARG0_INDEX + i, asStringTruncatedTo254(argArray[i]));
- }
- if (arrayLen < 4) {
- for (int i = arrayLen; i < 4; i++) {
- stmt.setString(ARG0_INDEX + i, null);
- }
- }
- }
- String asStringTruncatedTo254(Object o) {
- String s = null;
- if (o != null) {
- s = o.toString();
- }
- if (s == null) {
- return null;
- }
- if (s.length() <= 254) {
- return s;
- } else {
- return s.substring(0, 254);
- }
- }
- void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray) throws SQLException {
- StackTraceElement caller = extractFirstCaller(callerDataArray);
- stmt.setString(CALLER_FILENAME_INDEX, caller.getFileName());
- stmt.setString(CALLER_CLASS_INDEX, caller.getClassName());
- stmt.setString(CALLER_METHOD_INDEX, caller.getMethodName());
- stmt.setString(CALLER_LINE_INDEX, Integer.toString(caller.getLineNumber()));
- }
- private StackTraceElement extractFirstCaller(StackTraceElement[] callerDataArray) {
- StackTraceElement caller = EMPTY_CALLER_DATA;
- if (hasAtLeastOneNonNullElement(callerDataArray))
- caller = callerDataArray[0];
- return caller;
- }
- private boolean hasAtLeastOneNonNullElement(StackTraceElement[] callerDataArray) {
- return callerDataArray != null && callerDataArray.length > 0 && callerDataArray[0] != null;
- }
- Map<String, String> mergePropertyMaps(ILoggingEvent event) {
- Map<String, String> mergedMap = new HashMap<String, String>();
- // we add the context properties first, then the event properties, since
- // we consider that event-specific properties should have priority over
- // context-wide properties.
- Map<String, String> loggerContextMap = event.getLoggerContextVO().getPropertyMap();
- Map<String, String> mdcMap = event.getMDCPropertyMap();
- if (loggerContextMap != null) {
- mergedMap.putAll(loggerContextMap);
- }
- if (mdcMap != null) {
- mergedMap.putAll(mdcMap);
- }
- return mergedMap;
- }
- @Override
- protected Method getGeneratedKeysMethod() {
- return GET_GENERATED_KEYS_METHOD;
- }
- @Override
- protected String getInsertSQL() {
- return insertSQL;
- }
- static String buildInsertSQL(DBNameResolver dbNameResolver) {
- StringBuilder sqlBuilder = new StringBuilder("INSERT INTO ");
- sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT)).append(" (");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.TIMESTMP)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.FORMATTED_MESSAGE)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.LOGGER_NAME)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.LEVEL_STRING)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.THREAD_NAME)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.REFERENCE_FLAG)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG0)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG1)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG2)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG3)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_FILENAME)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_CLASS)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_METHOD)).append(", ");
- sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_LINE)).append(") ");
- sqlBuilder.append("VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
- return sqlBuilder.toString();
- }
- }
现在只需将配置中引用的DBAppender:
- <appender name="DB_APPENDER" class="ch.qos.logback.classic.db.DBAppender">
更改为自己重写的类:
- <appender name="DB_APPENDER" class="com.cenobitor.logging.db.LogDBAppender">
表logging_event_property、表logging_event_exception将可删除,至此基本的配置已完成,可以畅快的使用了。
三、使用
- log.info(MarkerFactory.getMarker("DB"), "logback!");
- 即可异步将该日志输出到数据库中。
Logback 日志持久化的更多相关文章
- Logback日志系统配置攻略
logback是log4j作者推出的新日志系统,原生支持slf4j通用日志api,允许平滑切换日志系统,并且对简化应用部署中日志处理的工作做了有益的封装. 官方地址为:http://logback.q ...
- lombok+slf4j+logback SLF4J和Logback日志框架详解
maven 包依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lomb ...
- mybatis用logback日志不显示sql的解决办法
mybatis用logback日志不显示sql的解决方法 1.mybatis-config.xml的设定 关于logimpl的设定值还不支持logback,如果用SLF4J是不好用的. 这是官方文档的 ...
- Logback日志配置的简单使用
Logback介绍 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logback-core,logback- classic和logback-access ...
- 在SpringBoot中添加Logback日志处理
前言 SpringBoot项目中在官方文档中说明,默认已经依赖了一些日志框架.而其中推荐使用的就是Logback,所以这一次我将在我的模版中加入Logback日志的配置,说明一下,SpringBoot ...
- 剑指架构师系列-spring boot的logback日志记录
Spring Boot集成了Logback日志系统. Logback的核心对象主要有3个:Logger.Appender.Layout 1.Logback Logger:日志的记录器 主要用于存放日志 ...
- Logback日志基础配置以及自定义配置
Logback日志基础配置 logback日志配置有很多介绍,但是有几个非常基础的,容易忽略的.下面是最简单的一个配置,注意加粗的描述 <?xml version="1.0" ...
- redis快照持久化和aof日志持久化
持久化就是即使断电/重启需要存储的数据不会丢失,即将数据存储在设备中,一般存在硬盘内 redis的持久化有2种方式 :1-rdb快照 2-aof日志,可以通过配置redis.conf文件进行配置 r ...
- springBoot(10)---logback日志
logback日志 一.概述 和log4j优点: 实际上,这两个日志框架都出自同一个开发者之手,Logback 相对于 Log4J 有更多的优点 (1)logback不仅性能提升了,初始化内存加载也 ...
随机推荐
- IPv6 Can't assign requested address
今天试了下 bind IPv6 的地址,报错 Can't assign requested address http://stackoverflow.com/questions/24780404/p ...
- 简单记录常用git 命令
声明:仅作笔记用 拉取远程代码 1.git pull 2.如果需要,输入账户名密码 将本地代码推送到远程 1.git push 2.如果需要,输入账户名密码 同步远程分支 1.git fetch 2. ...
- Redis---基础数据结构
1.String(字符串) 1.1 概述 字符串 string 是 Redis 最简单的数据结构.Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应 ...
- D15——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D15 20180926内容纲要: 1.CSS介绍 2.CSS的四种引入方式 3.CSS选择器 4.CSS常用属性 5.小结 6.练习 1 CSS介绍 层叠样式表 ...
- Python(27)--文件相关处理的应用(增、删、改、查)
文件名为message,文件内容如下: global log 127.0.0.1 local2 daemon maxconn 256 log 127.0.0.1 local2 info default ...
- 【6】JMicro微服务-服务日志监控
如非授权,禁止用于商业用途,转载请注明出处作者:mynewworldyyl 1. 微服务相关 在前面的1到5节中,总共涉及服务提供者,服务消费者,服务监听服务,发布订阅服务,熔断器服务5种类型的猪 ...
- SQLServer——SQLServer链接外部数据源
学习链接:https://www.cnblogs.com/licin/p/6244169.html 一.新建ODBC数据源 1.打开控制面板→管理工具→ODBC数据源→系统DSN 2.添加新系统数据源 ...
- Django模版语言自定义标签-实现前端 关联组合过滤查询
前端关联 组合过滤查询 实现效果如图: models.py 创建表代码 from django.db import models # Create your models here. class Le ...
- 剑指offer七之斐波那契数列
一.题目 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项.n<=39. 二.思路 序号: 0 1 2 3 4 5 ...
- Java动态代理总结
在之前的代码调用阶段,我们用action调用service的方法实现业务即可. 由于之前在service中实现的业务可能不能够满足当先客户的要求,需要我们重新修改service中的方法,但是servi ...