PHP Tips | 文字列処理:ログ・表示向け - ログ用 1 行整形

PHP PHP
スポンサーリンク

なぜ「ログ用 1 行整形」が必要なのか

ログって、本来は「あとから原因を追いやすくするための情報」ですよね。
でも、何も考えずに文字列をそのまま error_log() に投げていると、こうなりがちです。

複数行の文字列がそのまま出て、ログがガタガタになる。
制御文字やタブが混ざって、ビューアが崩れる。
1 件のログがやたら長くて、他の行が見えなくなる。

そこでやりたいのが、「どんな文字列でも“ログに優しい 1 行”に整形する」ユーティリティです。
これを 1 本持っておくと、ログの見通しが一気によくなります。


ログ用 1 行整形でやるべきこと

やりたいことを分解してみる

「ログ用 1 行整形」は、ざっくり言うと次のような処理の組み合わせです。

前後の余計な空白を削る。
改行やタブなどの制御文字を消すか、スペースに変える。
長すぎる文字列は途中で切って「…」を付ける。
必要なら、プレフィックス([user=123] など)を付ける。

これを毎回バラバラに書くのではなく、
「ログに出す前に必ず通す 1 本の関数」にまとめるのがポイントです。


基本形:シンプルな 1 行整形関数

改行も含めて制御文字を全部落とす版

まずは、「とにかく 1 行にしたい」ケース向けの基本形です。

/**
 * ログ用に「1 行の安全な文字列」に整形する
 *
 * - 前後の空白をトリム
 * - 制御文字(改行・タブなど)をすべて削除
 * - 長すぎる場合は末尾を「...」で省略
 */
function to_log_line(string $value, int $maxLength = 200): string
{
    // 前後の空白を削る
    $value = trim($value);

    // 制御文字(0x00-0x1F, 0x7F)をすべて削除
    $value = preg_replace('/[\x00-\x1F\x7F]/', '', $value) ?? '';

    // 長さ制限(マルチバイト対応)
    if (mb_strlen($value, 'UTF-8') > $maxLength) {
        $value = mb_substr($value, 0, $maxLength, 'UTF-8') . '...';
    }

    return $value;
}
PHP

ここでの重要ポイントは 2 つです。

制御文字を全部消しているので、「必ず 1 行になる」こと。
mb_strlen / mb_substr を使っているので、日本語でも文字数ベースで安全に切れること。


例題:ユーザー入力をログに出す

そのまま出すとログが壊れるパターン

例えば、ユーザーがこんなコメントを送ってきたとします。

こんにちは
テストです   \x07

\x07 はベル文字、タブも含まれています)

これをそのまま error_log() に渡すと、ログが複数行になり、制御文字も混ざります。

$rawComment = $_POST['comment'] ?? '';

error_log('comment=' . $rawComment);
PHP

ログビューアで見ると、1 件のログが 2 行以上に分かれてしまい、
他のログとの対応が追いづらくなります。

1 行整形を通してから出す

$rawComment = $_POST['comment'] ?? '';

$logComment = to_log_line($rawComment, 100);

error_log('comment=' . $logComment);
PHP

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

comment=こんにちはテストです

改行やタブ、ベルなどの制御文字が消え、1 行にまとまっています。
「何が入力されたか」は分かりつつ、ログの形はきれいなままです。


発展形:配列やオブジェクトを 1 行ログにする

var_export や json_encode と組み合わせる

ログに「配列やコンテキスト情報」を出したいことも多いですよね。
そのまま var_dump すると複数行になるので、
一度文字列化してから to_log_line() に通します。

/**
 * 任意の値を「ログ用 1 行文字列」に整形する
 */
function context_to_log_line(mixed $value, int $maxLength = 300): string
{
    // まず JSON で文字列化(失敗したら var_export)
    $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

    if ($json === false) {
        $json = var_export($value, true);
    }

    return to_log_line($json, $maxLength);
}
PHP

使い方の例です。

$context = [
    'user_id' => 123,
    'action'  => 'login',
    'ip'      => $_SERVER['REMOTE_ADDR'] ?? '',
];

error_log('context=' . context_to_log_line($context));
PHP

ログには、例えばこう出ます。

context={"user_id":123,"action":"login","ip":"203.0.113.1"}

JSON 形式で 1 行にまとまっているので、
人間にも機械にも扱いやすい形になります。


もう一歩:プレフィックスやタグを付けて見やすくする

ログの「型」をそろえる

1 行整形と一緒に、「ログのフォーマット」をそろえておくと、
あとから grep したり、集計したりするときに便利です。

例えば、こんなヘルパーを用意します。

/**
 * タグ付きの 1 行ログを出力する
 *
 * 例: [user=123][action=login] message...
 */
function log_with_tags(string $message, array $tags = []): void
{
    $parts = [];

    foreach ($tags as $key => $value) {
        $parts[] = sprintf('[%s=%s]', $key, to_log_line((string)$value, 50));
    }

    $prefix = implode('', $parts);

    error_log($prefix . ' ' . to_log_line($message, 300));
}
PHP

使い方の例です。

log_with_tags(
    'login failed: wrong password',
    ['user_id' => 123, 'ip' => $_SERVER['REMOTE_ADDR'] ?? '']
);
PHP

ログには、例えばこう出ます。

[user_id=123][ip=203.0.113.1] login failed: wrong password

ここでも to_log_line() を通しているので、
タグ部分もメッセージ部分も「1 行で安全な文字列」に整形されています。


まとめ:今日からの「ログ用 1 行整形」ユーティリティ

ログ用 1 行整形の本質は、「どんな入力でも“ログに優しい 1 行”に変換する」ことです。

制御文字を削除して、ログビューアを壊さない。
長さを制限して、1 行が暴走しないようにする。
配列やコンテキストも、文字列化してから 1 行にまとめる。

そのためのコア関数が、次のようなものです。

function to_log_line(string $value, int $maxLength = 200): string
{
    $value = trim($value);
    $value = preg_replace('/[\x00-\x1F\x7F]/', '', $value) ?? '';

    if (mb_strlen($value, 'UTF-8') > $maxLength) {
        $value = mb_substr($value, 0, $maxLength, 'UTF-8') . '...';
    }

    return $value;
}
PHP

もし、あなたのコードのどこかで「error_log($rawValue); をそのまま書いている」箇所があれば、
そこが to_log_line($rawValue) に差し替えるポイントです。
その 1 ステップを挟むだけで、ログの“読みやすさ”と“壊れにくさ”が一気に変わります。

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