Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot programmatic logging configuration

How can I configure logging programmatically in a spring boot application?

Using an xml or properties file is not flexible enough for my needs.

Update: I want to achieve something like this:

@Value("${logging.level.root}")
private String loggingLevelRoot;

@Value("${logging.level.myApp}")
private String loggingLevelMyApp;

@Value("${logging.file}")
private boolean fileAppenderEnabled;

....

setLevel(Logger.ROOT_LOGGER_NAME, Level.toLevel(loggingLevelRoot)));
setLevel("com.myapp", Level.toLevel(loggingLevelMyApp)));
setLevel("org.springframework", Level.WARN);
setLevel("org.apache.coyote", Level.INFO);
setLevel("org.apache.catalina", Level.INFO);
setLevel("org.apache.catalina.startup.DigesterFactory", Level.ERROR);
setLevel("org.apache.catalina.util.LifecycleMBeanBase", Level.ERROR);

Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
logger.addAppender(createConsoleAppender());
if (fileAppenderEnabled) {
    logger.addAppender(createFileAppender());
}

All I have per environment is:

  • logging.level.root=[INFO, DEBUG, ..]
  • logging.level.myApp=[INFO, DEBUG, ..]
  • logging.file=[true | false]

No duplication of XML, Groovy and other formats I really don't want to deal with.

At the end of the day, this is really about achieving the same flexibility for logging as Spring JavaConfig did for beans. XML or other file formats are simply too static, require too much duplication and not integrated well enough with the rest of the configuration of the application.

Why should logging be configured differently than any other bean or service? It makes no sense.

like image 479
Axel Fontaine Avatar asked Dec 10 '13 23:12

Axel Fontaine


1 Answers

I'm not sure you want or need to disable the default XML configuration of the logging system, but you do want to execute your customization calls after that was done. Fortunately that's pretty easy as it's done as early as possible in the initializer chain for a SpringApplication. The easiest place to put your code is probably a SpringApplicationInitializer (it has to implement ApplicationContextInitializer as well so it can be added to the SpringApplication). E.g.

SpringApplication application = new SpringApplication(MySources.class);
application.addInitializers(new LoggingInitializer());
application.run(args);

You won't be able to do dependency injection into the initializer if you do it that way, but it will ensure that it gets called as early as possible in the lifecycle. If your initializer implements EnvironmentAware then you will also be passed an instance of Environment before the call to SpringApplicationInitializer.initialize() - using that you can resolve the environment dependent pieces in your sample, e.g.

String loggingLevelRoot = environment.getProperty("logging.level.root");

Once you have it working, to avoid having to do the same thing for all apps you can make it declarative by adding a META-INF/spring.factories containing your initializer class:

org.springframework.context.ApplicationContextInitializer=\
my.pkg.for.LoggingInitializer

If you really need dependency injection and @Value resolution I think you are going to have to accept that the ApplicationContext will have fully refreshed before you get a chance to configure anything. If that's an acceptable compromise I recommend just adding a LoggingInitializer to your context and have it implement CommandLineRunner.

like image 162
Dave Syer Avatar answered Oct 25 '22 02:10

Dave Syer