Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I access Windows Event Viewer log data from Java

Is there any way to access the Windows Event Log from a java class. Has anyone written any APIs for this, and would there be any way to access the data from a remote machine?

The scenario is:

I run a process on a remote machine, from a controlling Java process. This remote process logs stuff to the Event Log, which I want to be able to see in the controlling process.

Thanks in advance.

like image 591
Matthew Farwell Avatar asked Nov 21 '08 22:11

Matthew Farwell


People also ask

How do you access Event Viewer logs?

Open "Event Viewer" by clicking the "Start" button. Click "Control Panel" > "System and Security" > "Administrative Tools", and then double-click "Event Viewer" Click to expand "Windows Logs" in the left pane, and then select "Application".

What is event Logging in Java?

Event Logging is a Java API for logging audit events conforming to the Event Logging XML Schema. The API uses a generated Java JAXB model of the Event Logging XML Schema.


1 Answers

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();
                }
            }
        }
    }

}

~

like image 76
Dan Fleet Avatar answered Sep 20 '22 06:09

Dan Fleet