Dependency injection in custom evaluator in logback in spring boot

529 views Asked by At

I am writing a custom evaluator in which I want to autowire another bean. I am unable to do so as evaluator gets initialized by logger where as beans are initialized by spring context. Below is the sample of my code:

In logback-spring.xml:

 <appender name="myAppender" class="ch.qos.logback.classic.net.SMTPAppender">
           <evaluator class="com.package.CustomEvaluator">
            <marker>FATAL</marker>
            <interval>1000000</interval>
           </evaluator>
 </appender>

My custom evaluator:

@Slf4j
@Component
public class CustomEvaluator extends OnMarkerEvaluator {
    @Autowired
    private MyService myService;

    @Override
    public boolean evaluate(ILoggingEvent event) throws EvaluationException {
        \\logic goes here
    }
}

I am getting object of MyService always as null(which is expected). Is there any work around for this?

1

There are 1 answers

4
Mark Bramnik On

It don't think its possible because the Evaluator being an internal logback abstraction is not managed / initialized by spring, so obviously spring can't autowire anything into the evaluator.

In addition note, that logback gets initialized even before application context starts.

Of course you could provide some global holder class for the application context and set the context to it in the main method, and then get the reference to it in this evaluator, something like this:

public class ApplicationContextHolder {
        private static ApplicationContext context;
        // call it in main() method 
        public static void setContext(ApplicationContext ctx) {context = ctx;}
        public static ApplicationContext getContext() {return context;} 
}

class CustomEvaluator extends OnMarkerEvaluator {
    public boolean evaluate(ILoggingEvent event) throws EvaluationException {
        ApplicationContext ctx = ApplicationContextHolder.getContext();
        if(ctx == null) {return false;} // not yet initialized / accessible
        MyService myService = ctx.getBean(MyService.class);
    }
}

But all-in-all I believe its a very ugly solution.

As a suggestion, I think you should consider refactoring of the logic so that the decision of whether to send an email based on logging event will be taken in the application (which is, I assume, spring boot driven so you have an access to the MyService)

Given the current implementation:

  public foo() {
     LOGGER.info("This should be sent by email");
  }

I suggest a part of application:

  @Component
  public class MyLogic {
      @Autowired MyService myService;

      public void foo() {
          if(myService.shouldSend()) {
             LOGGER.info("This should be sent by email");
          }
      }
  }