如何从Java访问Windows事件查看器日志数据

时间:2008-11-21 22:01:36

标签: java windows logging

有没有办法从java类访问Windows事件日志。有没有人为此编写任何API,有没有办法从远程机器访问数据?

场景是:

我在远程计算机上运行一个进程,来自一个控制Java进程。 这个远程进程将东西记录到事件日志中,我希望能够在控制过程中看到它。

提前致谢。

5 个答案:

答案 0 :(得分:17)

http://www.j-interop.org/是一个开源Java库,可以在不使用任何本机代码的情况下实现DCOM协议规范 。 (即,您可以使用它从远程Windows客户端上运行的Java代码访问远程Windows主机上的DCOM对象。)

Microsoft通过Windows Management Instrumentation(WMI)公开了大量系统信息。 WMI可通过DCOM远程访问,Microsoft网站上存在大量有关该主题的文档。碰巧,您可以通过这个可远程访问的界面访问Windows Event Logs

通过使用j-interop,您可以远程创建WbemScripting.SWbemLocator WMI对象的实例,然后连接到远程Windows主机上的Windows Management Instrumentation(WMI)服务。从那里,您可以提交query,无论何时写入新的事件日志条目,都会通知您。

请注意,这确实要求您在远程Windows主机上正确启用并配置DCOM,并且已在任何防火墙中设置了相应的例外。可以在线搜索关于此的详细信息,并且也可以从上面的j-interop站点引用。

以下示例使用其NT域,主机名,用户名和密码连接到远程主机,并且处于循环中,在Windows记录时转储每个事件日志条目。必须为用户授予适当的远程DCOM访问权限,但不必是管理员。

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

}

答案 1 :(得分:9)

在Java方面,您需要一个允许您进行本机调用的库。 Sun提供JNI,但听起来有点痛苦。还要考虑:

在Windows端,您所使用的功能是OpenEventLog。这应该允许您访问远程事件日志。另请参阅Querying for Event Information

如果这听起来不对,我也发现这个用于直接解析日志文件(不是我推荐的但仍然有趣的方法):

答案 2 :(得分:6)

阅读this article

JNA 3.2.8有两种方法可以从Windows事件日志中读取和写入。

您可以在log4jna中看到写入的示例。

以下是阅读的示例:

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

答案 3 :(得分:1)

如果您希望从远程计算机访问真正的事件日志,则必须找到实现EventLog Remoting Protocol Specification的库。不幸的是,我还没有在Java中找到任何这样的库。但是,JCIFS和JARAPAC项目已经奠定了实施该协议的大部分基础。协议本身(如果我没有弄错的话)运行在DCE / RPC协议(由JARAPAC实现)之上,该协议本身运行在SMB协议之上(由JCIFS实现)。

我已经使用JCIFS和JARAPAC来实现一些EventLog的堂兄协议,例如远程注册表访问。我可能是盲人,但关于JARAPAC的文件似乎有点稀缺。如果你有兴趣实现这一点,我可以与你分享我在闲暇时间学到的东西!

后来!

答案 4 :(得分:1)

这里有一百万(和一个)选项;)

你可以看看sigar

http://cpansearch.perl.org/src/DOUGM/hyperic-sigar-1.6.3-src/docs/javadoc/org/hyperic/sigar/win32/EventLog.html

请注意许可......

或者你可以快速而又脏,只是定期执行(并捕获输出) D:> cscript.exe c:\ WINDOWS \ system32 \ eventquery.vbs / v

然后使用事件过滤参数来细化结果等... http://technet.microsoft.com/en-us/library/cc772995(WS.10).aspx