Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Log it or lose it

Log events to the Windows NT Event Log with JNI

  • Print
  • Feedback

Page 4 of 4

Register and deregister SampleAppMessages.dll

With NT's regsvr32 utility, SampleAppMessages.dll can register and deregister the SampleApp as the event source with the Event Log. The regsvr32 utility is invoked as regsvr32 SampleAppMessages.dll for registering the SampleApp with the Event Log. regsvr32 -u SampleAppMessages.dll is executed to deregister the SampleApp from the Event Log. The SampleAppMessages.dll exports two functions, DllRegisterServer() and DllUnregisterServer(), that register and deregister the DLL with the Event Log using Window APIs such as RegCreateKey and RegSetValueEx.

The DllRegisterServer() creates a registry key SYSTEM\CurrentControlSet\Services\EventLog\Application\SampleApp. It sets the value for EventMessageFile and CategoryMessageFile as the full directory path for SampleAppMessages.dll. It sets the value of CategoryCount to "2" to reflect the number of categories defined in SampleAppMessages.mc. It also sets TypesSupported as EVENTLOG_ERROR_TYPE, EVENTLOG_WARNING_TYPE, or EVENTLOG_INFORMATION_TYPE out of the event types supported by the Event Log. DllUnregisterServer() deletes the SYSTEM\CurrentControlSet\Services\EventLog\Application\SampleApp to deregister the SampleApp from the Event Log.

Create the JNI DLL

JNIEventLog.dll, a Windows DLL created in Visual C++, acts as a JNI DLL, which the SampleApp Java program loads for logging events. The JNIEventLog.dll implements a JNI method, described below, that logs events into the Event Log.

The native method in SampleApp.java is:

private native void logMessage(String message,
      int eventID,
      int eventSeverity,
      int eventCategory);


The function logMessage() possesses these parameters:

  • message: replaces the placeholder in the message string described for the event in the message file
  • eventID: the event identifier defined in the message file
  • eventSeverity: the bitmask of the supported event types
  • eventCategory: the category ID to which the event belongs; also defined in the message file


The C header file generator: javah

After compiling the SampleApp Java application, run the javah utility on SampleApp.class as:

javah -jni SampleApp.class


javah reads the classfile, and, for each native method declaration, it generates the function prototype in a C or C++ header file. javah's output is a SampleApp.h file with the following declarations:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class SampleApp */
#ifndef _Included_SampleApp
#define _Included_SampleApp
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     SampleApp
 * Method:    logMessage
 * Signature: (Ljava/lang/String;III)V
 */
JNIEXPORT void JNICALL Java_SampleApp_logMessage
  (JNIEnv *, jobject, jstring, jint, jint, jint);
#ifdef __cplusplus
}
#endif
#endif


Here, the JNIEnv parameter acts as a hook allowing callback into the JVM. The jobject parameter is the reference to the object that called the native method.

Implement JNIEventLog.dll

The Visual C++ project JNIEventLog, which includes the SampleApp.h generated by javah, implements the Java_SampleApp_logMessage() JNI method:

JNIEXPORT void JNICALL Java_SampleApp_logMessage
   JNIEnv *env,
   jobject obj,
   jstring message,
   jint eventID,
   jint eventSeverity,
   jint eventCategory)
{
   const char *msg = env->GetStringUTFChars(message,0);
   WORD wEventID = (DWORD)eventID;
   WORD  wEventSeverity = (WORD)eventSeverity;
   WORD  wEventCategory = (WORD)eventCategory;
   //Get the handle to the event source 'SampleApp'
   HANDLE h = RegisterEventSource(NULL, L"SampleApp");
   if(h != NULL){
      //convert jstring inot WCHAR
      WCHAR *szMsg[1];
      zMsg[0] = (WCHAR *)malloc( 2 * strlen(msg) + 2);
      for(unsigned int i = 0; i < strlen(msg); i++)   {
         szMsg[0][i] = (WCHAR)msg[i];
      }        
      szMsg[0][strlen(msg)] = L'\0';
      //Log the event in the Event Log
      ReportEvent(h,           
           wEventSeverity, 
           wEventCategory, 
           wEventID,       
           NULL,           
           1,
                0,
           (LPCWSTR *)szMsg, 
           NULL);      
      DeregisterEventSource(h); 
      free(szMsg[0]);
   }
   env->ReleaseStringUTFChars(message, msg);
}


