log4j 学习(一)

       Log4j,很久就开始使用了。

       但是一直都没有花时间去学习一下,最近觉得不能再这样下去了,这样一个最基本的几乎每个项目都要使用的好东西不能只是会使用而不会灵巧的掌握,领会它的精髓,却总是云里雾里的飘。我必须得要好好的学习了一下,并留下点足迹。

       Log4j现在被纳入到apache开源组织的管理范围,我们很容易在apache的官方网站上找到它的最新版本和学习资料。资料里面还有关于log4j的历史介绍,这里就不再累赘了,直接进入学习主题

       Log4j最重要的三个核心组件:

       --- Loggers: 负责消息的记录以及消息级别的控制

       --- Appenders:负责控制消息保存或显示的位置

       --- Layouts:负责显示的格式

       OK,那就让我们一个一个来了解吧,先看下面的Logger类的代码:

package org.apache.log4j;
   
public class Logger { 
 
  // Logger creation & retrieval methods: 
  public static Logger getRootLogger(); 
  public static Logger getLogger(String name); 
     
  // printing methods: 
  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); 
 
  // printing methods for logging exceptions: 
  public void debug(Object message, Throwable t); 
  public void info(Object message, Throwable t); 
  public void warn(Object message, Throwable t); 
  public void error(Object message, Throwable t); 
  public void fatal(Object message, Throwable t); 
     
  // generic printing method: 
  public void log(Level p, Object message); 
} 

 

       从代码里面我们可以看出Logger的几个基本方法,即debug,info,warn,error,fatal,log(),一类是有异常,一类是没有异常。其实最终他们调用的都是log(level,message)这个方法。它这样分开这几个方法是有原因的,这几个方法代表不同的日志等级,而方便我们在记录日志是加以区分,或者是把他们分别放到不同的文件以便跟踪。另外我们可以看到,除了打印日志的方法,还有两个方法是获取实例的,一个是获取根Logger对象的方法getRootLogger,次对象是所有loggers的祖先,另一个是获取普通实例的方法。如何使用呢?让我们来看一个测试Logger的实例:

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

public class Logger4jTest
{
	static Logger logger = Logger.getLogger("mylog");	
	public static void main(String[] args)
	{		
		logger.info("start to logger ...");
	}
}

 

        这里,我们获取了一个名字叫做“mylog”的日志记录对象,如果你有配置好配置文件并加载完成就可以开始记录日志了,够简单吧。但是这里有一个问题,你的日志到底要输出到哪里呢?log4j中称输出地为“appender”,每一个logger对象都至少要有一个appender对象与之对应,否则,你会得到一个log4j的警告:

        log4j:WARN No appenders could be found for logger (chapter1.HelloWorld1).
        log4j:WARN Please initialize the log4j system properly.

 

        有关appender在学习(二)中再介绍,现在我们需要做的是,在打印日志之前加上配置代码,便可以输出日志了:

// 加载,实际上是为rootLogger对象添加一个ConsoleAppender对象,表示输出到控制台
BasicConfigurator.configure();

// 记录日志
logger.info("start to logging ...");

 

       嗯,很不错,我们已经学会log4j最基本的用法了。当我们这样写完代码然后查看打印的日志,在控制台上应该有类似于以下的输出:

0 [main] INFO mylog  - start to logging ...

 

        为什么会这样输出,这里就涉及Layouts了,暂时不多说,解释一下日志的含义:0表示从加载到输出的毫秒数,[main]表示打印日志的线程名,INFO表示输出级别,"start to logging ..."是打印的消息,也就是info的参数。别心急,让我们深入一点。

        开始我们在获取日志对象的时候有传入一个名字的参数,这个名字可以随便取,但是如果你不想让你的日志看起来混乱不堪的话,最好是不要这样做。一个较好的方法是使用包名加类名作为日志对象的名字,这也是log4j文档推荐的方法。像这样:

static Logger logger = Logger.getLogger(Logger4jTest.class);	

        另外还有一个方面,使得我们为什么要这样取名字。这里不得不说一下等级(Level),log4j记录的日志是分等级的,最常用的是debug,info,warn,error这四种,他们的等级由低到高,即:debug<info<warn<error,每一个Logger对象都有一个默认的输出等级,这个输出等级可以在运行时设定,只有在这个等级之上(包含本身)的消息才会被输出。看下面的例子:

// 设置等级为INFO
logger.setLevel(Level.INFO);

// debug<info,所以这个日志不会被输出
logger.debug("start to logger ...");

 

        如果不设定的话它会自动继承来自父亲的这个默认等级。每一个Logger都有它的父级Logger,Logger对象的层级关系就是由名字来规定的。log4j规定,名字中的“.”可以定义Logger的父子关系,举个例子:

