2013年8月30日金曜日

なつがおわりまーす

とか言いつつ今日は暑いです。

えー、8月も間もなく終わり、前回のエントリからほぼ3週間ですか。思いの外サボってしまいました…。

そんな時期もありますよね(白目)。ボチボチいきます…。

2013年8月10日土曜日

古くなったログファイルを削除する処理をC#+LINQで。

何かしら業務で利用するシステムであれば、大抵何かあったときの障害解析などに備え、ログファイルが作られることになります。で、そのログファイルは作られっぱなしだとそのうちディスクを圧迫するので、ある一定のルールの下で削除されていくことになるのが一般的だと思います。

その「削除されるルール」は、当然すべてきれいさっぱり消してしまっては、いざという時に役に立たないし、またシステムによってログの残し方の要件が違ったりで、設計や実装も結構厄介なもんです。

ここでは、いくつかログの残し方ルールをあげて、古いログファイルを削除する処理をC#+LINQを使って書いてみようと思います。

ログファイルの構成前提


前提として、ログファイルは以下のような形で残されていることとします。

  • ログフォルダにはログファイルのみが存在する。
  • ログファイルのファイル名は「log_yyyy-MM-dd_nn.log」とする。ただし、yyyyは西暦の4ケタ。MMは月の2ケタ。ddは日の2ケタ。nnはシーケンシャル番号の2ケタ。
  • ログファイルは以下のタイミングで新たに生成される。ただし、同一日のログファイルが存在しない場合はシーケンシャル番号は「0」。存在する場合はシーケンシャル番号の最大+1になるようにする。
    • 日が変わった以降最初のログ出力時。
    • 現在のログファイルが所定のサイズ以上になった時。
    • サービスの起動時。
そしてログファイル削除クラスとしてスタティックな「DeleteOldLogFiles」クラスを作ることとします。

バリエーション1:指定ファイル数のみ残して古いものを削除


指定された数のより新しいファイルを残し、それ以外のものを削除する仕様として処理を考えます。この場合、ファイル名を降順でソートして、新しいほうから所定の数は何もせず、それ以降はファイルを削除する。という処理を考えてみました。

ForEachメソッドを使いたいので、IEnumerableを一旦Listに変換しています。ま、しょうがない。

public static void ByCounts(string path, int keepCount)
{
  Directory.GetFiles(path)
    .OrderByDescending(f => f)
    .Skip(keepCount)
    .ToList()
    .ForEach(f => File.Delete(f));
}

ま、シンプルですよね。

バリエーション2:指定ファイルサイズに収まるようにして古いものを削除


バリエーション1の変形ですが、新しいほうからファイルサイズを足して行って、所定のサイズ以下のうちはスキップし、所定のサイズを超えた以降のファイルを削除する処理とするとシンプルに書けますね。

public static void BySize(string path, long size)
{
  long totalSize = 0;

  Directory.GetFiles(path)
    .OrderByDescending(f => f)
    .SkipWhile(f => (totalSize += new FileInfo(f).Length) < size)
    .ToList()
    .ForEach(f => File.Delete(f));
}


バリエーション3:日付が古いものを削除


ファイル名から日付部分を抜き出して、指定日数より以前のものは削除する仕様とします。このメソッドの引数「path」と「days」はまあいいとして、「startPos」はファイル名中の日付文字列の開始位置を、「dateForm」は日付文字列の解析フォームを渡します。

public static void ByDays(string path, int days, int startPos, string dateForm)
{
  var target = DateTime.Today.AddDays(-days);

  Directory.GetFiles(path)
    .Where(f => DateTime.ParseExact(
      Path.GetFileName(f).Substring(startPos, dateForm.Length),
      dateForm,
      System.Globalization.DateTimeFormatInfo.InvariantInfo) < target)
    .ToList()
    .ForEach(f => File.Delete(f));
}

今回のケースでログファイルの保持日数を3日とすると、ログファイル名は「log_yyyy-MM-dd_nn.log」なので、「startPos」は4、「dateForm」は「yyyy-MM-dd」となるので、以下のようにこのメソッドを呼び出すことになります。

DeleteOldLogFiles.ByDays(logFolderPath, 3, 4, "yyyy-MM-dd");


2013年8月1日木曜日

今週のマガジン

『あひるの空』が凄かった。なんだこれ?数週間のモヤモヤを一気に晴らす10ページ。一瞬何が起きたのかわからないくらいの一気。いやー。スゲー。
しかし、「どこに負けるか」をバラしておいて、「敗戦までのキセキ」を描くんですか。是非描ききってほしいです。

『ベイビーステップ』も暗い話にならなくて良かった。とはいえバッドエンドの地雷は埋まったままなので、ここからどう展開するのか楽しみ。