log4j从入门到了解

版权声明:大家随便用------------------------------水个文章 https://blog.csdn.net/qq_40182873/article/details/86525547

log4j简介

 本文部分来自于log4j1.2版本官方文档和api。本文只是简介log4j的一些概念型的东西,用于理解log4j的一些基本规则。实战请转到下一篇1。(文末)

日志介绍

  日志是软件的必不可少的一部分,在程序的开发,测试,维护,运行阶段,需要我们向控制台和系统其它位置输出日志,用于我们了解程序的运行状态。
   日志的根据用途和记录内容的不同,又会分为调试日志,运行日志,异常日志,安全日志等。
   常用的日志技术有java的logger、apache的log4j和log4j2、logback

log4j介绍

   log4j全称 log for java,是Apache的开源项目,用于记录java语言应用的日志,当然现在log4j已经有其他语言的版本。目前已经停止更新,官方推荐使用升级版的log4j2。

log4j的优点

  1. 通过编辑配置文件来控制日志记录行为,而无需触及应用程序二进制文件。
  2. 控制日志的输出级别。
  3. 可以输出日志到文件
    以下是官网介绍翻译
       使用log4j,可以在运行时启用日志记录而无需修改应用程序二进制文件。 log4j包的设计使得这些语句可以保留在已发布的代码中,而不会产生很高的性能成本。 可以通过编辑配置文件来控制日志记录行为,而无需触及应用程序二进制文件。
       日志记录为开发人员提供了应用程序故障的详细上下文。 另一方面,测试提供了应用程序的质量保证和信心。 不应混淆记录和测试。 它们是互补的。 当明智地使用日志记录时,它可以证明是一个必不可少的工具。
       log4j的一个显着特征是记录器中的继承概念。 使用记录器层次结构,可以控制以任意精细的粒度输出哪些日志语句,但也非常容易。 这有助于减少记录的输出量和记录的成本。
       日志输出的目标可以是文件,OutputStream,java.io.Writer,远程log4j服务器,远程Unix Syslog守护程序或许多其他输出目标。

log4j的官网和下载

log4j相关链接介绍

  百度现在进入的是log4j2的文档界面,在右侧中有进入log4j 的文档的链接
在这里插入图片描述

  1. Apache的日志项目官网:https://logging.apache.org/
  2. log4j2 的欢迎页面 :https://logging.apache.org/log4j/2.x/
  3. log4j 的欢迎页面:http://logging.apache.org/log4j/1.2/
  4. log4j 指南:http://logging.apache.org/log4j/1.2/manual.html
  5. log4j api 文档:http://logging.apache.org/log4j/1.2/apidocs/index.html

log4j的下载介绍

  下载地址: http://logging.apache.org/log4j/1.2/download.html
在这里插入图片描述

日志的输出级别介绍

为何要定义日志输出级别

  由于日志的输出会一定程度影响我们的程序的运行效率,而在我们的开发、运行和维护中或者根据客户需求我们需要输出日志语句。
  但是全部输出会影响程序的运行效率,所以定义日志的输出级别来控制输出的内容的多少。
  在log4j中我们只需要更改配置文件即可控制日志的输出级别和位置。

日志的级别介绍

org.apache.log4j.Level
  日志的输出级别是有log4j的level来定义的,从高到低分别是

Level 描述
OFF 这是最高等级,为了关闭日志记录
FATAL 指定非常严重的错误事件,这可能导致应用程序中止
ERROR 错误事件可能仍然允许应用程序继续运行
WARN 指定具有潜在危害的情况
INFO 指定能够突出在粗粒度级别的应用程序运行情况的信息的消息
DEBUG 指定细粒度信息事件是最有用的应用程序调试
TRACE 指定细粒度比DEBUG更低的信息事件
ALL 各级包括自定义级别的日志输出
  1. 日志的输出规则
    f>e>w>I>D>t log4j的日志级别并且低级向上兼容(比如打印DEBUG级别的日志,那么INFO、WARN以及跟高级别的日志都会输出,OFF就算了),在目录log4j的组件和控制文件中存在实例代码,方便理解
  2. 我们可以自定义日志的输出级别(子类继承org.apache.log4j.Level
  3. Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。
  4. TRACE的话使用IDE调试的话会更好

log4j的组件介绍

组件介绍

  Log4j有三个主要组件:记录器(loggers),追加器( appenders)和布局( layouts)。 这三种类型的组件协同工作,使开发人员能够根据消息类型和级别记录消息,并在运行时控制这些消息的格式以及报告的位置。

记录器

log4j logger 类的介绍

创建logger对象后输出对应级别的日志,通过getLogger来实例化,getRootLogger获取根记录器。

 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);
}

