Architect's Log

I'm a Cloud Architect. I'm highly motivated to reduce toils with driving DevOps.

log4netのログファイル出力エラーをイベントでハンドリングする

どういうこと?

log4netは内部で例外を握りつぶしているようで、アプリケーション側でtry-catchを使用してもログファイル出力エラーを補足できません。
カスタムエラーハンドラーを作成すればlog4net内で例外を補足することはできますが、そのエラーハンドラーから例外を再スローしても握りつぶされてしまうため、アプリケーション側では補足できません。
そこで、例外発生をアプリケーションにイベントで通知する方法を紹介します。

どうすれば?

ソースコード

カスタムエラーハンドラーを作成します。

using System;
using log4net.Core;

namespace ErrorHandling {
    public delegate void ErrorOccurredEventHandler(object sender, ErrorOccurredEventArgs e);

    public class CustomErrorHandler : IErrorHandler {
        public event ErrorOccurredEventHandler ErrorOccurred = null;

        protected virtual void OnErrorOccurred(ErrorOccurredEventArgs e) {
            if (this.ErrorOccurred == null)
                return;
            this.ErrorOccurred(this, e);
        }

        public void Error(string message) {
            this.OnErrorOccurred(new ErrorOccurredEventArgs(message));
        }

        public void Error(string message, Exception e) {
             this.OnErrorOccurred(new ErrorOccurredEventArgs(message, e));
       }

        public void Error(string message, Exception e, ErrorCode errorCode) {
            this.OnErrorOccurred(new ErrorOccurredEventArgs(message, e, errorCode));
        }
    }

    public class ErrorOccurredEventArgs : EventArgs {
        public string Message { get; private set; }
        public Exception Exception { get; private set; }
        public bool HasErrorCode { get; private set; }
        public ErrorCode ErrorCode { get; private set; }

        public ErrorOccurredEventArgs(string message)
            : this(message, null) {
        }

        public ErrorOccurredEventArgs(string message, Exception exception) {
            this.Message = message;
            this.Exception = exception;
            this.HasErrorCode = false;
        }

        public ErrorOccurredEventArgs(string message, Exception exception, ErrorCode errorCode) {
            this.Message = message;
            this.Exception = exception;
            this.HasErrorCode = true;
            this.ErrorCode = ErrorCode;
        }
    }
}
構成ファイル

作成したカスタムエラーハンドラーをハンドラーに設定します。
以下構成ファイルより抜粋。

<log4net>
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
        <!-- 省略 -->
        <errorHandler type="ErrorHandling.CustomErrorHandler" />
    </appender>
    <root>
        <level value="DEBUG" />
        <appender-ref ref="RollingFileAppender" /> 
    </root>
</log4net>

ご注意

カスタムエラーハンドラーが検出できるのはログファイル出力エラーです。DBログ出力エラーを検出することはできません。log4netのAdoNetAppenderクラスにErrorHandlerプロパティがないためです。
その他のAppenderは試していませんが、ErrorHandlerプロパティがあれば、そのままこのカスタムエラーハンドラーを使用できると思います。