Skip to main content

Adding custom handlers to GlassFish v3 loggers

Posted by carlavmott on December 9, 2008 at 2:08 PM PST

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.properties file 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 and flush 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 the logging.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.

Related Topics >>

Comments

Custom Log Handler for GlassFish v3

I tried the first technique of placing the files in the glassfish/lib directory and the handler class was never loaded. If I add it to the glassfish/lib/endorsed directory, the class loads, but the DAS hangs on startup and never completes or times out. Any thoughts?

Hangs on NoClassDefFound

I determined that the problem with the DAS hang on startup was that a dependent jar was missing from the endorsed directory. It's not a great implementation to hang with no diagnostic information. There was no stack trace to be found. I figured it out with no diagnostics.

Could you be a little more

Could you be a little more specific as to how you solved this problem? I am running into the same issue.

There should be diagnostic

There should be diagnostic information here, you are right.  Have you filed a bug on this?  Thanks.

Logging i Glassfish V2

I am struggling to try to add custom handlers using custom formatters to Glassfish V2.1 but I am not very successful. I have come to that I am able to log to the server.log, but the formatting changes to my custom formatter as soon as the log interface belonging to our custom logging utilities is initialized. Now I wonder if this is as far as I can get when using V2, or if there might be any hidden features that would enable me to let my custom handlers coexist with the Glassfish server handlers.

Steps do not work for Glassfish V3 Prelude

I followed the first part of the example exactly; I have a jar "LoggingTest.jar" that contains the class logging.NewHandler. However, I have copied the jar to glassfish/lib, glassfish/modules, and any other location I can think of. I have used the administration console to add the jar into the system classpath as well as the server classpath.

OK, after banging my head against the wall, I discovered the issue is the manner in which netbeans starts glassfish. If I start glassfish from the command line, everything works just fine. *shrug*. If anyone else has similar issues, try the command line