Log4j 2使用教程二 【详解】
配置
Log4j 2的配置可以通过4种方式中的1种完成:
1、通过使用XML,JSON,YAML或属性格式编写的配置文件。
2、以编程方式,通过创建一个ConfigurationFactory和配置实现。
3、以编程方式,通过调用配置界面中公开的API将组件添加到默认配置。
4、通过编程方式,通过调用内部Logger类的方法。
我主要是讲解配置文件的方式
编程的方式可以参考: Extending Log4j 2和Programmatic Log4j Configuration.
自动配置
Log4j能够在初始化期间自动配置自身。
当Log4j启动时,将找到所有ConfigurationFactory插件,并按照从最高到最低的加权顺序进行排列。
交付时,Log4j包含四个ConfigurationFactory实现:一个用于JSON,一个用于YAML,一个用于properties,一个用于XML。
1、Log4j将检查log4j.configurationFile
系统属性,如果设置,将尝试使用与文件扩展名匹配的ConfigurationFactory加载配置。
2、如果没有设置系统属性,则properties ConfigurationFactory
将在类路径中查找log4j2-test.properties
。
3、如果没有找到这样的文件,YAML ConfigurationFactory
将在类路径中查找log4j2-test.yaml
或log4j2-test.yml
。
4、如果没有找到这样的文件,JSON ConfigurationFactory
将在类路径中查找log4j2-test.json
或log4j2-test.jsn
。
5、如果没有找到这样的文件,XML ConfigurationFactory
将在类路径中查找log4j2-test.xml
。
6、如果找不到测试文件,则properties ConfigurationFactory
将在类路径中查找log4j2.properties
。
7、如果无法找到属性文件,则YAML ConfigurationFactory
将在类路径上查找log4j2.yaml
或log4j2.yml
。
8、如果无法找到YAML
文件,则JSON ConfigurationFactory
将在类路径上查找log4j2.json
或log4j2.jsn
。
9、如果无法找到JSON
文件,则XML ConfigurationFactory
将尝试在类路径上找到log4j2.xml
。
10、如果没有找到配置文件,将使用DefaultConfiguration
。这将导致日志输出转到控制台。
日志级别
log4j规定了默认的几个级别:trace<debug<info<warn<error<fatal
等。这里要说明一下:
①级别之间是包含的关系,意思是如果你设置日志级别是trace,则大于等于这个级别的日志都会输出。
②基本上默认的级别没多大区别,就是一个默认的设定。你可以通过它的API自己定义级别。你也可以随意调用这些方法,不过你要在配置文件里面好好处理了,否则就起不到日志的作用了,而且也不易读,相当于一个规范,你要完全定义一套也可以,不用没多大必要。
③这不同的级别的含义大家都很容易理解,这里就简单介绍一下:
level | 描述 |
---|---|
trace | 是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出。 |
debug | 调试么,我一般就只用这个作为最低级别,trace压根不用。是在没办法就用eclipse或者idea的debug功能就好了么。 |
info | 输出一下你感兴趣的或者重要的信息,这个用的最多了。 |
warn | 有些信息不是错误信息,但是也要给程序员的一些提示,类似于eclipse中代码的验证不是有error 和warn(不算错误但是也请注意,比如以下depressed的方法)。 |
error | 错误信息。用的也比较多。 |
fatal | 级别比较高了。重大错误,这种级别你可以直接停止程序了,是不应该出现的错误么!不用那么紧张,其实就是一个程度的问题。 |
日志使用
紧接上篇博文
例子1:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</appenders>
<loggers>
<!--我们只让这个logger输出trace信息,其他的都是error级别-->
<!--
additivity开启的话,由于这个logger也是满足root的,所以会被打印两遍。
-->
<logger name="cn.lsw.base.log4j2.Hello" level="trace" additivity="false">
<appender-ref ref="Console"/>
</logger>
<root level="error">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
先简单介绍一下下面这个配置文件。
1)根节点configuration,然后有两个子节点:appenders和loggers(都是复数,意思就是可以定义很多个appender和logger了)(如果想详细的看一下这个xml的结构,可以去jar包下面去找xsd文件和dtd文件)
2)appenders:这个下面定义的是各个appender,就是输出了,有好多类别,这里也不多说(容易造成理解和解释上的压力,一开始也未必能听懂,等于白讲),先看这个例子,只有一个Console,这些节点可不是随便命名的,Console就是输出控制台的意思。然后就针对这个输出设置一些属性,这里设置了PatternLayout就是输出格式了,基本上是前面时间,线程,级别,logger名称,log信息等,差不多,可以自己去查他们的语法规则。
3)loggers下面会定义许多个logger,这些logger通过name进行区分,来对不同的logger配置不同的输出,方法是通过引用上面定义的logger,注意,appender-ref引用的值是上面每个appender的name,而不是节点名称。
这个例子为了说明什么呢?我们要说说这个logger的name(名称)了(前面有提到)。
name机制
可以参考: http://logging.apache.org/log4j/2.x/manual/architecture.html
我们看到配置文件中的那个name
是非常重要的。这个name
要用好的,就不能随便乱起。
(随便用的话,那就随便取名字)。这个机制很简单,就类似于java package
一样。
上篇中创建logger
对象的时候,名称是通过Hello.class
或者Hello.class.getName()
这样的方法。为什么要这样做呢?很重要的原因就是有所谓的继承问题。比如 如果你给com.Hello
定义了一个logger
,那么它也适用于com.Hello.base
这个logger
。名称的继承是通过(.)点号分隔的。然后你会返现上面的loggers
里面有个子节点不是logger
而是root
,而且这个root
没有name
属性。
这个root
相当于根节点。你所有的logger
都适用于这个logger
,所以,即使你在很多类里面通过类名.class.getName()
或者类名.class
得到很多的logger
,而且你也没有在配置文件中进行任何配置,它们也能够都输出,因为他们都继承了root
的log
配置。
这种继承的说法官网的解释叫做logger 层次结构(Hierarchy)
官网的例子:
例如:Logger配置中name为com.foo是name为com.foo.Bar的父类。类似的有,java是java.util的父类,是java.util.Vector的祖先。
上面的那个配置文件里面还定义了一个logger
,他的名称是cn.lsw.base.log4j2.Hello
,这个名称其实就是通过前面的Hello.class.getName()
或者Hello.class
得到的。上面那个配置文件,我们为了给它做单独配置。意思是:只有cn.lsw.base.log4j2.Hello
这个logger
输出trace
信息。也就是它的日志级别为trace
,其他的logger
则继承root
的日志配置,日志级别为error
,只能打印出error
及以上级别的日志。
那么有人会问,你单独配置的那个logger
不也是继承了root
的配置么,那这样的话,岂不是会打印两遍日志? 这个问题确实是存在的。当然如果你设置了additivity=false
,就不会输出两遍。
我们再写一个测试类:
(我们先对上面的配置文件做下修改,一个是logger的name改为:test.Hello,第二把additivity=false
去掉或改为true)
package test;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Logger2Test {
private static Logger logger = LogManager.getLogger(Logger2Test.class.getName());
public static void main(String[] args){
logger.trace("start programe");
Hello hello = new Hello();
hello.getHello();
logger.trace("end programe");
}
}
结果就是:
2017-05-17 11:51:40.783 [main] TRACE test.Hello - Enter
2017-05-17 11:51:40.783 [main] TRACE test.Hello - Enter
2017-05-17 11:51:40.787 [main] TRACE test.Hello - 我是trace
2017-05-17 11:51:40.787 [main] TRACE test.Hello - 我是trace
2017-05-17 11:51:40.787 [main] INFO test.Hello - 我是info信息
2017-05-17 11:51:40.787 [main] INFO test.Hello - 我是info信息
2017-05-17 11:51:40.787 [main] ERROR test.Hello - 我是error
2017-05-17 11:51:40.787 [main] ERROR test.Hello - 我是error
2017-05-17 11:51:40.787 [main] FATAL test.Hello - 我是fatal
2017-05-17 11:51:40.787 [main] FATAL test.Hello - 我是fatal
2017-05-17 11:51:40.787 [main] TRACE test.Hello - 退出程序.
2017-05-17 11:51:40.787 [main] TRACE test.Hello - 退出程序.
2017-05-17 11:51:40.787 [main] TRACE test.Hello - Exit
2017-05-17 11:51:40.787 [main] TRACE test.Hello - Exit
我们可以看出主程序Logger2Test
并没有trace
日志输出,因为它继承了root
的日志配置(error级别及以上)。而Hello
输出了trace及以上级别的日志,但是呢,每个都输出了两遍。为什么会这样呢?前面也说过默认所有的logger
都继承root
的配置的。此时的Hello
既有自己单独的配置,也有从root
那里继承下来的配置,所以会打印两次。这样的特性,在其name
的层次结构中也是同样适用的,比如:
我创建三个logger
名称为test
、test.Hello
和test.Hello.Hello2
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</appenders>
<loggers>
<logger name="test.Hello" level="info" additivity="true">
<appender-ref ref="Console"/>
</logger>
<logger name="test.Hello" level="info" additivity="true">
<appender-ref ref="Console"/>
</logger>
<logger name="test.foo.Hello2" level="info" additivity="true">
<appender-ref ref="Console"/>
</logger>
<root level="error">
<appender-ref ref="Console" />
</root>
</loggers>
</configuration>
打印结果:
2017-05-17 14:18:09.388 [main] INFO test.Hello - 我是info信息
2017-05-17 14:18:09.388 [main] INFO test.Hello - 我是info信息
2017-05-17 14:18:09.388 [main] INFO test.Hello - 我是info信息
2017-05-17 14:18:09.392 [main] ERROR test.Hello - 我是error
2017-05-17 14:18:09.392 [main] ERROR test.Hello - 我是error
2017-05-17 14:18:09.392 [main] ERROR test.Hello - 我是error
2017-05-17 14:18:09.392 [main] FATAL test.Hello - 我是fatal
2017-05-17 14:18:09.392 [main] FATAL test.Hello - 我是fatal
2017-05-17 14:18:09.392 [main] FATAL test.Hello - 我是fatal
2017-05-17 14:18:09.393 [main] INFO test.foo.Hello2 - 我是info信息
2017-05-17 14:18:09.393 [main] INFO test.foo.Hello2 - 我是info信息
2017-05-17 14:18:09.393 [main] INFO test.foo.Hello2 - 我是info信息
2017-05-17 14:18:09.393 [main] ERROR test.foo.Hello2 - 我是error
2017-05-17 14:18:09.393 [main] ERROR test.foo.Hello2 - 我是error
2017-05-17 14:18:09.393 [main] ERROR test.foo.Hello2 - 我是error
2017-05-17 14:18:09.393 [main] FATAL test.foo.Hello2 - 我是fatal
2017-05-17 14:18:09.393 [main] FATAL test.foo.Hello2 - 我是fatal
2017-05-17 14:18:09.393 [main] FATAL test.foo.Hello2 - 我是fatal
可以看出打印三篇。
在实际使用过程中,我们其实就是需要一个就行了,这时你可以设置:additivity=false
。
它会把父类全部屏蔽掉。官方说法就是把追加性关闭。
现在我们看一个稍微复杂的例子:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error">
<!--先定义所有的appender-->
<appenders>
<!--这个输出控制台的配置-->
<Console name="Console" target="SYSTEM_OUT">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
<!--这个都知道是输出日志的格式-->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!--这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<SizeBasedTriggeringPolicy size="50MB"/>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--建立一个默认的root的logger-->
<root level="trace">
<appender-ref ref="RollingFile"/>
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
Log4j 2使用教程二 【详解】的更多相关文章
- 《手把手教你》系列基础篇(八十六)-java+ selenium自动化测试-框架设计基础-Log4j实现日志输出(详解教程)
1.简介 自动化测试中如何输出日志文件.任何软件,都会涉及到日志输出.所以,在测试人员报bug,特别是崩溃的bug,一般都要提供软件产品的日志文件.开发通过看日志文件,知道这个崩溃产生的原因,至少知道 ...
- GitHub 使用教程图文详解(转)
大纲: 一.前言 二.GitHub简介 三.注册GitHub账号 四.配置GitHub 五.使用GitHub 六.参与GitHub中其它开源项目 七.总结 注,GitHub官网:https://git ...
- GitHub 使用教程图文详解
大纲: 一.前言 二.GitHub简介 三.注册GitHub账号 四.配置GitHub 五.使用GitHub 六.参与GitHub中其它开源项目 七.总结 注,GitHub官网:https://git ...
- Windows Server 2008 架设 Web 服务器教程(图文详解)
Windows Server 2008 架设 Web 服务器教程(图文详解) 一.安装 IIS 7.0 : 虽然 Windows Server 2008 内置了I IS 7.0,但是默认情况下并没有安 ...
- MySQL 8.0.20 安装教程图文详解(windows 64位)
MySQL 8.0.20 安装教程图文详解(windows 64位) 更新时间:2020年05月09日 15:09:04 转载 作者:瘦肉粥不加糖 这篇文章主要介绍了MySQL 8.0. ...
- MySQL8.0.20安装教程图文详解,MySQL8.0.20安装教程winodws10
MySQL8.0.20安装教程图文详解,非常详细 一:mysql官网下载 https://dev.mysql.com/downloads/file/?id=494993 不用注册,直接下载就好 二:解 ...
- Windows系统Git安装教程(详解Git安装过程)
Windows系统Git安装教程(详解Git安装过程) 今天更换电脑系统,需要重新安装Git,正好做个记录,希望对第一次使用的博友能有所帮助! 获取Git安装程序 到Git官网下载,网站地址: ...
- Log4J日志整合及配置详解
Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合使用这三个组件可以轻松 ...
- BI之SSAS完整实战教程5 -- 详解多维数据集结构
之前简单介绍过多维数据集(Cube)的结构. 原来计划将Cube结构这部分内容打散,在实验中穿插讲解, 考虑到结构之间不同的部分都有联系,如果打散了将反而不好理解,还是直接一次性全部讲完. 本篇我们将 ...
- [js高手之路] html5 canvas系列教程 - 状态详解(save与restore)
本文内容与路径([js高手之路] html5 canvas系列教程 - 开始路径beginPath与关闭路径closePath详解)是canvas中比较重要的概念.掌握理解他们是做出复杂canvas动 ...
随机推荐
- [POJ2965]The Pilots Brothers' refrigerator (搜索/位运算)
题意 游戏“The Pilots Brothers:跟随有条纹的大象”有一个玩家需要打开冰箱的任务. 冰箱门上有16个把手.每个手柄可以处于以下两种状态之一:打开或关闭.只有当所有把手都打开时,冰箱才 ...
- golang str 首字母大写
首字母大写 //如果是小写字母, 则变换为大写字母 func strFirstToUpper(str string) string { if len(str) < 1 { return &quo ...
- python学习:删除空白
删除空白 删除尾部空白 确保字符串尾部没有空白,使用rstrip(); 删除字符串开头的空白,使用lstrip(); 同时删除字符串两端的空白,使用strip() 代码: >>> ...
- angularjs 初始化方法执行两次以及url定义错误导致传值错误问题
1.初始化方法执行两次以上的问题定义一个 route.如下所示:.state('main.channelQryDetail', { url:'/channelDetail/:channelNo/:pa ...
- C++调用C语言的库函数
在项目中,使用C语言编写了一个socket后台程序tkcofferd,并且为方便客户端的使用,提供了动态库,其中包含socket接口. 现在的需求是使用qt做一个前端界面,用来展示tkcofferd的 ...
- [LeetCode] Friends Of Appropriate Ages 适合年龄段的朋友
Some people will make friend requests. The list of their ages is given and ages[i] is the age of the ...
- python +ps 三方面库整理
-------------------------------------------端口进程相关------------------------------------------------cp ...
- 写给踏入IT行业的自己
在IT工程师和培训机构多如牛毛的时代,拜师学艺并不难.但自学编程对于毫无基础的同学来说却可能是个问题,相信有过类似经历的朋友都有一把辛酸泪和一肚不吐不快的体会.让我们从一个故事说起… 故事 某君在一个 ...
- 线段树合并+并查集 || BZOJ 2733: [HNOI2012]永无乡 || Luogu P3224 [HNOI2012]永无乡
题面:P3224 [HNOI2012]永无乡 题解: 随便写写 代码: #include<cstdio> #include<cstring> #include<iostr ...
- mysql-cluster集群配置
环境: centos7:192.168.1.16,192.168.1.170 mysql-cluster-community-7.6.8-1.el7.x86_64.rpm-bundle.tar 安装: ...