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动 ...
随机推荐
- 输入,输出与Mad Libs游戏
name1=input('请输入一个名字') name2=input('请输入一个名字') car=input('请输入一种车子') print('饥饿的{}看到{}穿着三级甲骑着{}下山'.form ...
- Java当中的IO一
1.IO操作的目标 什么是IO操作的目标? 输入: 从数据源当中读取数据 输出: 将数据写入到数据目的地当中 有数据进入到程序当中,这个过程就可以被叫做输入 流:即在数据源与程序之间建立的传输通道 2 ...
- java文件与流课后作业
1,编写一个程序,指定一个文件夹,能自动计算出其总容量, 2,编写一个文件加解密程序,通过命令行完成加解密工作3,编写一个文件分割工具,能把一个大文件分割成多个小的文件.并且能再次把它们合并起来得到完 ...
- 简单的SQL查询,循环插入
- C++中的常量函数
(1)常量成员函数不修改对象. (2)常量成员函数在定义和声明中都需要加上 const; (3)非常量成员函数不能被常量成员函数调用,但构造函数和析构函数除外. (4)常量(cosnt对象)对象不能调 ...
- 获取node异步执行结果的方式
拿数据库操作举例: var connection = mysql.createConnection(); connection.query(sql,function(err,rows){xxx} ); ...
- PAT甲级1091 Acute Stroke【三维bfs】
题目:https://pintia.cn/problem-sets/994805342720868352/problems/994805375457411072 题意: 求三维的连通块 思路: 简单b ...
- github=>git=>composer Packages 使用教程
2018年12月17日14:32:05 因为要做搜索,所以需要用分词工具php的分词不借助的第三方的真的很少, 目前选择的是 http://www.phpbone.com/phpanalysis/ 但 ...
- Python 学习笔记6 变量-字典
字典是python中一个十分重要的变量,他是一个可变的容器对象.通过一组key(键)和value(值)对组成一个元素. 组成形式为{'key':'value', 'key':'value'}.整个字典 ...
- 【C++】满二叉树问题
/* 给出一棵满二叉树的先序遍历,有两种节点:字母节点(A-Z,无重复)和空节点(#).要求这个树的中序遍历.输出中序遍历时不需要输出#. 满二叉树的层数n满足1<=n<=5. Sampl ...