记录器规则介绍

  1. 记录器的日志级别是可以继承的,如果没有定义,默认继承临近的父节点记录器的日志级别,为了确保所有日志记录器最终都能继承一个级别,根日志记录器总是有一个指定的级别。
  2. 日志记录器的的输出方法会根据记录器实例化时的日志级别打印对应级别既往上的日志。
   //获取记录器的实例对象
   Logger  logger = Logger.getLogger("com.foo");

  //设置记录器打印日志的级别,通常这是在配置文件中实现的
   logger.setLevel(Level.INFO);
	
	//创建上一个记录器的子记录器
   Logger barlogger = Logger.getLogger("com.foo.Bar");

   // 那么warn级别的日志是输出的,因为 WARN >= INFO.
   logger.warn("Low fuel level.");

   // 不输出,因为 DEBUG < INFO.
   logger.debug("Starting search for nearest gas station.");

  //这个barlogger记录器实例从logger 中继承日志级别,因为info>=info  ,可以输出
   barlogger.info("Located nearest gas station.");

   // 不可以输出 DEBUG < INFO.
   barlogger.debug("Exiting gas station search"); 
  1. 使用相同名称调用getLogger方法,返回的是同一个一个记录器对象在内存中的引用, 因此,可以配置记录器,然后在代码中的其他位置检索相同的实例,而不传递引用。
   Logger x = Logger.getLogger("wombat");
   Logger y = Logger.getLogger("wombat");
  1. 可以按任何顺序创建和配置log4j记录器。 父记录器可以在记录器后实例化

  2. log4j环境的配置通常在应用程序初始化时完成。 首选方法是读取配置文件

  3. == 记录器名称等于类的完全限定名称。 这是定义记录器的有用且直接的方法==。 由于日志输出带有生成记录器的名称,因此该命名策略可以轻松识别日志消息的来源。 但是,这只是命名记录器的一种可能的策略,尽管是常见的策略。 Log4j不限制可能的记录器集。 开发人员可以根据需要自由命名记录器。

  4. 记录器之间的关系是通过LoggerRepository来管理的 ,其是一个Logger的容器,它负责创建、缓存Logger实例,同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由Logger的Name来决定,其中以’.’分隔。

记录器实例

实例统一在log4j的配置中讲解。

追加器

追加器的介绍

  一个记录器可以连接多个追加器,而追加器决定了计录器日志的输出位置,所以log4j一个记录器可以向多个位置(存在控制台,文件,GUI组件,远程套接字服务器,JMS,NT事件记录器和远程UNIX Syslog守护程序的appender)输出日志
addAppender方法将appender添加到给定的记录器。
在这里插入图片描述

追加器规则

  1. 一个记录器可以连接多个追加器
  2. 所有的追加器是可以被子记录器从父类中继承的,那么记录器最终的输出位置是由本身有的追加器和父记录器及其祖先中有的记录器中的追加器决定的。
  3. 如果父记录器中的标志设置为false,那么子记录器除了继承父记录器中的追加器外,不在往上继承。(父类和其本身的追加器)
  4. 图表说明规则

追加器实例介绍和图表显示继承关系

记录器名称 添加的追加器 添加标志 起作用的追加器 意见
root A A 根记录器是匿名的,但可以使用Logger.getRootLogger()方法访问。 root没有附加默认的appender。
x 1,2 ture A,1,2 输出x和根记录器中的追加器
x.y ture A,1,2 输出x.y、x、root中的追加器
x.y.z 3 ture A,1,2,3 输出x.y.z、x.y、x、root中的追加器
security A-sec false A-sec 添加标志设置成false
security.access none true A-sec 仅仅附加父节点security中的追加器,root中不附加

