log4j学习

xiaoxiao2021-02-27  232

一.Log4J三大组件

1.Logger(记录器):只负责(根据日志级别)记录日志,不负责日志存储位置

2.Appender(存放器):将Logger记录的日志,存放到配置文件中所指向的地方,只处理日志的存放过程.

3.Layout(布局):将日志格式化后输出

一个Logger可配置多个Appender,可同时输出到多个设备上

每个Appender都有一个Layout来格式化输出内容

  

二.Log4J配置

参考

http://blog.csdn.net/azheng270/article/details/2173430/  

配置文件,.分别设置输出到控制台的logger,和输出到文件的logger ### 设置根Logger ### #log4j.rootLogger=[level],appendName1,appendName2,... #level为debug,appendName1为1,appendName2为D,... #根记录器的默认级别是Level.DEBUG #指定三个rootLogger,stdout,D,E log4j.rootLogger = debug,stdout,D,E    ############################################################## ### 输出信息到控制台 ### #Log4j提供的appender包括:ConsoleAppender(控制台),FileAppender(文件),DailyRollingFileAppender(每天产生一个日志文件) #RollingFileAppender(文件大小到达指定尺寸的时候产生一个新文件),WriterAppender(将日志信息以流格式发送到任意指定地方) log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out #layout包括:HTMLLayout(以HTML表格形式布局),PatternLayout(灵活指定布局模式),SimpleLayout(包含日志信息的级别和信息字符串),TTCCLayout(包含日志产生的时间,线程,类别等信息) log4j.appender.stdout.layout = org.apache.log4j.PatternLayout #格式化日志信息 #-x x信息输出时左对齐    #%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL #%d 输出日志时间点的日期,如%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2002-10-18 22:10:28,921 #%l 输出日志事件的发生位置,包括类目名,发生的线程,以及在代码中的行数,输出类似:TestLog.main(TestLog.java:10) #%n 输出一个回车换行符,windows中"\r\n",unix中"\n" #%m 输出代码中指定的消息 #%r 输出自应用启动 至 输出该log信息 所耗费的毫秒数 #%c 输出类的全名 #%t 输出产生该日志事件的线程名 #以下配置输出类似于 #[WARN ] 2017-07-09 21:43:28,765 method:com.zc.zlog.TestLog2.<init>(TestLog2.java:12) #yes ~~~ log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ##############################################################    ### 输出到日志文件 ### #一.输出DEBUG 级别以上的日志到E://logs/log.log中 #名为D的Logger对象的appender为DailyRollingFileAppender类型(每天产生一个日志文件) log4j.appender.D = org.apache.log4j.DailyRollingFileAppender #名为D的Logger对象的输出文件,也可使用相对路径 log4j.appender.D.File = E://logs/log.log log4j.appender.D.Append = true #输出DEBUG级别及以上的日志,Threshold指定日志消息的输出最低层次 log4j.appender.D.Threshold = DEBUG #名为D的Logger对象的layout log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n    ###二.输出ERROR 级别以上的日志到=E://logs/error.log ### log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File =E://logs/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

  

三.程序运行期 动态修改这些日志级别

看看它的代码来找办法吧。

1.本地库:G:\MyMavenRepository\log4j\log4j\1.2.16

源码:D:\workspace-e2\log4j-1.2.16-sources

demo:D:\workspace-e2\MyLog4j

doc:G:\MyMavenRepository\log4j\log4j\1.2.16\log4j-1.2.16-javadoc.jar

2.参照javadoc以及源码大致了解每个包的作用,大致锁定最需要关心的几个包,如下

org.apache.log4j:核心包

3.以 PropertyConfigurator.configure("src\\main\\resources\\log4j.properties");

为入口,大致阅读解析配置的过程

org.apache.log4j.PropertyConfigurator.doConfigure(java.lang.String, org.apache.log4j.spi.LoggerRepository) line: 369     //先使用Properties读取配置文件 public   void doConfigure(String configFileName, LoggerRepository hierarchy) {     Properties props = new Properties();     FileInputStream istream = null;     try {       istream = new FileInputStream(configFileName);       props.load(istream);       istream.close();     } ......    //1.处理Properties文件中的配置信息!!!     // If we reach here, then the config file is alright.     doConfigure(props, hierarchy);   }

PropertyConfigurator.doConfigure()中

//1.给rootLogger设置配置的属性 configureRootCategory(properties, hierarchy); //2. configureLoggerFactory(properties); //3. parseCatsAndRenderers(properties, hierarchy);

4.获取logger实例的过程

