なぜ「長文をログ向け短縮」する必要があるのか
業務システムのログには、ときどき「とんでもなく長い文字列」が流れてきます。
巨大な JSON、ユーザーの長文入力、外部 API のレスポンス全文などが典型です。
それをそのまま error_log() に投げると、こうなります。
1 行が何千文字にもなって、他のログが見えなくなる。
ログビューアが重くなる、折り返しで読みにくくなる。
grep してもヒット位置が分かりづらい。
ログは「あとから原因を追うための道具」なので、
「ざっくり中身が分かる程度に短くする」ユーティリティを用意しておくと、運用がかなり楽になります。
コアの考え方:「先頭だけ残して、残りは省略記号」
一番シンプルな短縮ルール
ログ向け短縮の基本ルールは、とてもシンプルです。
最大文字数を決める。
それを超えたら、先頭だけ残して末尾に「…」を付ける。
例えば、最大 200 文字にするなら、
元の文字列が 150 文字 → そのまま。
元の文字列が 500 文字 → 先頭 200 文字+「…」。
これだけでも、「どんな長文でもログが暴走しない」という効果があります。
実装例:マルチバイト対応のログ向け短縮関数
日本語を安全に扱うためのポイント
日本語を含む文字列を安全に短縮するには、mb_strlen と mb_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)) に差し替えていく。
それだけで、ログの「読みやすさ」と「壊れにくさ」が一段上がります。
