1
votes

I'm running a Spring Boot application that uses logback. The idea is to have the log messages be sent to a RabbitMQ server. To accomplish this, I created an appender that extends ch.qos.logback.core.AppenderBase.

Here is my logback-spring.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="messaging" class="com.foo.logging.appender.MessagingAppenderLogback" />
    <root level="info">
        <appender-ref ref="messaging" />
    </root>
</configuration>

All is well. However, in MessagingAppenderLogback, RabbitTemplate which I wish to use to send the message is null.

@Component
public class MessagingAppenderLogback extends AppenderBase<ILoggingEvent> {
    @Autowired
    RabbitTemplate rabbitTemplate;

    public MessagingAppenderLogback(){ }

    @Override
    protected void append(ILoggingEvent event) {
        System.out.println("  MessagingAppenderLogback#append w/ event " + event);
        // rabbitTemplate.convertAndSend(event);
    }   
}

Per the documentation, I know that "The logging system is initialized early in the application ..."

I'd like to know what I have to do to have rabbitTemplate available in my appender.

3
@gonqin You won't be able to see the autowired component because Spring is creating an instance and Logback is also creating another instance using the empty constructor you're using. I had the same problem as well but if you get rid of the empty constructor and use constructor injection to add your EventPublisher component to the controller everything should work. Sorry I had to post this as an answer because I don't have the reputation to comment yet. - Brij Patel

3 Answers

2
votes

Your appender is not a Spring bean, it is rather instantiated by a logback framework. You can't use Spring's dependency injection (@Component, @Autowired are not processed). You'll need to instantiate you RabbitTemplate explicitly.

Anyway, you might be looking for this: http://docs.spring.io/spring-amqp/docs/current/api/org/springframework/amqp/rabbit/logback/AmqpAppender.html

2
votes

I played around with it for a moment and got this working:

@Component
public class BeanAppender extends AppenderBase<ILoggingEvent> {

    @Autowired
    private UserService userService;

    @Override
    protected void append(ILoggingEvent eventObject) {
        System.out.println("message " + eventObject.getMessage() + " " + eventObject.getLoggerName() + " bean reference" + userService);
    }

    @PostConstruct
    public void init() {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        context.getLoggerList().forEach(new Consumer<Logger>() {

            @Override
            public void accept(Logger logger) {
                logger.addAppender(BeanAppender.this);
            }
        });

        setContext(context);
        start();
    }
}

Assuming that you don't mind configuring the appender programmatically it can do the trick for you. You don't need to touch the logback.xml then.

0
votes

I had the exact same problem. However, after applying your code, my Autowired fields are still null :-(? Anything I should check?

Here is my code:

import java.util.Date;
import java.util.function.Consumer;

import javax.annotation.PostConstruct;

import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Component;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;

@Component
@EnableAutoConfiguration
public class RabbitMQAppender extends AppenderBase<ILoggingEvent>
{
    @Autowired
    private EventPublisher eventPublisher;

    public RabbitMQAppender()
    {
    }

    @Override
    protected void append(ILoggingEvent logEvent)
    {
        System.out.println("RabbitMQ logger in appender: " + event.toString());
    }

    @PostConstruct
    public void init() {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        context.getLoggerList().forEach(new Consumer<Logger>() {

            @Override
            public void accept(Logger logger) {
                logger.addAppender(RabbitMQAppender.this);
            }
        });

        setContext(context);
        start();
    }    
}

When you said "not to touch logback.xml", did you mean not having it at all or just keep it as is? When I kept it, I got null for autowired field. When not having it, my appender was not used at all.