欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

log4j学习

程序员文章站 2022-05-23 19:52:48
...

log4j学习

2017710

1:45

.Log4J三大组件

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

log4j学习

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

log4j学习

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

log4j学习

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

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

  

.Log4J配置

参考

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

  

配置文件,.分别设置输出到控制台的logger,和输出到文件的logger

### 设置根Logger ###

#log4j.rootLogger=[level],appendName1,appendName2,...

#leveldebug,appendName11,appendName2D,...

#根记录器的默认级别是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

#名为DLogger对象的appenderDailyRollingFileAppender类型(每天产生一个日志文件)

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender

#名为DLogger对象的输出文件,也可使用相对路径

log4j.appender.D.File = E://logs/log.log

log4j.appender.D.Append = true

#输出DEBUG级别及以上的日志,Threshold指定日志消息的输出最低层次

log4j.appender.D.Threshold = DEBUG

#名为DLogger对象的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

  

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

api猜 或者 看他的代码 来得出办法

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:核心包

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实例的过程

  

  1.   public
  2.   static
  3.   Logger getLogger(final String name) {
  4.      // Delegate the actual manufacturing of the logger to the logger repository.

        return getLoggerRepository().getLogger(name);
  5.   }

  

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

......

子主题 1

  • org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(org.apache.log4j.spi.LoggingEvent) line: 59  
  • /**

         Call the <code>doAppend</code> method on all attached appenders.  */
  •   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;
  •   }

......

子主题 1

  • 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类和日志等级直接相关

log4j学习

父类Priority中定义的部分属性

public final static int OFF_INT = Integer.MAX_VALUE;

  1.   public final static int FATAL_INT = 50000;
  2.   public final static int ERROR_INT = 40000;
  3.   public final static int WARN_INT  = 30000;
  4.   public final static int INFO_INT  = 20000;
  5.   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点出它的所有方法

  

  1.   /**

         Set the level of this Category. If you are passing any of

         <code>Level.DEBUG</code>, <code>Level.INFO</code>,

         <code>Level.WARN</code>, <code>Level.ERROR</code>,

         <code>Level.FATAL</code> as a parameter, you need to case them as

         Level.

         <p>As in <pre> &nbsp;&nbsp;&nbsp;logger.setLevel((Level) Level.DEBUG); </pre>

         <p>Null values are admitted.  */
  2.   public
  3.   void setLevel(Level level) {
  4.     this.level = level;
  5.   }

没错,我发现

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

api猜 或者 看他的代码 来得出办法

实际上就一行代码!

不管了

  

  • 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日志");
    }
}

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