14

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.

Axel Fontaine
  • 34,542
  • 16
  • 106
  • 137
  • What isn't flexible enough using xml? If you use Logback you could use Groovy to configure logback. – M. Deinum Dec 11 '13 at 07:27
  • 1
    @M.Deinum No, you can't; Spring Boot (actually, I think Spring Autoconfig) stomps on Groovy configurations. – chrylis -cautiouslyoptimistic- Dec 11 '13 at 08:02
  • I also need a different config per environment, with different appender types, ... So no, XML doesn't cut it. – Axel Fontaine Dec 11 '13 at 08:24
  • 1
    @chrylis `logback.groovy` is supposed to be explicitly supported in Spring Boot (since at least M6, maybe before that I forget). If it isn't working I suggest you raise an issue in github and explain a bit. – Dave Syer Dec 11 '13 at 09:12
  • 1
    @AxelFontaine you can provide a different XML (or groovy or whatever) configuration per environment just be changing the `logging.config` per Spring profile (or just as a System property on startup). Would that not be flexible enough? – Dave Syer Dec 11 '13 at 09:19
  • @DaveSyer The problem is that this results in a lot of duplication (pattern format strings, ...). Configuration through code allows me to keep a few simple flags in a property file, together with the rest of the configuration of my app for each environment. I don't want a separate system property. All I want is to tell my app in which environment it is, and it should configure itself automatically for that using a properties file. I ship my app with all properties files (one per environment) and select the right one on startup using environment detection. – Axel Fontaine Dec 11 '13 at 12:28
  • Just so you know, pattern format strings can be extracted into System properties in most logging systems. Anyway, I suggest you modify your original question to contain some code examples of the sort of thing you need to be able to do, and it might then be possible to raise the level of the discussion. – Dave Syer Dec 11 '13 at 12:33
  • OK I get it (although I see nothing there that couldn't be done succinctly in XML, I have some sympathy for not wanting to do it that way). What stops you from doing it? – Dave Syer Dec 11 '13 at 13:20
  • Well that was my question :-) How can I disable the loading of the XML and where can I hook in to do that using code instead? – Axel Fontaine Dec 11 '13 at 13:27
  • @DaveSyer As a note, the support for `logback.groovy` as well as the test configs wasn't added until revision 9db55a3b7166b4794fde3beb44d9f30c4eb52348, which was after M6 but is included in M7. – chrylis -cautiouslyoptimistic- Jan 03 '14 at 19:18
  • @AxelFontaine can you recall how you achieved above described scenario ? because I almost need same behavior in my application – user2846382 Jun 04 '18 at 11:48

1 Answers1

19

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.

Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • A custom `LoggingSystem` implementation may do the job as well, isn't it? It won't be autodetected however, so you should add `-Dorg.springframework.boot.logging.LoggingSystem=` (or set system properties). – Bertrand Renuart Jun 16 '15 at 11:48
  • @DaveSyer `EnvironmentAware` wouldn't work in this case. If you need Environment instance then it is better to take it from application context instance inside the `initialize()` method. – ydrozhdzhal Oct 19 '16 at 21:32
  • I found this explanation of alternative strategies extremely helpful. I did need to refer to this question for an example of how to do some of what is recommended here: https://stackoverflow.com/questions/46636599/how-do-i-set-the-logging-properties-in-a-spring-java-configuration?noredirect=1&lq=1 – user1445967 Jan 10 '18 at 07:03