×

日志框架 --- Log4j

前端技术网 前端技术网 发表于2024-01-23 00:18:16 浏览898 评论0

抢沙发发表评论

一、log4j的使用步骤

一、使用步骤:

第一步

日志框架 --- Log4j

加入log4j-1.2.8.jar(可以选择log4j的更高版本)到lib下。

第二步

在CLASSPATH下建立log4j.properties。内容如下(数字为行号):

1 log4j.rootCategory=INFO, stdout, R

2

3 log4j.appender.stdout=org.apache.log4j.ConsoleAppender

日志框架 --- Log4j

4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

5 log4j.appender.stdout.layout.ConversionPattern=[QC]%p [%t]%C.%M(%L)|%m%n

6

7 log4j.appender.R=org.apache.log4j.DailyRollingFileAppender

8 log4j.appender.R.File=D:\\Tomcat 5.5\\logs\\qc.log

9 log4j.appender.R.layout=org.apache.log4j.PatternLayout

10 log4j.appender.R.layout.ConversionPattern=%d-[TS]%p%t%c-%m%n

11

12 log4j.logger.com.neusoft=DEBUG

13 log4j.logger.com.opensymphony.oscache=ERROR

14 log4j.logger.net.sf.navigator=ERROR

15 log4j.logger.org.apache.commons=ERROR

16 log4j.logger.org.apache.struts=WARN

17 log4j.logger.org.displaytag=ERROR

18 log4j.logger.org.springframework=DEBUG

19 log4j.logger.com.ibatis.db=WARN

20 log4j.logger.org.apache.velocity=FATAL

21

22 log4j.logger.com.canoo.webtest=WARN

23

24 log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN

25 log4j.logger.org.hibernate=DEBUG

26 log4j.logger.org.logicalcobwebs=WARN

第三步

相应的修改其中属性,修改之前就必须知道这些都是干什么的,在第二部分讲解。

第四步

在要输出日志的类中加入相关语句:

定义属性:static Logger logger= Logger.getLogger(LogDemo.class);//LogDemo为相关的类

在相应的方法中:

