【VB.NET】EventLogReaderクラスを使ってイベントログを高速に取得する

【VB.NET】EventLogReaderクラスを使ってイベントログを高速に取得する
ネットワーク経由でサーバーや他PCのイベントログを取得するサンプルです。

VB.NETではEventLogクラスとEventLogReaderクラスを使う方法がありますが、EventLogReaderクラスの方が高速です。

以下、サンプルコードになります。


Eventing.Reader.EventLogReaderを用いたイベントログ取得関数

'' Eventing.Reader.EventLogReaderを使用したイベントログ取得メソッド
Private Sub readEventLog()
    ''暗号化文字列
    Dim secPassword As Security.SecureString = Nothing
    ''EventLogReaderクラス
    Dim evReader As Eventing.Reader.EventLogReader = Nothing
    ''クエリ定義クラス
    Dim evQuery As Eventing.Reader.EventLogQuery = Nothing
    ''イベント読み取りクエリ
    Dim queryString As String = "*[System[(Level=1 or Level=2 or Level=3)]]"
    ''イベントプロパティのレコード
    Dim evRecord As Eventing.Reader.EventRecord = Nothing

    Try
        ''SecureStringにリモートコンピュータのパスワードを設定
        secPassword = New Security.SecureString
        For Each c As Char In "[パスワード]" ''←パスワード文字列を一つずつ格納する
            secPassword.AppendChar(c)
        Next

        ''クエリ定義クラスのインスタンス生成
        evQuery = New Eventing.Reader.EventLogQuery(
                  "Application" _
                  , Eventing.Reader.PathType.LogName _
                  , queryString)

        ''イベントログにアクセスするセッション生成
        evQuery.Session = New Eventing.Reader.EventLogSession(
                            "[接続するコンピュータ名]" _
                            , "[ドメイン名]" _
                            , "[ユーザー名]" _
                            , secPassword _
                            , Eventing.Reader.SessionAuthentication.Default)

        ''新しいログから取得する
        evQuery.ReverseDirection = True

        ''クエリを指定してEventLogReaderインスタンスを生成
        evReader = New Eventing.Reader.EventLogReader(evQuery)

        While True
            ''イベントログを1行取得
            evRecord = evReader.ReadEvent()
            ''取得できない場合は処理終了
            If evRecord Is Nothing Then
                Exit While
            End If
            ''イベントログの値を出力
            Console.WriteLine(evRecord.TimeCreated.ToString)
            Console.WriteLine(evRecord.FormatDescription)
            Console.WriteLine(evRecord.LevelDisplayName)
            Console.WriteLine(evRecord.LogName)
            Console.WriteLine(evRecord.MachineName)
        End While

    Catch ex As Exception
        Console.WriteLine(ex.Message)
    Finally
        If secPassword IsNot Nothing Then secPassword.Dispose()
    End Try
End Sub

処理説明

以下、簡単な処理説明です。

パスワードの設定

まず初めに、接続先PCのパスワードを設定しています。

パスワードは「Security.SecureString」に格納する必要があります。
secPassword = New Security.SecureString
For Each c As Char In "[パスワード]"
    secPassword.AppendChar(c)
Next

イベントログ検索クエリの生成

次に、EventLogQueryクラスを用いて、イベントログを検索するクエリを作成します。

クエリは一見すると複雑そうですが、イベントビューアから簡単に自動生成することが出来ます。

イベントビューアを開いて、右側の「操作」ウィンドウから「現在のログをフィルタ...」を選択します。


イベントログを検索する条件を設定します。


XMLタブを開くと、条件に沿ったXMLが表示されます。


このXMLの「<Select Path="Application>」から「</Select>」までの間に書かれている文字列がクエリになります。

コピペでOKですが、「&gt;」と「&lt;」はVBソース上では「>」「<」に書き換える必要があります。

セッション生成

EventLogSessionクラスのインスタンスを生成します。

引数は以下の通りです。
  • 接続するコンピュータ名
  • ドメイン名
  • ログインユーザー名(イベントログ取得権限ありのユーザー)
  • パスワード(Security.SecureString)
  • 認証の種類(SessionAuthentication)

ログの読み込み順

ReverseDirectionプロパティがTrueの場合は日付の降順(新しいログから)、Falseの場合は昇順(古いログから)で読み込みを行います。

クエリ指定

EventLogReaderクラスのコンストラクタにEventLogQueryを指定して、インスタンスを生成します。
evReader = New Eventing.Reader.EventLogReader(evQuery)

イベントログの取得

While文でループしてログを取得しています。

ReadEventメソッドでログを1レコード取得して、Nothingの場合は処理終了としています。


System.Diagnostics.EventLogは遅くて使い物にならない

EventLogクラスでもイベントログを取得できますが、遅すぎて使い物になりません。

ローカルPCのイベントログを取得するならまだ我慢出来ますが、ネットワーク経由だと処理が全然終わりませんでした。

EventLogReaderとの比較に使用したサンプルコードは以下です。
'' System.Diagnostics.EventLogを使用したイベントログ取得メソッド
Private Sub getEventLogEntry()
    Dim evLog As System.Diagnostics.EventLog = Nothing
    Try
        ''EventLogインスタンス生成
        evLog = New System.Diagnostics.EventLog()
        '' 取得するイベントログの名前
        evLog.Log = "Application"
        '' リモートコンピュータ名
        evLog.MachineName = "[コンピュータ名]"

        ''イベントログからエラーと警告のみを取得する
        Dim query = evLog.Entries.Cast(Of EventLogEntry).AsQueryable() _
            .Where(Function(item) item.EntryType = EventLogEntryType.Error _
                           OrElse item.EntryType = EventLogEntryType.Warning)

        For Each entry As EventLogEntry In query ''←リモート接続だとこココでかなり時間がかかる

            Console.WriteLine(entry.TimeGenerated.ToString) ''イベント生成時間
            Console.WriteLine(entry.EntryType) ''イベントの種類
            Console.WriteLine(entry.Message) ''メッセージ
        Next

    Catch ex As Exception
        Console.WriteLine(ex.Message)
    Finally
        If evLog IsNot Nothing Then evLog.Close()
    End Try
End Sub

まとめ

ネットワーク経由で2万件のイベントログを取得した場合、EventLogクラスでは10分以上かかりましたが、EventLogReaderクラスだと1分以内で全件取得できました。

EventLogReaderクラスは接続先のPC上でクエリを発行してデータを絞っているので、処理速度が速いのでしょうか。

EventLogクラスは設定項目が少なくて簡単に使用できますが、処理が冗長な感じです。

イベントログ取得にはEventLogReaderクラスの使用をおすすめします。

以上

<スポンサーリンク>


0 件のコメント :

コメントを投稿