I have a requirement wherein I need to create log files such that all the logs based on a tag (Preferably text) need to be logged to corresponding log files.
For example,
I have logs regarding apples, oranges, and Mangoes.
logger.info("Apples: They are red in color");
logger.info("Oranges: They are orange in color");
logger.info("Mangoes: They are yellowish in color");
As per my requirement, the first log should be logged to Apples.log, the second to Oranges.log and the third to Mangoes.log
The log files should be created dynamically.
Below shown is my logback.xml file
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>
%d %-5p - %marker%m%n
</Pattern>
</encoder>
</appender>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<key>fruitName</key>
<defaultValue>Common_logs</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${instanceName}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./Logs/${fruitName}/${instanceName}.log</fileNamePattern>
<maxHistory>50</maxHistory>
<cleanHistoryOnStart>false</cleanHistoryOnStart>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d %-5p - %m%n</pattern>
</encoder>
</appender>
</sift>
</appender>
<logger name="AssetInstanceService" level="info" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="SIFT" />
</logger>
<root level="info">
<appender-ref ref="SIFT" />
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
Given below is my LogManager.java file
package Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
public class LogManager {
static String configFile = "./logback.xml";
private static Logger _logger = LoggerFactory.getLogger(LogManager.class);
private static boolean _isInitialized = false;
private LogManager() {
}
public static Logger Instance() {
if (_logger == null) {
_logger = LoggerFactory.getLogger(LogManager.class);
}
return _logger;
}
public static Logger Instance(String instanceName) {
if (!_isInitialized) {
if (!CommonMethods.CheckFileExist(configFile)) {
configFile = "../logback.xml";
if (!CommonMethods.CheckFileExist(configFile)) {
return null;
}
}
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
lc.reset();
configurator.setContext(lc);
try {
configurator.doConfigure(configFile);
MDC.put("fruitName", instanceName);
} catch (JoranException e) {
System.out.println(e);
}
_isInitialized = true;
}
if (_logger == null) {
_logger = LoggerFactory.getLogger(LogManager.class);
}
return _logger;
}
}
Here my logs are created when the jar file is run as I initialize the logger in my main method.
But my need is to have different log files created with different names based on the tag or id in the log .
SLF4J and Log4j-API both provide Markers to do what you want. In SLF4J you would create your markers with:
Marker apples = MarkerFactory.getMarker("Apples");
Marker oranges = MarkerFactory.getMarker("Oranges");
Marker mangos = MarkerFactory.getMarker("Mangos");
In addition, the markers can have a parent so you could do:
Marker fruit = MarkerFactory.getMarker("Fruit");
Marker apples = MarkerFactory.getMarker("Apples");
apples.add(fruit);
Marker oranges = MarkerFactory.getMarker("Oranges");
apples.add(fruit);
Marker mangos = MarkerFactory.getMarker("Mangos");
apples.add(fruit);
In the configuration you can either check for the specific Marker or if you want to check for all Markers that are a Fruit you would check for that Marker.
You then use the markers in your application as:
logger.info(apples, "They are red in color");
logger.info(oranges, "They are orange in color");
logger.info(mangoes, "They are yellowish in color");
Finally, in your configuration you can use a turbo filter to do:
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
<Marker>Apples</Marker>
<OnMatch>NEUTRAL</OnMatch>
<OnMismatch>DENY</OnMismatch>
</turboFilter>
to filter at a global level or you can use one of the evaluator filters to filter on the Appender. You could also use the SiftingAppender to create Appenders dynamically if you write a custom Discriminator.
Things are a bit easier with the Log4j API. To create a Marker without a parent:
Marker apples = MarkerManager.getMarker("Apples");
Marker oranges = MarkerManager.getMarker("Oranges");
Marker mangos = MarkerManager.getMarker("Mangos");
or with a parent:
Marker fruit = MarkerManager.getMarker("Fruit");
Marker apples = MarkerManager.getMarker("Apples").setParents(fruit);
Marker oranges = MarkerManager.getMarker("Oranges").setParents(fruit);
Marker mangos = MarkerManager.getMarker("Mangos").setParents(fruit);
The code to use them in Log4j is exactly the same:
logger.info(apples, "They are red in color");
logger.info(oranges, "They are orange in color");
logger.info(mangoes, "They are yellowish in color");
The big difference is in the configuration. Log4j only has one kind of Filter and it can be used at the global level (like a turbo filter) or on a Logger, an Appender reference, or on an Appender. In your case you would want to use the MarkerFilter:
<MarkerFilter marker="Apples" onMatch="NEUTRAL" onMismatch="DENY"/>
and add that either to each Appender reference or each Appender.
Similar to the SiftingAppender Log4j also provides a RoutingAppender. It uses Lookups to determine how to perform the routing. Out of the box Log4j doesn't provide one to retrieve data from the log event but it would be simple to write one or add one to Log4j, or you could use a script to retrieve the marker from the event.
You should be aware that there is some overhead to using the MarkerFilters, although not much. Here is a benchmark from the log4j-perf module when running 4 threads on my MacBook Pro. Even in the worst case where Logback is checking for a parent marker against an event containing a child marker each comparison still only takes 17 nanoseconds on average.
Benchmark Mode Cnt Score Error Units
MarkerFilterBenchmark.baseline avgt 10 2.412 ± 0.088 ns/op
MarkerFilterBenchmark.log4jParentMarker avgt 10 8.337 ± 0.186 ns/op
MarkerFilterBenchmark.log4jSimpleMarker avgt 10 8.043 ± 0.145 ns/op
MarkerFilterBenchmark.log4jTooFine avgt 10 2.825 ± 0.281 ns/op
MarkerFilterBenchmark.logbackParentMarker avgt 10 17.865 ± 0.533 ns/op
MarkerFilterBenchmark.logbackSimpleMarker avgt 10 10.471 ± 0.089 ns/op
MarkerFilterBenchmark.logbackTooFine avgt 10 4.255 ± 0.014 ns/op
A final thought. Ceki Gulcu invented the concept of Markers when he created SLF4J and he deserves credit for that as it was a fantastic idea.