On this page
Usage
静态 Logger 与非静态 Logger
与 Java 中的任何变量一样,Logger 可以声明为静态变量或类成员变量。但是,在选择将 Logger 声明为静态与非静态时,需要考虑一些因素。通常,最好将 Loggers 声明为静态。
当使用默认的 ContextSelector ClassLoaderContextSelector时,实例化一个新的 Logger 是相当昂贵的操作。创建 Logger 时,ClassLoaderContextSelector 将为与 Logger 关联的类找到 ClassLoader,并将 Logger 添加到与该 ClassLoader 关联的 LoggerContext 中。
创建 Logger 后,在删除与之关联的 LoggerContext 之前,不会将其删除。通常,仅在应用程序关闭或未部署时才会发生这种情况。对具有相同 Logger 名称的 getLogger 的每次调用都将返回相同的 Logger 实例。因此,静态或非静态 Logger 之间的差异很小。
静态和非静态 Logger 之间没有行为上的区别。两者都将在创建时分配给 Logger 名称,通常是它们所关联的类的名称。有关更多信息,请参见下面有关 Logger 名称与类名称的讨论以及示例。
记录 Logger 名称与类名称
创建 Logger 时,将指定 Logger 的 Logger 名称。调用日志方法时,日志事件中的类名称值将反映调用该日志方法的类的名称,该名称不必与创建 Logger 的类相同。以下示例对此进行了说明。
Base Class 创建一个静态 Logger 和一个初始化为该 Logger 的 logger 变量。它具有一种执行日志记录的方法,但提供了对两个 Logger 的访问权,一个是静态的,一个可以被覆盖。
package org.apache.logging;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
/**
*
*/
public abstract class Parent {
// The name of this Logger will be "org.apache.logging.Parent"
protected static final Logger parentLogger = LogManager.getLogger();
private Logger logger = parentLogger;
protected Logger getLogger() {
return logger;
}
protected void setLogger(Logger logger) {
this.logger = logger;
}
public void log(Marker marker) {
logger.debug(marker,"Parent log message");
}
}
此类扩展了基础类。它提供了自己的 Logger,并具有三种方法,一种使用此类中的 Logger,一种使用 Base Class 中的静态 Logger,以及一种可以将 Logger 设置为父项或子项的方法。
package org.apache.logging;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
/**
*
*/
public class Child extends Parent {
// The name of this Logge will be "org.apache.logging.Child"
public Logger childLogger = LogManager.getLogger();
public void childLog(Marker marker) {
childLogger.debug(marker,"Child logger message");
}
public void logFromChild(Marker marker) {
getLogger().debug(marker,"Log message from Child");
}
public void parentLog(Marker marker) {
parentLogger.debug(marker,"Parent logger, message from Child");
}
}
该应用程序将练习所有记录方法四次。Base Class 中的前两次 Logger 设置为静态 Logger。第二次将 Base Class 中的 Logger 设置为使用子类中的 Logger。在每个方法的第一次和第三次调用中,将传递一个空标记。在第二个和第四个中,传递了一个名为“ CLASS”的标记。
package org.apache.logging;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
public class App {
public static void main( String[] args ) {
Marker marker = MarkerManager.getMarker("CLASS");
Child child = new Child();
System.out.println("------- Parent Logger ----------");
child.log(null);
child.log(marker);
child.logFromChild(null);
child.logFromChild(marker);
child.parentLog(null);
child.parentLog(marker);
child.setLogger(child.childLogger);
System.out.println("------- Parent Logger set to Child Logger ----------");
child.log(null);
child.log(marker);
child.logFromChild(null);
child.logFromChild(marker);
}
}
该配置利用 Log4j 的功能,可以基于日志事件的属性选择模式。在这种情况下,当使用 CLASS 标记时使用%C,即类名模式,当不使用 CLASS 标记时,使用%c,即 Logger 名。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout>
<MarkerPatternSelector defaultPattern="%sn. %msg: Logger=%logger%n">
<PatternMatch key="CLASS" pattern="%sn. %msg: Class=%class%n"/>
</MarkerPatternSelector>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="TRACE">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
以下输出说明了在模式中使用 Logger 名称和 Class 名称之间的区别。所有奇数编号的项目均打印 Logger 的名称(%c),而所有偶数编号的项目均打印调用日志记录方法的类的名称(%C)。下表中结果描述中的数字与输出中显示的相应数字匹配。
使用带有 Logger 名称模式的静态 Logger 在父类中执行记录。Logger 名称与父类的名称匹配。
使用具有类名模式的静态 Logger 在父类中执行记录。尽管该方法是针对 Child 实例调用的,但它是在 Parent 中实现的,因此出现了。
使用父级中的 Logger 在“子级”中执行记录,因此将父级名称打印为 Logger 名称。
使用父级中的 Logger 在“子级”中执行记录。由于调用日志记录方法的方法位于 Child 中,因此它是显示的类名。
使用父级中的静态 Logger 在“子级”中执行记录,因此将父级名称打印为 Logger 名称。
使用父级中的静态 Logger 在“子级”中执行记录。由于调用日志记录方法的方法位于 Child 中,因此它是显示的类名。
使用 Child 的 Logger 在父类中执行记录。Logger 名称与孩子的姓名匹配,因此将其打印出来。
使用 Child 的 Logger 在父类中执行记录。尽管该方法是针对 Child 实例调用的,但它是在 Parent 中实现的,因此显示为类名。
使用设置为子 Logger 的父级中的 Logger 在“子项”中执行记录,因此将子名称作为 Logger 名称打印。
使用设置在子 Logger 中的父 Logger 在“子记录”中执行记录。由于调用日志记录方法的方法位于 Child 中,因此它是显示的类名。
------- Parent Logger ----------
1. Parent log message: Logger=org.apache.logging.Parent
2. Parent log message: Class=org.apache.logging.Parent
3. Log message from Child: Logger=org.apache.logging.Parent
4. Log message from Child: Class=org.apache.logging.Child
5. Parent logger, message from Child: Logger=org.apache.logging.Parent
6. Parent logger, message from Child: Class=org.apache.logging.Child
------- Parent Logger set to Child Logger ----------
7. Parent log message: Logger=org.apache.logging.Child
8. Parent log message: Class=org.apache.logging.Parent
9. Log message from Child: Logger=org.apache.logging.Child
10. Log message from Child: Class=org.apache.logging.Child
在上面的示例中,声明了两个 Logger。一种是静态的,一种是非静态的。查看结果时,很明显,无论 Logger 如何声明,结果都是完全相同的。Logger 的名称将始终源自创建 Logger 的类,并且每个日志事件中的类名称将始终反映调用记录方法的类。
应该注意的是,打印位置信息(类名,方法名和行号)会大大降低性能。如果方法名称和行号不重要,通常最好确保每个类都有自己的 Logger,以便 Logger 名称准确反映执行记录的类。