How to set up separate logging threads for log4j?

Say I have a class like this:

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); } } 

Now, when it comes to printing this information, I want to be able to turn on and off various types of information (parameters, results, time).

The problem with using registration levels for differentiation is that finer levels of detail also cover coarser levels.

How can I easily configure this?

+5
source share
3 answers

You set different logging rules by creating custom logging classes.

Here is my solution:

 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); } } 

Setup in 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 

When using these recorders:

To use these registrars:

  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(); } } 

Log output:

 12:27:29 DEBUG LoggerTwo:18 - class maventestspace.MyMain :log 2 12:27:29 DEBUG LoggerThree:18 - class maventestspace.MyMain :Foo time taken:1 

Note that LoggerOne does not print because it is set to ERROR in the properties file.

To redirect these individual log files, you need to configure new applications and loggers.

 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 
+2
source

I think that John Ament meant that the name of the registrar (or category, as it is sometimes called) can be freely chosen. Call

 LoggerFactory.getLogger(MyClass.class) 

- it's just a convenience to call

 LoggerFactory.getLogger(MyClass.class.getName()) 

There are no requirements in the logging structure that you call your registrars according to the full name of the class. This is just the convention supported by the first getLogger overload above.

So, instead of three different Logger implementations with the same name as in your example:

  private AbstractLogger l1= new LoggerOne(this.getClass()); private AbstractLogger l2= new LoggerTwo(this.getClass()); private AbstractLogger l3= new LoggerThree(this.getClass()); 

You can simply use the standard Logger implementation with three different names:

 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); } } 

Since log4j loggers are hierarchical, you can manage them together or individually as needed. Therefore, if you want to include all of them:

 log4j.logger.org.myproject.MyClass=DEBUG, stdout 

If later you need to disable the results:

 log4j.logger.org.myproject.MyClass=DEBUG, stdout log4j.logger.org.myproject.MyClass.results=OFF 

In the same way, you can send output to different destinations, if necessary.

Using Markers

All of the above was written using only the basic functions available in any implementation of SLF4J. If you use Log4j 2 or want to switch to logback, you can use tokens instead to achieve the same, but globally. Thus, instead of having multiple loggers in a class, you can have multiple tokens, for example:

 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); } } 

This will allow you to switch logging of parameters, results and duration globally using Log4j 2.0 MarkerFilter or logback MarkerFilter .

Configuration in Log4j 2.0

Log4j 2.0 gives you great flexibility in using MarkerFilter:

  • You can apply it as a context filter and thus disable all duration logging, for example.
  • You can apply it to the org.myproject.MyClass log to disable result logging (for example) for this particular class.
  • You can apply it to a specific application and, thus, register the parameters in a separate file from the results log or similar.

Log Configuration

In logback, the story is more complicated, depending on what you want to achieve. To disable the entire log of a given marker around the world, simply use MarkerFilter. This is TurboFilter, so it applies to the entire logging context. If you want to register different markers to separate sources, you can use SiftingAppender and write marker-based discriminator by expanding AbstractDiscriminator. Since logback does not support filters directly on loggers, if you need to configure output for each class for each marker, for example, turn off logging of results for MyClass, but not enabling it for other classes, you should use cluster markers instead of global ones.

Here is an example marker-based discriminator implementation for use with 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(); } } 

This implementation is heavily inspired by the standard ContextBasedDiscriminator . You would use MarkerBasedDiscriminator as follows:

 <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
source

It seems that the most straightforward solution is to use different categories for what you are registering. Using this as an example

 String baseCategory = MyClass.class.getName(); String params = baseCategory+".params"; String duration = baseCategory+".duration"; 

You would simply pass these names in the form of factory logs.

+1
source

All Articles