You can configure appenders programmatically. Almost all appenders are tested using programmatic configuration. It follows that there are many examples of programmatic appender configuration in the logback project source code. For a logback-core appender, look under logback-core/src/test/java, and for a logback-classic appender look under logback-classic/src/test/java.
Why I need programmaticaly configure a logger? Because, I packaging my app (Spring Boot) into a jar file. Consequently Logback.xml file is appear to be hide inside a jar. Though, it is not to be convenient to unpackage and change it. And I do not need any logback.xml file beside my app.jar. I have only app.yaml file which contains all configuration properties for app.
As a reference, when you try to modify code responsible for creating loggers, there is a bunch of rules that must be satisfied in order for a logger to work.
Now I have experience with programmatic configuration of slf4j/logback.
Task
A program must open separate log file for each processed input file.
Solution for task
Instead of configuring logback via xml, the one needs to “manually” instantiate encoders, appenders and loggers, then configure and link them together.
Caveat 1
Logback goes crazy on attempt to share encoder (i.e. PatternLayoutEncoder) between appenders.
Solution for caveat 1
Create separate encoder for each appender.
Caveat 2
Logback refuses to log anything, if encoders and appenders are not associated with logging context.
Solution for caveat 2
Call setContext on each encoder and appender, passing LoggerFactory
as a parameter.
Caveat 3
Logback refuses to log anything, if encoders and appenders are not started.
Solution for caveat 3
encoders and appenders need to be started in the correct order, i.e. first encoders, then appenders.
Caveat 4
RollingPolicy objects (i.e. TimeBasedRollingPolicy) produce strange error messages like “date format not recognized”, when they are not attached to the same context as appender.
Solution for caveat 4
call setContext on RollingPolicy same way as on encoders and appenders.
Here is working example of “manual” logback configuration:
Not allowed to comment (yet?), I'd just like to add three tips;
regarding the caveats above, if you have problems, just add a call to
StatusPrinter.print(context);
after everything has been configured, that is, after having added your
appenders the root/"Main" appender: it will tell you what is wrong.
I like very much to separate logging-levels in different files; when looking for errors I begin with looking in the error file and so on, having them set up like
routing by means of a simple private filter class such as
private static class ThresholdLoggerFilter extends Filter<ILoggingEvent> {
private final Level level;
private ThresholdLoggerFilter(Level level){
this.level = level;
}
@Override
public FilterReply decide(ILoggingEvent event) {
if (event.getLevel().isGreaterOrEqual(level)) {
return FilterReply.NEUTRAL;
} else {
return FilterReply.DENY;
}
}
}
and then just call myFilter.start() and myAppender.addFilter(myFilter);.
Lastly, putting it together, I usually want to be able to change log levels dynamically having the setup implement some simple interface like
public interface LoggingService {
void setRootLogLevel(Level level);
}
keeping the root logging level in some property-file which is monitored so that whenever there is some valid input there, I just call this service implemented like
@Override
public void setRootLogLevel(Level level) {
if (context != null && context.isStarted()) {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
}
}
In the current versions (logback=1.3.0-alpha16,slf4j=2.0.0-alpha7) you can use SPI to build your configuration programmatically.
You need a text file in your jar with the name: ch.qos.logback.classic.spi.Configurator located in META-INF/services within your jar for Logback to pick it up. The file should contain the full qualified class name of your class that does the initialization.
Then create a class that does the initialization like so:
So this example should have a file called ch.qos.logback.classic.spi.Configurator with the content: example.ProgrammaticLoggingConfigurator for it to be picked up.