The MX4J implementation exposes some internal functionality via a public API that can be used by MBeans and applications.
However using these API ties the MBean or the application to the MX4J implementation, making them non-portable across
other JMX implementations.
MX4J has a flexible logging system that allows you to tune the logging priority and to redirect MX4J internal logging to other logging systems such as Commons Logging or Log4J.
The MX4J logging system has six logging priorities; from the lowest priority to the highest they are:
For example, you can start the JVM with this command to have MX4J log at a debug level:
java -Dmx4j.log.priority=debug MyMainClass
The fatal level is never used by MX4J.
MX4J default logging is done on the console via System.out, but can be redirected to other logging systems using the MX4J logging API, or through the broadcaster MBean (see below).
For example, let's assume you want to redirect MX4J logging to Log4J. Below is the code needed to do so:
Example 4.2. Logging redirection to Log4J
import org.apache.log4j.PropertyConfigurator; import mx4j.log.*; public class Main { public static void main(String[] args) throws Exception { // Configure Log4J PropertyConfigurator.configureAndWatch("log4j.properties"); // Or use the XML version below // DOMConfigurator.configureAndWatch("log4j.xml"); // Redirect MX4J logging to Log4J Log.redirectTo(new Log4JLogger()); // Normal code here MBeanServer server = MBeanServerFactory.createMBeanServer(); ... // Reset redirection, log in the normal way (to console) Log.redirectTo(null); } }
It is also shown how to reset logging redirection to the standard one, that logs on the console.
The prototype for the new Logger, in the example above Log4JLogger, can be specified either in the code or by setting the system property "mx4j.log.prototype" to the full qualified name of the Logger subclass, for example:
java -Dmx4j.log.prototype=mx4j.log.Log4JLogger MyMainClass
In this case, using system properties, your MBean or application remains portable across JMX implementations.
It is possible to redirect logging to the MX4J broadcaster MBean, that will send notifications to registered listeners. The MBeanServer mechanism is used to emit these notifications, so that every listener can register itself along with a filter, to exclude notifications in which it's not interested in. The example below shows the code necessary to redirect the logging system to the broadcaster MBean:
Example 4.3. Logging redirection to the broadcaster MBean
import mx4j.log.*; public class Main { public static void main(String[] args) throws Exception { MBeanServer server = MBeanServerFactory.createMBeanServer(); // Register the broadcaster logger mbean ObjectName name = new ObjectName("Logger:type=broadcaster"); server.createMBean("mx4j.log.LoggerBroadcaster", name, null); // The filter: only errors are logged NotificationFilter filter = new NotificationFilter() { public boolean isNotificationEnabled(Notification notification) { if (notification.getType().equals("mx4j.logger.error")) {return true;} return false; } }; // The listener: logs on System.err instead of System.out NotificationListener listener = new NotificationListener() { public void handleNotification(Notification notification, Object handback) { System.err.println("[MX4J ERROR]: " + notification); } }; // Register the listener along with the filter server.addNotificationListener(name, listener, filter, null); // Starts the redirector LoggerBroadcasterMBean redirector = (LoggerBroadcasterMBean)MBeanServerInvocationHandler.newProxyInstance(server, name, LoggerBroadcasterMBean.class, true); redirector.start(); ... // Stops the redirector redirector.stop(); } }
It is also shown how to reset logging redirection to the standard one, that logs on the console.
Various classes in the MX4J implementation log their activities.
Each class logs with a certain category, that organizes logging into a hierarchy of categories, following a
dotted scheme introduced by
Log4J.
The categories are simply the full qualified names of the classes that log their activities.
The MX4J implementation allows client code to add custom interceptors for the MBeanServer calls that may end up in a call to an MBean instance.
When the MX4J implementation of the MBeanServer is created, a configurator for the MBeanServer-to-MBean interceptor chain is
also created, along with a default set of interceptors (that cannot be removed by the client code).
Each one of the default interceptors is also an MBean, so it can be monitored/managed as a normal MBean, and also the
configurator is an MBean, registered with ObjectName "JMImplementation:type=MBeanServerInterceptorConfigurator".
The interceptor configurator exposes a management API that can be invoked as usual via the MBeanServer.
The API allows client code to add and remove custom interceptors, even at runtime, to perform additional tasks
such as logging, performance timing, redirection and so on.
Writing a custom interceptor is simple, and requires the client code to implement the interface mx4j.server.interceptor.MBeanServerInterceptor or to extend the class mx4j.server.interceptor.DefaultMBeanServerInterceptor.
Once you have written your custom interceptor, you can add it to the interceptor chain using the API provided by the MBeanServerInterceptorConfigurator, via these methods:
addInterceptor(MBeanServerInterceptor interceptor)
registerInterceptor(MBeanServerInterceptor interceptor, ObjectName name)
See the Javadoc API documentation relative to the class
mx4j.server.interceptor.MBeanServerInterceptorConfigurator and its management interface
for further details.
Below you can find a simple example of how to use the
MBeanServerInterceptorConfigurator API.
Example 4.4. Using the interceptor configurator API
public class Main { public static void main(String[] args) throws Exception { // Create an MBeanServer instance // This will also create and configure an instance of the // MBeanServerInterceptorConfigurator MBean MBeanServer server = MBeanServerFactory.createMBeanServer(); // The name under which the configurator is registered ObjectName name = new ObjectName("JMImplementation:type=MBeanServerInterceptorConfigurator"); // Create a custom interceptor GetMBeanInfoLoggerInterceptor custom = new GetMBeanInfoLoggerInterceptor(); // Install the custom interceptor server.invoke(name, "addInterceptor", new Object[] {custom}, new String[] {MBeanServerInterceptor.class.getName()}); // Now every call to getMBeanInfo will be logged MBeanInfo info = server.getMBeanInfo(name); } public static class GetMBeanInfoLoggerInterceptor extends DefaultMBeanServerInterceptor { public MBeanInfo getMBeanInfo(MBeanMetaData metadata) { // Use whatever logging system is better for you... System.out.println("Call to getMBeanInfo !"); return super.getMBeanInfo(metadata); } } }