我认为 John Ament 的意思是,记录器名称(或类别,有时也被称为)可以自由选择。电话
LoggerFactory.getLogger(MyClass.class)
主要只是为了方便打电话
LoggerFactory.getLogger(MyClass.class.getName())
日志记录框架不要求您根据类的全名命名记录器。这只是上面第一个 getLogger 重载支持的约定。
因此,不要使用与示例中相同名称的三个不同 Logger 实现:
private AbstractLogger l1= new LoggerOne(this.getClass());
private AbstractLogger l2= new LoggerTwo(this.getClass());
private AbstractLogger l3= new LoggerThree(this.getClass());
您可以简单地使用具有 3 个不同名称的标准 Logger 实现:
public class MyClass
{
private static final String loggerBaseName = MyClass.class.getName();
private final Logger paramsLogger = LoggerFactory.getLogger(loggerBaseName + ".params");
private final Logger resultsLogger = LoggerFactory.getLogger(loggerBaseName + ".results");
private final Logger durationLogger = LoggerFactory.getLogger(loggerBaseName + ".duration");
public void foo(Params p)
{
paramsLogger.info("Foo params: {}", p);
long t1 = System.currentTimeMillis();
Result r = someMethod(p);
long t2 = System.currentTimeMillis();
resultsLogger.info("Foo result: {}", r)
durationLogger.info("Foo time taken: {}", (t2-t1)/1000);
}
}
由于 log4j 记录器是分层的,因此您可以根据需要一起或单独控制它们。因此,如果您想启用所有这些:
log4j.logger.org.myproject.MyClass=DEBUG, stdout
如果您稍后需要关闭结果:
log4j.logger.org.myproject.MyClass=DEBUG, stdout
log4j.logger.org.myproject.MyClass.results=OFF
如果需要,您可以以同样的方式将输出发送到不同的目的地。
使用标记
以上所有内容都是仅使用任何 SLF4J 实现中可用的基本功能编写的。如果您正在使用 Log4j 2 或者愿意切换到 logback,您可以使用markers实现同样的目标,但是是在全球范围内。因此,您可以拥有多个标记,而不是在类中拥有多个记录器,如下所示:
public class GlobalMarkers
{
public static final Marker PARAMS = MarkerFactory.getMarker("PARAMS");
public static final Marker RESULTS = MarkerFactory.getMarker("RESULTS");
public static final Marker DURATION = MarkerFactory.getMarker("DURATION");
}
public class MyClass
{
private Logger logger = LoggerFactory.getLogger(MyClass.class);
public void foo(Params p)
{
logger.info(GlobalMarkers.PARAMS, "Foo params: {}", p);
long t1 = System.currentTimeMillis();
Result r = someMethod(p);
long t2 = System.currentTimeMillis();
logger.info(GlobalMarkers.RESULTS, "Foo result: {}", r)
logger.info(GlobalMarkers.DURATION, "Foo time taken: {}", (t2-t1)/1000);
}
}
这将允许您通过使用全局切换参数、结果和持续时间的记录Log4j 2.0 标记过滤器 or logback标记过滤器.
Log4j 2.0中的配置
Log4j 2.0 在如何使用 MarkerFilter 方面为您提供了很大的灵活性:
- 您可以将其应用为上下文范围的过滤器,从而关闭all例如,记录持续时间。
- 您可以将其应用于 org.myproject.MyClass 记录器,以关闭该特定类的结果记录(例如)。
- 您可以将其应用于特定的附加程序,从而将参数日志记录到结果日志记录或类似文件中的单独文件中。
logback中的配置
在 logback 中,故事更加复杂,具体取决于您希望实现的目标。要全局关闭给定标记的所有日志记录,只需使用 MarkerFilter。这是一个 TurboFilter,因此它适用于整个日志记录上下文。如果您想将不同的标记记录到不同的源,您可以使用筛选Appender并通过扩展 AbstractDiscriminator 编写一个基于标记的鉴别器。由于 logback 不支持直接在记录器上进行过滤,因此如果您需要配置每个类每个标记的输出,例如关闭 MyClass 的结果记录但为其他类保留它,则应该使用特定于类的标记而不是全局标记。
以下是与 SiftingAppender 一起使用的基于标记的鉴别器的示例实现:
public class MarkerBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> {
private static final String KEY = "markerName";
private String defaultValue;
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public String getKey() {
return KEY;
}
public void setKey() {
throw new UnsupportedOperationException("Key not settable. Using " + KEY);
}
public String getDiscriminatingValue(ILoggingEvent e) {
Marker eventMarker = e.getMarker();
if (eventMarker == null)
return defaultValue;
return eventMarker.getName();
}
}
这个实现很大程度上受到了标准的启发基于上下文的鉴别器。您可以像这样使用 MarkerBasedDiscriminator:
<configuration>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator class="org.myproject.MarkerBasedDiscriminator">
<defaultValue>general</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${markerName}" class="ch.qos.logback.core.FileAppender">
<file>${markerName}.log</file>
<append>false</append>
<encoder>
<pattern>%d [%thread] %level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
</sift>
</appender>
<root level="DEBUG">
<appender-ref ref="SIFT" />
</root>
</configuration>