C# Tips | ログ・例外・診断:ログ圧縮

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

はじめに:「ログ圧縮」は“ログを捨てずに、場所だけ小さくする”技

業務システムでログをちゃんと出し始めると、必ずぶつかる問題があります。
「ディスクがログでパンパンになる」です。

ログは残しておきたい。
でも、無限にディスクは使えない。

このジレンマを解決する代表的な手段が ログ圧縮 です。
ざっくり言うと、

古いログは ZIP などで圧縮してサイズを小さくし、
必要になったときだけ解凍して中身を見る

という運用です。

ここでは、プログラミング初心者向けに

なぜログ圧縮が必要になるのか
C# でのシンプルな ZIP 圧縮のやり方
「ローテーション+圧縮」の実務的な流れ
圧縮タイミングや保管期間をどう決めるか

を、例題付きでかみ砕いて説明します。


なぜログ圧縮が必要になるのか

「ログは増え続ける」が前提だから

ログは、アプリが動いている限り増え続けます。
1 日 10MB でも、1 年で 3.6GB。
複数サービス・複数サーバーがあれば、あっという間に数十 GB になります。

だからといって、すぐ消してしまうと、
「3 ヶ月前の障害を調べたい」となったときに何も残っていません。

そこで、

直近の数日〜数週間分はそのまま(生ログ)
それより古いものは圧縮して保管
さらに古いものは削除

という段階的な運用がよく使われます。

ここでの重要ポイントは、「ログ圧縮は“ログを残すための工夫”であって、“ケチっている”わけではない」ということです。
ちゃんと残したいからこそ、圧縮して場所を節約します。


C# で ZIP 圧縮する基本:ZipFile を使う

System.IO.Compression.ZipFile のシンプルな使い方

.NET には、標準で ZIP を扱うためのクラスが用意されています。
名前空間は System.IO.CompressionSystem.IO.Compression.FileSystem です。

まず、単一のログファイルを ZIP にまとめる例から見てみます。

using System.IO;
using System.IO.Compression;

public static class LogCompressor
{
    public static void CompressLogFile(string logFilePath)
    {
        if (!File.Exists(logFilePath))
        {
            return;
        }

        var zipPath = logFilePath + ".zip";

        using var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create);
        zip.CreateEntryFromFile(logFilePath, Path.GetFileName(logFilePath), CompressionLevel.Optimal);

        File.Delete(logFilePath);
    }
}
C#

使い方はシンプルです。

LogCompressor.CompressLogFile("logs/app-20260417.log");
C#

これで、

logs/app-20260417.loglogs/app-20260417.log.zip に圧縮
元の .log ファイルは削除

という動きになります。

ここでの重要ポイントは、「圧縮後に元ファイルを削除するかどうかを、ユーティリティ側で明確に決める」ことです。
削除しないとディスク節約にならないし、削除するなら「いつ圧縮するか」がより重要になります。


日ごとのログファイルをまとめて ZIP にする

「1 日分のログを 1 つの ZIP にする」パターン

ログローテーションで、日ごとにファイルを分けているケースを考えます。

例:
app-20260415.log
app-20260416.log
app-20260417.log

これを、「2 日以上前のものは ZIP にまとめる」というユーティリティを書いてみます。

using System;
using System.IO;
using System.IO.Compression;

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

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

        foreach (var file in dir.GetFiles("app-*.log"))
        {
            // ファイル名から日付部分を取り出す想定: app-YYYYMMDD.log
            var name = Path.GetFileNameWithoutExtension(file.Name); // app-20260415
            var datePart = name.Substring("app-".Length);           // 20260415

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

            if (date >= threshold)
            {
                continue; // 新しすぎるものはまだ圧縮しない
            }

            var zipPath = Path.Combine(logDirectory, $"{name}.zip");

            using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create))
            {
                zip.CreateEntryFromFile(file.FullName, file.Name, CompressionLevel.Optimal);
            }

            file.Delete();
        }
    }
}
C#

使い方はこうです。

DailyLogArchiver.CompressOldLogs("logs", keepDays: 2);
C#

これで、「2 日より前の .log ファイル」が .zip に変わり、元ファイルは削除されます。

ここでの重要ポイントは、「ファイル名に日付を含めておくと、“どれを圧縮対象にするか”を簡単に判定できる」ことです。
ログローテーションの設計と、圧縮ユーティリティの設計はセットで考えるとスッキリします。


GZip で「1 ファイルをそのまま圧縮」する

拡張子 .gz のシンプルな圧縮

ZIP ではなく GZip(.gz)で圧縮したい場合もあります。
GZip は「1 ファイルをそのまま圧縮する」形式で、ストリームとして扱いやすいのが特徴です。

