0

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?

codeLover
  • 2,571
  • 1
  • 11
  • 27

1 Answers1

0

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");
          }
      }
  }
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Thanks for your suggestion. Can you provide more details on "decision of whether to send an email based on logging event will be taken in the application " – codeLover Aug 08 '19 at 05:43
  • BTW, maybe you'll be intereted in reading this thread (with a pretty similar question) https://stackoverflow.com/questions/42859896/dependency-injection-into-logback-appenders-with-spring-boot – Mark Bramnik Aug 08 '19 at 05:54
  • Thanks..I will look into the solution provided by you. I went through this thread bit I am looking something for evaluator ..not for appender – codeLover Aug 08 '19 at 05:59
  • Yes, sure, its an evaluator, but its the same from the point of view of spring integration. Hence the answers are pretty similar... – Mark Bramnik Aug 08 '19 at 06:01