.NET Framework でイベントログに書き込むとき、イベントソースの存在確認をしてはいけない

.NET Framework でイベントログに書き込むときは System.Diagnostics名前空間の System.Diagnostics.Eventlog クラスを用いる。

ここでよくあるサンプルは次のような感じになっている。

Sample 1

using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            const string EVENTSOURCE = "EventSource1";
            const string MESSAGE = "EventLogTest";

            if (!EventLog.SourceExists(EVENTSOURCE))
            {
                EventLog.CreateEventSource(EVENTSOURCE, null);
            }
            EventLog.WriteEntry(EVENTSOURCE, MESSAGE);

        }
    }
}

ところが、このサンプルにはよくない。

Sample 1 で登場する EventLog クラスのメソッドの動作の概要は次の通り。

SourceExists イベントソースが登録されているかを返す。管理者権限が無ければ例外エラーになる。
CreateEventSource イベントソースを登録する。既に対象のイベントソースが登録されているならば例外エラーになる。また、管理者権限が無ければ例外エラーになる。
WriteEntry イベントログに指定したイベントソースで書き込む。対象のイベントソースが登録されていない場合、管理者権限があれば新たにイベントソースを登録する。対象のイベントソースが登録されていない場合で管理者権限が無ければ例外エラーになる。

つまり、

Sample 1 は管理者権限が無ければ動作しない

のである。

特に、最新の Visual Studio は動かす際に管理者権限が無くても良くなっているが、一昔前の Visual Studio は動かす時に管理者権限が必要だったため、 Sample 1 のバグ(僕はこれはバグであると断じる)は

単体テストで OK で、結合テストで NG

となる可能性が高いものである。

前にいたプロジェクトで Sample 1のようなイベントソースの存在チェックをおこなっているバグがに遭遇したことがあったし、実際にググったら Sample 1 のようなものしか出てこない。(2016-9-12 現在)

正しくは次のようにする必要がある。

Sample 2

using System.Diagnostics;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            const string EVENTSOURCE = "EventSource1";
            const string MESSAGE = "EventLogTest";

            EventLog.WriteEntry(EVENTSOURCE, MESSAGE);
        }
    }
}

Sample 1 と Sample 2 の動作の違いは次のようになる。

 

管理者権限がある場合 管理者権限がない場合
イベントソースが登録済 イベントソースが未登録 イベントソースが登録済 イベントソースが未登録
Sample 1 イベントログに書き込まれる。 イベントソースを登録してイベントログに書き込まれる (管理者権限が無いことによる)例外エラー (管理者権限が無いことによる)例外エラー
Sample 2 イベントログに書き込まれる。 イベントソースを登録してイベントログに書き込まれる イベントログに書き込まれる。 (管理者権限が無いことによる)例外エラー

したがって、.NET Framework でイベントログに書き込むプログラムを作る場合、次の点に注意する。

  • イベントソースの存在チェックを行ってはいけない。
  • イベントソースは構築時に別プログラムや PowerShell  Script を管理者権限のある状態で動かして登録する、管理者権限がある状態で動かすインストーラに組み込むなどして事前に登録しておくようにする。