To set up Log4j2 loggers programmatically, wield the LoggerContext to handle logging configuration. Set up a logger with a ConsoleAppender and a PatternLayout using this code:
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
ConfigurationBuilder<BuiltConfiguration> builder = Configurator.newConfigurationBuilder();
builder.add(builder.newAppender("stdout", "Console")
.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d{HH:mm:ss.SSS} %-5level - %msg%n"))); //You'll see this pattern a lot. Like Deja vu, but less Matrix-y.builder.add(builder.newLogger("MyLogger", Level.DEBUG)
.add(builder.newAppenderRef("stdout"))
.addAttribute("additivity", false)); //It's a false truth, these aren't the logs you're looking for.Configurator.initialize(builder.build());
This snippet creates a ConsoleAppender with timestamped log messages, and initializes a logger named MyLogger at DEBUG level.
A new era: Programmatically configuring your loggers
The baptism: Early initialization
Kick off your logger setup with an init() method, setting up your logging like a newborn starting a fresh life:
publicclassLoggingConfigurer{
static {
init(); //Baptize the configurations with holy grace! }
publicstaticvoidinit(){
// Programmatic configuration code here }
}
Master of all: Controlling with root logger
Omnipotent and omnipresent, the root logger controls across the application landscape:
Class-by-class refinement: The logger for each class
Exploit the power of object-oriented programming and log for specific classes:
LoggerConfig loggerConfig = config.getLoggerConfig("com.example.MyClass");
loggerConfig.setLevel(Level.INFO); //The logs are everywhere! Mighty INFO, save us!context.updateLoggers(); // Changes without updates are like forgotten dreams.
Because change is constant: Dynamic logger updates
Gracefully alter your logging at runtime, because change is the only constant.
Code patterns to live by
Wrapping the present: Use SLF4J
Wrap SLF4J around Log4j2. Like a gift wrapper for a log. Magic abstraction hides the scary bits:
Embrace lazy initialization. Like slow-cooked meals, they taste better:
publicclassLazyLoggerProvider{
privatestatic Logger LOGGER = null; // Null today, initialized tomorrowpublicstatic Logger getLogger(){
if (LOGGER == null) {
LOGGER = LoggerFactory.getLogger(LazyLoggerProvider.class); // Initialize when needed, like a loyal rottweiler. }
return LOGGER;
}
}
Directing the logs: Appender output
Guide your console appender's output to System.out, like a shepherd guiding his sheep:
Appender consoleAppender = ... // your console appender configurationconsoleAppender.setTarget(ConsoleAppender.Target.SYSTEM_OUT); // System.out, the Little House on the Prairie for your logs.consoleAppender.start();
// Add appender to root loggerrootLogger.addAppender(consoleAppender); //This appender and root logger, best friends forever!
Balancing act: Threshold management
Set thresholds for each appender. Each log has its place, like books in a shelf:
Appender debugAppender = ... // Create appender for debuggingAppender errorAppender = ... // Create appender for errorsdebugAppender.setThreshold(Level.DEBUG);
errorAppender.setThreshold(Level.ERROR); // This appender has no time for your debug levity!// Assign appenders where they belong