说我有这样的课程:
public class MyClass
{
private Logger log = LoggerFactory.getLogger(MyClass.class); //org.slf4j.LoggerFactory
public void foo(Params p)
{
log.info("Foo params: " + p);
long t1 = System.currentTimeMillis();
Result r = someMethod(p);
long t2 = System.currentTimeMillis();
log.info("Foo result: " + r)
log.info("Foo time taken: + (t2-t1)/1000);
}
}
现在,在打印此信息时,我希望能够打开和关闭不同类型的信息(参数,结果,拍摄时间)。
使用日志记录级别进行区分的问题在于,更精细的粒度级别也包含更粗略的级别。
如何轻松设置?
答案 0 :(得分:2)
通过创建自定义日志记录类来设置不同的日志记录行为。
这是我的解决方案:
public abstract class AbstractLogger {
protected Logger log;
protected Class callingClass;
public AbstractLogger(Class c)
{
this.log = LoggerFactory.getLogger(this.getClass());
this.callingClass = c;
}
public void log(String s)
{
log.debug(this.callingClass + " :" + s);
}
}
public class LoggerOne extends AbstractLogger {
public LoggerOne(Class c) {
super(c);
}
}
public class LoggerTwo extends AbstractLogger {
public LoggerTwo(Class c) {
super(c);
}
}
public class LoggerThree extends AbstractLogger {
public LoggerThree(Class c) {
super(c);
}
}
Log4j.properties中的设置
#Define custom levels by package
#set to ERROR to turn them off
log4j.logger.org.myproject.loggers.LoggerOne=ERROR
log4j.logger.org.myproject.loggers.LoggerTwo=DEBUG
log4j.logger.org.myproject.loggers.LoggerThree=DEBUG
使用这些记录器时:
使用这些记录器:
public class MyMain {
// private Logger log = LoggerFactory.getLogger(MyMain.class);
private AbstractLogger l1= new LoggerOne(this.getClass());
private AbstractLogger l2= new LoggerTwo(this.getClass());
private AbstractLogger l3= new LoggerThree(this.getClass());
public void run()
{
l1.log("log 1");
long t1 = System.currentTimeMillis();
try {
Thread.sleep(1000); //1000 milliseconds is one second.
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
long t2 = System.currentTimeMillis();
l2.log("log 2");
l3.log("Foo time taken:" + (t2-t1)/1000);
}
public static void main(String[] args) {
MyMain me = new MyMain();
me.run();
}
}
日志输出:
12:27:29 DEBUG LoggerTwo:18 - class maventestspace.MyMain :log 2
12:27:29 DEBUG LoggerThree:18 - class maventestspace.MyMain :Foo time taken:1
请注意,LoggerOne不打印,因为它在属性文件中设置为ERROR。
要重定向这些单独的日志文件,您需要设置新的appender和logger。
log4j.logger.org.myproject.loggers.LoggerOne=DEBUG, file1, stdout
log4j.logger.org.myproject.loggers.LoggerTwo=DEBUG, file2, stdout
log4j.logger.org.myproject.loggers.LoggerThree=DEBUG, file3, stdout
# Direct log messages to a log file
log4j.appender.file1=org.apache.log4j.RollingFileAppender
#log4j.appender.TextProcessor.Threshold=debug
log4j.appender.file1.File=E:\\logs\\log1.txt
log4j.appender.file1.MaxFileSize=10MB
log4j.appender.file1.MaxBackupIndex=1
log4j.appender.file1.layout=org.apache.log4j.PatternLayout
log4j.appender.file1.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
# Direct log messages to a log file
log4j.appender.file2=org.apache.log4j.RollingFileAppender
#log4j.appender.TextProcessor.Threshold=debug
log4j.appender.file2.File=E:\\logs\\log2.txt
log4j.appender.file2.MaxFileSize=10MB
log4j.appender.file2.MaxBackupIndex=1
log4j.appender.file2.layout=org.apache.log4j.PatternLayout
log4j.appender.file2.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
# Direct log messages to a log file
log4j.appender.file3=org.apache.log4j.RollingFileAppender
#log4j.appender.TextProcessor.Threshold=debug
log4j.appender.file3.File=E:\\logs\\log3.txt
log4j.appender.file3.MaxFileSize=10MB
log4j.appender.file3.MaxBackupIndex=1
log4j.appender.file3.layout=org.apache.log4j.PatternLayout
log4j.appender.file3.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
答案 1 :(得分:2)
我认为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 MarkerFilter或logback MarkerFilter全局切换参数,结果和持续时间的记录。
Log4j 2.0为您提供了如何使用MarkerFilter的灵活性:
在回归中,故事更复杂,取决于您希望实现的目标。要全局关闭给定标记的所有日志记录,只需使用MarkerFilter即可。这是一个TurboFilter,因此它适用于整个日志记录上下文。如果要将不同的标记记录到不同的源,可以使用SiftingAppender并通过扩展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();
}
}
此实施受标准ContextBasedDiscriminator的启发。您可以像这样使用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>
答案 2 :(得分:1)
听起来最直接的解决方案是利用不同的类别来记录您的日志。以此为例
String baseCategory = MyClass.class.getName();
String params = baseCategory+".params";
String duration = baseCategory+".duration";
您只需将这些名称作为记录器工厂类别传递。