How do I access Windows Event Viewer log data from Java
http://www.j-interop.org/ is an open-source Java library that implements the DCOM protocol specification without using any native code. (i.e. you can use it to access DCOM objects on a remote Windows host from Java code running on a non-Windows client).
Microsoft exposes a plethora of system information via Windows Management Instrumentation (WMI). WMI is remotely accessible via DCOM, and considerable documentation on the subject exists on Microsoft's site. As it happens, you can access the Windows Event Logs via this remotely accessible interface.
By using j-interop you can create an instance of the WbemScripting.SWbemLocator WMI object remotely, then connect to Windows Management Instrumentation (WMI) services on the remote Windows host. From there you can submit a query that will inform you whenever a new event log entry is written.
Note that this does require that you have DCOM properly enabled and configured on the remote Windows host, and that appropriate exceptions have been set up in any firewalls. Details on this can be searched online, and are also referenced from the j-interop site, above.
The following example connects to a remote host using its NT domain, hostname, a username and a password, and sits in a loop, dumping every event log entry as they are logged by windows. The user must have been granted appropriate remote DCOM access permissions, but does not have to be an administrator.
import java.io.IOException;
import java.util.logging.Level;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.common.JISystem;
import org.jinterop.dcom.core.JIComServer;
import org.jinterop.dcom.core.JIProgId;
import org.jinterop.dcom.core.JISession;
import org.jinterop.dcom.core.JIString;
import org.jinterop.dcom.core.JIVariant;
import org.jinterop.dcom.impls.JIObjectFactory;
import org.jinterop.dcom.impls.automation.IJIDispatch;
public class EventLogListener
{
private static final String WMI_DEFAULT_NAMESPACE = "ROOT\\CIMV2";
private static JISession configAndConnectDCom( String domain, String user, String pass ) throws Exception
{
JISystem.getLogger().setLevel( Level.OFF );
try
{
JISystem.setInBuiltLogHandler( false );
}
catch ( IOException ignored )
{
;
}
JISystem.setAutoRegisteration( true );
JISession dcomSession = JISession.createSession( domain, user, pass );
dcomSession.useSessionSecurity( true );
return dcomSession;
}
private static IJIDispatch getWmiLocator( String host, JISession dcomSession ) throws Exception
{
JIComServer wbemLocatorComObj = new JIComServer( JIProgId.valueOf( "WbemScripting.SWbemLocator" ), host, dcomSession );
return (IJIDispatch) JIObjectFactory.narrowObject( wbemLocatorComObj.createInstance().queryInterface( IJIDispatch.IID ) );
}
private static IJIDispatch toIDispatch( JIVariant comObjectAsVariant ) throws JIException
{
return (IJIDispatch) JIObjectFactory.narrowObject( comObjectAsVariant.getObjectAsComObject() );
}
public static void main( String[] args )
{
if ( args.length != 4 )
{
System.out.println( "Usage: " + EventLogListener.class.getSimpleName() + " domain host username password" );
return;
}
String domain = args[ 0 ];
String host = args[ 1 ];
String user = args[ 2 ];
String pass = args[ 3 ];
JISession dcomSession = null;
try
{
// Connect to DCOM on the remote system, and create an instance of the WbemScripting.SWbemLocator object to talk to WMI.
dcomSession = configAndConnectDCom( domain, user, pass );
IJIDispatch wbemLocator = getWmiLocator( host, dcomSession );
// Invoke the "ConnectServer" method on the SWbemLocator object via it's IDispatch COM pointer. We will connect to
// the default ROOT\CIMV2 namespace. This will result in us having a reference to a "SWbemServices" object.
JIVariant results[] =
wbemLocator.callMethodA( "ConnectServer", new Object[] { new JIString( host ), new JIString( WMI_DEFAULT_NAMESPACE ),
JIVariant.OPTIONAL_PARAM(), JIVariant.OPTIONAL_PARAM(), JIVariant.OPTIONAL_PARAM(), JIVariant.OPTIONAL_PARAM(), new Integer( 0 ),
JIVariant.OPTIONAL_PARAM() } );
IJIDispatch wbemServices = toIDispatch( results[ 0 ] );
// Now that we have a SWbemServices DCOM object reference, we prepare a WMI Query Language (WQL) request to be informed whenever a
// new instance of the "Win32_NTLogEvent" WMI class is created on the remote host. This is submitted to the remote host via the
// "ExecNotificationQuery" method on SWbemServices. This gives us all events as they come in. Refer to WQL documentation to
// learn how to restrict the query if you want a narrower focus.
final String QUERY_FOR_ALL_LOG_EVENTS = "SELECT * FROM __InstanceCreationEvent WHERE TargetInstance ISA 'Win32_NTLogEvent'";
final int RETURN_IMMEDIATE = 16;
final int FORWARD_ONLY = 32;
JIVariant[] eventSourceSet =
wbemServices.callMethodA( "ExecNotificationQuery", new Object[] { new JIString( QUERY_FOR_ALL_LOG_EVENTS ), new JIString( "WQL" ),
new JIVariant( new Integer( RETURN_IMMEDIATE + FORWARD_ONLY ) ) } );
IJIDispatch wbemEventSource = (IJIDispatch) JIObjectFactory.narrowObject( ( eventSourceSet[ 0 ] ).getObjectAsComObject() );
// The result of the query is a SWbemEventSource object. This object exposes a method that we can call in a loop to retrieve the
// next Windows Event Log entry whenever it is created. This "NextEvent" operation will block until we are given an event.
// Note that you can specify timeouts, see the Microsoft documentation for more details.
while ( true )
{
// this blocks until an event log entry appears.
JIVariant eventAsVariant = (JIVariant) ( wbemEventSource.callMethodA( "NextEvent", new Object[] { JIVariant.OPTIONAL_PARAM() } ) )[ 0 ];
IJIDispatch wbemEvent = toIDispatch( eventAsVariant );
// WMI gives us events as SWbemObject instances (a base class of any WMI object). We know in our case we asked for a specific object
// type, so we will go ahead and invoke methods supported by that Win32_NTLogEvent class via the wbemEvent IDispatch pointer.
// In this case, we simply call the "GetObjectText_" method that returns us the entire object as a CIM formatted string. We could,
// however, ask the object for its property values via wbemEvent.get("PropertyName"). See the j-interop documentation and examples
// for how to query COM properties.
JIVariant objTextAsVariant = (JIVariant) ( wbemEvent.callMethodA( "GetObjectText_", new Object[] { new Integer( 1 ) } ) )[ 0 ];
String asText = objTextAsVariant.getObjectAsString().getString();
System.out.println( asText );
}
}
catch ( Exception e )
{
e.printStackTrace();
}
finally
{
if ( null != dcomSession )
{
try
{
JISession.destroySession( dcomSession );
}
catch ( Exception ex )
{
ex.printStackTrace();
}
}
}
}
}
~
On the Java side, you'll need a library that allows you to make native calls. Sun offers JNI, but it sounds like sort of a pain. Also consider:
- https://github.com/twall/jna/
- http://johannburkard.de/software/nativecall/
- http://www.jinvoke.com/
On the Windows side, the function you're after is OpenEventLog. This should allow you to access a remote event log. See also Querying for Event Information.
If that doesn't sound right, I also found this for parsing the log files directly (not an approach I'd recommend but interesting nonetheless):
- http://msdn.microsoft.com/en-us/library/bb309026.aspx
- http://objectmix.com/java/75154-regarding-windows-event-log-file-parser-java.html
Read this article.
JNA 3.2.8 has both methods to read and write from the Windows event log.
You can see an example of write in log4jna.
Here's an example of read:
EventLogIterator iter = new EventLogIterator("Application");
while(iter.hasNext()) {
EventLogRecord record = iter.next();
System.out.println(record.getRecordNumber()
+ ": Event ID: " + record.getEventId()
+ ", Event Type: " + record.getType()
+ ", Event Source: " + record.getSource());
}
If you want true event log access from a remote machine, you will have to find a library which implements the EventLog Remoting Protocol Specification. Unfortunately, I have not yet found any such library in Java. However, much of the foundation for implementing this protocol has already been laid by the JCIFS and JARAPAC projects. The protocol itself (if I'm not mistaken) runs on top of the DCE/RPC protocol (implemented by JARAPAC) which itself runs on top of the SMB protocol (implemented by JCIFS).
I have already been using JCIFS and JARAPAC to implement some of EventLog's cousin protocols, such as remote registry access. I may be blind, but documentation seemed a little scarce regarding JARAPAC. If you are interested in implementing this, I can share with you what I have learned when I get some spare time!
Later!