public static Logger getLogger(final String name) {    // Delegate the actual manufacturing of the logger to the logger repository.     return getLoggerRepository().getLogger(name); }

5.logger按不同日志级别打日志的大致流程

public void warn(Object message) {     if(repository.isDisabled( Level.WARN_INT))       return; //判断WARN大于等于设置的日志级别     if(Level.WARN.isGreaterOrEqual(this.getEffectiveLevel()))       forcedLog(FQCN, Level.WARN, message, null);   }

......

public int appendLoopOnAppenders(LoggingEvent event) {     int size = 0;     Appender appender;     if(appenderList != null) {       size = appenderList.size();       for(int i = 0; i < size; i++) { //取出不同的appender,如ConsoleAppender,DailyRollingFileAppender等 appender = (Appender) appenderList.elementAt(i); //1.每个appender都要打日志      appender.doAppend(event);       }     }         return size;   }

......

//org.apache.log4j.ConsoleAppender(org.apache.log4j.WriterAppender).subAppend(org.apache.log4j.spi.LoggingEvent) line: 310     protected void subAppend(LoggingEvent event) {     this.qw.write(this.layout.format(event));     if(layout.ignoresThrowable()) {       String[] s = event.getThrowableStrRep();       if (s != null) {      int len = s.length;      for(int i = 0; i < len; i++) {        this.qw.write(s[i]);        this.qw.write(Layout.LINE_SEP);      }       }     }     if(shouldFlush(event)) { //1.flush       this.qw.flush();     }   }

6.查看org.apache.log4j包,发信Level类和日志等级直接相关

父类Priority中定义的部分属性

public final static int OFF_INT = Integer.MAX_VALUE;   public final static int FATAL_INT = 50000;   public final static int ERROR_INT = 40000;   public final static int WARN_INT  = 30000;   public final static int INFO_INT  = 20000;   public final static int DEBUG_INT = 10000;

Level类中定义的Level实例属性

final static public Level OFF = new Level(OFF_INT, "OFF", 0);   final static public Level FATAL = new Level(FATAL_INT, "FATAL", 0);   final static public Level ERROR = new Level(ERROR_INT, "ERROR", 3);   final static public Level WARN  = new Level(WARN_INT, "WARN",  4);   final static public Level INFO  = new Level(INFO_INT, "INFO",  6);

发现可修改日志等级的toLevel方法

public static Level toLevel(int val, Level defaultLevel) {     switch(val) {      case ALL_INT: return ALL;      case DEBUG_INT: return Level.DEBUG;     case INFO_INT: return Level.INFO;      case WARN_INT: return Level.WARN;      case ERROR_INT: return Level.ERROR;      case FATAL_INT: return Level.FATAL;      case OFF_INT: return OFF;      case TRACE_INT: return Level.TRACE;      default: return defaultLevel;     }   }

然而以上发现并不能解决问题

直到使用logger点出它的所有方法

  public void setLevel(Level level) {     this.level = level;   }

没错,我发现

程序运行期 动态修改这些日志级别

实际上就一行代码!

不管了

package com.zc.zlog; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; public class TestLog2 {     private static Logger logger = Logger.getLogger(TestLog2.class);     public static void main(String[] args)     {         PropertyConfigurator.configure("src\\main\\resources\\log4j.properties");         logger.debug("这是改级别前的debug日志");         logger.info("这是改级别前的info日志");         logger.warn("这是改级别前的warn日志");         logger.error("这是改级别前的error日志");         logger.fatal("这是改级别前的fatal日志");         logger.getEffectiveLevel();         logger.getLevel();         logger.getPriority(); //修改日志级别         logger.setLevel(Level.ERROR);         logger.debug("这是改级别后的debug日志");         logger.info("这是改级别后的info日志");         logger.warn("这是改级别后的warn日志");         logger.error("这是改级别后的error日志");         logger.fatal("这是改级别后的fatal日志");     } }

但是修改不了文件中的日志级别,所以当然不可能这么简单.

7.重新整理思路,我现在需要根据不同的Appender设置不同的日志级别,而且应该能够根据配置文件中不同logger名配置不同的日志级别.

那么去Appender类中看看有没有相关方法吧,没有.但是在其子类AppenderSkeleton中存在.

/** Set the threshold level. All log events with lower level than the threshold level are ignored by the appender. <p>In configuration files this option is specified by setting the value of the <b>Threshold</b> option to a level string, such as "DEBUG", "INFO" and so on. @since 0.8.3 */ public void setThreshold(Priority threshold) { this.threshold = threshold; }

至于传入的Priority参数,正好可以传入之前已经发现的Level实例.

 

下一个问题就是如何获取到Appender

联系配置文件中log4j.rootLogger = debug,stdout,D,E和

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

rootLogger和appender之间一定存在某种联系.

最终通过rootLogger获取appender,遍历并调用setThreshold()修改即可.

package com.zc.zlog; import java.util.Enumeration; import org.apache.log4j.Appender; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.jdbc.JDBCAppender; public class TestLog3 { private static Logger logger = Logger.getLogger(TestLog3.class); public static void main(String[] args) { PropertyConfigurator.configure("src\\main\\resources\\log4j.properties"); logger.debug("这是改级别前的debug日志"); logger.info("这是改级别前的info日志"); logger.warn("这是改级别前的warn日志"); logger.error("这是改级别前的error日志"); logger.fatal("这是改级别前的fatal日志"); Logger rootLogger = Logger.getRootLogger(); @SuppressWarnings("rawtypes") Enumeration allCurrAppenders = rootLogger.getAllAppenders(); while (allCurrAppenders.hasMoreElements()) { Appender currAppender = (Appender) allCurrAppenders.nextElement(); //可以获取到配置的appender的name,则可以根据不同的name动态修改不同的日志级别 System.out.println(currAppender.getName()); if (currAppender instanceof AppenderSkeleton) { ((AppenderSkeleton) currAppender).setThreshold(Level.ERROR); } else if (currAppender instanceof JDBCAppender) { ((JDBCAppender) currAppender).setThreshold(Level.ERROR); } } //可修改日志级别,但是修改不了文件中的日志级别 logger.setLevel(Level.ERROR); logger.debug("这是改级别后的debug日志"); logger.info("这是改级别后的info日志"); logger.warn("这是改级别后的warn日志"); logger.error("这是改级别后的error日志"); logger.fatal("这是改级别后的fatal日志"); } }

运行后无论是输出到控制台,或者文件的日志级别都都能够得到更改.

转载请注明原文地址: https://www.6miu.com/read-11269.html

最新回复(0)