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 名称准确反映执行记录的类。