I recently blogged about the changes to logging GlassFish Prelude. Building on that I wanted to show how to add custom handlers to your installation of v3. You may find that you want to log messages to a database, send them to a remote server or log messages from specific loggers to your own file. This can be done by writing a custom log handler. It is pretty straight forward and actually there are two approaches you can take to add the handler. Either you can write an HK2 service for the handler or a Java class as specified by the JDK. Either approach will work in GlassFish v3 Prelude so I have examples for both approaches in this blog. In the examples below I simple print some messages to a specific file but you can replace that code with something that interests you.

Option 1

First I'll implement the handler as a simple Java class. This requires for me to write a class that implements the Handler APIs, package it in a jar file and add the jar to the GlassFish classpath. The last step is to update the logging.propertiesfile to include the name of the class of the handler. At server startup the handler is added to the root logger automatically. Since the handler is added to root logger it is available to all loggers.

My NewHandler class implements the publish, close andflush Handler APIs. In the publish method, my handler simply writes to a file all messages that come from the web and deployment loggers as those are the ones that I'm interested in seeing. All messages are still logged to server.log so nothing is lost.

package logging;

/**
 *
 * @author cmott
 */

import java.util.logging.*;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

/**
 * New Handler
 *
 */
public class NewHandler extends Handler {

    static BufferedWriter f = null;
    String webLogger = "javax.enterprise.system.container.web";
    String deployLogger = "javax.enterprise.system.tools.deployment";

    public NewHandler(){
        try {
              String userDir = System.getProperty("user.dir");
              f = new BufferedWriter(new FileWriter(userDir + "/mylogging.log"));       
            } catch (IOException e) {
                System.out.println("not able to create log file."+e);
       }
    }

 /**
  * Overridden method used to capture log entries   *
  * @param record The log record to be written out.
  */
 public void publish(LogRecord record)
 {
  // first see if this entry should be filtered out
  // the filter should keep anything
  if ( getFilter()!=null ) {
   if ( !getFilter().isLoggable(record) )
    return;
  }
  
  try { 
     if (webLogger.equals(record.getLoggerName()) || deployLogger.equals(record.getLoggerName()) ) {
         f.write ("NewHandler output - ");
         f.write("logger name: "+record.getLoggerName());
         f.write(" source classname: "+record.getSourceClassName());
         f.write(" message: "+record.getMessage());
         f.newLine();
         f.flush();
     }
  } catch (IOException ex){
        System.out.println("not able to write to log file."+ex);
  }
 
 }
 
 /**
  * Called to close this log handler.
  */
 public void close()
 {
        try {
         f.close();
        } catch (IOException ex){
  }
}
 
 /**
  * Called to flush any cached data 
  */
 public void flush()
 {
// not used
 }
}

Next I need to do is to package it in a jar file and copy that jar to glassfish/lib directory. By default the handler is associated with the root logger so all loggers will have messages send to the handler.

Update the property handler in thelogging.properties file to include the name of the new handler as below.

handlers= java.util.logging.ConsoleHandler, logging.NewHandler

Restarting the server with the new jar file on the classpath will include the new handler. To test this out I deployed a simple web app and looked at the log file. There you will see the messages from the new handler we just wrote.

Option 2

The other option is to create service using HK2. See Chapter 2 Writing HK2 Components of the GlassFish documentation for more information on writing an HK2 component. Again you need to implement the handler code much like the example above. I started with that example and added code to make it an HK2 service. I had to implement PostConstruct and specify that the service contract is provided by the Handler.class which is part of the JDK. I built the module and then add the jar file to the GlassFish modules directory. At server startup the module is found and the handler is added to the root logger. The handler code now looks like:

package logging;

/**
 *
 * @author cmott
 */
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.annotations.*;
import org.jvnet.hk2.component.*;


import java.util.logging.*;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

/**
 * New Handler
 *
 */
//specify that the contract is provided by handler.class in the JDK
@Service
@ContractProvided(Handler.class) 
@Scoped(Singleton.class)
public class NewHandler extends Handler implements PostConstruct {
    @Inject

    Habitat habitat;
    static BufferedWriter f = null;
    String webLogger = "javax.enterprise.system.container.web";
    String deployLogger = "javax.enterprise.system.tools.deployment";
    
    public void postConstruct(){
        try {
             String userDir = System.getProperty("user.dir");
             f = new BufferedWriter(new FileWriter(userDir + "/mylogging.log"));       
        } catch (IOException e) {
            System.out.println("not able to create log file."+e);
        }
    }
    
 /**
  * Overridden method used to capture log entries   *
  * @param record The log record to be written out.
  */
 public void publish(LogRecord record)
 {
  // first see if this entry should be filtered out
  // the filter should keep anything
  if ( getFilter()!=null ) {
   if ( !getFilter().isLoggable(record) )
    return;
  }

  try {
     if (webLogger.equals(record.getLoggerName()) || deployLogger.equals(record.getLoggerName()) ) {
         f.write ("NewHandler output - ");
         f.write("logger name: "+record.getLoggerName());
         f.write(" source classname: "+record.getSourceClassName());
         f.write(" message: "+record.getMessage()); 
         f.newLine();
         f.flush();
     }
  } catch (IOException ex){
    System.out.println("not able to write to log file."+ex);
  }

 }

 /**
  * Called to close this log handler.
  */
 public void close()
 {
    try {
        f.close();
    } catch (IOException ex){
  }
}

 /**
  * Called to flush any cached data that
  * this log handler may contain.
  */
 public void flush()
 {
// not used
 }
}

I created this as its own module and included the pom files I used to build the module below. In the example I have a very simple directory structure with handler-module as the parent containing one directory, handler. I've zipped up the standalone module, handler-module.zip rather than include the pom.xml files needed to create it.

There is no need to update the logging.properties file when creating a service. All that is needed is to drop the newly created jar file in the modules directory of the v3 installation. GlassFish detects the service in the modules directory automatically. Again I deployed a simple web app to test and look at mylogging.log file to see the expected messages.

That's it.