log4j
Posted 天空奇点
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j相关的知识,希望对你有一定的参考价值。
01 日志概览
日志系统一共分为桥接模式、适配模式,非适配模式三种模式
1.1 桥接模式
1.2 适配模式
1.3 非适配模式
02 log4j
日志整个流程是日志记录者(loggers)将message通过某种布局(layouts)输出到不同的终端(appenders),如文件、标准输出流中。可以看出日志中三大核心要件是loggers、layouts、appenders。
2.1 三者之间关系图
2.2 Logger组件
2.2.1 Logger日志的继承关系
loggers是树形的继承关系,它的树形关系是通过命名系统来表达的。根节点是通过Logger.getRootLogger()静态方法进行检索的,其它节点是通过Logger.getLogger()方法来检索的
1 祖先关系
如com是com.example.demo祖先
2 父子关系
如com.example是com.example.demo父亲
3 根节点
根节点总是存在的
根节点不能通过名称来检索
2.2.2 Logger日志记录等级及继承关系
1 记录等级
logger日志有六个记录等级分别如下:TRACE、DEBUG、INFO、WRAN、ERROR、FATAL
package org.apache.log4j;
public class Logger {
// Creation & retrieval methods:
public static Logger getRootLogger();
public static Logger getLogger(String name);
// printing methods:
public void trace(Object message);
public void debug(Object message);
public void info(Object message);
public void warn(Object message);
public void error(Object message);
public void fatal(Object message);
// generic printing method:
public void log(Level l, Object message);
}
2 继承关系
logger之间的等级也存在继承关系,但这里的继承关系是重载关系,如果子Logger定义自己的Level,那么它就不需要继承父类的level
为了保证所有的日志最终都继承一个日志水平,根节点的总是会分配一个日志水平的
基本规则:如果一个日志请求level是p,它的Logger日志水平是q,如p>=q, 那么这个请求是可用
等级水平:DEBUG < INFO < WRAN < ERROR < FATAL
注意:通过相同名称得到的实例总是同一个实例,使用单例模式
// 通过名称 "com.foo"得到一个日志实例
Logger logger = Logger.getLogger("com.foo");
// 现在设置它的等级,正常情况下,不是通过代码进行进行设置的,它通常是通过配置文件进行设置的。
logger.setLevel(Level.INFO);
Logger barlogger = Logger.getLogger("com.foo.Bar");
// 这个请求方法是可用的,因为 WARN >= INFO.
logger.warn("Low fuel level.");
// 这个请求方法是不可用的, 因为 DEBUG < INFO.
logger.debug("Starting search for nearest gas station.");
// 通过名称"com.foo.Bar"获取的日志实例,它的日志等级将继承“com.far”名称的实例等级,因此下面的请求是可用的,因为INFO >= INFO
barlogger.info("Located nearest gas station.");
// 请求是不可用的, 因为 DEBUG < INFO.
barlogger.debug("Exiting gas station search");
2.3 Appender组件
日志系统可以把日志同时输出到多个不同的终端(Appender),终端可以是控制面板、文件、图形界面、远程socket服务,JMS等。
定义:appender就是输出的终端,可以输出到不同的终端,如上面描述的,同时还提供日志的异步写入。
一个Logger可以绑定多个终端,即一条日志记录可以同时输出到不同的终端。
每个可用的日志请求都将转发到高于Appender等级的所有的Appender中
appender Additivtiy规则:每个可用的日志请求都会发送到与该logger关联的apender和该logger的每一个父logger的Appender。
2.4 Layout组件
使用appender可用输出到不同的终端,layout可以对输出的日志以什么格式显示进行控制,一个appender只能对应一个layout。
一般推荐使用PatternLayout,它类似C语言中printf模式。
1 通用布局格式
格式一
%r [%t] %-5p %c %x - %m%n
格式二
%-6r [%15.15t] %-5p %30.30c %x - %m%n
2 常用的转换说明符
转换说明符 |
含义 |
示例 |
%c |
logger名字空间的全名称,如果加上{<层数>}表示列出最内层到指定层数的名字空间 |
假设当前logger名称空间为com.foo.bar %c 终端打印出来的是com.foo.bar %c{2} 终端打印出来的是com.foo %20c 如果名字空间长度小于20,左边通过补充空格进行填充 %-20c 如果名字空间长度小于20,右边通过补充空格进行填充 %.30c 如果名字空间长度大于30,截去多余的空间 |
%C |
列出调用logger请求的全限定类名 |
假设调用当前的全限定类名为com.foo.bar.Demo %C 终端输出的是com.foo.bar.Demo |
%d |
显示日志记录的格式,使用{<日期格式>},如果没有指定日期格式,默认是用ISO8601格式 |
%d{yyyy-MM-dd HH:mm:ss, SSS} 终端显示的日期格式为2020-01-11 21:10:10 117 |
%F |
显示调用日志请求的源文件名称 |
%F 终端输出的是Demo.class |
%l |
输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 |
%l 终端输出的是Demo.main(Demo.java:110) |
%L |
输出日志请求的在源文件的代码行数 |
%L 终端输出的是 110 |
%m |
显示应用程序的输出消息 |
%m 终端输出 parse json exception |
%M |
调用日志请求的方法名 |
%M 终端输出的是main |
%n |
当前平台的换行符 |
|
%p |
显示当前日志的优先级 |
%p 终端输出的是error |
%r |
从程序启动时到调用日志请求的毫秒数 |
%r 终端输出的 1110 |
%t |
日志请求的线程名称 |
%t Demo |
%x |
按线程NDC(线程堆栈)顺序输出日志 |
|
%X |
按MDC(Mapped Diagnostic Context,线程映射表)输出日志。通常用于多个客户端连接同一台服务器,方便服务器区分是那个客户端访问留下来的日志。 |
|
%% |
显示一个百分号 |
%% 终端输出的是% |
2.5 配置
Log4j支持两种配置文件方式,分别是xml和properties(key=value)格式。
实际上,log4j配置文件就是配置上面三个组件
2.5.1 配置logger
logger配置root logger和其它应用logger的方式
1 配置root logger
格式
log4j.rootLogger=[${logLevel}],[${appenderName1}],[${appenderName2}],....
示例
log4j.rootLogger=info,console,file
2 配置其他logger(推荐使用类名的全路径)
格式
log4j.logger.${loggerName}=[${logLevel}],[${appenderName1}],[${appenderName2}],....
示例
log4j.logger.org.springframework=error
2.5.2 配置日志输出终端
语法格式
log4j.logger.${appenderName}= 全限定类名(可以是内置Appender,也可以是自定义的Appender)
2.5.3 配置日志输出格式
log4j.logger.${appenderName}.layout=全限定类名(一般使用内置Layout类)
2.6 log4j最佳实践
2.6.1 引入jar
<!--slf4j api接口-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--log4j 实现-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--用于log4j适配slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
2.6.2 配置文件
# Log4J Settings for log4j 1.2.x (via jakarta-commons-logging)
# The five logging levels used by Log are (in order):
# # 1. DEBUG (the least serious)
# 2. INFO
# 3. WARN
# 4. ERROR
# 5. FATAL (the most serious)
# Set root logger level to INFO and append to stdout
log4j.rootLogger=DEBUG,stdout,file
# # stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=[LOG-DMEO]%-d{HH:mm:ss SS} %5p (%c:%L) - %m%n
# # file
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=/logs/gift/log-demo.log
log4j.appender.file.DatePattern='.'yyyy-MM-dd
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[LOG-DEMO]%-d{yyyy-MM-dd HH:mm:ss SS} [%c]-[%p] %m%n
# # Print only messages of level INFO or above in the package noModule.
log4j.logger.noModule=FATAL
# Spring Stuff
log4j.logger.org.springframework=INFO
# application self
log4j.logger.com.log=INFO
2.6.3 加载日志
使用Log4jConfigListener(Spring)进行动态加载,具体功能如下
参数 |
描述 |
log4jRefreshInterval |
定时刷新配置文件,即使修改log4j.properties配置文件,不需要重启web。只需要在web.xml配置即可 |
log4jConfigLocation |
获取classpath路径下log4j.properties配置文件 |
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
03 日志规范
3.1 通用规范
应用中不可直接使用日志系统(Log4j、logback)中API,而是依赖使用日志框架中SLF4J中的API,使用门面模式的日志框架,有利维护和各个类的日志处理方式统一
在日志输出时,字符串变量之间的拼接方式使用占位符的方式,如logger.info("process trade with id : {}", id);
避免重复打印日志,浪费磁盘空间,务必在log4j.xml中设置additivity=false。如<logger name="com.taobao.dubbo.config" additivity="false">
异常信息应该包含两类信息:案发现场信息和异常堆栈信息,如logger.error(各类参数或对象toString()+ "_" + e.getMessage(), e),如果不做处理,那么通过关键字throws往上抛。
可以使用warn日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从,如非必要,请不要在此场景打出 error 级别,避免频繁报警。
尽量用英文来描述日志错误信息,如果日志中的错误信息用英文描述不清楚的话使用中文描述即可,否则容易产生歧义
3.2 服务器日志和应用日志分离
很多开发同学都没有意识到将不同模块()日志分离带来的好处。实际上服务器日志、应用日志分离将有利于问题排查和快速定位问题。
# Rules reminder:
# TRACE < DEBUG < INFO < WARN < ERROR < FATAL
# Global logging configuration
log4j.rootLogger=info,console, system
# Console output...
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%p] %d [%t] %c (%F:%L) - %m%n
## File output...
log4j.appender.system=org.apache.log4j.DailyRollingFileAppender
log4j.appender.system.File=${LOG_DIR}/system.log
log4j.appender.system.Append=true
log4j.appender.system.Threshold=INFO
log4j.appender.defaultLogger.DatePattern='.'yyyy-MM-dd
log4j.appender.system.layout=org.apache.log4j.PatternLayout
log4j.appender.system.layout.ConversionPattern=%d [%p] [%t] %c (%F:%L): %m%n
## 3rdparty logging configuration
log4j.logger.org.springframework=WARN
# My logging(Application) logging configuration
log4j.logger.com.log.demo.mbrowser=INFO, business
log4j.additivity.com.log.demo.business=false
# File output...
log4j.appender.business=org.apache.log4j.DailyRollingFileAppender
log4j.appender.business.File=${LOG_DIR}/mbrowser.log
log4j.appender.business.Append=true
log4j.appender.business.Threshold=INFO
log4j.appender.defaultLogger.DatePattern='.'yyyy-MM-dd
log4j.appender.business.layout=org.apache.log4j.PatternLayout
log4j.appender.business.layout.ConversionPattern=%d [%p] [%t] %c (%F:%L): %m%n
04 日志官方文档
【1】apache commons loggings
http://commons.apache.org/proper/commons-logging/
【2】slf4j http://www.slf4j.org/
【3】log4j http://logging.apache.org/log4j/1.2/
【4】log4j2 http://logging.apache.org/log4j/2.x/
【5】logback http://logback.qos.ch/
以上是关于log4j的主要内容,如果未能解决你的问题,请参考以下文章
log4j怎样控制只输出自己写的代码的日志,不输出框架中的日志
Log4j反序列化远程代码执行漏洞(CVE-2019-17571)
log4jspringboot项目启动 ,使用的druid数据源,log4j报错 log4j:WARN Please initialize the log4j system properly.(代码片