The code obtains the message string's handle by calling the JNI API GetStringUTFChars and converting it into a wchar string. To retrieve the SampleApp's handle (already registered as an event source with the NT Event Log employing SampleAppMessages.dll), call RegisterEventSource. Using that event source handle, call the ReportEvent Windows API to report the event along with the information passed from the Java program. Then deregister the event source. You must release the memory by calling the JNI API ReleaseStringUTFChars; otherwise, you'll encounter memory leaks in the program.

To build the JNIEventLog.dll, you'll need to add the following to your directories path:

<JAVA_HOME>\jdk1.1.8\include, <JAVA_HOME>\jdk1.1.8\include\win32


You should also add the following to your libraries:

<JAVA_HOME>\jdk1.1.8\lib


Sample Java application

The SampleApp Java application demonstrates event logging into the Event Log. It employs the native method logMessage() from the JNIEventLog.dll for logging events into the Event Log. SampleApp comprises two Java classes:

  • SampleApp
  • EventLogIds


SampleApp Java application

The sample application's main class, SampleApp, performing typical database operations, inserts and reads employee records. It logs the SQL statements into the Event Log, as well as any exceptions.

SampleApp contains this JNI method declaration, which logs the events into the Event Log:

private native void logMessage(String message,
    int eventID,
    int eventSeverity,
    int eventCategory);


The SampleApp class loads the JNI DLL on startup through the following code:

static{ System.loadLibrary("JNIEventLog"); }


The SampleApp application inserts an employee record in the writeData() method. The method writeData() logs the SQL statement with SQLSTMT as the event ID, EVENTLOG_INFORMATION_TYPE as the severity, and CAT_DATA_WRITE as the category. Figure 3 shows the output.

Figure 3. SampleApp event message

And here's the code:

String query = "Insert Into employee (emp_id,name) Values("+
   empID+",'"+empName+"')";
//Log the data write query in Windows event Log for future //reference
logMessage(query, 
   EventLogIds.SQLSTMT,
   EventLogIds.EVENTLOG_INFORMATION_TYPE,
   EventLogIds.CAT_DATA_WRITE);


After inserting the employee record, the readData() method reads the inserted employee record. At the same time, the program logs the Select SQL statement, but with the CAT_DATA_READ category. The catch block in the writeData() method logs all exceptions with the category CAT_DATA_WRITE and in readData() with the category CAT_DATA_READ. All exceptions are logged with ERROR as the event ID and EVENTLOG_ERROR_TYPE as the severity.

EventLogIds

The sample application's second class, EventLogIds, contains the constants required for logging different event types from the Java program. By keeping such constants in a class, you can easily maintain them as more and more messages and categories are added with time. Here's the code:

public class EventLogIds{
   //Severities for Windows Event Log
   public static final int EVENTLOG_ERROR_TYPE = 1;
   public static final int EVENTLOG_WARNING_TYPE = 2;
   public static final int EVENTLOG_INFORMATION_TYPE = 4;
   //Category IDs defined for the application
   public static final int CAT_DATA_READ = 1;
   public static final int CAT_DATA_WRITE = 2;
   
   //Event IDs defined for the application
   public static final int SQLSTMT = 0x00001000;
   public static final int ERROR = 0x00001001;
}


The various event severities possibly used in SampleApp for reporting events are defined exactly in accordance with the Windows API documentation. This way, the Java application is well protected from any change in the values of event severities, as only this class would need to be changed.

You'll also find the categories and messages, along with their identifiers, defined in the SampleAppMessages.mc file.

Log roll

In Windows NT environments, Java servers must output events in the NT Event Log. By reading such Event Logs, support staff can troubleshoot the conditions that caused errors and determine the context in which those errors occurred. As we've seen, however, the JDK does not provide direct support for writing events to the NT Event Log. But you can write events to the Event Log by using JNI with a C method.

After reading this article, you should understand the NT event logging mechanism, and know how to create sample message files and embed them in the self-registering DLL, how to create a JNI method in C to log events to NT Event Log, and, lastly, how to use a JNI method in a sample Java program. Happy logging!

About the author

Nitin Nanda works as associate project manager in Quark's R&D center, based in Chandigarh, India. He is responsible for R&D on a suite of products targeted for the print catalog market. These products are engineered in RMI, CORBA, and the .Net platform. He coauthored Professional Java Data from Wrox Press. Sunil Kumar is an associate team lead in Quark's R&D Center. He is responsible for design and development on the server side of a print catalog product being engineered in RMI/DCOM/MTS. Prior to joining Quark, he worked with RAMCO Systems developing generic ERP software.

Read more about Tools & Methods in JavaWorld's Tools & Methods section.

  • Print
  • Feedback

Resources