It is useful for web applications deployed at application server where several application share same System Properties.
Currently it can be done via factories plus use @Ordered
annotation. But it will be good to allow set it explicitly from code.
Comment From: philwebb
I’m a little concerned about providing a programmatic way to change the LoggingSystem
since it’s quite an unusual thing to do and would need to be something that the user needs to remember to do before calling any other Spring Boot APIs. Can you explain a little bit more about your setup? What App Server do you use? What is the underlying logging library that you prefer? And why do you need to set it per-application?
Comment From: Fuud
Hi.
We are using tomcat. The problem here: due to some security reasons for several applications we cannot use embedded tomcat and need to host more than one application on shared tomcat instance.
My team is using log4j2 with custom appender. This appender waits for kafka producer to be set in static field and then sends buffered messages there. I like how I configure log level in runtime using actuator. I do not want Spring to manage logger lifecycle and set following logging system:
/**
* This logging system better than o.s.b.l.log4j2.Log4J2LoggingSystem because
* it does not affect existing configuration: it does not filter events out, it does not close contexts it just a wrapper over log4j2
*/
class Log4J2LoggingSystem(classLoader: ClassLoader) : org.springframework.boot.logging.log4j2.Log4J2LoggingSystem(classLoader) {
override fun initialize(initializationContext: LoggingInitializationContext?, configLocation: String?, logFile: LogFile?) {
// intentionally left blank: we will use existing slf4j/log4j2 instead of spring-configured
}
override fun reinitialize(initializationContext: LoggingInitializationContext?) {
// intentionally left blank: we will use existing slf4j/log4j2 instead of spring-configured
}
override fun beforeInitialize() {
// intentionally does not call super as it will break existing log4j2 context
// this call is copied from Slf4jLoggingSystem
configureJdkLoggingBridgeHandler();
}
override fun cleanUp() {
// intentionally left blank: we will use existing slf4j/log4j2 instead of spring-configured and do not want to stop on context refresh
}
private fun configureJdkLoggingBridgeHandler() {
val rootLogger = LogManager.getLogManager().getLogger("")
val handlers = rootLogger.handlers
handlers.forEach { rootLogger.removeHandler(it) } // cleanup default handlers (probably it will be single Console Handler)
SLF4JBridgeHandler.install()
}
}
In non-tomcat version we just set system property: System.setProperty(LoggingSystem.SYSTEM_PROPERTY, Log4J2LoggingSystem::class.qualifiedName!!)
But in tomcat with shared system properties because it will affect other applications.
I want a clear way to set custom LoggingSystem in cases when I know what I really want (spring-factories are too implicit and invisible for such cases).
Comment From: Fuud
@philwebb any updates?
Comment From: philwebb
We discussed this today on our team call and decided that we don't want to expose a setter for the LoggingSystemFactory
. In a complex setup such as yours we recommend setting the org.springframework.boot.logging.LoggingSystem
system property to none
and using direct log4j configuration instead.
Comment From: Fuud
we recommend setting the org.springframework.boot.logging.LoggingSystem system property to none
@philwebb in Application Servers we cannot modify system properties: it will affect other applications on this server. For shared infrastructure it is critical.
Could you please explain reasons why Spring allow to set something via factories, via system property but not via code?
Comment From: philwebb
Logging is a notoriously difficult problem to solve, especially in an application server setup where the same JVM is running multiple applications, possibly with complex classloader setups.
To be honest, these complex setups are something that we don't have the bandwidth to support. It's quite hard to even test such setups. Since you have a way to do what you need with spring.factories
we'd rather not increase the surface area of the LoggingSystem
API.
Comment From: Fuud
Thank you.