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>