import org.apache.log4j.Level;
import org.apache.log4j.Logger;
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;

@ManagedResource(description="Log4j debug level modifier")
public class Log4jLevelChanger {

@ManagedOperation(description = "Change log level of a package")
@ManagedOperationParameters({
@ManagedOperationParameter(name = "loggerName", description = "package name"),
@ManagedOperationParameter(name = "level", description = "log level (DEBUG, INFO, etc.)")
})
public void setLogLevel(String loggerName, String level) {
if ("debug".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.DEBUG);
} else if ("info".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.INFO);
} else if ("error".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.ERROR);
} else if ("fatal".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.FATAL);
} else if ("warn".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.WARN);
} else if ("trace".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.TRACE);
} else if ("off".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.OFF);
} else if ("all".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.ALL);
}
}
}

On peut remarquer qu'on a déjà préparé le terrain pour Spring avec les annotations @ManagedXXX. Cette classe sera instanciée par Spring comme singleton et exposée via JMX :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<!-- RECUPERATION DU SERVEUR MBEAN DE LA PLATEFORME -->
<bean id="mbeanServer" class="java.lang.management.ManagementFactory" factory-method="getPlatformMBeanServer" />

<!-- bean Log4jLevelChanger en singleton -->
<bean id="log4jLevelChanger" class="fr.openfarm.util.Log4jLevelChanger" />

<!-- utilisation du MBean exporter de Spring pour exposer la méthode setLogLevel dans JMX -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="fr.openfarm.util:name=Log4jLevelChanger" value-ref="log4jLevelChanger" />
</map>
</property>
<property name="assembler" ref="metadataAssembler"/>
<property name="namingStrategy" ref="namingStrategy"/>
</bean>

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

<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>

<bean id="metadataAssembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>

On indique que les annotations seront utilisées comme source de données de configuration pour JMX avec le bean jmxAttributeSource. Ensuite, on indique que la stratégie de nommage des MBeans JMX sera faite à partir des annotations (bean namingStrategy) et que les metadata du MBean également utiliseront les annotations (bean metadataAssembler). Si les metadata ne sont pas utilisés, les noms de paramètres affichés dans la console JMX seront alors nommés arbitrairement p1 et p2.

Enfin, les deux beans namingStrategy et metadataAssembler sont passés en attribut de l'exporter qui sera chargé d'exposer le ou les MBeans dans JMX.

Voici le résultat dans JVisualVM (ou jconsole) :

log4j debug level modifier