logback使用与解析
项目中使用了logback,蹭着项目发布后的闲暇时间总结下。
先说使用,了解怎么用后带着疑问再看具体的解析。
maven依赖
项目中使用了maven进行管理,logback的maven依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j_version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback_version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback_version}</version>
</dependency>
jar包间的关系
可以看到引入了slf4j-api、logback-core、logback-classic这三个jar包。
slf4j:Simple Logging Facade for Java,为java提供的日志接口。
logback当前分成三个模块:logback-core,logback-classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个 改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging。logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能。
关系图:
web.xml配置
我们的项目中在web.xml中进行了配置,使得spring与logback结合:
在web.xml中配置一个listener.同时配置一个上下文参数,用于指定logback.xml配置文件的路径。
<!-- 添加日志监听器 -->
<context-param>
<param-name>logbackConfigLocation</param-name>
<param-value>classpath:logback.xml</param-value>
</context-param>
<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
</listener>
logback.xml文件解析
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_PATH" value="/usr/local/tomcat/tomcat_log/manage-server"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<Target>System.out</Target>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] [%logger.%M:%L] |%msg%n</pattern>
<!--<charset>utf-8</charset>-->
</encoder>
<!-- 此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
</appender>
<appender name="platform" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/platform.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个文件,存放到目录下-->
<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/platform.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<!-- 如果当个文件大于500M,则重新生成文件-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} > %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<appender name="platform.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="platform"/>
</appender>
<appender name="access" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个文件,存放到目录下-->
<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/access.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<!-- 如果当个文件大于500M,则重新生成文件-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} > %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<appender name="access.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="access"/>
</appender>
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个文件,存放到目录下-->
<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/debug.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<!-- 如果当个文件大于500M,则重新生成文件-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} > %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<appender name="debug.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="debug"/>
</appender>
<appender name="system" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/system.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个文件,存放到目录下-->
<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/system.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<!-- 如果当个文件大于500M,则重新生成文件-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} > %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<appender name="system.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="system"/>
</appender>
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个文件,存放到目录下-->
<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<!-- 如果当个文件大于500M,则重新生成文件-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} > %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<appender name="error.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="error"/>
</appender>
<logger name="myph.log.access" level="info" additivity="false">
<appender-ref ref="access.async"/>
</logger>
<logger name="myph.log.exception" level="info" additivity="false">
<appender-ref ref="error.async"/>
</logger>
<logger name="myph.log.system" level="info" additivity="false">
<appender-ref ref="system.async"/>
</logger>
<logger name="myph.log.debug" level="info" additivity="false">
<appender-ref ref="debug.async"/>
</logger>
<!--输入spring的error -->
<logger name="com.myph" level="debug"/>
<logger name="com.alibaba.dubbo" level="debug"/>
<logger name="com.ibatis" level="debug"/>
<logger name="org.springframework" level="debug"/>
<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="debug"/>
<logger name="org.apache.commons" level="debug"/>
<logger name="catalia" level="debug"/>
<logger name="org.apache" level="debug"/>
<logger name="org.apache.shiro" level="debug"/>
<logger name="com.ibatis.common.jdbc.SimpleDataSource" level="debug"/>
<logger name="java.sql.ResultSet" level="debug"/>
<logger name="com.ibatis.common.jdbc.ScriptRunner" level="debug"/>
<logger name="java.sql.Connection" level="debug"/>
<logger name="java.sql.PreparedStatement" level="debug"/>
<!--定义root 级别 -->
<root level="info">
<appender-ref ref="platform.async"/>
<!--<!– 本地测试开始–>-->
<appender-ref ref="STDOUT"/>
</root>
</configuration>
logback.xml文件结构
configuration
根节点configuration包含以下参数:
scan:当此属性为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:scan为true时此属性生效。设置检测配置文件是否有修改的时间间隔,默认单位毫秒,默认时间间隔一分钟。
debug:当此属性为true时,将打印出logback内部日志信息,实时查看logback运行状态,默认false。
样例:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- -->
</configuration>
property
property用于定义变量值,通过property定义的值会被插入到logger上下文中,定义变量后,可以使用‘${}’来使用变量。
样例:
<property name="LOG_PATH" value="/usr/local/tomcat/tomcat_log/manage-server"/>
appender
appender是configuration的子节点,是负责写日志的组件。
appender有有两个必要属性name和class。name指定appender名称,class指定appender的全限定名。
配置文件中有这样几种class:ConsoleAppender、RollingFileAppender、AsyncAppender。
ConsoleAppender
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<Target>System.out</Target>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] [%logger.%M:%L] |%msg%n</pattern>
<!--<charset>utf-8</charset>-->
</encoder>
<!-- 此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
</appender>
把日志添加到控制台,有以下子节点:
encoder:对日志进行格式化。
target:字符串 System.out 或者 System.err ,默认 System.out ;
这里还用到了filter标签,filter是过滤器。过滤器被添加到 中,为 添加一个或多个过滤器后,可以用任意条件对日志进行过滤。 有多个过滤器时,按照配置顺序执行。
这里用到了ThresholdFilter,临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。
RollingFileAppender
<appender name="platform" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/platform.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个文件,存放到目录下-->
<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/platform.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<!-- 如果当个文件大于500M,则重新生成文件-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} > %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。有以下子节点:
file:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
append:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
encoder:对记录事件进行格式化。
rollingPolicy:当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名。
triggeringPolicy : 告知 RollingFileAppender 合适激活滚动。
prudent:当为true时,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但是有两个限制,1不支持也不允许文件压缩,2不能设置file属性,必须留空。
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。有以下子节点:
fileNamePattern:
必要节点,包含文件名及“%d”转换符, “%d”可以包含一个 java.text.SimpleDateFormat指定的时间格式,如:%d{yyyy-MM}。如果直接使用 %d,默认格式是 yyyy-MM-dd。 RollingFileAppender 的file字节点可有可无,通过设置file,可以为活动文件和归档文件指定不同位置,当前日志总是记录到file指定的文件(活动文件),活动文件的名字不会改变;如果没设置file,活动文件的名字会根据fileNamePattern 的值,每隔一段时间改变一次。“/”或者“\”会被当做目录分隔符。
maxHistory:
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每个月滚动,且 maxHistory是6,则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。
AsyncAppender
AsyncAppender:异步记录日志。需要注意的是,用了异步后,日志可能会缺失。缺失是由于其工作原理决定的。
<appender name="platform.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="platform"/>
</appender>
工作原理:
当Logging Event进入AsyncAppender后,AsyncAppender会调用appender方法,append方法中在将event填入Buffer(这里选用的数据结构为BlockingQueue)中前,会先判断当前buffer的容量以及丢弃日志特性是否开启,当消费能力不如生产能力时,AsyncAppender会超出Buffer容量的Logging Event的级别,进行丢弃,作为消费速度一旦跟不上生产速度,中转buffer的溢出处理的一种方案。AsyncAppender有个线程类Worker,它是一个简单的线程类,是AsyncAppender的后台线程,所要做的工作是:从buffer中取出event交给对应的appender进行后面的日志推送。
从上面的描述中可以看出,AsyncAppender并不处理日志,只是将日志缓冲到一个BlockingQueue里面去,并在内部创建一个工作线程从队列头部获取日志,之后将获取的日志循环记录到附加的其他appender上去,从而达到不阻塞主线程的效果。因此AsynAppender仅仅充当事件转发器,必须引用另一个appender来做事。
在使用AsyncAppender的时候,有些选项还是要注意的。由于使用了BlockingQueue来缓存日志,因此就会出现队列满的情况。正如上面原理中所说的,在这种情况下,AsyncAppender会做出一些处理:默认情况下,如果队列80%已满,AsyncAppender将丢弃TRACE、DEBUG和INFO级别的event,从这点就可以看出,该策略有一个惊人的对event丢失的代价性能的影响。另外其他的一些选项信息,也会对性能产生影响。
参数:
queueSize:BlockingQueue的最大容量,默认情况下,大小为256。
discardingThreshold:默认情况下,当BlockingQueue还有20%容量,他将丢弃TRACE、DEBUG和INFO级别的event,只保留WARN和ERROR级别的event。为了保持所有的events,设置该值为0。
includeCallerData:提取调用者数据的代价是相当昂贵的。为了提升性能,默认情况下,当event被加入到queue时,event关联的调用者数据不会被提取。默认情况下,只有”cheap”的数据,如线程名。
logger与root
由于root也是loger元素,因此这两个可以一起总结。
< loger>
用来设置某一个包或者具体的某一个类的日志打印级别、以及指定。仅有一个name属性,一个可选的level和一个可选的addtivity属性。
name:
用来指定受此< loger>约束的某一个包或者具体的某一个类。
level:
用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
如果未设置此属性,那么当前loger将会继承上级的级别。
addtivity:
是否向上级loger传递打印信息。默认是true。
loger可以包含零个或多个appender-ref元素,标识这个appender将会添加到这个loger。
< root>
也是< loger>元素,但是它是根loger。只有一个level属性,应为已经被命名为”root”.
level:
用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。
默认是DEBUG。
< root>可以包含零个或多个< appender-ref>元素,标识这个appender将会添加到这个loger。
<logger name="myph.log.access" level="info" additivity="false">
<appender-ref ref="access.async"/>
</logger>
<logger name="myph.log.exception" level="info" additivity="false">
<appender-ref ref="error.async"/>
</logger>
<logger name="myph.log.system" level="info" additivity="false">
<appender-ref ref="system.async"/>
</logger>
<logger name="myph.log.debug" level="info" additivity="false">
<appender-ref ref="debug.async"/>
</logger>
<!--输入spring的error -->
<logger name="com.myph" level="debug"/>
<logger name="com.alibaba.dubbo" level="debug"/>
<logger name="com.ibatis" level="debug"/>
<logger name="org.springframework" level="debug"/>
<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="debug"/>
<logger name="org.apache.commons" level="debug"/>
<logger name="catalia" level="debug"/>
<logger name="org.apache" level="debug"/>
<logger name="org.apache.shiro" level="debug"/>
<logger name="com.ibatis.common.jdbc.SimpleDataSource" level="debug"/>
<logger name="java.sql.ResultSet" level="debug"/>
<logger name="com.ibatis.common.jdbc.ScriptRunner" level="debug"/>
<logger name="java.sql.Connection" level="debug"/>
<logger name="java.sql.PreparedStatement" level="debug"/>
<!--定义root 级别 -->
<root level="info">
<appender-ref ref="platform.async"/>
<!--<!– 本地测试开始–>-->
<appender-ref ref="STDOUT"/>
</root>
如果是代码中手动打的debug,此断配置将生效
<logger name="myph.log.debug" level="info" additivity="false">
<appender-ref ref="debug.async"/>
</logger>
如果是引入的jar包中打的debug,此断配置将生效
<logger name="com.ibatis" level="debug"/>
由于此段没有指定appender-ref,因此将向上级传递到root中。
root中指定了appender-ref,将按照此appender-ref打印日志。
附上项目中打印日志的方法:
调用:
MyphLogger.error(e, "合规异常,入参{}", complianceDto.toString());
MyphLogger工具类:
package com.myph.common.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INFO: info
* User: zhaokai
* Date: 2016/8/19 - 16:30
* Version: 1.0
* History: <p>如果有修改过程,请记录</P>
*/
public class MyphLogger {
private final static String AccessLoggerName = "myph.log.access";
private final static String ExceptionLoggerName = "myph.log.exception";
private final static String SystemLoggerName = "myph.log.system";
private final static String DebugLoggerName = "myph.log.debug";
private final static String OutInterfaceLoggerName = "myph.log.outInterface";
public static final String LOG_SEPARATOR = "|";
/**
* sfl4j的logger
*/
private final static Logger accessLogger = LoggerFactory.getLogger(AccessLoggerName);
private final static Logger exceptionLogger = LoggerFactory.getLogger(ExceptionLoggerName);
private final static Logger systemLogger = LoggerFactory.getLogger(SystemLoggerName);
private final static Logger debugLogger = LoggerFactory.getLogger(DebugLoggerName);
private final static Logger outLogger = LoggerFactory.getLogger(OutInterfaceLoggerName);
//----------------------------------------------------------- Logger method start
/**
* Debug等级日志,小于Info<br>
* 由于动态获取Logger,效率较低,建议在非频繁调用的情况下使用!!
*
* @param message 输出消息
*/
public static void debug(String message) {
// 刷新参数
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isDebugEnabled) {
debugLogger.info(message);
}
}
/**
* Debug等级日志,小于Info<br>
* 由于动态获取Logger,效率较低,建议在非频繁调用的情况下使用!!
*
* @param format 格式文本,{} 代表变量
* @param arguments 变量对应的参数
*/
public static void debug(String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isDebugEnabled) {
debugLogger.info(format, arguments);
}
}
/**
* @param e
* @param message
*/
public static void debug(Throwable e, String message) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isDebugEnabled) {
debugLogger.info(message, e);
}
}
/**
* @param e
* @param format 格式文本,{} 代表变量
* @param arguments
*/
public static void debug(Throwable e, String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isDebugEnabled) {
debugLogger.info(format(format, arguments), e);
}
}
/**
* @param message 输出消息
*/
public static void access(String message) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isAccessEnabled) {
accessLogger.info(message);
}
}
/**
* @param format 格式文本,{} 代表变量
* @param arguments 变量对应的参数
*/
public static void access(String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isAccessEnabled) {
accessLogger.info(format, arguments);
}
}
/**
* @param message 输出消息
*/
public static void outInterface(String message) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isInterfaceEnabled) {
outLogger.info(message);
}
}
/**
* @param format 格式文本,{} 代表变量
* @param arguments 变量对应的参数
*/
public static void outInterface(String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isInterfaceEnabled) {
outLogger.info(format, arguments);
}
}
/**
* @param message 输出消息
*/
public static void info(String message) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isSystemEnabled) {
systemLogger.info(message);
}
}
/**
* @param format 格式文本,{} 代表变量
* @param arguments 变量对应的参数
*/
public static void info(String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isSystemEnabled) {
systemLogger.info(format, arguments);
}
}
/**
* @param e
* @param message
*/
public static void info(Throwable e, String message) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isSystemEnabled) {
systemLogger.info(message, e);
}
}
/**
* @param e
* @param format 格式文本,{} 代表变量
* @param arguments
*/
public static void info(Throwable e, String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isSystemEnabled) {
systemLogger.info(format(format, arguments), e);
}
}
/**
* @param message 输出消息
*/
public static void error(String message) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isExceptionEnabled) {
exceptionLogger.info(message);
}
}
/**
* @param format 格式文本,{} 代表变量
* @param arguments 变量对应的参数
*/
public static void error(String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isExceptionEnabled) {
exceptionLogger.info(format, arguments);
}
}
/**
* @param e
* @param message
*/
public static void error(Throwable e, String message) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isExceptionEnabled) {
exceptionLogger.info(message, e);
}
}
/**
* @param e
* @param format 格式文本,{} 代表变量
* @param arguments
*/
public static void error(Throwable e, String format, Object... arguments) {
LogPropsReload.refreshLogProperties();
if (LogPropsReload.isExceptionEnabled) {
exceptionLogger.info(format(format, arguments), e);
}
}
//----------------------------------------------------------- Private method start
/**
* 格式化文本
*
* @param template 文本模板,被替换的部分用 {} 表示
* @param values 参数值
* @return 格式化后的文本
*/
private static String format(String template, Object... values) {
return String.format(template.replace("{}", "%s"), values);
}
}
LogPropsReload工具类
package com.myph.common.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Properties;
/**
* INFO: info
* User: zhaokai
* Date: 2016/8/23 - 13:51
* Version: 1.0
* History: <p>如果有修改过程,请记录</P>
*/
public class LogPropsReload {
private static final Logger LOG = LoggerFactory.getLogger(LogPropsReload.class);
/*@PostConstruct
private void tt() {
new Thread() {
@Override
public void run() {
while (true) {
try {
refreshLogProperties();
Thread.sleep(5000);
} catch (Exception e) {
// 不允许停止监控
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// just ignore
}
LOG.error("LogThread error:"
+ e.getMessage());
}
}
}
}.start();
}*/
private static final String LOG4J_HIDEPARAMS = "log4j.hideParams";
private static final String LOG4J_RELOAD_INTERVAL = "log4j.reload.interval";
private static final String LOG4J_DEBUGLOGGER_ISOPEN = "log4j.DebugLogger.isopen";
private static final String LOG4J_ACCESSLOGGER_ISOPEN = "log4j.AccessLogger.isopen";
private static final String LOG4J_OUTINTERFACELOGGER_ISOPEN = "log4j.OutInterfaceLogger.isopen";
private static final String LOG4J_EXCEPTIONLOGGER_ISOPEN = "log4j.ExceptionLogger.isopen";
private static final String LOG4J_SYSTEMLOGGER_ISOPEN = "log4j.SystemLogger.isopen";
//定义类变量初始化属性
// 刷新间隔
private static long interval = -1l;
// 出入参隐藏
protected static boolean hideParams = true;
/**
* 上次修改时间
*/
protected static Date lastUpdateDate = new Date();
// 调试日志开关
protected static boolean isDebugEnabled = false;
// 访问日志开关
protected static boolean isAccessEnabled = false;
// 外部访问日志开关
protected static boolean isInterfaceEnabled = false;
// 异常日志开关
protected static boolean isExceptionEnabled = false;
// 系统日志开关
protected static boolean isSystemEnabled = false;
private static long default_interval = 60000L;
/**
* 本方法用于 当刷新log.properties日志获取最新参数出现异常情况下,默认赋予的运行配置参数
*/
private static void initDefault() {
// 刷新间隔
interval = default_interval;
// 出入参隐藏
hideParams = true;
lastUpdateDate = new Date();
// 调试日志开关
isDebugEnabled = false;
// 访问日志开关
isAccessEnabled = false;
// 外部访问日志开关
isInterfaceEnabled = false;
// 异常日志开关
isExceptionEnabled = true;
// 系统日志开关
isSystemEnabled = true;
LOG.debug("LogThread fileid value is interval {},hideParams {},lastUpdateDate {},isDebugEnabled {}," +
"isAccessEnabled {},isInterfaceEnabled {},isExceptionEnabled {},isSystemEnabled:{}", interval, hideParams, lastUpdateDate, isDebugEnabled,
isAccessEnabled, isInterfaceEnabled, isExceptionEnabled, isSystemEnabled);
}
/**
* @author jun.wang
* @Description: 整点刷新
* @date 2014-9-16 上午11:28:25
*/
public static void refreshLogProperties() {
LOG.debug("new Date().getTime():{} - lastUpdateDate.getTime():{} to:{} > interval:{}", new Date().getTime(), lastUpdateDate.getTime(), new Date().getTime() - lastUpdateDate.getTime(), interval);
// 间隔时间
if (new Date().getTime() - lastUpdateDate.getTime() > interval) {
ClassPathResource config = new ClassPathResource("/config/log.properties");
if (!config.exists()) {
LOG.error("日志刷新服务异常,配置文件log.properties不存在,将使用默认配置");
initDefault();
} else {
Properties pros = new Properties();
InputStream in = null;
try {
in = config.getInputStream();
pros.load(in);
} catch (IOException ex) {
LOG.error("日志刷新服务异常,配置文件log.properties不存在");
initDefault();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
LOG.error("日志刷新服务异常,配置文件log.properties不存在");
initDefault();
}
}
}
// 重新加载时间间隔
interval = Long.parseLong(pros.getProperty(LOG4J_RELOAD_INTERVAL));
// 参数
hideParams = Boolean.parseBoolean(pros.getProperty(LOG4J_HIDEPARAMS));
// 调试日志开关
try {
isDebugEnabled = Boolean.parseBoolean(pros.getProperty(LOG4J_DEBUGLOGGER_ISOPEN));
} catch (Exception e) {
LOG.error("日志刷新服务异常,配置文件log.properties格式错误:{}", LOG4J_DEBUGLOGGER_ISOPEN);
}
// 访问日志开关
try {
isAccessEnabled = Boolean.parseBoolean(pros.getProperty(LOG4J_ACCESSLOGGER_ISOPEN));
} catch (Exception e) {
LOG.error("日志刷新服务异常,配置文件log.properties格式错误:{}", LOG4J_ACCESSLOGGER_ISOPEN);
}
// 接口日志
try {
isInterfaceEnabled = Boolean.parseBoolean(pros.getProperty(LOG4J_OUTINTERFACELOGGER_ISOPEN));
} catch (Exception e) {
LOG.error("日志刷新服务异常,配置文件log.properties格式错误:{}", LOG4J_OUTINTERFACELOGGER_ISOPEN);
}
// 异常日志
try {
isExceptionEnabled = Boolean.parseBoolean(pros.getProperty(LOG4J_EXCEPTIONLOGGER_ISOPEN));
} catch (Exception e) {
LOG.error("日志刷新服务异常,配置文件log.properties格式错误:{}", LOG4J_EXCEPTIONLOGGER_ISOPEN);
}
// 系统日志
try {
isSystemEnabled = Boolean.parseBoolean(pros.getProperty(LOG4J_SYSTEMLOGGER_ISOPEN));
} catch (Exception e) {
LOG.error("日志刷新服务异常,配置文件log.properties格式错误:{}", LOG4J_SYSTEMLOGGER_ISOPEN);
}
// 更新时间
lastUpdateDate = new Date();
config = null;
LOG.debug("LogThread fileid value is interval {},hideParams {},lastUpdateDate {},isDebugEnabled {}," +
"isAccessEnabled {},isInterfaceEnabled {},isExceptionEnabled {},isSystemEnabled:{}", interval, hideParams, lastUpdateDate, isDebugEnabled,
isAccessEnabled, isInterfaceEnabled, isExceptionEnabled, isSystemEnabled);
}
}
}
}