C# Tips | ログ・例外・診断:ログ削除

C# C#
スポンサーリンク

はじめに:「ログ削除」は“未来のトラブル調査”と“ディスク容量”の綱引き調整

ちゃんとログを出し始めると、次に必ず出てくるのが「ログが消えない問題」です。
ログは残しておきたい、でもディスクは有限。このバランスを取るために必要なのが ログ削除の仕組み です。

ここで大事なのは、「なんとなく古そうだから消す」ではなく、
「何日分は必ず残す」「圧縮済みは何ヶ月残す」といった“ルールをコードにする”ことです。
そのルールを C# の小さなユーティリティとして形にしていきます。


ログ削除の考え方を整理する

「どこまで遡れれば十分か」を先に決める

まず最初に決めるべきは、「どこまで遡ってログを見られれば業務的に十分か」です。
例えば、次のようなイメージです。

アプリケーションログは 30 日分あれば十分
監査ログは 1 年分必要
圧縮済みログは 90 日分残せばよい

この「保管期間」が決まると、削除のルールが一気にシンプルになります。
「作成日時が X 日より前のファイルは削除する」という形に落とし込めるからです。

ここでの重要ポイントは、「技術側だけで勝手に期間を決めない」ことです。
監査・法務・業務担当と話して、「どこまで必要か」を合意してからコードにしましょう。


基本形:古いログファイルを日数で削除する

DirectoryInfo と FileInfo を使ったシンプルな削除

まずは一番シンプルな「N 日より前のログを削除する」ユーティリティを書いてみます。

using System;
using System.IO;

public static class LogCleaner
{
    public static void DeleteOldLogs(string logDirectory, string searchPattern, int keepDays)
    {
        var dir = new DirectoryInfo(logDirectory);
        if (!dir.Exists) return;

        var threshold = DateTime.Now.AddDays(-keepDays);

        foreach (var file in dir.GetFiles(searchPattern))
        {
            if (file.CreationTime < threshold)
            {
                file.Delete();
            }
        }
    }
}
C#

使い方の例は次の通りです。

// 拡張子 .log のログを 30 日より前のものは削除
LogCleaner.DeleteOldLogs("logs", "*.log", keepDays: 30);

// 圧縮済みログ (.zip) を 90 日より前のものは削除
LogCleaner.DeleteOldLogs("logs", "*.zip", keepDays: 90);
C#

ここでの重要ポイントは、「削除対象を searchPattern で絞る」ことです。
*.logapp-*.log のようにしておけば、同じフォルダに他のファイルがあっても巻き込まずに済みます。


ファイル名に日付が入っている場合の削除

「ファイル名の日付」を基準にするパターン

ログローテーションで、ファイル名に日付を含めているケースも多いです。
例として、次のようなファイル名を考えます。

app-20260415.log
app-20260416.log
app-20260417.log

この場合、「ファイルの作成日時」ではなく「ファイル名の中の日付」で判定したいことがあります。
バックアップやコピーの影響で CreationTime が変わることがあるからです。

using System;
using System.IO;
using System.Globalization;

public static class LogCleanerByName
{
    public static void DeleteOldLogsByDateInName(string logDirectory, int keepDays)
    {
        var dir = new DirectoryInfo(logDirectory);
        if (!dir.Exists) return;

        var threshold = DateTime.Today.AddDays(-keepDays);

        foreach (var file in dir.GetFiles("app-*.log"))
        {
            var nameWithoutExt = Path.GetFileNameWithoutExtension(file.Name); // app-20260415
            var datePart = nameWithoutExt.Substring("app-".Length);          // 20260415

            if (!DateTime.TryParseExact(
                    datePart,
                    "yyyyMMdd",
                    CultureInfo.InvariantCulture,
                    DateTimeStyles.None,
                    out var date))
            {
                continue;
            }

            if (date < threshold)
            {
                file.Delete();
            }
        }
    }
}
C#

使い方はシンプルです。

LogCleanerByName.DeleteOldLogsByDateInName("logs", keepDays: 30);
C#

ここでの重要ポイントは、「ファイル名の規則を決めておくと、削除ロジックがシンプルになる」ことです。
app-YYYYMMDD.log のような一貫した命名は、ローテーション・圧縮・削除すべてで効いてきます。


圧縮ログと生ログで保管期間を分ける

「生ログは短く、圧縮ログは長く」残す設計

前の会話で触れたように、ログ圧縮とログ削除はセットで考えるときれいです。
例えば、次のようなルールをコードに落とし込めます。

