出力したログと全く同じ内容を文字列で取得

log4netを使っていて、「××がログファイルに出力されること」みたいなテスト項目があったとする。無理矢理ログファイルをオープンして読む以外に自動的に確認する方法はあるんだろうか。

ということで、ファイルに書き込むと同時にStringWriterにも同じ内容を書き込んで保持しておくAppenderを作って使ってみる。

using System;
using System.IO;

using log4net.Appender;
using log4net.Core;
using log4net.Util;

namespace MyTest
{
    class TapFileAppender : FileAppender
    {
        private TapQuietTextWriter _tapWriter = null;

        // FileAppenderが書き込みに使う QuietWriter プロパティを
        // セットする箇所を上書き
        override protected void SetQWForFiles(TextWriter writer)
        {
            _tapWriter = new TapQuietTextWriter(writer, ErrorHandler);
            base.SetQWForFiles(_tapWriter);
        }

        public String Text
        {
            get { return _tapWriter.Text; }
        }
    }

    class TapQuietTextWriter : QuietTextWriter
    {
        private StringWriter _tapWriter;

        public TapQuietTextWriter(TextWriter writer, IErrorHandler errorHandler)
            : base(writer, errorHandler)
        {
            _tapWriter = new StringWriter();
        }

        public override void Write(char value)
        {
            base.Write(value);
            _tapWriter.Write(value);
        }

        public override void Write(char[] buffer, int index, int count)
        {
            base.Write(buffer, index, count);
            _tapWriter.Write(buffer, index, count);
        }

        override public void Write(string value)
        {
            base.Write(value);
            _tapWriter.Write(value);
        }

        public String Text
        {
            get { return _tapWriter.ToString(); }
        }
    }
}

log4netの設定で、AppenderをTapFileAppenderに変更

	<log4net>
		<appender name="LogFileAppender"
				  type="MyTest.TapFileAppender" >
			<param name="File" value="..\log\test.log" />
			<param name="AppendToFile" value="true" />
			<layout type="log4net.Layout.PatternLayout">
				<param name="ConversionPattern"
					   value="%d [%t] %-5p %c - %m%n" />
			</layout>
		</appender>

		<root>
			<level value="ALL" />
			<appender-ref ref="LogFileAppender" />
		</root>
	</log4net>

Appenderのうまい取得方法がよく分からないが、とりあえず以下のように取れるらしい。

  log4net.Repository.Hierarchy.Hierarchy repo =
      (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository();
  TapFileAppender appender =
      (TapFileAppender)repo.Root.GetAppender("LogFileAppender");

appender.Text で書き込んだ全内容が取れる。

だめなところ

  • FileAppenderのAppendメソッドをオーバーライドしてRenderLoggingEventを呼ぶだけで同じことができるような気もする。とりあえず中身がよくわからないので「ファイルに出力したのと全く同じ内容」にするため、より低レベルなところ(ファイル書き込み寸前のところ)でキャプチャーするようにしてみた。
  • 中身はStringWriterなのでそのままではマルチスレッドでは悲惨になるっぽい。
  • 一回 Text プロパティを読んだらそこまでの内容は捨てるようにしたい。
  • log4netを使いはじめて2時間のド素人なので何が正しいのかよくわからない。