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

标签 java windows logging

有什么方法可以从 java 类访问 Windows 事件日志。有没有人为此编写过任何 API,是否有任何方法可以从远程计算机访问数据?

场景是:

我从一个控制 Java 进程在远程机器上运行一个进程。 这个远程进程将内容记录到事件日志中,我希望能够在控制进程中看到它。

提前致谢。

最佳答案

http://www.j-interop.org/是实现 DCOM 协议(protocol)规范的开源 Java 库,不使用任何 native 代码。 (即,您可以使用它从运行在非 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,并且已在任何防火墙中设置适当的异常(exception)。可以在线搜索有关这方面的详细信息,也可以从上面的 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();
                }
            }
        }
    }

}

~

关于java - 如何从 Java 访问 Windows 事件查看器日志数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/310355/

相关文章:

java - 打印大型 Swing 组件

java - 致命异常 : AsyncTask #1

windows - 使用命令行创建在特定用户登录时运行的计划任务

python - 重置 python 解释器以进行日志记录

python - 哪个记录器在gunicorn 中输出这个?

java - 请解释这个 Java 数组引用参数传递行为

java - 使用字符串变量时读取文件的差异

windows - "rm -rf"等同于 Windows PowerShell?

c++ - 如何编写可以在 Linux 和 Windows 中轻松编译的 C++ 程序?

logging - 如何在控制台上抑制 Hadoop 日志消息