生ログ(.log)は 7 日分だけ残す
圧縮ログ(.zip / .gz)は 90 日分残す

これをユーティリティとしてまとめると、こんなイメージになります。

public static class LogMaintenance
{
    public static void Cleanup(string logDirectory)
    {
        // 生ログは 7 日で削除
        LogCleaner.DeleteOldLogs(logDirectory, "*.log", keepDays: 7);

        // 圧縮ログは 90 日で削除
        LogCleaner.DeleteOldLogs(logDirectory, "*.zip", keepDays: 90);
        LogCleaner.DeleteOldLogs(logDirectory, "*.gz", keepDays: 90);
    }
}
C#

アプリ起動時や、専用のバッチからこう呼び出します。

LogMaintenance.Cleanup("logs");
C#

ここでの重要ポイントは、「“どの拡張子のログを何日残すか”をコードとして明文化する」ことです。
これを 1 箇所にまとめておくと、運用ルールの変更にも強くなります。


削除処理をいつ・どこで動かすか

「本処理とは切り離したメンテナンス」として動かす

ログ削除は、基本的に「定期メンテナンス」です。
本来の業務処理とは切り離して、次のようなタイミングで動かすのが現実的です。

コンソールアプリなら、起動時に一度だけ Cleanup を呼ぶ
Web アプリやサービスなら、Windows タスクスケジューラや cron で、1 日 1 回専用バッチを実行する

例えば、ログメンテナンス専用の小さなコンソールアプリを作っておきます。

static void Main(string[] args)
{
    var logDir = args.Length > 0 ? args[0] : "logs";
    LogMaintenance.Cleanup(logDir);
}
C#

これを OS のスケジューラから毎日実行するだけで、
ログ削除が自動で回り続けます。

ここでの重要ポイントは、「ログ削除が本処理のパフォーマンスや安定性に影響しないようにする」ことです。
重い削除処理を業務ピーク時間に走らせるのは避けましょう。


「消しすぎない」ための注意点

削除前に一度は「本当に消していいか」を確認する

ログ削除は、一度やると元には戻せません。
なので、最初に導入するときは、次のようなステップを踏むと安全です。

最初の数日は「削除対象をログに出すだけ」にする
削除対象の一覧を見て、「本当に消してよいファイルだけが対象になっているか」を確認する
問題なさそうなら、実際の file.Delete() を有効にする

例えば、こういう形にしておきます。

public static void DeleteOldLogsWithPreview(string logDirectory, string searchPattern, int keepDays, bool dryRun)
{
    var dir = new DirectoryInfo(logDirectory);
    if (!dir.Exists) return;

    var threshold = DateTime.Now.AddDays(-keepDays);

    foreach (var file in dir.GetFiles(searchPattern))
    {
        if (file.CreationTime < threshold)
        {
            Console.WriteLine($"Delete target: {file.FullName}");

            if (!dryRun)
            {
                file.Delete();
            }
        }
    }
}
C#

最初は dryRun: true で実行し、
対象が妥当だと確認できたら dryRun: false に切り替えます。

ここでの重要ポイントは、「“削除ロジックのテスト”を、実際に消す前に必ずやる」ことです。
特にパターンマッチ(*.log など)を間違えると、関係ないファイルまで消してしまう危険があります。


まとめ:「ログ削除ユーティリティ」は“ログを守りつつディスクも守るための最後の番人”

ログ削除の本質は、

必要な期間分のログはきちんと残しつつ、
それを超えたものは自動で整理し、
ディスクを健全な状態に保つこと

です。

押さえておきたいポイントは次の通りです。

「どこまで遡れれば十分か」を業務側と合意し、その日数をコードに落とすこと。
DirectoryInfo / FileInfo を使えば、「N 日より前のファイルを削除する」処理はシンプルに書けること。
ファイル名に日付を含めておくと、名前ベースで削除対象を判定できて、ローテーション・圧縮と連携しやすいこと。
生ログと圧縮ログで保管期間を分け、「短期は生ログ・長期は圧縮ログ」という設計にするとバランスが良いこと。
削除処理は本処理とは切り離し、定期バッチとして安全な時間帯に動かすこと。
導入時は必ず「ドライラン(削除対象の確認)」を挟んでから本番削除に切り替えること。

ここまで腹落ちしていれば、
「ディスクがいっぱいになってから慌てて手動削除する」状態から卒業して、
“計画的にログを残し、計画的にログを消す”エンジニアに一歩近づけます。

タイトルとURLをコピーしました