Remote Logging with Log4j JMSAppender and ActiveMQ – A Practical Guide

This article explains how to centralize Java application logs by configuring Log4j's JMSAppender to send log events to an ActiveMQ broker, demonstrates project setup with Maven, shows Spring integration, and provides detailed code examples and troubleshooting steps for reliable remote logging.

Architecture Digest
Architecture Digest
Architecture Digest
Remote Logging with Log4j JMSAppender and ActiveMQ – A Practical Guide

The article introduces the need for remote logging in growing Java projects, highlighting benefits such as centralized management, reduced server load, and improved performance by offloading log handling to a dedicated log server.

Installing ActiveMQ Download ActiveMQ, extract it, and start it with activemq.bat. Verify the broker is running by accessing http://localhost:8161 with the default credentials (admin/admin) and checking the Manage ActiveMQ broker UI.

Project structure Two Maven projects are used: Product (simulating an online application) and Logging (running on a log server). Each project contains four key files: pom.xml, Main.java, log4j.properties, and jndi.properties.

pom.xml <project>...</project> (dependencies are declared; the full content is shown in the original article).

log4j.properties

log4j.rootLogger=INFO, stdout, jms

## Be sure that ActiveMQ messages are not logged to 'jms' appender
log4j.logger.org.apache.activemq=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c - %m%n

## Configure 'jms' appender. You'll also need jndi.properties file in order to make it work
log4j.appender.jms=org.apache.log4j.net.JMSAppender
log4j.appender.jms.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory
log4j.appender.jms.ProviderURL=tcp://localhost:61616
log4j.appender.jms.TopicBindingName=logTopic
log4j.appender.jms.TopicConnectionFactoryBindingName=ConnectionFactory

jndi.properties topic.logTopic=logTopic When the configuration is incomplete, a javax.naming.NameNotFoundException: logTopic error occurs. Adding the correct JNDI entry resolves the issue.

Spring integration The Logging project is turned into a web application running on Tomcat. The Spring configuration ( spring.xml) defines beans for JmsTemplate, ConnectionFactory, TopicDestination, and a DefaultMessageListenerContainer that references a custom LogMessageListener bean.

<beans xmlns="http://www.springframework.org/schema/beans" ...>
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
    </bean>
    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
    </bean>
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616"/>
    </bean>
    <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg name="name" value="logTopic"/>
    </bean>
    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="topicDestination"/>
        <property name="messageListener" ref="logMessageListener"/>
    </bean>
    <bean id="logMessageListener" class="com.demo.logging.LogMessageListener"/>
</beans>

LogMessageListener implementation

package com.demo.logging;

import javax.jms.Message;
import javax.jms.MessageListener;
import org.apache.activemq.command.ActiveMQObjectMessage;
import org.apache.log4j.spi.LoggingEvent;

public class LogMessageListener implements MessageListener {
    public void onMessage(Message message) {
        try {
            // receive log event in your consumer
            LoggingEvent event = (LoggingEvent)((ActiveMQObjectMessage)message).getObject();
            System.out.println("Logging project: [" + event.getLevel() + "]: " + event.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Running the Logging project starts the listener; executing Product 's Main prints logs locally and sends them to the ActiveMQ topic. The messages appear in the broker UI and are consumed by the listener, which prints them to the console.

JMSAppender analysis The JMSAppender serializes LoggingEvent objects into ObjectMessage and publishes them to a JMS Topic. The source code shows the activateOptions() method that creates a TopicConnection, TopicSession, and TopicPublisher, and the append() method that sends each log event via topicPublisher.publish(msg). This confirms that the appender works only with topics, not queues.

public void activateOptions() {
    TopicConnectionFactory topicConnectionFactory;
    try {
        Context jndi;
        LogLog.debug("Getting initial context.");
        if (initialContextFactoryName != null) {
            Properties env = new Properties();
            env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
            if (providerURL != null) {
                env.put(Context.PROVIDER_URL, providerURL);
            }
            // additional security settings omitted for brevity
            jndi = new InitialContext(env);
        } else {
            jndi = new InitialContext();
        }
        LogLog.debug("Looking up [" + tcfBindingName + "]");
        topicConnectionFactory = (TopicConnectionFactory) lookup(jndi, tcfBindingName);
        LogLog.debug("About to create TopicConnection.");
        if (userName != null) {
            topicConnection = topicConnectionFactory.createTopicConnection(userName, password);
        } else {
            topicConnection = topicConnectionFactory.createTopicConnection();
        }
        LogLog.debug("Creating TopicSession, non-transactional, in AUTO_ACKNOWLEDGE mode.");
        topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
        LogLog.debug("Looking up topic name [" + topicBindingName + "]");
        Topic topic = (Topic) lookup(jndi, topicBindingName);
        LogLog.debug("Creating TopicPublisher.");
        topicPublisher = topicSession.createPublisher(topic);
        LogLog.debug("Starting TopicConnection.");
        topicConnection.start();
        jndi.close();
    } catch (JMSException e) {
        errorHandler.error("Error while activating options for appender named [" + name + "]", e, ErrorCode.GENERIC_FAILURE);
    } catch (NamingException e) {
        errorHandler.error("Error while activating options for appender named [" + name + "]", e, ErrorCode.GENERIC_FAILURE);
    } catch (RuntimeException e) {
        errorHandler.error("Error while activating options for appender named [" + name + "]", e, ErrorCode.GENERIC_FAILURE);
    }
}
public void append(LoggingEvent event) {
    if (!checkEntryConditions()) {
        return;
    }
    try {
        ObjectMessage msg = topicSession.createObjectMessage();
        if (locationInfo) {
            event.getLocationInformation();
        }
        msg.setObject(event);
        topicPublisher.publish(msg); // send to topic
    } catch (JMSException e) {
        errorHandler.error("Could not publish message in JMSAppender [" + name + "]", e, ErrorCode.GENERIC_FAILURE);
    } catch (RuntimeException e) {
        errorHandler.error("Could not publish message in JMSAppender [" + name + "]", e, ErrorCode.GENERIC_FAILURE);
    }
}

The guide concludes that by deploying the two projects on separate machines and pointing them to a shared ActiveMQ broker, the solution can be used in production environments for reliable remote logging.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaspringlog4jActiveMQJMSAppenderRemote Logging
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.