追加器的实现类讲解:LinkinPark的log4j追加器源码解析
在下文中的log4j配置中,有简单的实例可以帮助理解。

布局

布局介绍

  用户通常不仅要定制输出目的地,还要定制输出格式。 这是通过将布局与appender相关联来实现的。 布局负责根据用户的意愿格式化日志记录请求,而appender负责将格式化的输出发送到其目的地。
在这里插入图片描述

PatternLayout是标准log4j发行版的一部分,它允许用户根据类似于C语言printf函数的转换模式指定输出格式。

例如,具有转换模式“%r [%t]%-5p%c - %m%n”的PatternLayout将输出类似于:

第一个字段是自程序启动以来经过的毫秒数。 第二个字段是发出日志请求的线程。 第三个字段是日志语句的级别。 第四个字段是与日志请求关联的记录器的名称。 ’ - '后面的文本是语句的消息。

同样重要的是,log4j将根据用户指定的标准呈现日志消息的内容。 例如,如果您经常需要记录当前项目中使用的对象类型Oranges,那么您可以注册OrangeRenderer,只要需要记录橙色,就会调用该对象。

对象呈现遵循类层次结构。 例如,假设橙子是水果,如果您注册FruitRenderer,所有水果包括橙子将由FruitRenderer呈现,除非您注册橙色特定的OrangeRenderer。

对象渲染器必须实现ObjectRenderer接口。

布局的格式化字符介绍和布局的实现类介绍

org.apache.log4j.PatternLayout

在这里插入图片描述org.apache.log4j.PatternLayout
我们要自定义输出日志的格式,那么就使用此类,PatternLayout有两个默认的输出格式属性,可以在api中具体查看
在这里插入图片描述他们的默认内容分别是

TTCC_CONVERSION_PATTERN : %r [%t] %p %c %x - %m%n
DEFAULT_CONVERSION_PATTERN : %m%n

还有自定义输出格式的属性
String conversionPattern
一下是在PatternLayout具体的定义输出格式的字符,在

  1. %m 输出代码中指定的消息;
  2. %M 输出打印该条日志的方法名;
  3. %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL;
  4. %r 输出自应用启动到输出该log信息耗费的毫秒数;
  5. %C 输出所属的类目,通常就是所在类的全名;
  6. %c 用于输出日志事件的类别
  7. %t 输出产生该日志事件的线程名;
  8. %n 输出一个回车换行符,Windows平台为"rn”,Unix平台为"n”;
  9. %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2002-10-18 22:10:28,921;
    但是log4j并不推荐是是simpleDateFormate的语法进行格式化,性能差,推荐使用对应的字符串调用其自定义的输出日期格式
    a. 可以使用字符串“ABSOLUTE”、“DATE”和“ISO8601”中的一个指定这些参数,分别指定AbsoluteTimeDateFormatDateTimeDateFormatISO8601DateFormat
    1. %d{ABSOLUTE} 22:23:30,117  简单的时分秒
    2. %d{DATE} 12 Oct 2005 22:23:30,117  英文的年月日时分秒
    3. %d{ISO8601} 2005-10-12 22:23:30,117  中文的年月日时分秒
    4. %d{yyyy/MM/dd HH:mm:ss,SSS} 2005/10/12 22:23:30,117
  10. %l 用于输出生成日志事件的调用者的位置信息。包括类目名、发生的线程,以及在代码中的行数
  11. %L 输出日志事件的发生位置,及在代码中的行数;
  12. [QC]是log信息的开头,可以为任意字符,一般为项目简称(当然这个相当于在日志中的)。
  13. %% 输出一个%
  14. %x 按NDC(Nested Diagnostic Context,线程堆栈)顺序输出日志。用于输出与生成日志事件的线程关联的NDC(嵌套诊断上下文)。
  15. %X 按MDC(Mapped Diagnostic Context,线程映射表)输出日志。通常用于多个客户端连接同一台服务器,方便服务器区分是那个客户端访问留下来的日志。X转换字符后面必须跟着大括号之间的映射的键,如%X{clientNumber}中clientNumber是键。与键对应的MDC中的值将被输出。
  16. %r 用于输出从布局的构造(创建,一般程序启动时就会初始化布局。)到日志事件的创建(输出这条日志)所经过的毫秒数。

