log4j手册

在看hive源码的时候,因为对log4j不了解而苦于不知道该如何打印log来进行调试。虽然最后我选择了直接使用System.err.println来打印

log,但是log4j是一个很好的log框架,在很多多线程系统中都有用到,所以值得花点时间好好学习下。

简介:

log4j是apache的一个开源项目,是一个流行的java log框架,如今已经有了c,c++,python,shell等各语言的相关版本,如使用于shell的log4shell.

打印log是最简单和最直接的程序调试方法。即便如此,打印log有时却是唯一可以用的程序调试方法,尤其是对于大型的多线程或分布式系统。它们很难通过gdb等工具就进行单步调试。

打印log是软件开发过程中非常重要的一环。log提供了程序的相关信息。丰富的log信息有利于对程序的了解与审核。当然log也有它的缺点,冗余的log影响了程序的执行效率,导致满屏的文字信息。正是基于这点的考虑,log4j被设计成可靠,快速而灵活的框架。log4j简单易用,可以方便的控制log的粒度,从而让程序员专注于主程序的业务逻辑。

Loggers, Appenders and Layouts

log4j由3个主要模块组成:loggers,appenders,layouts,开发人员通过这3个模块,控制程序运行时log输出的级别,目的地和格式等。

logger层级

log4j相较于直接使用System.out.println的最主要的一个优势是,它可以方便的控制在运行时哪些log生效而哪些log失效。这种能力是通过把log语句块根据用户定义的category(类别)划分到不同的logging空间来实现的。因此category在log4j中很重要的一个概念。

loggers是一个具名实体。logger的名字是大小写敏感的,遵循层级命名规则。

比如,名字为"com.foo"的logger是名字为"com.foo.Bar"的父亲,这种层级的命名方式对大多开发人员来说应该并不陌生。

root logger处于logger层级的最顶层,它相对其它logger有两个特点,

  1. 1. root logger是必须存在的

  2. 2. 它不能通过名字来获取

root logger只能通过静态方法Logger.getRootLogger来获取。其它的logger通过静态方法Logger.getLogger来获取,并且需要传递名字作为参数。

Logger的基本方法的使用如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}

loggers具有level属性,默认的level集合为:TRACE,DEBUG,INFO,WARN,ERROR and FATAL。level集合在org.apache.log4j.level类中定义。我们可以通过继承level类来定义自己的level,但是我们并不鼓励这样做。

如果一个logger没有指定level属性,那么它将继承离它最近的那个祖先的level。

logging请求通过调用logger实例的打印方法实现,而打印方法决定了打印的level,如,c是一个logger实例,则c.info(...)将响应一条INFO级别的logging请求。logging请求只有在其level大于等于logger本身的level时才会被响应。

这个规则是log4j的核心,它假定level之间是有序的。在标准级别中,DEBUG<INFO<WARN<ERROR<FATAL。

如下代码体现了这一规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// get a logger instance named "com.foo"
   Logger  logger = Logger.getLogger("com.foo");
  
   // Now set its level. Normally you do not need to set the
   // level of a logger programmatically. This is usually done
   // in configuration files.
   logger.setLevel(Level.INFO);
  
   Logger barlogger = Logger.getLogger("com.foo.Bar");
  
   // This request is enabled, because WARN >= INFO.
   logger.warn("Low fuel level.");
  
   // This request is disabled, because DEBUG < INFO.
   logger.debug("Starting search for nearest gas station.");
  
   // The logger instance barlogger, named "com.foo.Bar",
   // will inherit its level from the logger named
   // "com.foo" Thus, the following request is enabled
   // because INFO >= INFO.
   barlogger.info("Located nearest gas station.");
  
   // This request is disabled, because DEBUG < INFO.
   barlogger.debug("Exiting gas station search");

调用Logger.getLoggger方法时如果传入的是同一个名字参数,那么返回的将是同一个logger实例。所以即使不通过指针也可以在不同的代码段中调用同一个logger。与现实中的父子关系不同,log4j logger的创建和配置是没有顺序要求的,也就是说父logger可以后于子孙logger创建。

logger的命名是完全自由的。一种比较好的做法是用所在类的类名来命名logger。这种简单的定义logger的方法非常管用。因为输出的log将携带创建该log的logger的名字信息。因此可以方便的识别出某条log是在哪个类中输出的。

待续。。。。。。

原文地址:https://www.cnblogs.com/simonote/p/3095274.html