log4j2 项目日志组件
在项目运行过程中,常常需要进行功能调试以及用户行为的跟踪和记录,部分人习惯使用System.out,但这并不建议,它仅仅是使用方便但不便于维护也无扩展性。相比log4j的话,log4j可以控制日志信息的输送目的地、输出格式以及级别等等,使我们能够更加细致地控制日志的生成过程。
Log4j2是对Log4j1的升级,在性能和功能上有显著的改进,包括多线程中吞吐量的增强、占位符的支持、配置文件自动重新加载等
一、入门介绍
1、下载jar包
pox.xml
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>
2、配置文件
Log4j包含四个配置工厂实现:JSON、YAML、properties、XML,本文介绍常用的方式XML。
Log4j具有在初始化期间自动配置自身的能力。当Log4j启动时,它将定位类路径下所有符合名称的文件,优先级顺序:log4j2-test.properties > log4j2-test.xml > log4j2.properties > log4j2.xml
3、一个简单的实例
xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
java代码:
private static final Logger logger = LogManager.getLogger(MyApp.class);
@Test
public void testLog4j(){
logger.info("hello world!");
}
}
控制台信息
22:17:47.146 [main] INFO MyApp - hello world!
二、模块介绍
<Configuration>
| 属性 | 描述 |
| monitorInterval | 如果文件被修改了,指定时间后会重新加载配置。单位秒,最小值是5 |
| packages | 以逗号隔开的包名列表,用于搜索插件,比如自定义filter、appender等。插件仅会加载一次,所以要想修改后生效必须重启项目 |
| status | 内部日志级别,设置值为debug可以在控制台上清晰地看到整个日志事件流程,所使用的Logger是org.apache.logging.log4j.core.LOGGER |
| strict | 允许使用严格的XML格式。不支持JSON配置 |
| verbose | 在加载插件时启用诊断信息 |
<Appenders>
Log4j允许将日志请求打印到多个目的地。在log4j语言中,输出目的地称为Appender。目前,appender存在于控制台、文件、远程套接字服务器、Apache Flume、JMS、远程UNIX Syslog守护进程和各种数据库api中。以下介绍几种比较常用的appender,如需了解更多可以到官网上进行查阅。
1、ConsoleAppender
输出到控制台,<Console>
|
参数名称 |
类型 |
描述 |
|
filter |
Filter |
过滤器 |
|
layout |
Layout |
日志输出格式 |
|
follow |
boolean |
|
|
direct |
boolean |
|
|
name |
String |
Appender的名称 |
|
ignoreExceptions |
boolean |
默认true,忽略写入异常 |
|
target |
String |
SYSTEM_OUT或SYSTEM_ERR,默认是SYSTEM_OUT |
2、FileAppender
输出到文件,<File>
|
参数 |
类型 |
描述 |
|---|---|---|
|
append |
boolean |
默认是true,新记录将追加到文件尾部 |
|
bufferedIO |
boolean |
默认是true,使用缓冲区可以显著地提高性能 |
|
bufferSize |
int |
当bufferedIO是true时,这个属性缓冲区大小,默认是8192字节。 |
|
createOnDemand |
boolean |
appender按需创建文件。只有当一个日志事件通过所有过滤器并被路由到这个appender时,appender才会创建这个文件。默认值为假 |
|
filter |
Filter |
一个过滤器来确定事件是否应该由这个Appender处理。使用复合过滤器可以使用多个筛选器 |
|
fileName |
String |
要写入的文件的名称。如果文件或它的任何父目录不存在,它们将被创建 |
|
immediateFlush |
boolean |
默认true,每次写入后都将有一个刷新。这将保证缓冲区的数据被写入磁盘,但可能会影响性能。 |
|
layout |
Layout |
日志格式 |
|
locking |
boolean |
文件锁,默认false |
|
name |
String |
Appender的名称 |
|
ignoreExceptions |
boolean |
默认true,忽略写入异常 |
|
filePermissions |
String |
定义文件权限 例: rw------- or rw-rw-rw- etc... |
|
fileOwner |
String |
定义文件所有者 |
|
fileGroup |
String |
定义文件组 |
3、JDBCAppender
JDBCAppender使用标准JDBC将日志事件写入到关系数据库表中。它可以配置为使用JNDI数据源或自定义工厂方法获得JDBC连接。无论采用哪种方法,都必须由连接池来支持。否则,日志记录性能将受到极大的影响。如果已配置的JDBC驱动程序支持批处理语句,并且将缓冲区大小配置为一个正数,那么日志事件将被批处理。
(1)<JDBC>
|
参数 |
类型 |
描述 |
|---|---|---|
|
name |
String |
必须,appender的名称 |
|
ignoreExceptions |
boolean |
默认true,忽略日志事件异常 |
|
filter |
Filter |
过滤器 |
|
bufferSize |
int |
如果一个大于0的整数,这将导致appender缓冲日志事件,并在缓冲区达到该大小时刷新写入数据 |
|
connectionSource |
ConnectionSource |
必须,可被检索到的数据库连接 |
|
tableName |
String |
必须,插入日志事件的数据表名 |
|
columnConfigs |
ColumnConfig[] |
必须,需要插入到数据库的字段,由多个<Column>元素组成 |
|
columnMappings |
ColumnMapping[] |
必须,字段映射配置 |
(2)使用<DataSource>来获得JDBC的连接,这里仅列出jndi:
| 参数 | 类型 | 描述 |
|---|---|---|
| jndiName | String | 必需的,如已配置的jndi为jdbc/LoggingDatabase,那此处的值为java:comp/env/jdbc/LoggingDatabase。数据源必须由连接池来支持;否则,日志记录将非常缓慢。 |
(3)使用<Column>来指定要写入表中的哪些列,以及如何对它们进行写入。它没有SQL注入漏洞。
| 参数 | 类型 | 描述 |
|---|---|---|
| name | String |
必须,表字段名称 |
| pattern | String |
使用PatternLayout模式插入值,注:同一个Column元素中,patter、literal、isEventTimestamp3个属性只能存在一个 |
| literal | String |
该值将直接包含在SQL语句中执行,比如:rand()函数将生成随机数,类似myibats中的${} |
| isEventTimestamp | boolean |
是否时间格式java.sql.Types.TIMESTAMP |
| isUnicode | boolean |
除非指定pattern,否则该属性将被忽略。如果是true,该值将插入Unicode。否则,该值将被插入非Unicode。 |
| isClob | boolean |
除非指定pattern,否则该属性将被忽略。如果是true,该值将插入CLOB,否则将插入varchar、nvarchar |
实例:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
<Appenders>
<JDBC name="databaseAppender" tableName="dbo.application_log">
<DataSource jndiName="java:/comp/env/jdbc/LoggingDataSource" />
<Column name="eventDate" isEventTimestamp="true" />
<Column name="level" pattern="%level" />
<Column name="logger" pattern="%logger" />
<Column name="message" pattern="%message" />
<Column name="exception" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="databaseAppender"/>
</Root>
</Loggers>
</Configuration>
<PatternLayout>
(1)日期,%d / %date
|
Pattern |
示例 |
|---|---|
|
%d{DEFAULT} |
2012-11-02 14:34:02,781 |
|
%d{ISO8601} |
2012-11-02T14:34:02,781 |
|
%d{ISO8601_BASIC} |
20121102T143402,781 |
|
%d{ABSOLUTE} |
14:34:02,781 |
|
%d{DATE} |
02 Nov 2012 14:34:02,781 |
|
%d{COMPACT} |
20121102143402781 |
|
%d{HH:mm:ss,SSS} |
14:34:02,781 |
|
%d{dd MMM yyyy HH:mm:ss,SSS} |
02 Nov 2012 14:34:02,781 |
|
%d{HH:mm:ss}{GMT+0} |
18:34:02 |
|
%d{UNIX} |
1351866842 |
|
%d{UNIX_MILLIS} |
1351866842781 |
当然你也可以自定义格式,比如 %d{yyyy-MM-dd HH:mm:ss}
(2)记录器,%c / %logger
|
Conversion Pattern |
Logger Name |
结果 |
|---|---|---|
|
%c{1} |
org.apache.commons.Foo |
Foo |
|
%c{2} |
org.apache.commons.Foo |
commons.Foo |
|
%c{10} |
org.apache.commons.Foo |
org.apache.commons.Foo |
|
%c{-1} |
org.apache.commons.Foo |
apache.commons.Foo |
|
%c{-2} |
org.apache.commons.Foo |
commons.Foo |
|
%c{-10} |
org.apache.commons.Foo |
org.apache.commons.Foo |
|
%c{1.} |
org.apache.commons.Foo |
o.a.c.Foo |
|
%c{1.1.~.~} |
org.apache.commons.test.Foo |
o.a.~.~.Foo |
|
%c{.} |
org.apache.commons.test.Foo |
....Foo |
{?} - ?是正整数时表示从右边开始取n个部分,负整数表示从左边开始移除n个部分,那为什么%c{-10}是完整的名称我也不清楚,欢迎留言
(3)日志信息,%m / %msg / %message
(4)日志级别,%level
(5)换行符,%n
<Filter>
log4j2自带多种filter供直接使用,也可以由我们自己来定义filter:
MyFilter.java
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message; @Plugin(name = "MyFilter", category = "Core", elementType = "filter", printObject = true)
public final class MyFilter extends AbstractFilter { private final Level level; private MyFilter(Level level, Result onMatch, Result onMismatch) {
super(onMatch, onMismatch);
this.level = level;
} public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
return filter(level);
} public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
return filter(level);
} public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
return filter(level);
} @Override
public Result filter(LogEvent event) {
return filter(event.getLevel());
} private Result filter(Level level) { /*
* 业务逻辑
* */ return level.isMoreSpecificThan(this.level) ? onMatch : onMismatch;
} @Override
public String toString() {
return level.toString();
} @PluginFactory
public static MyFilter createFilter(@PluginAttribute(value = "level", defaultString = "ERROR") Level level,
@PluginAttribute(value = "onMatch", defaultString = "NEUTRAL") Result onMatch,
@PluginAttribute(value = "onMismatch", defaultString = "DENY") Result onMismatch) {
return new MyFilter(level, onMatch, onMismatch);
}
}
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5" packages="your packages" verbose="false" strict="true">
<Appenders> <Console name="Console" target="SYSTEM_OUT" ignoreExceptions="true">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %level %logger{10} - %msg%n"/>
<MyFilter level="info" onMatch="ACCEPT"/>
</Console> </Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
补充:
在实际应用中,有时需要对用户的访问信息进行记录,比如请求参数、用户id等等。在log4j1中我们会使用MDC和NDC来存储应用程序的上下文信息,而log4j2使用ThreadContext来实现MDC和NDC两者的功能。
(1)NDC采用类似栈的机制来存储上下文信息,线程独立。
在PatternLayout中使用 %x 来输出,注意x是小写。
实例:
Test.java
ThreadContext.push("hello world!");
log4j2.xml
<Column name="tip" pattern="%x" />
(2)MDC采用类似map的机制来存储信息,线程独立。
在PatternLayout中使用 %X{userId} 来输出,注意X是大写。
实例:
Test.java
ThreadContext.put("userId","1");
log4j2.xml
<Column name="userId" pattern="%X{userId}" />
注意使用完后调用clearAll()清除上下文映射和堆栈。
api:http://logging.apache.org/log4j/2.x/javadoc.html
官网地址:https://logging.apache.org/log4j/2.x/index.html
转载请注明出处:http://www.cnblogs.com/cjh-notes/p/8017855.html
log4j2 项目日志组件的更多相关文章
- 日志组件二:log4j2
一.背景 随着业务服务(Server App)逐渐增加,我们的业务系统中的日志输出面临的问题越来越多,高并发下对磁盘io这块消耗的越来越大,因此,急需要一个高性能且最好能够支持异步输出日志的日志框架, ...
- 使用Slf4j集成Log4j2构建项目日志系统的完美解决方案
一.背景 最近因为公司项目性能需要,我们考虑把以前基于的log4j的日志系统重构成基于Slf4j和log4j2的日志系统,因为,使用slf4j可以很好的保证我们的日志系统具有良好的兼容性,兼容当前常见 ...
- Work Time Manager【开源项目】- 创建自己日志组件 2.0重构
Hello all , 我又回来了 这次我们真是开始来聊聊开源项目里,小而有用的模块或者组件的开发思想. 同时,软件已经更新到1.60的版本了,支持新用户注册,可以不再使用统一的test账户了. 您可 ...
- 普通Java项目中使用Sl4j+Log4j2打印日志
因工作需要,采用JavaFx开发了一个windows窗口程序.在开发过程中,由于没有引入日志框架,只能自己手动在控制台打印些信息,给调试带来了很多麻烦:因此决定引入日志框架.由于之前接触的项目 ...
- java日志组件介绍(common-logging,log4j,slf4j,logback )
转自:http://www.blogjava.net/daiyongzhi/archive/2014/04/13/412364.html common-logging是apache提供的一个通用的日志 ...
- 五分钟秒懂Java日志组件
Java中有许多种日志记录方式,有些API有占位符,有些API没占位符,初学的人可能会搞不清楚这些日志组件的由来.我一开始的时候也是很懵逼的,后来一点点弄懂了于是就又了这篇文章. 在Java中进行日志 ...
- Java中日志组件详解
avalon-logkit Java中日志组件详解 lanhy 发布于 2020-9-1 11:35 224浏览 0收藏 作为开发人员,我相信您对日志记录工具并不陌生. Java还具有功能强大且功能强 ...
- C#组件系列———又一款日志组件:Elmah的学习和分享
前言:好久没动笔了,都有点生疏,12月都要接近尾声,可是这月连一篇的产出都没有,不能坏了“规矩”,今天还是来写一篇.最近个把月确实很忙,不过每天早上还是会抽空来园子里逛逛.一如既往,园子里每年这个时候 ...
- 我心中的核心组件(可插拔的AOP)~第十五回 我的日志组件Logger.Core(策略,模版方法,工厂,单例等模式的使用)
回到目录 之前的讲过两篇关于日志组件的文章,分别是<第一回 日志记录组件之自主的Vlog>和<第三回 日志记录组件之log4net>,而今天主要说一下我自己开发的另一种日志 ...
随机推荐
- Xilinx ISE14.1用Verilog语言实现一个半加器并测试
<一>建立一个工程 注:Xilinx ISE的安装在此不再过多说明,网上有参考资料 1.打开软件进入如下界面 2.创建工程 File-->New Project 3.创建文件(我取名 ...
- A Simple Math Problem(矩阵快速幂)(寒假闭关第一题,有点曲折啊)
A Simple Math Problem Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- replace to
要注意的是:插入数据的表必须有主键或者是唯一索引!否则的话,replace into 会直接插入数据,这将导致表中出现重复的数据. MySQL replace into 有三种形式: 1. repla ...
- empty()和remove()的区别
这两个都是删除元素,但是两者还是有区别的. remove()这个方法呢是删除被选元素的所有文本和子元素,当然包括被选元素自己. 而empty()呢,被选元素自己是不会被删除的. 比如: <div ...
- 技嘉 gigabyte b75m d3v 主板 定时开机无效问题解决
BIOS 里面设置定时开机后发现到点并没有正常启动~~~ 百思不得解.后来发现原来是WIN8系统下的控制面板的关机并非正常关机,而是不保存设置的非正常关机,在开始菜单右键——关闭或注销——关闭计算机 ...
- Skylin CityBuilder 6.6.1 提示授权过期解决(License Manager中显示未过期)
最近工作中使用到Skyline 系列软件,授权一切正常,启动CityBuilder时提示授权过期 运行License Manger之后查看授权状态 ' 一切正常,并没有出现授权过期等情况. 如何解决: ...
- div内长串数字或字母不断行处理
比如: <div>1111tryrt645645rt4554111112324353453454364</div> <div>qwewretrytuytuiyiuo ...
- 使用JavaScript生成二维码教程-附qrcodejs中文文档
使用javascript生成二维码 依赖jquery 需要使用到的库 https://github.com/davidshimjs/qrcodejs DIV <div id="qrco ...
- 使用Microsoft.AspNetCore.TestHost进行完整的功能测试
简介 Microsoft.AspNetCore.TestHost是可以用于Asp.net Core 的功能测试工具.很多时候我们一个接口写好了,单元测试什么的也都ok了,需要完整调试一下,检查下单元测 ...
- javascript语言基础
js的基本语法 /* 多行注释 * */ //单行注释 // 变量赋值 默认以换行符作为结束符,有分号以分号作为结束符号 var i; i=10; s="hello"; var b ...