前段时间一直用jWebSocket开发长连接的web服务,发现log4j记录log极为好用,今天准备仔细研究一下。
1. Log4j2的导入
首先到http://logging.apache.org/log4j/2.x/download.html 上下载最新的log4j2的jar包,然后再eclipse中加入log4j-api-2.0-beta2.jar和log4j-core-2.0-beta2.jar,需要注意的是不要将所有jar都导入工程造成不必要的混乱。
2. 测试用例
log4j 2.0的使用非常简单,只要用LogManager的getLogger函数获取一个logger,就可以使用logger记录日志,代码如下:
运行程序,输出结果为:
注意到,输出的log都是在ERROR level上的,log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为: OFF、FATAL、ERROR、WARN、INFO、DEBUG、 TRACE、 ALL。如果将log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来。例如,如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出,而INFO、DEBUG、TRACE、 ALL级别的log则会被忽略。从我们实验的结果可以看出,log4j默认的优先级为ERROR或者WARN(实际上是ERROR)。
3. 配置文件
log4j是apache的一个开源项目,在写这篇博客的时候已经发布了2.0的beta版本,首先需要注意的是,log4j 2.0与以往的1.x有一个明显的不同,其配置文件只能采用.xml, .json或者 .jsn。在默认情况下,系统选择configuration文件的优先级如下:(classpath为scr文件夹)
- classpath下名为 log4j-test.json 或者log4j-test.jsn文件
- classpath下名为 log4j2-test.xml
- classpath下名为 log4j.json 或者log4j.jsn文件
- classpath下名为 log4j2.xml
此时,HelloLog4j则会在error级别上输出log,而其他类则会在trace级别上输出log。需要注意的是 additivity选项,如果设置为true(默认值)则HelloLog4j的log会被打印两次,第二次打印是由于HelloLog4j同时也满足root里面定义的trace
4. 其他特征
<?xml version="1.0" encoding="UTF-8"?>
<!--主要配置为error与debug日志分别打印文件,errorLog按照分钟存档,debugLog按照日志文件大小存档,最多保存10个-->
<!-- status="debug" Eclipse的console看到Log4j2启动和加载配置文件时的打印信息 -->
<!-- monitorInterval 表示每30秒配置文件会动态加载一次。在程序运行过程中,如果修改配置文件,程序会随之改变 -->
<Configuration status="debug" strict="true" name="heiferConf"
motorInterval="30">
<properties>
<property name="LOG_HOME">/data/www/logs/heifer</property>
<property name="LOGGING_LEVEL">INFO</property>
<property name="DEFAULT-APPENDER">default</property>
<property name="CONTROLLER_FILE_NAME">controller</property>
<property name="SERVICE_FILE_NAME">service</property>
<property name="DAO_FILE_NAME">dao</property>
<property name="REDIS_FILE_NAME">redis</property>
<property name="QUARTZ_JOB">quartz_job</property>
<property name="QUARTZ_TRIGGER">quartz_trigger</property>
<property name="REFUND_FILE_NAME">refund</property>
<property name="ERROR_FILE_NAME">error</property>
<property name="DEBUG_FILE_NAME">debug</property>
<property name="INFO_FILE_NAME">info</property>
<property name="PERFORMANCE_FILE_NAME">performance</property>
<property name="DATA_CHANGE_NAME">dataChangeLog</property>
<property name="DUBBO">dubbo</property>
</properties>
<!-- 输出器 -->
<Appenders>
<!-- log4j2支持的输出源有很多,有控制台Console、文件File、RollingRandomAccessFile、MongoDB、Flume 等 -->
<!-- 级别顺序 trace<debug<info<warn<error<fatal -->
<!-- 日志级别指定文件名 和文件的位置 -->
<!-- 自动追加日志信息到文件中,直至文件达到预定的大小,然后自动重新生成另外一个文件来记录之后的日志。 -->
<!-- %X{requestId} -->
<!-- %d 显示日志记录时间 -->
<!-- %-5p 优先级 不足5位 空格补充 -->
<!-- %t 输出产生该日志事件的线程名 -->
<!-- %C{2} 打印出类的全名 org.apache.xyz.SomeClass xyz.SomeClass -->
<!-- %F 显示调用logger的源文件名 %F MyClass.java -->
<!-- %L 显示调用logger的代码行 -->
<!-- %m 显示输出消息 -->
<!-- %n 当前平台下的换行符 -->
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L)(%X{requestId} ) - %m%n" />
</Console>
<!-- RollingRandomAccessFile 默认日志文件写入策略为异步刷盘,引出一个缓冲区(buffer)的概念,RollingRandomAccessFile
会将日志信息先写入到缓冲区,然后缓冲区满后刷到磁盘,并清空缓冲区,默认缓冲区的大小在8-256kb,具体大小可以自己设置 -->
<RollingRandomAccessFile name="default"
fileName="${LOG_HOME}/${DEFAULT-APPENDER}/${DEFAULT-APPENDER}.log"
filePattern="${LOG_HOME}/${DEFAULT-APPENDER}/${DEFAULT-APPENDER}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<ThresholdFilter level="ERROR" onMatch="ACCEPT"
onMismatch="DENY" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="debugFile"
fileName="${LOG_HOME}/${DEBUG_FILE_NAME}/${DEBUG_FILE_NAME}.log"
filePattern="${LOG_HOME}/${DEBUG_FILE_NAME}/${DEBUG_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="errorFile"
fileName="${LOG_HOME}/${ERROR_FILE_NAME}/${ERROR_FILE_NAME}.log"
filePattern="${LOG_HOME}/${ERROR_FILE_NAME}/${ERROR_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<!-- 过滤器ThresholdFilter的level设置成error这样的较高级别,匹配就接受 否则拒绝-->
<ThresholdFilter level="ERROR" onMatch="ACCEPT"
onMismatch="DENY" />
<SizeBasedTriggeringPolicy size="500 MB" /><!-- 限制文件大小 -->
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="infoFile"
fileName="${LOG_HOME}/${INFO_FILE_NAME}/${INFO_FILE_NAME}.log"
filePattern="${LOG_HOME}/${INFO_FILE_NAME}/${INFO_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<!-- 性能日志 -->
<RollingRandomAccessFile name="performanceFile"
fileName="${LOG_HOME}/${PERFORMANCE_FILE_NAME}/${PERFORMANCE_FILE_NAME}.log"
filePattern="${LOG_HOME}/${PERFORMANCE_FILE_NAME}/${PERFORMANCE_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<!-- 业务层 -->
<RollingRandomAccessFile name="controllerFile"
fileName="${LOG_HOME}/${CONTROLLER_FILE_NAME}/${CONTROLLER_FILE_NAME}.log"
filePattern="${LOG_HOME}/${CONTROLLER_FILE_NAME}/${CONTROLLER_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="serviceFile"
fileName="${LOG_HOME}/${SERVICE_FILE_NAME}/${SERVICE_FILE_NAME}.log"
filePattern="${LOG_HOME}/${SERVICE_FILE_NAME}/${SERVICE_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="daoFile"
fileName="${LOG_HOME}/${DAO_FILE_NAME}/${DAO_FILE_NAME}.log"
filePattern="${LOG_HOME}/${DAO_FILE_NAME}/${DAO_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="redisFile"
fileName="${LOG_HOME}/${REDIS_FILE_NAME}/${REDIS_FILE_NAME}.log"
filePattern="${LOG_HOME}/${REDIS_FILE_NAME}/${REDIS_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="refundFile"
fileName="${LOG_HOME}/${REFUND_FILE_NAME}/${REFUND_FILE_NAME}.log"
filePattern="${LOG_HOME}/${REFUND_FILE_NAME}/${REFUND_FILE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="dataChangeLogFile"
fileName="${LOG_HOME}/${DATA_CHANGE_NAME}/${DATA_CHANGE_NAME}.log"
filePattern="${LOG_HOME}/${DATA_CHANGE_NAME}/${DATA_CHANGE_NAME}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<!-- quratz_job执行日志 -->
<RollingRandomAccessFile name="quartz_job"
fileName="${LOG_HOME}/${QUARTZ_JOB}/${QUARTZ_JOB}.log" filePattern="${LOG_HOME}/${QUARTZ_JOB}/${QUARTZ_JOB}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<!-- quratz_trigger执行日志 -->
<RollingRandomAccessFile name="quartz_trigger"
fileName="${LOG_HOME}/${QUARTZ_TRIGGER}/${QUARTZ_TRIGGER}.log"
filePattern="${LOG_HOME}/${QUARTZ_TRIGGER}/${QUARTZ_TRIGGER}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
<!-- dubbo 请求参数,返回 结果 日志-->
<RollingRandomAccessFile name="dubbo"
fileName="${LOG_HOME}/${DUBBO}/${DUBBO}.log"
filePattern="${LOG_HOME}/${DUBBO}/${DUBBO}%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) (requestId: %X{requestId}) - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Logger name="default-logger" additivity="false" level="${LOGGING_LEVEL}">
<AppenderReF ref="default" />
<AppenderReF ref="errorFile"></AppenderReF>
</Logger>
<!-- 全部日志 若使用了additivity="false"表示不继承父logger的配置-->
<logger name="com.anzhi.heifer" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="default" />
<AppenderReF ref="errorFile" />
</logger>
<!-- 使用异步Logger方式输出日志 -->
<!-- 要加上位置信息比如哪个类,第几行 includeLocation="true -->
<AsyncLogger name="controller-logger" level="${LOGGING_LEVEL}"
includeLocation="true" >
<!-- 在那些文件追加日志 如果没有此项 那么不会追加日志 以下的AppenderReF都会打印
如果java方法里的等级高于${LOGGING_LEVEL} 那么不会打印日志-->
<AppenderReF ref="errorFile" />
<AppenderRef ref="controllerFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<AsyncLogger name="service-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="serviceFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<AsyncLogger name="dao-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="daoFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<AsyncLogger name="redis-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="redisFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<AsyncLogger name="data-change-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="dataChangeLogFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<AsyncLogger name="perf-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="performanceFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<!-- quartz -->
<AsyncLogger name="quartz-job-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="quartz_job" />
<AppenderReF ref="default" />
</AsyncLogger>
<AsyncLogger name="quartz-trigger-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="quartz_trigger" />
<AppenderReF ref="default" />
</AsyncLogger>
<AsyncLogger name="refund-logger" level="${LOGGING_LEVEL}" includeLocation="true">
<AppenderReF ref="errorFile" />
<AppenderRef ref="refundFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<!-- 错误日志 -->
<AsyncLogger name="error-logger" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderReF ref="default" />
</AsyncLogger>
<!-- DAO日志 -->
<Logger name="druid.sql" additivity="false" level="${LOGGING_LEVEL}">
<AppenderReF ref="daoFile"></AppenderReF>
<AppenderReF ref="errorFile"></AppenderReF>
<AppenderRef ref="STDOUT" />
<AppenderReF ref="default" />
</Logger>
<Logger name="java.sql" additivity="false" level="${LOGGING_LEVEL}">
<AppenderReF ref="daoFile"></AppenderReF>
<AppenderReF ref="errorFile"></AppenderReF>
<AppenderRef ref="STDOUT" />
<AppenderReF ref="default" />
</Logger>
<Logger name="com.mybatis" additivity="false" level="${LOGGING_LEVEL}">
<AppenderReF ref="daoFile"></AppenderReF>
<AppenderReF ref="errorFile"></AppenderReF>
<AppenderRef ref="STDOUT" />
<AppenderReF ref="default" />
</Logger>
<logger name="jdbc.sqltiming" additivity="false" level="${LOGGING_LEVEL}">
<AppenderReF ref="daoFile"></AppenderReF>
<AppenderReF ref="errorFile"></AppenderReF>
<AppenderRef ref="STDOUT" />
<AppenderReF ref="default" />
</logger>
<logger name="com.mysql" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="daoFile"></AppenderReF>
<AppenderRef ref="STDOUT" />
<AppenderReF ref="default" />
</logger>
<!--dao\ service\ controller\redis -->
<logger name="com.anzhi.heifer.dao" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderRef ref="daoFile" />
<AppenderRef ref="STDOUT" />
<AppenderReF ref="default" />
</logger>
<logger name="com.anzhi.heifer.service" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="serviceFile" />
<AppenderReF ref="default" />
</logger>
<logger name="com.heifer.controller" level="${LOGGING_LEVEL}" additivity="false">
<AppenderRef ref="controllerFile" />
<AppenderReF ref="errorFile" />
<AppenderReF ref="default" />
</logger>
<logger name="com.heifer.redis" level="${LOGGING_LEVEL}" additivity="false">
<AppenderRef ref="redisFile" />
<AppenderReF ref="errorFile" />
<AppenderReF ref="default" />
</logger>
<!-- dubbo 日志输出到info中 -->
<logger name="com.alibaba.dubbo" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<!-- 各个provider调用 -->
<logger name="com.anzhi.app" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<logger name="com.anzhi.pay" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<logger name="com.anzhi.marketing" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<logger name="com.anzhi.msg" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<logger name="com.anzhi.tms" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<logger name="com.anzhi.user" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<!-- 框架日志 -->
<logger name="com.anzhi.platform.framework" additivity="false"
level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderReF ref="infoFile" />
<AppenderReF ref="default" />
</logger>
<!-- 第三方 -->
<logger name="org.springframework" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="default" />
</logger>
<logger name="org.apache.http" level="${LOGGING_LEVEL}" additivity="false">
<AppenderReF ref="errorFile" />
<AppenderReF ref="default" />
<AppenderReF ref="default" />
</logger>
<!-- 需要注意的是 additivity选项,如果设置为true(默认值)则dubbo的log会被打印两次,第二次打印是由于dubbo同时也满足root里面定义的info -->
<AsyncLogger name="dubbo" level="${LOGGING_LEVEL}">
<AppenderReF ref="errorFile" />
<AppenderRef ref="dubbo" />
<AppenderReF ref="default" />
</AsyncLogger>
<Root level="${LOGGING_LEVEL}">
<AppenderRef ref="errorFile" />
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
</Configuration>
<context-param>
<param-name>isLog4jAutoInitializationDisabled</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>classpath:log4j2.xml</param-value>
</context-param>
1,ConsoleAppender
输出结果到System.out或是System.err。
2,FileAppender
输出结果到指定文件,同时可以指定输出数据的格式。append=“false”指定不追加到文件末尾
3,RollingFileAppender
自动追加日志信息到文件中,直至文件达到预定的大小,然后自动重新生成另外一个文件来记录之后的日志。
过滤标签
1,ThresholdFilter
用来过滤指定优先级的事件。
2,TimeFilter
设置start和end,来指定接收日志信息的时间区间。