はじめに
C#でログを出力するLoggerを作成しました。
C#では、便利なライブラリがありますが、
ここでは、コピペですぐに使えるものを目指しています。
機能としては下記の通りです。
- ログレベル
- ログローテート
- 出力先指定
下記にC#でログ出力を行うライブラリを紹介しているリンクを記載します。
実行環境
今回実行した環境は下記の通りです。
実行環境
Microsoft .NET Framework 3.5
C#の古いバージョンでも動作するように実装しています。
使い方
使い方は下記の通りです。
Logger log = Logger.GetInstance();
log.Error("error log.");
log.Warn("warn log.");
log.Info("info log.");
log.Debug("debug log.");
出力されるログは下記の通りです。
[2020-04-08 22:38:56.081][1][DEBUG] debug log.
[2020-04-08 22:38:56.082][1][ERROR] error log.
[2020-04-08 22:38:56.082][1][WARN] warn log.
[2020-04-08 22:38:56.082][1][INFO] info log.
「1」この数字は、プロセスIDを表示しています。
Loggerの実装
configでLoggerに関する設定を行います。
app.config
<setting name="IS_LOGFILE" serializeAs="String">
<value>True</value>
</setting>
<setting name="LOG_LEVEL" serializeAs="String">
<value>3</value>
</setting>
<setting name="LOGDIR_PATH" serializeAs="String">
<value>./logs/</value>
</setting>
<setting name="LOGFILE_NAME" serializeAs="String">
<value>console</value>
</setting>
<setting name="LOGFILE_MAXSIZE" serializeAs="String">
<value>10485760</value>
</setting>
<setting name="LOGFILE_PERIOD" serializeAs="String">
<value>30</value>
</setting>
- 「IS_LOGFILE」はログファイル出力フラグです。
- 「LOG_LEVEL」はログレベルです。
- 「LOGDIR_PATH」はログファイル出力ディレクトリです。
- 「LOGFILE_NAME」はログファイル名です。
- 「LOGFILE_MAXSIZE」はログファイル最大サイズ(Byte)です。
- 「LOGFILE_PERIOD」はログ保存期間(日)です。
Loggerクラスの実装は下記の通りです。
Logger.cs
class Logger
{
/// <summary>
/// ログレベル
/// </summary>
private enum LogLevel
{
ERROR,
WARN,
INFO,
DEBUG
}
private static Logger singleton = null;
private readonly string logFilePath = null;
private readonly object lockObj = new object();
private StreamWriter stream = null;
/// <summary>
/// インスタンスを生成する
/// </summary>
public static Logger GetInstance()
{
if (singleton == null)
{
singleton = new Logger();
}
return singleton;
}
/// <summary>
/// コンストラクタ
/// </summary>
private Logger()
{
this.logFilePath = Settings.Default.LOGDIR_PATH + Settings.Default.LOGFILE_NAME + ".log";
// ログファイルを生成する
CreateLogfile(new FileInfo(logFilePath));
}
/// <summary>
/// ERRORレベルのログを出力する
/// </summary>
/// <param name="msg">メッセージ</param>
public void Error(string msg)
{
if ((int)LogLevel.ERROR <= Settings.Default.LOG_LEVEL)
{
Out(LogLevel.ERROR, msg);
}
}
/// <summary>
/// ERRORレベルのスタックトレースログを出力する
/// </summary>
/// <param name="ex">例外オブジェクト</param>
public void Error(Exception ex)
{
if ((int)LogLevel.ERROR <= Settings.Default.LOG_LEVEL)
{
Out(LogLevel.ERROR, ex.Message + Environment.NewLine + ex.StackTrace);
}
}
/// <summary>
/// WARNレベルのログを出力する
/// </summary>
/// <param name="msg">メッセージ</param>
public void Warn(string msg)
{
if ((int)LogLevel.WARN <= Settings.Default.LOG_LEVEL)
{
Out(LogLevel.WARN, msg);
}
}
/// <summary>
/// INFOレベルのログを出力する
/// </summary>
/// <param name="msg">メッセージ</param>
public void Info(string msg)
{
if ((int)LogLevel.INFO <= Settings.Default.LOG_LEVEL)
{
Out(LogLevel.INFO, msg);
}
}
/// <summary>
/// DEBUGレベルのログを出力する
/// </summary>
/// <param name="msg">メッセージ</param>
public void Debug(string msg)
{
if ((int)LogLevel.DEBUG <= Properties.Settings.Default.LOG_LEVEL)
{
Out(LogLevel.DEBUG, msg);
}
}
/// <summary>
/// ログを出力する
/// </summary>
/// <param name="level">ログレベル</param>
/// <param name="msg">メッセージ</param>
private void Out(LogLevel level, string msg)
{
if (Settings.Default.IS_LOGFILE)
{
int tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
string fullMsg = string.Format("[{0}][{1}][{2}] {3}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), tid, level.ToString(), msg);
lock (this.lockObj)
{
this.stream.WriteLine(fullMsg);
FileInfo logFile = new FileInfo(this.logFilePath);
if (Settings.Default.LOGFILE_MAXSIZE < logFile.Length)
{
// ログファイルを圧縮する
CompressLogFile();
// 古いログファイルを削除する
DeleteOldLogFile();
}
}
}
}
/// <summary>
/// ログファイルを生成する
/// </summary>
/// <param name="logFile">ファイル情報</param>
private void CreateLogfile(FileInfo logFile)
{
if (!Directory.Exists(logFile.DirectoryName))
{
Directory.CreateDirectory(logFile.DirectoryName);
}
this.stream = new StreamWriter(logFile.FullName, true, Encoding.UTF8)
{
AutoFlush = true
};
}
/// <summary>
/// ログファイルを圧縮する
/// </summary>
private void CompressLogFile()
{
this.stream.Close();
string oldFilePath = Settings.Default.LOGDIR_PATH + Settings.Default.LOGFILE_NAME + "_" + DateTime.Now.ToString("yyyyMMddHHmmss");
File.Move(this.logFilePath, oldFilePath + ".log");
FileStream inStream = new FileStream(oldFilePath + ".log", FileMode.Open, FileAccess.Read);
FileStream outStream = new FileStream(oldFilePath + ".gz", FileMode.Create, FileAccess.Write);
GZipStream gzStream = new GZipStream(outStream, CompressionMode.Compress);
int size = 0;
byte[] buffer = new byte[Settings.Default.LOGFILE_MAXSIZE + 1000];
while (0 < (size = inStream.Read(buffer, 0, buffer.Length)))
{
gzStream.Write(buffer, 0, size);
}
inStream.Close();
gzStream.Close();
outStream.Close();
File.Delete(oldFilePath + ".log");
CreateLogfile(new FileInfo(this.logFilePath));
}
/// <summary>
/// 古いログファイルを削除する
/// </summary>
private void DeleteOldLogFile()
{
Regex regex = new Regex(Settings.Default.LOGFILE_NAME + @"_(\d{14}).*\.gz");
DateTime retentionDate = DateTime.Today.AddDays(-Settings.Default.LOGFILE_PERIOD);
string[] filePathList = Directory.GetFiles(Settings.Default.LOGDIR_PATH, Settings.Default.LOGFILE_NAME + "_*.gz", SearchOption.TopDirectoryOnly);
foreach (string filePath in filePathList)
{
Match match = regex.Match(filePath);
if (match.Success)
{
DateTime logCreatedDate = DateTime.ParseExact(match.Groups[1].Value.ToString(), "yyyyMMddHHmmss", null);
if (logCreatedDate < retentionDate)
{
File.Delete(filePath);
}
}
}
}
}
スレッドセーフにするために、lockを使用しています。
さいごに
ソースコードをGitHubに公開しています。
以上です。