Search code examples
javaandroidlogback

CyclicBufferAppender in Logback


There is an appender called CyclicBufferAppender in logback and there seems to be no examples for it. What does it do? How does it work in the context of a RollingFileappender ? does it work with RollingFileappender or works independently ? Is it similar to AsyncAppender? Any example programmatically written is welcome.


Solution

  • Let me share an example of where I use it to let my Spring Boot web application show its own log messages in the UI.

    I have a CyclicBufferAppender subclass, InMemoryAppender whose only purpose is to let Spring manage it as a bean. (Inspired by this answer by @NeemePraks.)

    @Component
    public class InMemoryAppender extends CyclicBufferAppender<ILoggingEvent> implements SmartLifecycle {
        @Override
        public boolean isRunning() {
            return isStarted();
        }
    }
    

    I'm adding that component during startup (this means it will miss Spring Boot startup log messages, as indicated in the linked answer, but I'm OK with that):

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(WebApplication.class, args);
    
        LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
        Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
        rootLogger.addAppender(context.getBean(InMemoryAppender.class));
    }
    

    I have a simple Controller with just an index page, which reads the contents of the appender:

    @Controller
    @RequestMapping(path = "/logs")
    public class LogController {
        public LogController(InMemoryAppender appender) {
            this.appender = appender;
        }
    
        private final InMemoryAppender appender;
    
        @GetMapping
        public String index(Model model) throws Exception {
            ILoggingEvent[] events = new ILoggingEvent[appender.getLength()];
            for (int i = 0; i < appender.getLength(); i++) {
                events[i] = appender.get(i);
            }
            model.addAttribute("logs", events);
            return "logs/index";
        }
    }
    

    and finally a simple Thymeleaf page to show the logs:

    <h1>Logs</h1>
    
    <table style="border-collapse: separate; border-spacing: 2px;">
        <thead>
            <tr>
                <th scope="col">Timestamp</th>
                <th scope="col">Level</th>
                <th scope="col">Message</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="log: ${logs}" th:object="${log}">
                <td th:text="*{#dates.format(timeStamp, 'yyyy-MM-dd HH:mm:ss.SSS')}" style="white-space: nowrap;">[timestamp]</td>
                <td th:text="*{level}">[level]</td>
                <td th:text="*{message}">[message]</td>
            </tr>
        </tbody>
    </table>
    

    This is how it looks like:

    enter image description here