Monitoring application servers is a daunting task. Without the right instrumentation of your code it is even impossible. I try to use log4j as much as possible and in general I’m very generous regarding logging levels. But occasionally the console just gets flooded with logging output. Typically then I would modify the logging configuration files and restart the server which may be very time consuming and unnecessary.

In the past I had used JMX to switch logging levels at runtime for various projects. The first time now I have tried this with the Spring framework and it worked like a charm (did you expect anything else). I’m making the solution public in case I need this for future projects and to document the steps required!

First, a bean is required that uses the logging API (here log4j) to manage the logging level.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
 * Copyright 2002-2008 Martin Ahrer.
 *
 * $Id$
 */
package at.martinahrer.blueprint.log4j;

import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;

/**
 * @author Martin Ahrer
 *
 */

@ManagedResource(objectName = "at.martinahrer.blueprint.log4j:name=Logging", description = "Logging", log = true, logFile = "jmx.log", currencyTimeLimit = 15, persistPolicy = "OnUpdate", persistPeriod = 200, persistLocation = "logging", persistName = "logging")
public class Logging {
  @ManagedOperation(description = "Set the logging level for a category")
  @ManagedOperationParameters( { @ManagedOperationParameter(name = "category", description = "Logger category"),
          @ManagedOperationParameter(name = "level", description = "Logging level") })
  public void setLoggerLevel(String category, String level) {
      LogManager.getLogger(category).setLevel(Level.toLevel(level));
  }

  @ManagedOperation(description = "Get the logging level for a category")
  @ManagedOperationParameters( { @ManagedOperationParameter(name = "category", description = "Logger category") })
  public String getLoggerLevel(String category) {
      return LogManager.getLogger(category).getLevel().toString();
  }
}

It’s been annotated with Spring JMX annotations to provide JMX meta data. Next, we need to export the bean as a JMX Bean. This is supported very well by the Springframework’s MBeanExporter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
      <property name="assembler" ref="assembler" />
      <property name="namingStrategy" ref="namingStrategy" />
      <property name="autodetect" value="true" />
  </bean>

  <bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />

  <!-- will create management interface using annotation metadata -->
  <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
      <property name="attributeSource" ref="jmxAttributeSource" />
  </bean>

  <!-- will pick up the ObjectName from the annotation -->
  <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
      <property name="attributeSource" ref="jmxAttributeSource" />
  </bean>

  <bean id="log4jConfig" class="at.martinahrer.blueprint.log4j.Logging"></bean>
</beans>

Finally to connect to the MBeanServer using jconsole the JVM (running the MBean) must be started with the system property -Dcom.sun.management.jmxremote set.

Comments