RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Set the Logging Level Over a Tomcat Cluster Dynamically : Page 3

Rather than modifying your Log4J configuration and then redeploying the correct code version with the modified logging level, you can dynamically set the Log4J logging level over each node in a Tomcat cluster with JMX.


Set the Logging Level for All the Nodes in Your Cluster

Once you have made the previously discussed changes, you will be able to set the Log4J logging level dynamically for any application running in Tomcat. The next challenge is to make sure you can set the logging level for every node in the cluster. You could either call this servlet manually on each node in the cluster or get the servlet to send a message to every other node in the cluster so the logging levels are always the same across the cluster.

To send a message to the other nodes in a Tomcat cluster, you need to call the send(ClusterMessage) method on the org.apache.catalina.cluster.SimpleTcpCluster class. When implementing a ClusterMessage, you must populate the details of the cluster node that was responsible for sending the request. (Listing 1 provides an example that retrieves the information through the Tomcat MBeans.) The first step is to provide an implementation of the ClusterMessage interface to pass the new logging level and to determine for which application you want to set the logging level.

It may seem strange, but you must use JMX over RMI to send the message to the cluster because the interface implemented in Listing 1 is defined within the Tomcat server class loader (i.e., it exists in a jar file in the server directory). You will instantiate that class in a Web application, which uses a totally separate class loader (it exists in the WEB-INF directory of your Web application). This means that the cluster message you instantiate in your Web application and send to the SimpleTcpCluster class will be different from the cluster message definition that is placed in the Tomcat server class loader. If you try to send your message directly through JMX, your cluster message instance will not match the cluster message definition accessible to the SimpleTcpCluster class and the call will fail. (See the Tomcat documentation on class loaders for a definition of how the class loaders are configured for the Tomcat server.)

The workaround is to use JMX over RMI so the cluster message is serialized in the Web application class loader, instantiated in the Tomcat server class loader (and that is the important point), and then de-serialized. The following method opens a connection to the local JVM's JMX server over RMI, looks up the MBean that wraps the SimpleTcpCluster, and then invokes the send(ClusterMessage) operation on the MBean:

public static void sendLogMessageEvent(String applicationName, String logLevel) {
    String port = System.getProperty("com.sun.management.jmxremote.port");
    String urlForJMX = JMX_SERVICE_PREFIX + HOST + ":" + port + JMX_SERVICE_SUFFIX;

    try {
        JMXServiceURL url = new JMXServiceURL(urlForJMX);
        JMXConnector connector = JMXConnectorFactory.connect(url, null);
        MBeanServerConnection server = connector.getMBeanServerConnection();
        ObjectName cluster = new ObjectName("Catalina:type=Cluster,host=localhost");
        log.debug("Cluster cluster: " + cluster.toString());

        LogLevelMessage message = new LogLevelMessage();

        Object[] params = new Object[] {message};
        String[] types = new String[] {"org.apache.catalina.cluster.ClusterMessage"};
        server.invoke(cluster, "send", params, types);
    } catch (Exception e) {
        log.error(e.getMessage(), e);

You should call this method in your SetLog4JLevelServlet to ensure that a message is sent to the other members of the cluster when you set the logging level.

You will need to configure your Tomcat server to allow a JMX connection to be opened up over RMI. Simply add the following line to your catalina.bat file in the bin directory:

set JAVA_OPTS=%JAVA_OPTS% -Dcom.sun.management.jmxremote.port=8012 

For a quick guide on applying security to this configuration, see the Tomcat documentation. For a more in-depth look at configuring JMX Remote, see the Sun documentation.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date