详情请参考官方api;
诗人不写诗的博客

默认情况下,相关信息按原样输出。但是,有了格式修改器,可以更改最小字段宽度、最大字段宽度和对齐方式。

下表中像是了可以截取长度,控制显示字符

格式化修改器 左对齐 最小宽度 最大宽度 评论
%20c false 20 none 如果类别名称长度小于20个字符,则用空格填充(左边)。
%-20c true 20 none 如果类别名称长度小于20个字符,则用空格填充。(右边)
%.30c NA none 30 如果类别名称长于30个字符,则从开始处截断
%20.30c false 20 30 如果类别名称小于20个字符,则左侧用空格填充。但是,如果类别名称长于30个字符,则从头开始截断。
%-20.30c true 20 30 如果类别名称小于20个字符,则右侧空格填充。但是,如果类别名称长于30个字符,则从头开始截断。

其他
当然还有诸如%C{1}就是输出简单类名(原先是完全限定类名)
还有%c{2},用于输出日志事件类别,假定类别名称为a.b.c ,这里输出b.c
以上禁用%c举例,可以自行参考用于其他的格式化符号上

其他的布局(layout)
  1. org.apache.log4j.helpers.DateLayout这个抽象布局负责所有与日期相关的选项和格式化工作。
  2. SimpleLayout :仅仅打印日志的级别和具体的输出日志语句。例: DEBUG - Hello world
  3. EnhancedPatternLayout : 是PatternLayout的增强类,修复了PatternLayout的不同步和其他问题,和PatternLayout并没有继承关系。log4j推荐使用这个代替PatternLayout
  4. HTMLLayout:此布局在HTML表中输出事件。使用这种布局的appender应该将其编码设置为UTF-8或UTF-16,否则包含非ASCII字符的事件可能导致损坏的日志文件。
  5. XMLLayout:XMLLayout将日志消息打印成XML文件格式,打印出的XML文件不是一个完整的XML文件,它可以外部实体引入到一个格式正确的XML文件中。如XML文件的输出名为abc,则可以通过以下方式引入:
<?xml version="1.0" ?>
  <!DOCTYPE log4j:eventSet PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd" [<!ENTITY data SYSTEM "abc">]>
  <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/">
          &data;
</log4j:eventSet>

LinkinPark的博客提供的源码分析
林兴洋的博客

log4j的配置

log4j虽然可以通过编程的方式完全配置,但是官方是推荐通过配置文件配置的,这会是log4j更灵活。

在代码中直接配置简介

import com.foo.Bar;

 // Import log4j classes.
 import org.apache.log4j.Logger;
 import org.apache.log4j.BasicConfigurator;

 public class MyApp {
	//定义一个静态的logger变量,使用类的全限定名称创建
   static Logger logger = Logger.getLogger(MyApp.class);

   public static void main(String[] args) {

     // 设置一个简单的格式输出日志在控制台,下图中有具体介绍
     BasicConfigurator.configure();

     logger.info("Entering application.");
     Bar bar = new Bar();
     bar.doIt();
     logger.info("Exiting application.");
   }
 }

为根记录器添加追加器和布局
在这里插入图片描述上下两个类是存在继承关系的

package com.foo;
 import org.apache.log4j.Logger;

 public class Bar {
   static Logger logger = Logger.getLogger(Bar.class.getName());

   public void doIt() {
     logger.debug("Did it again!");
   }
 }

输出结果

   [main] INFO  MyApp  - Entering application.
   [main] DEBUG com.foo.Bar  - Did it again!
   [main] INFO  MyApp  - Exiting application.

