一.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日志"); } }运行后无论是输出到控制台,或者文件的日志级别都都能够得到更改.
