PHP Tips | 文字列処理:ログ・表示向け - 長文をログ向け短縮

PHP PHP
スポンサーリンク

なぜ「長文をログ向け短縮」する必要があるのか

業務システムのログには、ときどき「とんでもなく長い文字列」が流れてきます。
巨大な JSON、ユーザーの長文入力、外部 API のレスポンス全文などが典型です。

それをそのまま error_log() に投げると、こうなります。

1 行が何千文字にもなって、他のログが見えなくなる。
ログビューアが重くなる、折り返しで読みにくくなる。
grep してもヒット位置が分かりづらい。

ログは「あとから原因を追うための道具」なので、
「ざっくり中身が分かる程度に短くする」ユーティリティを用意しておくと、運用がかなり楽になります。


コアの考え方:「先頭だけ残して、残りは省略記号」

一番シンプルな短縮ルール

ログ向け短縮の基本ルールは、とてもシンプルです。

最大文字数を決める。
それを超えたら、先頭だけ残して末尾に「…」を付ける。

例えば、最大 200 文字にするなら、

元の文字列が 150 文字 → そのまま。
元の文字列が 500 文字 → 先頭 200 文字+「…」。

これだけでも、「どんな長文でもログが暴走しない」という効果があります。


実装例:マルチバイト対応のログ向け短縮関数

日本語を安全に扱うためのポイント

日本語を含む文字列を安全に短縮するには、mb_strlenmb_substr を使うのが鉄則です。
strlen / substr だと「バイト数」で切ってしまい、文字化けの原因になります。

/**
 * 長文を「ログ向けに短縮した文字列」にする
 *
 * - 最大長を超えたら、先頭だけ残して「...」を付ける
 * - マルチバイト文字に対応
 */
function shorten_for_log(string $value, int $maxLength = 200): string
{
    // まず前後の空白を軽くトリム(お好み)
    $value = trim($value);

    $length = mb_strlen($value, 'UTF-8');

    if ($length <= $maxLength) {
        // そもそも短ければそのまま
        return $value;
    }

    // 先頭 maxLength 文字だけ残して「...」を付ける
    $head = mb_substr($value, 0, $maxLength, 'UTF-8');

    return $head . '...';
}
PHP

ここでの重要ポイントは、「省略記号を含めて何文字にするか」をどう考えるかです。
上の実装だと「先頭 maxLength 文字+...」なので、実際の長さは maxLength+3 文字になります。
もし「全体でちょうど maxLength 文字に収めたい」なら、$maxLength - 3 で切るように調整します。


例題:ユーザーの長文入力をログに残す

そのまま出すとログが読めなくなるパターン

例えば、問い合わせフォームでユーザーが 2000 文字の長文を送ってきたとします。

$rawBody = $_POST['body'] ?? '';

error_log('inquiry_body=' . $rawBody);
PHP

これをやると、ログの 1 行が 2000 文字以上になり、
他のログが画面から押し出されてしまいます。

短縮してからログに出す

$rawBody = $_POST['body'] ?? '';

$logBody = shorten_for_log($rawBody, 200);

error_log('inquiry_body=' . $logBody);
PHP

ログには、例えばこんな感じで出ます。

inquiry_body=いつもお世話になっております。先日注文した商品の件でご連絡いたしました。……(途中まで)...

先頭 200 文字だけで「どんな内容か」はだいたい分かりますし、
ログの行も暴走しません。


もう一歩:先頭と末尾を両方残す短縮

中身より「頭と尻」が大事なケース

長い JSON や SQL などでは、「先頭」と「末尾」に重要な情報があることが多いです。
その場合、「先頭だけ」ではなく「先頭と末尾の両方を少しずつ残す」短縮も有効です。

/**
 * 先頭と末尾を残して中間を「...」で省略する短縮
 *
 * 例: headLen=50, tailLen=50
 * [先頭 50 文字]...[末尾 50 文字]
 */
function shorten_head_tail_for_log(string $value, int $headLen = 100, int $tailLen = 100): string
{
    $value = trim($value);

    $length = mb_strlen($value, 'UTF-8');

    // そもそも短ければそのまま
    if ($length <= $headLen + $tailLen) {
        return $value;
    }

    $head = mb_substr($value, 0, $headLen, 'UTF-8');
    $tail = mb_substr($value, -$tailLen, null, 'UTF-8');

    return $head . '...' . $tail;
}
PHP

使い方の例です。

$sql = 'SELECT ... 非常に長い SQL ... ORDER BY created_at DESC';

error_log('sql=' . shorten_head_tail_for_log($sql, 80, 80));
PHP

ログには、先頭と末尾だけが残るので、

どのテーブルを触っているか。
どんな WHERE 条件や ORDER が付いているか。

といった情報を、短い 1 行で把握できます。


「短縮」と「1 行整形」を組み合わせる

制御文字も一緒に掃除したい場合

長文を短縮するだけでなく、「制御文字も消して 1 行にしたい」ことが多いです。
その場合は、前にやった「ログ用 1 行整形」と組み合わせます。

function to_log_line_shortened(string $value, int $maxLength = 200): string
{
    // まず制御文字を削除して 1 行化
    $value = preg_replace('/[\x00-\x1F\x7F]/', '', $value) ?? '';
    $value = trim($value);

    // そのうえで長さ制限
    if (mb_strlen($value, 'UTF-8') > $maxLength) {
        $value = mb_substr($value, 0, $maxLength, 'UTF-8') . '...';
    }

    return $value;
}
PHP

使い方の例です。

$response = $client->getBody(); // 外部 API のレスポンス

error_log('api_response=' . to_log_line_shortened($response, 300));
PHP

これで、「制御文字なし」「1 行」「最大 300 文字まで」という、
ログにとって扱いやすい形に整えられます。


まとめ:今日からの「長文をログ向け短縮」ユーティリティ

長文をログ向けに短縮する本質は、「ログを壊さず、でも中身の雰囲気は分かるようにする」ことです。

最大長を決めて、そこを超えたら「…」で省略する。
日本語を含むので、mb_strlen / mb_substr で文字数ベースに切る。
必要に応じて「先頭だけ」か「先頭+末尾」を残すかを選ぶ。

まずは、この 1 本をプロジェクトに置いておくと良いです。

function shorten_for_log(string $value, int $maxLength = 200): string
{
    $value = trim($value);

    if (mb_strlen($value, 'UTF-8') <= $maxLength) {
        return $value;
    }

    return mb_substr($value, 0, $maxLength, 'UTF-8') . '...';
}
PHP

そして、外部入力や長文をログに出している箇所を見つけたら、
error_log(shorten_for_log($text)) に差し替えていく。
それだけで、ログの「読みやすさ」と「壊れにくさ」が一段上がります。

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