一下为输出结果解析,category是logger的父类,1.2版本之前使用,理解为记录器即可。
在log4j中,子记录器仅链接到它们现有的祖先。 特别是,名为com.foo.Bar的记录器直接链接到根记录器,从而绕过未使用的com或com.foo记录器。 这显着提高了性能并减少了log4j的内存占用。(子记录器可以不使用全限定类名直接连接到根记录器
在这里插入图片描述

在配置文件中配置

import com.foo.Bar;

 import org.apache.log4j.Logger;
 import org.apache.log4j.PropertyConfigurator;

 public class MyApp {

   static Logger logger = Logger.getLogger(MyApp.class.getName());

   public static void main(String[] args) {
     // 通过输入设置main方法的启动参数获取配置文件的位置
     //和上文代码配置的 BasicConfigurator.configure();相同作用
     PropertyConfigurator.configure(args[0]);

     logger.info("Entering application.");
     Bar bar = new Bar();
     bar.doIt();
     logger.info("Exiting application.");
   }
 }

在这里插入图片描述

# 设置根记录器打印日志的级别,和其唯一的追加器A1
log4j.rootLogger=DEBUG, A1

# 将A1设置成为控制台追加器
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# 设置A1的布局(输出的格式)
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

那么以上代码的输出和代码配置日志的输出结果是相同的

当然也可以更改一下来控制指定的包下的日志的输出级别。

log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

#  ISO 8601 的格式输出日期
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n

#  打印日志WARN级别及其上的日志,在 com.foo.和其子记录器中。但是在子记录器没有定义其他
#  级别
log4j.logger.com.foo=WARN

输出结果

2000-09-07 14:07:41,508 [main] INFO  MyApp - Entering application.
2000-09-07 14:07:41,529 [main] INFO  MyApp - Exiting application.

输出结果解析由于在bar类中没有设置日期的输出级别,那么从其父节点com.foo继承,所以doIt()方法中的debug级别的日志不在输出。(上文中有原理解析)

多个追加器的配置文件介绍
请理解上文中一个追加器的情况

log4j.rootLogger=debug,stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 输出调用方的名称和所在行号
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
# 继承自org.apache.log4j.FileAppender,输出到文件,但是会在日志文件达到一定大小是备
# 份他们
log4j.appender.R=org.apache.log4j.RollingFileAppender
# 输出的文件名称
log4j.appender.R.File=example.log
log4j.appender.R.MaxFileSize=100KB
# 保存一个备份文件
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

控制台的输出结果

 INFO [main] (MyApp2.java:12) - Entering application.
 DEBUG [main] (Bar.java:8) - Doing it again!
 INFO [main] (MyApp2.java:15) - Exiting application.

此外,由于根记录器已分配了第二个appender,因此输出也将定向到example.log文件。 当文件达到100KB时,该文件将被翻转。 发生翻转时,旧版本的example.log会自动移动到example.log.1。

log4j的默认初始化过程

log4j 的初始化 : https://blog.csdn.net/iteye_11587/article/details/82674767
log4j库不对其环境做任何假设。 特别是,没有默认的log4j appender。 但是,在某些明确定义的情况下,Logger类的静态inializer将尝试自动配置log4j。 Java语言保证在将类加载到内存期间,类的静态初始化程序只调用一次。 重要的是要记住,不同的类加载器可能会加载同一类的不同副本。 JVM认为同一类的这些副本完全不相关(就是说jvm会在类加载时调用logger和其子类的静态代码块进行初始化)。

log4j的默认初始化在应用程序中的的切入点取决于运行时环境是很有作用的。
例如,同一个应用程序可以用作独立应用程序,小应用程序(applet)或 作为web服务器的一项服务(servlet )。

默认初始化算法定义如下:

  1. 将log4j.defaultInitOverride系统属性设置为任何其他值,然后“false”将导致log4j跳过默认初始化过程(此过程)。

  2. 将资源(resource)字符串变量(文件或包路径)设置为log4j.configuration系统属性的值。 指定默认初始化文件的首选方法是通过log4j.configuration系统属性。 如果未定义系统属性log4j.configuration,则将字符串变量资源设置为其默认值“log4j.properties”

  3. log4j会尝试将资源变量转换成资源路径(文件路径)(url)。

  4. 如果资源变量无法转换为URL(例如由于MalformedURLException(不符合条件的路劲异常)),则通过调用返回URL的org.apache.log4j.helpers.Loader.getResource(resource,Logger.class)从类路径中搜索资源。 。 注意字符串“log4j.properties”不是一个正确格式的URL。
    有关搜索位置的列表,请参阅Loader.getResource(java.lang.String)。

  5. 如果找不到URL,则中止默认初始化。 否则,请从URL配置log4j。

  6. PropertyConfigurator将用于解析URL以配置log4j,除非URL以“.xml”扩展名结尾,在这种情况下将使用DOMConfigurator。

  7. 您可以选择指定自定义配置程序, 您指定的自定义配置程序必须实现Configurator接口。

主要是一些初始化的方式,不太重要

Tomcat下的默认初始化

  1. 在Tomcat 中,你将log4j.properties文件放置在web应用程序的WEB-INF/classes目录下。log4j就会找到它并进行初始化。

  2. 您还可以选择设置系统属性log4j。在启动Tomcat之前进行配置。对于Tomcat 3。使用TOMCAT_OPTS环境变量设置命令行选项。对于Tomcat 4.0,设置CATALINA_OPTS环境变量,而不是TOMCAT_OPTS。
    例1

Unix shell命令

export TOMCAT_OPTS =“ - Dlog4j.configuration = foobar.txt”

告诉log4j使用文件foobar.txt作为默认配置文件。 此文件应位于Web应用程序的WEB-INF / classes目录下。 将使用PropertyConfigurator读取该文件。 每个Web应用程序将使用不同的默认配置文件,因为每个文件都与Web应用程序相关。

例2

Unix shell命令

export TOMCAT_OPTS =“ - Dlog4j.debug -Dlog4j.configuration = foobar.xml”

告诉log4j输出log4j内部调试信息,并使用文件foobar.xml作为默认配置文件。 此文件应位于Web应用程序的WEB-INF / classes目录下。 由于文件以.xml扩展名结尾,因此将使用DOMConfigurator进行读取。 每个Web应用程序将使用不同的默认配置文件,因为每个文件都与Web应用程序相关。

例3

Windows shell命令

设置TOMCAT_OPTS = -Dlog4j.configuration = foobar.lcf -Dlog4j.configuratorClass = com.foo.BarConfigurator

告诉log4j使用文件foobar.lcf作为默认配置文件。 此文件应位于Web应用程序的WEB-INF / classes目录下。 由于log4j.configuratorClass系统属性的定义,将使用com.foo.BarConfigurator自定义配置程序读取该文件。 每个Web应用程序将使用不同的默认配置文件,因为每个文件都与Web应用程序相关。

例4

Windows shell命令

设置TOMCAT_OPTS = -Dlog4j.configuration = file:/ c:/foobar.lcf

告诉log4j使用文件c:\ foobar.lcf作为默认配置文件。 配置文件由URL文件完全指定:/ c:/foobar.lcf。 因此,相同的配置文件将用于所有Web应用程序。

不同的Web应用程序将通过各自的类加载器加载log4j类。 因此,log4j环境的每个映像将独立地执行并且没有任何相互同步。 例如,FileAppenders在多个Web应用程序配置中以完全相同的方式定义将全部尝试写入相同的文件。 结果可能不太令人满意。 您必须确保不同Web应用程序的log4j配置不使用相同的基础系统资源。

初始化servlet

在servlet中初始化log4j

package com.foo;

import org.apache.log4j.PropertyConfigurator;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.IOException;

public class Log4jInit extends HttpServlet {

  public void init() {
	//从web.xml中获取文件位置  
    String prefix =  getServletContext().getRealPath("/");
    String file = getInitParameter("log4j-init-file");
    // if the log4j-init-file is not set, then no point in trying
    if(file != null) {
      PropertyConfigurator.configure(prefix+file);
    }
  }

  public void doGet(HttpServletRequest req, HttpServletResponse res) {
  }
}

web.xml 中 的配置

<servlet>
    <servlet-name>log4j-init</servlet-name>
    <servlet-class>com.foo.Log4jInit</servlet-class>

    <init-param>
      <param-name>log4j-init-file</param-name>
      <param-value>WEB-INF/classes/log4j.lcf</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
  </servlet>

在服务器启动是会加载sevlet,调用其init()方法,实现了log4j的初始化。

log4j性能

经常引用的反对日志记录的论据之一是它的计算成本。 这是一个合理的问题,即使是中等规模的应用程序也可以生成数千个日志请求。 花在测量和调整日志记录性能上的工作量很大。 Log4j声称快速而灵活:速度第一,灵活性第二。

用户应该了解以下性能问题。

 关闭日志记录时的记录性能。

 当完全关闭日志记录或仅针对一组级别关闭日志记录时,日志请求的开销包括方法调用和整数比较。 在233 MHz Pentium II机器上,此成本通常在5到50纳秒范围内。

 但是,方法调用涉及参数构造的“隐藏”成本。

 例如,对于一些记录器猫,写作,

      logger.debug(“条目号:”+ i +“是”+ String.valueOf(entry [i]));
 导致构造消息参数的成本,即将整数i和entry [i]都转换为String,并连接中间字符串,无论消息是否被记录。 参数构造的成本可能非常高,并且取决于所涉及的参数的大小。

 为避免参数构造成本写入:

       if(logger.isDebugEnabled(){

         logger.debug(“条目号:”+ i +“是”+ String.valueOf(entry [i]));

       }
       
 如果禁用调试,这将不会产生参数构造的成本。 另一方面,如果记录器是启用调试的,则会产生两倍于评估记录器是否启用的成本:一次在debugEnabled中,一次在调试中。 这是一个无关紧要的开销,因为评估记录器需要大约1%的实际日志记录时间。

 在log4j中,对Logger类的实例进行日志记录请求。 Logger是一个类而不是接口。 这显着降低了方法调用的成本,但代价是具有一定的灵活性。

 某些用户使用预处理或编译时技术来编译所有日志语句。 这导致了与日志记录相关的完美性能效率。 但是,由于生成的应用程序二进制文件不包含任何日志语句,因此无法为该二进制文件打开日志记录。 在我看来,这是一个不成比例的代价,以换取小的业绩增长。

 打开日志时决定是否记录日志的性能。

 这实际上是走逻辑器层次结构的性能。 打开日志记录时,log4j仍需要将日志请求的级别与请求记录器的级别进行比较。 但是,记录器可能没有指定的级别; 他们可以从记录器层次结构继承它们。 因此,在继承级别之前,记录器可能需要搜索其祖先。

 为了使这种层次结构尽可能快地进行,已经进行了认真的努力。 例如,子记录器仅链接到其现有的祖先。 在前面显示的BasicConfigurator示例中,名为com.foo.Bar的记录器直接链接到根记录器,从而绕过了不存在的com或com.foo记录器。 这显着提高了步行的速度,尤其是在“稀疏”层次结构中。

 步行层次结构的典型成本通常比完全关闭日志记录慢3倍。

 实际输出日志消息

 这是格式化日志输出并将其发送到目标目标的成本。 在这里,我们再次努力使布局(格式化程序)尽可能快地执行。 appender也是如此。 实际记录的典型成本约为100到300微秒。

 有关实际数据,请参阅org.apache.log4.performance.Logging。

虽然log4j有很多功能,但它的第一个设计目标是速度。 一些log4j组件已被重写多次以提高性能。 然而,贡献者经常提出新的优化。 您应该很高兴知道,当使用SimpleLayout进行配置时,性能测试显示log4j的记录速度与System.out.println一样快。

log4j的总结

  Log4j是一个用Java编写的流行的日志包。 它的一个显着特征是记录器中的继承概念。 使用记录器层次结构,可以控制以任意粒度输出哪些日志语句。 这有助于减少记录的输出量并最大限度地降低日志记录的成本。

  log4j API的一个优点是可管理性。== 将日志语句插入代码后,可以使用配置文件对其进行控制。== 可以选择性地启用或禁用它们,并以用户选择的格式将它们发送到不同的和多个输出目标。 log4j包的设计使得日志语句可以保留在已发布的代码中,而不会产生很高的性能成本。

下一篇:log4j实践


  1. 下一篇:log4j实战 ↩︎

猜你喜欢

转载自blog.csdn.net/qq_40182873/article/details/86525547