Contents
  1. 1. 定义一个logger
  2. 2. logger的配置 – 决定输出级别
  3. 3. appender的配置 – 决定输出位置
  4. 4. layout的配置 – 决定输出格式
  5. 5. logger工作流程
  6. 6. 一些注意点
  7. 7. Refers

log4j的主要组件如下图:
log4j组件

定义一个logger

private static final Logger logger = LogManager.getLogger(AndroidQueryServiceImpl.class);
LogManager.getLogger会指定logger的名称。一般取类的名称,也可以指定特定的名称,如:
private static final Logger tracerlogger = LogManager.getLogger("tracer-business");
如果取相同的名称,则x,y指向同一个logger对象,如:

1
2
Logger x = LogManager.getLogger("wombat");
Logger y = LogManager.getLogger("wombat");

logger的配置 – 决定输出级别

  • logger的日志级别分为TRACE, DEBUG, INFO, WARN, ERROR, and FATAL。
  • logger的日志级别有继承关系。如果x是y的父类,则y继承x的日志级别,除非重新指定。
  • 继承时会继承关系最近一级指定的级别,如:
Logger name Assigned level Effective level
root DEBUG DEBUG
X INFO INFO
X.Y none INFO
X.Y.Z ERROR ERROR

appender的配置 – 决定输出位置

  • 一个logger可以被输出多次,取决于其appender的配置。
  • 一个logger L的输出状态会被它的appender以及其祖先的appender的状态决定。这被称为“appender additivity”。
  • 因此,如果不想继承其上部分的appender的状态,需将“additivity flag”设置为false。(否则可能会导致相同日志输出好几遍)
  • However, if an ancestor of logger L, say P, has the additivity flag set to false, then L’s output will be directed to all the appenders in L and its ancestors up to and including P but not the appenders in any of the ancestors of P. Loggers have their additivity flag set to true by default.
Logger Name Attached Appenders Additivity Flag Output Targets Comment
root A1 not applicable A1 Since the root logger stands at the top of the logger hierarchy, the additivity flag does not apply to it.
x A-x1, A-x2 true A1, A-x1, A-x2 Appenders of “x” and of root.
x.y none true A1, A-x1, A-x2 Appenders of “x” and of root.
x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1 Appenders of “x.y.z”, “x” and of root.
security A-sec false A-sec 因为设置了false, 只会使用 appender A-sec。
security.access none true A-sec 继承父类,只会使用 appender A-sec,因为其父类设置了false.

layout的配置 – 决定输出格式

PatternLayout可以指定输出日志的格式,如:
%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestURIWithQueryString}]%-5level %logger{20} - %msg%n
各参数的含义详见: conversionWord

logger工作流程

以打印logger.info("The new entry is {}.", entry);为例

  • 先过fliter信息,如Marker, Level, Logger,message等。若通过,则进行下一步。
  • 判断effective logger与当前logger等级的关系。如通过,则进行下一步。
  • 创建loggingEvent对象,包括当前logger的信息,比如当前时间,线程,级别,logger等信息
  • 引用appenders
  • 格式化输出
  • 将loggingEvent输出到对应的appenders
    logger工作流程

一些注意点

  • 当加入多个参数时,如
    logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    不管该日志是否会被打印出来,都会进行参数的拼接。因此为了减小开销,可以用isDebugEnabled()先判断一下
1
2
3
if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
  • 使用{}进行format则可以提升性能。在debug被禁止的情况下,用2比1快30%。(format先判断再替换)

    1
    2
    logger.debug("The new entry is "+entry+".");
    logger.debug("The new entry is {}.", entry);
  • 如果需要多个参数(2个以上),可以使用一个array。如果在后面使用多个元素,其实也是调用的array方法,一样的。
    logger.debug("Value {} was inserted between {} and {}.", paramArray);

  • 落本地盘比较快,落数据库慢

  • 使用量大的循环里尽量不要打log
  • 打印error堆栈信息的时候,可以用logger.error(“error :”, e); 而不要用 logger.error(“error” +e); 否则打印不出堆栈。

logger.error(String msg, Throwable t)自己实现了e.printStackTrace();
和用stringBuilder写for循环相比,应该也不会占用很多资源。(没有实际验证过)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public ThrowableProxy(Throwable throwable) {

this.throwable = throwable;
this.className = throwable.getClass().getName();
this.message = throwable.getMessage();
this.stackTraceElementProxyArray = ThrowableProxyUtil.steArrayToStepArray(throwable
.getStackTrace());

Throwable nested = throwable.getCause();

if (nested != null) {
this.cause = new ThrowableProxy(nested);
this.cause.commonFrames = ThrowableProxyUtil
.findNumberOfCommonFrames(nested.getStackTrace(),
stackTraceElementProxyArray);
}
if(GET_SUPPRESSED_METHOD != null) {
// this will only execute on Java 7
try {
Object obj = GET_SUPPRESSED_METHOD.invoke(throwable);
if(obj instanceof Throwable[]) {
Throwable[] throwableSuppressed = (Throwable[]) obj;
if(throwableSuppressed.length > 0) {
suppressed = new ThrowableProxy[throwableSuppressed.length];
for(int i=0;i<throwableSuppressed.length;i++) {
this.suppressed[i] = new ThrowableProxy(throwableSuppressed[i]);
this.suppressed[i].commonFrames = ThrowableProxyUtil
.findNumberOfCommonFrames(throwableSuppressed[i].getStackTrace(),
stackTraceElementProxyArray);
}
}
}
} catch (IllegalAccessException e) {
// ignore
} catch (InvocationTargetException e) {
// ignore
}
}

}

Refers

logback Manual

log4j 2

logback配置详解

Contents
  1. 1. 定义一个logger
  2. 2. logger的配置 – 决定输出级别
  3. 3. appender的配置 – 决定输出位置
  4. 4. layout的配置 – 决定输出格式
  5. 5. logger工作流程
  6. 6. 一些注意点
  7. 7. Refers