// x是所有以"x."开头的Logger类的父Logger
Logger x = Logger.getLogger("x");
		
// xy是所有以"x.y."开头的Logger类的父Logger
Logger xy = Logger.getLogger("x.y");
		
// xyz是所有以"x.y.z."开头的Logger类的父Logger
Logger xyz = Logger.getLogger("x.y.z");

        在上例中,x是xy的父亲,是xyz的爷爷,xy是xyz的父亲,依此类推。那么谁是x的父亲呢?rootLogger。rootLogger是所有Logger对象的祖先。当子类定义了默认Level级别的时候,会使用定义的Level级别来输出日志。如果没有定义,则会继承自它父类的默认输出级别,一直到rootLogger,rootLogger默认的级别为DEBUG。这个有时候是很有用的,比如,我们要控制com.action包下的所有类只输出info级别以上的日志,而com.dao包下所有类的输出日志都为debug,那么,只要这样定义一下就好了:

// action logger
Logger actionLogger = Logger.getLogger("com.action");
actionLogger.setLevel(Level.INFO);

// dao logger
Logger daoLogger = Logger.getLogger("com.dao");
daoLogger.setLevel(Level.DEBUG);

        这就是为什么我们要好好的对Logger取名,为什么Log4j文档主张使用类名作为参数的原因了。

        控制日志的输出级别还有一种方法是设定Threshold属性,注意这个属性是一个全局属性,而且优先于每一个logger对象设定的Level,只要某一个logger对象设定此属性,其他所有的logger对象会先考虑Threshold是否满足,再来考虑自己本身的日志输出级别。举例如下:

BasicConfigurator.configure();
		
// 获取三个日志对象
Logger x = Logger.getLogger("x");		
Logger xy = Logger.getLogger("x.y");		
Logger xyz = Logger.getLogger("x.y.z");
		
// 分别设置输出级别
x.setLevel(Level.DEBUG);
xy.setLevel(Level.INFO);
xyz.setLevel(Level.WARN);		
		
x.debug("x is output debug messages ...");    // 会输出,debug>=debug(x.Level) 
xy.info("xy is output info messages ...");	  // 会输出,info>=info(xy.Level)	
xyz.warn("xyz is output warn messages ...");  // 会输出,warn>=warn(xyz.Level)
		
// 设置x的threadhold为INFO
x.getLoggerRepository().setThreshold(Level.INFO);		
// 不会输出,因为debug<info(threshold),尽管debug>=debug(x.Level)
x.debug("x is output debug messages ...");		
// 会输出,因为满足info>=info(threshold),且info>=info(xy.Level)
xy.info("xy is output info messages ...");		
// 会输出,因为满足warn>=info(threshold),且warn>=warn(xyz.Level)
xyz.warn("xyz is output warn messages ...");
		
// 设置xyz的threadhold为WARN
xyz.getLoggerRepository().setThreshold(Level.WARN);		
// 不会输出,因为debug<warn(threshold),尽管debug>=debug(x.Level)
x.debug("x is output debug messages ...");		
// 不会输出,因为info<warn(threshold),尽管info>=info(xy.Level)
xy.info("xy is output info messages ...");
// 会输出,因为满足warn>=warn(threshold),且warn>=warn(xyz.Level)
xyz.warn("xyz is output warn messages ...");
		
// 设置xy的threadhold为ERROR
xy.getLoggerRepository().setThreshold(Level.ERROR);		
// 以下所有日志都不会被输出了,因为debug,info,warn都比ERROR等级低
x.debug("x is output debug messages ...");		
xy.info("xy is output info messages ...");
xyz.warn("xyz is output warn messages ...");

  

        从上面的例子我们可以很清晰的看到threshold属性所起的作用,这个属性可以通过任意一个Logger对象获取一个LoggerRepository对象来设定,注意我们随便设置某一个logger对象中的threshold属性,对其他的Logger对象也起作用了,所以这个属性是全局的,静态的,编程时一定要注意,最好是不要在程序运行过程中改变。当然,一般的做法是放在配置文件(配置文件以后再说)里面,可以灵活的调节。另外还可以关闭所有日志输出或打开所有输出:

x.getLoggerRepository().setThreshold(Level.OFF); // 关闭输出
x.getLoggerRepository().setThreshold(Level.ALL); // 开启输出

 

        讲了这么多,都是围绕这Loggers,开始也说过,它是log4j中最重要的组建之一,我想,明白上面这些也就差不多了,下面该谈谈Appender对象了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值