using System.IO;
using System.IO.Compression;

public static class GZipLogCompressor
{
    public static void CompressToGZip(string logFilePath)
    {
        if (!File.Exists(logFilePath))
        {
            return;
        }

        var gzipPath = logFilePath + ".gz";

        using (var input = File.OpenRead(logFilePath))
        using (var output = File.Create(gzipPath))
        using (var gzip = new GZipStream(output, CompressionLevel.Optimal))
        {
            input.CopyTo(gzip);
        }

        File.Delete(logFilePath);
    }
}
C#

使い方は ZIP と同じように呼び出すだけです。

GZipLogCompressor.CompressToGZip("logs/app-20260417.log");
C#

ここでの重要ポイントは、「GZip は“1 ファイル=1 圧縮ストリーム”なので、まとめて複数ファイルを入れる用途には向かない」ということです。
複数ファイルを 1 つにまとめたいなら ZIP、一つのログファイルをそのまま圧縮したいなら GZip、という使い分けになります。


圧縮のタイミングをどう決めるか

「いつ圧縮するか」が運用の肝

圧縮のタイミングは、大きく分けて次のようなパターンがあります。

アプリ起動時に「古いログをまとめて圧縮」
夜間バッチとして、1 日 1 回圧縮処理を走らせる
ログローテーションの直後に圧縮する

例えば、コンソールアプリの Main で、起動時に古いログを圧縮するイメージです。

static void Main(string[] args)
{
    DailyLogArchiver.CompressOldLogs("logs", keepDays: 3);

    // ここから本来の処理
}
C#

Web アプリやサービスなら、専用のバッチやスケジューラ(Windows タスクスケジューラ、cron など)で、
1 日 1 回 CompressOldLogs を呼ぶようにするのが現実的です。

ここでの重要ポイントは、「圧縮処理は“本来の業務処理”とは切り離して、定期的なメンテナンスとして走らせる」ことです。
ログ圧縮が重くて本処理に影響する、というのは避けたいパターンです。


保管期間と削除のルールを決める

「圧縮したログをいつまで残すか」

圧縮したとはいえ、永遠に残しておくと、いつかはディスクがいっぱいになります。
なので、圧縮後のログにも「保管期間」を決めておくのが普通です。

例えば、

生ログ(.log)は 7 日分
圧縮ログ(.zip / .gz)は 90 日分
それより古いものは削除

というルールにしておきます。

削除も C# で簡単に書けます。

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

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

    foreach (var file in dir.GetFiles("app-*.zip"))
    {
        if (file.CreationTime < threshold)
        {
            file.Delete();
        }
    }
}
C#

ここでの重要ポイントは、「“どれくらい前までのログが必要か”を、ビジネス側と合意しておく」ことです。
監査要件や法的要件が絡む場合もあるので、勝手に短くしないようにします。


実務での「ログ圧縮ユーティリティ」の位置づけ

ログローテーション・圧縮・削除をまとめて“ログメンテナンス”として設計する

ここまで見てきたように、ログ圧縮は単体のテクニックではなく、

日ごとのログファイルを作る(ローテーション)
一定期間経ったら圧縮する
さらに古くなったら削除する

という「ログメンテナンス」の一部として設計するのが自然です。

C# のユーティリティとしては、

古い .log を .zip / .gz に圧縮するメソッド
古い .zip / .gz を削除するメソッド
それらを定期的に呼び出す小さなバッチ(コンソールアプリ)

を用意しておくと、どのプロジェクトでも再利用しやすくなります。

ここでの重要ポイントは、「“ログをどう残し、どう小さくし、いつ消すか”をコードと運用の両方で決めておく」ことです。
圧縮ユーティリティは、その約束を守るための道具になります。


まとめ:「ログ圧縮ユーティリティ」は“ログを守りながらディスクを守る”ための道具

ログ圧縮の本質は、

ログを安易に捨てず、
でもディスクを圧迫しないように、
古いログを小さく畳んで保管する

ことです。

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

ログは増え続ける前提なので、「ローテーション+圧縮+削除」の流れを最初から設計すること。
C# では System.IO.Compression を使って、ZIP や GZip で簡単に圧縮できること。
ファイル名に日付を含めておくと、「どれを圧縮対象にするか」を判定しやすいこと。
圧縮のタイミングは、本処理とは切り離した定期バッチとして動かすのが現実的であること。
圧縮後の保管期間も決めておき、「どこまで遡れるか」をチームで共有しておくこと。

ここまでイメージできていれば、
「ログが増えてきたから手動で消す」という場当たり対応から卒業して、
“ログをちゃんと残しつつ、ディスクも守る”設計ができるようになります。

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