if(logger.isDebugEnabled()){

logger.debug(“System…..”);

二、log4j的简单介绍:

Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

三、参考资料:

http://baike.baidu.com/link?url=6PYC2iFZ6PPRStX30CayDL4un_UUtsh9Nfm-hcbLiMBVjygQQNActSKMmv2Z84r-V7DeQj7e4nBF8CnfBwxAHq

二、log4j2源码分析

1.概述

1.1.组件概览

1.2.灵活的配置

1.2.1.插件发现机制

1.2.2.插件装配机制

1.2.3.配置文件基本元素与对象的映射关系

2.属性占位符

2.1.概述

2.2.Interpolator插值器

2.3.默认属性配置

3.Logger

3.1.配置示例

3.2.配置详解

3.3.Logger继承机制

4.Appender

4.1.概述

4.2.框架支持的Appender实现

4.3.常用Appender详解

4.3.1.ConsoleAppender

4.3.2.RollingFileAppender

5.Layout

5.1.概述

5.2.PatternLayout

5.2.1.模式字符串

6.Manager

7.Filter

在log4j2中,LogManager就是日志的门面,相当于slf4j-api中的LoggerFactory.

框架为每个类加载分配了一个单独的LoggerContext,用于管理所有创建出来的Logger实例.

ContextSelector则负责管理类加载器到对应的LoggerContext实例之间的映射关系.

log4j2中,有5个关键概念:

组件架构如下:

在log4j2中,一切皆插件,框架通过 PluginRegistry扫描并发现插件配置.

PluginRegistry支持两种扫描方式

插件配置以 PluginType的形式保存在插件注册表中, PluginType的作用类似于spring中 BeanDefinition,定义了如何创建插件实例.

插件类通过@PluginFactory注解或者@PluginBuilderFactory注解配置插件实例的实例化和属性注入方式.

log4j2知道如何实例化插件后,我们就可以通过编写配置文件(如:log4j2.xml),进行插件的实例化和属性注入了.

Configuration全局配置对象负责保存所有解析到的配置.

通过 ConfigurationFactory.getConfiguration()可以使用不同的工厂生产不同的配置对象,不同的 Configuration实现可以解析不同格式的配置,如:xml,yaml,json等.

以xml文件为例,文件中每个元素都会最终对应一个插件实例,元素名称实际就是PluginType中的name,实例的属性可以从子元素对应的实例获取,也可以从自身元素的属性配置获取.

因此,xml中dom树的元素嵌套关系,也就是log4j组件实例的引用嵌套关系.

xml,yaml,json格式文件都可以描述这种嵌套关系,因此log4j2中定义了与文件格式无关的数据结构,Node来抽象配置.

AbstractConfiguration.setup()负责提取配置,形成Node树.

AbstractConfiguration.doConfigure()负责根据Node树,进行插件实例化和属性注入.

在log4j2中,环境变量信息(键值对)被封装为StrLookup对象,该对象作用类似于spring框架中的PropertySource.

在配置文件中,基本上所有的值的配置都可以通过参数占位符引用环境变量信息,格式为:${prefix:key}.

Interpolator内部以Map<String,StrLookup>的方式,封装了很多StrLookuo对象,key则对应参数占位符${prefix:key}中的prefix.

同时,Interpolator内部还保存着一个没有prefix的StrLookup实例,被称作默认查找器,它的键值对数据来自于log4j2.xml配置文件中的<Properties>元素的配置.

当参数占位符${prefix:key}带有prefix前缀时,Interpolator会从指定prefix对应的StrLookup实例中进行key查询,

当参数占位符${key}没有prefix时,Interpolator则会从默认查找器中进行查询.

Interpolator中默认支持的StrLookup查找方式如下(StrLookup查找器实现类均在org.apache.logging.log4j.core.lookup包下):

注意:Properties元素一定要配置在最前面,否则不生效.

log4j2框架会根据LoggerConfig的name建立对象之间的继承关系.这种继承机制与java的package很像,name以点进行名称空间分割,子名称空间继承父名称空间.

名称空间可以是全限定类名,也可以是报名.整个配置树的根节点就是RootLogger.

举例:假如我们的配置的Logger如下:

当通过LogManager.getLogger(name)获取Logger实例时,会根据name逐级递归直到找到匹配的LoggerConfig,或者递归到Root根节点为止.

追加器,负责控制Layout进行LogEvent的序列化,以及控制Manager对序列化后的字节序列进行输出.

在log4j2.xml配置文件中,配置方式如下:

控制台追加器,用于把日志输出到控制台,一般本地调试时使用.

配置示例如下:

文件滚动追加器,用于向本地磁盘文件中追加日志,同时可以通过触发策略(TriggeringPolicy)和滚动策略(RolloverStrategy)控制日志文件的分片,避免日志文件过大.

线上环境常用.

常用的触发策略包含两种:

滚动策略的实现包含两种:

配置示例如下:

布局对象,职责是把指定的LogEvent转换成可序列化对象(如:String),或者直接序列化成字节数组.

log4j2支持很多的序列化格式,如:普通模式字符串,JSON字符串,yaml字符串,XML格式字符串,HTML字符串等等.

类体系如下:

模式布局是我们最常使用的,它通过PatternProcessor模式解析器,对模式字符串进行解析,得到一个List<PatternConverter>转换器列表和List<FormattingInfo>格式信息列表.

在PatternLayout序列化时,会遍历每个PatternConverter,从LogEvent中取不同的值进行序列化输出.

模式字符串由3部分组成,格式为:%(格式信息)(转换器名称){选项1}{选项2}...

模式字符串的格式为:

%-(minLength).-(maxLength)(转换器名称){选项字符串}

minLength代表字段的最小长度限制,当字段内容长度小于最小限制时,会进行空格填充.

minLength前面的-负责控制对齐方式,默认为右对齐(左边空格填充),如果加上-,则会切换为左对齐方式(右边空格填充)

maxLength代表字段的最大长度限制,当字段内容长度大于最大限制时,会进行内容阶段

maxLength前面的-负责控制阶段方向,默认为左侧阶段,如果加上-,则会切换为右侧阶段

minLength和maxLength之间用点分隔.

格式信息中所有属性都是可选的,不配置,则使用默认值

log4j2会通过 PluginManager收集所有类别为Converter的插件,同时分析插件类上的@ConverterKeys注解,获取转换器名称,并建立名称到插件实例的映射关系.

PatternParser识别到转换器名称的时候,会查找映射.

框架支持的所有转换器如下:

有时我们需要对特定的转换器进行特殊的配置,如:给DatePatternConverter配置时间格式,这个时候需要通过选项字符串配置.

PatternParser会提取模式字符串中的所有选项,保存在一个List<String>中,每个{}包裹的内容作为一个选项.

当创建转换器时,框架会自动扫描转换器类中声明的静态工厂方法newInstance,同时支持两种可选的形参,一种是Configuration,另一种String[]则会注入选项列表.

选项列表的识别由不同的转换器各自定义.

最后,以一个实际的例子解释配置:

日志会输出时间,类名,方法名,消息以及一个换行符.

同时,我们给DatePatternConverter指定了了时间格式,并且限制全限定类名最小长度为5,右截断,最大为10,左对齐.

管理器的职责主要是控制目标输出流,以及把保存在ByteBuffer字节缓冲区中的日志序列化结果,输出到目标流中.

如:RollingFileManager需要在每次追加日志之前,进行滚动检查,如果触发滚动还会创建新的文件输出流.

manager继承体系如下:

过滤器的核心职责就是对 LogEvent日志事件进行匹配,匹配结果分为匹配和不匹配,结果值有3种:接受,拒绝,中立.可由用户自定义匹配和不匹配的行为结果.

所有实现了 Filterable接口的组件都可以引用一个过滤器进行事件过滤,包含 LoggerConfig和 AppenderControl等.

框架实现的过滤器如下:

三、为什么要使用SLF4J而不是Log4J

每一个Java程序员都知道日志对于任何一个Java应用程序,尤其是服务端程序是至关重要的,而很多程序员也已经熟悉各种不同的日志库如java.util.logging、Apache log4j、logback。但如果你还不知道SLF4J(Simple logging facade for Java)的话,那么是时候去在你项目中学习使用SLF4J了。

在这篇文章中,我们将学习为什么使用SLF4J比log4j或者java.util.logging要优秀。自从上次我写Java程序员的10个日志技巧已经有一段时间了,我已经不记得我写的关于日志的一切了。

不管怎样,让我们回到这个话题,SLF4J不同于其他日志类库,与其它有很大的不同。SLF4J(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层( abstraction layer),它允许你在后台使用任意一个日志类库。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。

如果一个项目已经使用了log4j,而你加载了一个类库,比方说 Apache Active MQ——它依赖于于另外一个日志类库logback,那么你就需要把它也加载进去。但如果Apache Active MQ使用了SLF4J,你可以继续使用你的日志类库而无语忍受加载和维护一个新的日志框架的痛苦。

总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是一个对于开发API的开发者很好的思想。虽然抽象日志类库的思想已经不是新鲜的事物而且Apache commons logging也已经在使用这种思想了,但现在SLF4J正迅速成为Java世界的日志标准。让我们再看看几个使用SLF4J而不是log4j、logback或者java.util.logging的理由。

SLF4J对比Log4J,logback和java.util.Logging的优势

正如我之前说的,在你的代码中使用SLF4J写日志语句的主要出发点是使得你的程序独立于任意特定的日志类库,依赖于特定类可能需要不同与你已有的配置,并且导致更多维护的麻烦。但除此之外,还要一个SLF4J API的特性使得我坚持使用SLF4J而抛弃我长期间钟爱的Lof4j的理由,是被称为占位符(place holder),在代码中表示为“{}”的特性。占位符是一个非常类似于在String的format()方法中的%s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的String对象。即使你可能没需要那些对象,但这个依旧成立,取决于你的生产环境的日志级别,例如在DEBUG或者INFO级别的字符串连接。因为String对象是不可修改的并且它们建立在一个String池中,它们消耗堆内存( heap memory)而且大多数时间他们是不被需要的,例如当你的应用程序在生产环境以ERROR级别运行时候,一个String使用在DEBUG语句就是不被需要的。通过使用SLF4J,你可以在运行时延迟字符串的建立,这意味着只有需要的String对象才被建立。而如果你已经使用log4j,那么你已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。

这是你在Log4j中使用的方案,但肯定这一点都不有趣并且降低了代码可读性因为增加了不必要的繁琐重复代码(boiler-plate code):

if(logger.isDebugEnabled()){

logger.debug("Processing trade with id:"+ id+" symbol:"+ symbol);

}

另一方面,如果你使用SLF4J的话,你可以得到在极简洁的格式的结果,就像以下展示的一样:

logger.debug("Processing trade with id:{} and symbol:{}", id, symbol);

在SLF4J,我们不需要字符串连接而且不会导致暂时不需要的字符串消耗。取而代之的,我们在一个以占位符和以参数传递实际值的模板格式下写日志信息。你可能会在想万一我有很个参数怎么办?嗯,那么你可以选择使用变量参数版本的日志方法或者用以Object数组传递。这是一个相当的方便和高效方法的打日志方法。记住,在生产最终日志信息的字符串之前,这个方**检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间。这里是使用SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar中的Log4j的适配器类Log4jLoggerAdapter。

public void debug(String format, Object arg1, Object arg2){

if(logger.isDebugEnabled()){

FormattingTuple ft= MessageFormatter.format(format, arg1, arg2);

logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());

}

}

同时,我们也很值得知道打日志是对应用程序的性能有着很大影响的,在生产环节上只进行必要的日志记录是我们所建议的。

怎么用SLF4J做Log4J的日志记录

除了以上好处,我想还有一个告诫,就是为了使用SLF4J,你不仅需要包含SLF4J的API jar包,例如 slf4j-api-1.6.1.jar,还需要相关Jar包,这取决于你在后台使用的日志类库。如果你想要使用和Log4J一起使用SLF4J,Simple Logging Facade for Java,,你需要包含以下的Jar包在你的classpath中,取决于哪个SLF4J和你在使用的Log4J的版本。例如:

slf4j-api-1.6.1.jar– JAR for SLF4J API

log4j-1.2.16.jar– JAR for Log4J API

slf4j-log4j12-1.6.1.jar– Log4J Adapter for SLF4J

如果你在使用Maven去管理你的项目依赖,你只需要包含SLF4J JAR包,maven会包含它的依赖的相关包。为了和SLF4J一起中使用Log4J,你可以包含以下的依赖在你项目中的pom.xml。

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>1.6.1</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>1.6.1</version>

</dependency>

还有,如果你对于使用变量参数版本(variable argument version)的日志方法感兴趣的话,那么就导入SLF4J 1.7的版本吧。

总结

总结这次说的,我建议使用SLF4J的而不是直接使用 Log4j, commons logging, logback或者 java.util.logging已经足够充分了。

在你的开源或内部类库中使用SLF4J会使得它独立于任何一个特定的日志实现,这意味着不需要管理多个日志配置或者多个日志类库,你的客户端会很感激这点。

SLF4J提供了基于占位符的日志方法,这通过去除检查isDebugEnabled(), isInfoEnabled()等等,提高了代码可读性。

通过使用SLF4J的日志方法,你可以延迟构建日志信息(Srting)的开销,直到你真正需要,这对于内存和CPU都是高效的。

作为附注,更少的暂时的字符串意味着垃圾回收器(Garbage Collector)需要做更好的工作,这意味着你的应用程序有为更好的吞吐量和性能。

这些好处只是冰山一角,你将在开始使用SL4J和阅读其中代码的时候知道更多的好处。我强烈建议,任何一个新的Java程序员,都应该使用SLF4J做日志而不是使用包括Log4J在内的其他日志API。

感谢您花时间阅读本文!我们希望通过对日志框架 --- Log4j和为什么要使用SLF4J而不是Log4J的问题进行探讨,为您提供了一些有用的见解和解决方案。如果您需要更多帮助或者有其他疑问,请不要犹豫与我们联系。