spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.13k stars 40.68k forks source link

Allow addition of custom logback Actions #24992

Open a1dutch opened 3 years ago

a1dutch commented 3 years ago

Hi,

I was looking at adding a custom Action via the SpringBootJoranConfigurator but this is not currently possible.

My use case is to enable or disable an appender based on a property via the spring Environment class.

I managed to add a custom action based on the newRule extension, but its not possible to get access to the Environment class.

A way to either configure the SpringBootJoranConfigurator with the LoggingInitializationContext or a new way to enable/disable based on a property would be handy, e.g. like a <conditionalOnProperty property="my.property.enabled" havingValue="true" matchOnMissing=true />

Cheers

Andy

philwebb commented 3 years ago

I wonder if we could update SpringBootJoranConfigurator to add additional JoranConfigurator instances loaded via spring.factories.

I managed to add a custom action based on the newRule extension

@a1dutch How did you do this? Can you share some code?

wilkinsona commented 3 years ago

In Logback, conditional configuration is typically handled via Janino. It would be less general purpose, but for this specific use case I wonder if we should looking at making the Environment available to Janino's expressions? By default they are "a Java expression in which only context properties or system properties are accessible".

a1dutch commented 3 years ago

@philwebb - here is the docs for newRule: http://logback.qos.ch/manual/onJoran.html#newRule

here is my working example based on classes present, the following is added to logback-spring.xml

<newRule pattern="configuration/enabledWhenClassPresent" actionClass="****.logging.slf4j.EnabledWhenClassPresentAction"/>

<enabledWhenClassPresent class="some.classname.Here">
...
</enabledWhenClassPresent>

The EnabledWhenClassPresentAction is the same as SpringPropertyAction apart from its doing a Class.forname to check if the class exists.

i think two new actions supported by default (enabled based on a property & enabled based on class present) would be a good addition to spring boot and cover any possible use cases.

@wilkinsona i initially looked at Janino expressions, i will take another look and see if its possible to use springProperty in conjunction with this.

philwebb commented 3 years ago

I wonder if we can put the org.springframework.boot.logging.LoggingInitializationContext class into ch.qos.logback.core.Context? That way Actions could get it via the InterpretationContext. E.g.:

LoggingInitializationContext lic = (LoggingInitializationContext) context.getContext().getObject(LoggingInitializationContext.class.getName());
a1dutch commented 3 years ago

@philwebb yes that would be super handy

michaelplavnik commented 3 years ago

@philwebb

"to add additional JoranConfigurator"

sounds like introducing

interface JoranConfiguratorCustomizer { void addInstanceRules(RuleStore rs, SpringBootJoranConfigurator c); }

and environment property to manage a set of customizers

Frettman commented 11 months ago

<newRule> has essentially been turned into a no-op: It's still there, but a critical line has been commented out for security reasons:

That has made this issue all the more pressing. I'm using custom actions as shortcuts to create fully configured appenders (turning like 10 lines into a one liner). And I don't see any remotely reasonable way to port them now, because none of the potential extension points seem exposed. The only workaround I see would be to register my own JoranConfigurator (if it even reliably works to override the one registered by Spring Boot) and copy over everything from SpringBootJoranConfigurator. But I think it's obvious why I'm reluctant to do this. Spring Boot creates its SpringBootJoranConfigurator directly and doesn't rely on Logback to create it via Java's service loader, so there really seems to be no way.

Frettman commented 10 months ago

In the meantime I went with the following solution: Instead of implementing rules/Actions, I implemented my own Appenders that just wrap the preconfigured (Rolling)FileAppender. For my use case that's good enough.

This just slightly changes the required syntax in logback.xml. So instead of previously

<rollingFileLog name="logname" encoderPattern="mypattern" />

I now have to write

<appender name="logname" class="RollingFileAppender">
  <encoderPattern>mypattern</encoderPattern>
</appender>

A bit more verbose, but not too bad.