「注文したのに届かない」を5分で追う — 問い合わせ対応のログ調査4パターン

技術解説

手がかりは、注文番号1つ。

問い合わせフォームから来た文面には、それ以外に何も書かれていません。記録は5つのシステムに散らばっています。相手を待たせずに、それを1本の線に戻せるでしょうか。

鍵は、検索の速さではありません。

問い合わせ対応のログ調査は、障害対応とは別の競技です。障害なら「何が起きたか」を突き止めれば終わりますが、問い合わせは特定の1件について、事実の並びを人に説明できる形で出さなければなりません。しかも向こうは待っています。

4つの現場を並べます。ECの配送遅延・ゲームの不正報告・通信のCDR突合・SaaSのテナント混在。業種はばらばらですが、4つとも詰まるのは同じ1点です。

先に結論: UwView は開いた瞬間から全体を検索でき、絞り込んだ結果からさらに絞り込む多段検索で「注文ID→決済ID→伝票番号」と乗り換えていく調査を、原本を抜き出さずに追えます。Pro なら一度開いた47.73GBのファイルは2回目以降0.02〜0.07秒で行番号付きのまま開き直せます(特定環境での実測。環境により異なります。詳細は記事末尾)


1. 【EC】注文IDだけが分かっていて、そこから先がない

状況

「先週注文した商品が届きません」。文面にあるのは注文番号 ORD-2026-0831-88213 だけです。

注文APIのログを grep します。1行出ます。受注は成っています。次に決済ログを同じ注文番号で引きます。0件。 決済ログに注文番号は入っていません。

なぜ起きるか

IDが、システムの境界で乗り換わるからです。

注文APIは注文番号を持ちます。決済代行に渡すときは向こうの transaction_id になり、返ってくるのはその ID です。在庫引当は SKU と引当番号で動き、出荷指示は出荷ロット番号、配送会社との連携では伝票番号になります。注文番号を最後まで貫通させている設計は、実際にはあまり見かけません。

だから調査は必ず多段になります。注文番号で決済リクエストIDを得て、それで決済ログを引き、返ってきた transaction_id で出荷連携ログを引き、そこで得た伝票番号で配送APIのログを引く。1回の検索の結果が、次の検索の入力になります。

さらに、EC のログは日付でファイルが分かれているのが普通です。注文が8/31、出荷が9/2、配送連携が9/3。4段の検索が、それぞれ別のファイルに対して走ります。

汎用ツールでの対処と限界

素直に4回叩くのが定石です。

grep 'ORD-2026-0831-88213' order-2026-08-31.log
grep 'req-8f2a11' payment-2026-08-31.log            # 1本目で得たID
grep 'txn_9931882' shipping-2026-09-02.log          # 2本目で得たID
grep 'DENPYO-4410-2299' delivery-2026-09-03.log     # 3本目で得たID

書いてしまえば4行です。問題はこの4行を打つまでの時間にあります。

限界は3つ。ひとつめ、逐次であることです。 1本目の結果を目で読み、必要なIDを見つけ、コピーして2本目を書く。この「読んで・拾って・貼る」が4回。1回あたり数GBのファイルを走査するなら、待ち時間もそのまま4回直列で積みます。相手が待っている調査で、待ち時間が直列に積むのは効きます。

ふたつめ、結果が端末に流れて消えることです。 4段目まで来たときに「2段目の決済ログの時刻をもう一度見たい」と思うと、スクロールバックを遡るか、同じ grep をもう一度打ちます。第10回で書いた「開き直しの2分」が、ここでは1件の問い合わせのなかで何度も発生します。

みっつめ、説明できる形にならないことです。 顧客への回答やエスカレーション票には「8/31 14:22 に受注、決済は 14:22 に承認、出荷指示は 9/2 09:14、配送会社への引き渡しは記録なし」と時系列で書く必要があります。4つの端末出力を人力で並べ直す作業が、調査のあとに待っています。


2. 【ゲーム】通報が来たが、不正は「1行」には出ていない

状況

「このプレイヤー、明らかにおかしい」という通報が届きました。分かっているのはプレイヤーIDと、だいたいの時刻。

行動ログは1日に数億行あります。grep でそのプレイヤーIDの行を抜き出します。数千行出ました。眺めても、何も異常に見えません。

なぜ起きるか

不正の証拠は、1行ではなく行の並びに出るからです。

移動座標のログを1行だけ見ても正常です。異常が見えるのは、直前の行との差分を取ったときです——0.2秒で300メートル動いている。あるいは攻撃間隔が人間には不可能なほど均一である。パケットの再送を除いた実効レートが、公称の入力上限を超えている。どれも「隣の行との関係」でしか判定できません。

そしてもうひとつ、当該プレイヤーの行だけ抜き出すと判定できなくなるという厄介さがあります。「その時刻、そのマップで、他のプレイヤーはどうだったか」が要ります。全員がラグで飛んでいたなら、それはサーバ側の問題です。抜き出した瞬間、その比較対象が消えます。

通報は1日に何十件も来ます。同じ日の同じログファイルに対して、違うプレイヤーIDで何度も入り直すのが日常です。

汎用ツールでの対処と限界

抜き出して、周辺も付ける、が定石です。

grep -n 'player=88213' action-2026-09-05.log > case.txt
grep -n -C 20 'player=88213' action-2026-09-05.log | less    # 前後20行つき
awk '$1>="14:20:00" && $1<="14:25:00"' action-2026-09-05.log | less

2本目の -C は効きます。周囲の他プレイヤーの行が一緒に付いてくるので、比較の材料が残ります。

限界は3つ。ひとつめ、-C の幅を先に決めなければならないことです。 20行では足りず、100行にすると読めない量が出ます。適切な幅は開いてみるまで分からないのに、grep は開く前に決めろと言います。幅を変えるたびに数億行の走査をやり直します。

ふたつめ、通報1件ごとに全量走査が走ることです。 1日30件の通報を処理すると、同じ1ファイルを30回走査します。第4回で「消すか残すか」を考えたログが、ここでは残っていても毎回同じ待ち時間を課してくる資産になります。

みっつめ、抜き出したファイルが増えることです。 case.txt が案件の数だけ溜まります。それぞれが誰のどの通報に対応するのか、3日経つと分からなくなります。しかも中身は他プレイヤーの行動を含むので、扱いに注意が要る副本です。


3. 【通信】2つの巨大ファイルを突き合わせたいだけなのに

状況

請求データに載っている通話が、交換機側のCDR(通話明細記録)に見当たりません。逆もあります。

どちらも数千万レコード。片方は固定長の独自フォーマット、片方はCSV。片方だけを見ても、何も分かりません。

なぜ起きるか

突合は「2つの集合の差」であり、キーが完全一致しないからです。

同じ通話でも、開始時刻が交換機側は秒まで、請求側は分に丸められている。発信番号が片方は +81-90-...、片方は 090...。タイムゾーンが片方 UTC、片方 JST。キーを作る前に、正規化のルールを決めなければなりません。

そしてそのルールは、たいてい1回では当たりません。「秒を切り捨てて突合したら差分が8万件出た」→「切り上げだったかもしれない」→やり直し。正規化のルールを1つ変えるたびに、数千万行の処理が最初から走ります。

もうひとつ。差分が出たあとに必ず来るのが「その1件の前後を見たい」です。突合の結果として「この呼が片側にない」と分かったら、次はその時刻の交換機ログを前後含めて読みたくなります。ところが sort を通した後のファイルは、元の並びが壊れています。

汎用ツールでの対処と限界

正規化して、ソートして、joincomm が定石です。

awk -F, '{gsub(/[-+]/,"",$2); print $2"_"substr($3,1,16)}' billing.csv | sort > a.key
cut -c 1-11,20-35 cdr.dat | tr -d ' ' | sort > b.key
comm -23 a.key b.key | head          # 請求にあってCDRにない
join -t, -1 1 -2 1 a.key b.key | wc -l

考え方は正しいです。詰まるのは実行のほうです。

限界は3つ。ひとつめ、sort が一時領域を要求することです。 数千万行のソートは、入力とほぼ同じ大きさの作業領域を /tmp に取ります。作業サーバの /tmp が小さくて No space left on device で落ちる、というのは突合作業でよく見る事故です。-T で逃がせますが、逃がした先の速度は落ちます。

ふたつめ、やり直しのコストが一定でないことです。 awk の正規化を1文字直すだけで、また数千万行が流れます。試行が10回になれば、待ち時間も10回ぶんです。仮説を試す気力のほうが先に尽きます。

みっつめ、並びが壊れて、原本に戻れないことです。 comm が返すのはキーだけで、そのキーが原本の何行目に対応するかは残りません。「この呼の直前に、交換機は何をしていたか」に答えるには、原本を別に開いて時刻で探し直すことになります。第7回で扱った「時刻で飛ぶ」が、ここでは突合の後工程として必ず要ります。


4. 【SaaS】テナントが分離されていないログを、運用でカバーする

状況

マルチテナントのSaaSで、A社から「特定の画面だけ極端に遅い」と問い合わせが来ました。

アプリケーションログは全テナント共通の1本です。A社に見せる資料を作るために、B社・C社の行が混ざったログを開いて調べることになります。

なぜ起きるか

tenant_id が、すべての行に入っていないからです。

アプリケーション層のログには入っています。しかしその下——ORMが出すスロークエリログ、ランタイムのGCログ、ロードバランサのアクセスログ、接続プールの枯渇警告——には入っていません。テナントを識別できるのは、良くてリクエストIDだけです。

そして、遅延の原因はたいていその下の層にあります。 A社の遅い画面の裏で起きているのは「B社の巨大なバッチがコネクションプールを占有していた」かもしれません。tenant_id=A で絞り込むと、原因が視界から消えます。

ここに、問い合わせ対応特有の制約が乗ります。調査の過程では他社の行を見ざるを得ないが、回答資料に他社の行を混入させてはいけない。1文字の混入が、そのままインシデントになります。

汎用ツールでの対処と限界

抜き出して、それを調査対象にするのが定石です。

grep 'tenant=acme' app-2026-09-05.log > acme.log       # A社ぶんを抽出
grep -f req_ids.txt app-2026-09-05.log > acme_full.log # リクエストIDでも拾い直す
grep -c 'tenant=' app-2026-09-05.log                   # そもそも何行に入っているか

3本目を先に打つべきです。全体の何割に tenant= が付いているかで、この方針が成立するかどうかが決まります。

限界は3つ。ひとつめ、落ちる行が、いちばん見たい行であることです。 tenant= を含まない行を落とすフィルタは、スロークエリログとGCログを確実に落とします。原因の候補を最初に捨てる絞り込みになっています。

ふたつめ、抜き出したファイルが新しい管理対象になることです。 acme.log は「他社データを含まないと確認済み」でなければ配れません。確認するには結局、全行を見ることになります。そして acme_full.log のほうは、リクエストIDで拾い直しているぶん混入の可能性が上がっています。 抜き出しは作業を減らしたつもりで、検証作業を増やしています。

みっつめ、原本の行番号が失われることです。 抽出ファイルの「412行目」は原本の412行目ではありません。開発チームへエスカレーションするとき、「原本の何行目」で指せないと、相手はまた自分で探すところから始めます。第9回で書いた「加工した先はもう原本ではない」が、ここでは社内の受け渡しコストとして返ってきます。


4つに共通していたもの

場面 手がかり 調査の形 汎用ツールが止まる場所
ECの配送遅延 注文番号1つ IDを乗り換えながら4段 1段ごとに待ち時間が直列に積む
ゲームの不正報告 プレイヤーIDと時刻 周囲の行と比べる -C の幅を開く前に決めさせられる
CDRの突合 2つのファイル 正規化して差を取る やり直しのたびに全量が流れる
SaaSのテナント混在 テナント名 絞り込みたいが絞れない 絞ると原因の行が落ちる

3列目を縦に読むと、正体が見えます。どれも「1回の検索では終わらない」調査です。 前の結果が次の入力になり、幅を変えて見直し、正規化を変えて試し、絞り込みの条件そのものを疑う。問い合わせ対応のログ調査は、検索を何回繰り返せるかで結果が決まります。

4列目も揃っています。汎用ツールが止まるのは、いつも「もう一度、条件を変えて」と思った瞬間です。1回の検索が速いかどうかより、2回目・5回目・20回目が軽いかどうかが効きます。しかも問い合わせは1日に何件も来るので、この繰り返しは1件のなかだけでなく、件と件のあいだでも起きます。

必要なものは3つです。

  • 絞り込んだ結果から、さらに絞り込めること — 「注文番号で絞る → その中から決済関連だけ」を、原本を抜き出さずに重ねられること。抜き出しが増えないなら、副本の管理も検証も要りません。
  • 絞り込みが、原本の座標を保つこと — 検索結果の1行が、原本の何行目かを持ち続けること。エスカレーションでも報告でも、指せる座標があるかどうかで相手の手間が変わります。
  • 開き直しが軽いこと — 同じファイルを1日に何十回も開くのが、この仕事の形です。1回あたりの待ちが小さいことが、そのまま試せる仮説の数になります。

冒頭の「手がかりは注文番号1つ」に戻ります。5分で追えるかどうかを決めているのは、検索1回の速さではありません。 検索を4回続けざまに打てるかどうか、打ち直しをためらわずに済むかどうかです。


使っている道具

私が開発している UwView(無料)は、巨大なテキストを開いた瞬間から全体を表示・スクロール・検索できるビューアです。索引はバックグラウンドで作られ、完成すると行番号が付きます(他のビューアの多くは索引が終わるまで先頭しか見えません)。分割も抽出もしないので、原本は1本のまま・無改変のまま扱えます。編集機能を持たないので、他社データを含むログを開くこと自体が安全な操作です。

  • 絞り込みの上に絞り込みを重ねる: 検索結果に対してさらに検索をかけるドリルダウン検索があります。第1章の「注文番号 → 決済リクエストID → 伝票番号」も、第4章の「A社で絞る → リクエストIDで拾い直す」も、抽出ファイルを作らずに画面の上で重ねられます。
  • 並び順のまま、複数の条件を順に当てる: シーケンシャル検索は「Aの後にBが来る」という並びを条件にできます。第2章の「同じ間隔が続いている」のような、1行では判定できない並びの異常を探すときに使えます。
  • 検索結果と原本を行き来する: 結果の一覧は別ウィンドウに残り、そこから原本の該当行へ飛べます。飛んだ先は原本なので、周囲を好きなだけ広げて読めます。第2章の「-C の幅を先に決めさせられる」問題が、そもそも発生しません。詳しくは検索一覧と現場を行き来する仕組みに書きました。
  • 開き直しを軽くする: UwView Pro は索引と圧縮を保存するので、2回目以降は行番号付きのまま開き直せます(47.73GBのテキストで実測0.02〜0.07秒。環境により異なります)。1日30件の通報を捌く第2章の状況で、この差は件数ぶん効きます。

正直に書いておくと、UwView は突合の道具ではありません。第3章の「2つのファイルのキーを正規化して差を取る」は sortjoin の仕事で、この道具の出番はその(正規化のルールを決めるために生データの桁を確かめる)と、(差分として出た1件の前後を原本で読む)です。差分そのものを計算する機能はありません。同様に、集計もしません。第2章の「移動距離を時間で割る」ような計算は別の道具が要ります。できるのは、見つけること・辿ること・原本のまま示すことです。また、複数ファイルを横断して一括検索する機能はありません。第1章の4つのファイルは、4つとも開いて追うことになります。

そして、ストレージを専有している巨大ログを圧縮して保管し、さらに高速に検索したいなら UwView Pro をどうぞ。永続索引・圧縮キャッシュ検索・約1/9保管で、開き直しも検索も一段速くなります(全OS対応・買い切り/月額プランあり)。問い合わせ対応のように、同じログを日に何度も別の条件で開き直す使い方では、2回目以降が軽いことがそのまま処理できる件数になります。

リンク

  • 第1回: 巨大ファイルに沈む4つの定番ツールと、その先: https://uvp.y42u.net/blog/uwview-ps01-huge-file-tool-limits/
  • 第2回: ログの因果を遡る4つの技法: https://uvp.y42u.net/blog/uwview-ps02-log-causality-tracing/
  • 第3回: 文字コード4つの罠と切り分け手順: https://uvp.y42u.net/blog/uwview-ps03-japanese-encoding-traps/
  • 第4回: 消す・残す・圧縮するの判断基準: https://uvp.y42u.net/blog/uwview-ps04-log-retention-decision/
  • 第5回: 開発ログとの正しい付き合い方4選: https://uvp.y42u.net/blog/uwview-ps05-debug-log-practices/
  • 第6回: 認証ログ数千万行から痕跡を拾う4手順: https://uvp.y42u.net/blog/uwview-ps06-intrusion-triage-auth-logs/
  • 第7回: タイムスタンプを武器にする4つの読み方: https://uvp.y42u.net/blog/uwview-ps07-timestamp-driven-triage/
  • 第8回: 圧縮保管と検索の両立: https://uvp.y42u.net/blog/uwview-ps08-compressed-archive-search/
  • 第9回: 構造化データを生のまま読む4場面: https://uvp.y42u.net/blog/uwview-ps09-read-raw-structured-data/
  • 第10回: 深夜2時の自分を助ける4つの仕込み: https://uvp.y42u.net/blog/uwview-ps10-oncall-night-preparation/
  • 第11回: レガシー文字コードを2026年に読む: https://uvp.y42u.net/blog/uwview-ps11-legacy-encoding-euc-utf16/
  • 巨大ログが開けないときの手順: https://uvp.y42u.net/blog/uwview-huge-log-cannot-open/
  • 絞り込んだ先からさらに絞り込む: https://uvp.y42u.net/blog/uvp-drilldown-search/
  • 並び順を条件にして探す: https://uvp.y42u.net/blog/uvp-sequence-search/
  • ソースコード(GitHub): https://github.com/amru195704/UwView

開発者より: アプリ・Kindle本・オープンソースの一覧は GitHub: amru195704 にまとめています。


お願い
本記事の情報は参考目的で掲載しており、正確性・完全性を保証するものではありません。記載したシステム構成・ID体系・ログ項目は説明のための一般化した例であり、特定の実在するサービスの実装を示すものではありません。CDRのフォーマットやテナント識別子の付与方針は事業者・製品により大きく異なります。記載した実測値は特定の1環境での測定例であり、同じ結果を保証するものではありません。コマンド例は環境(GNU/BSD、awksort の実装差、grep のオプション対応の有無等)により調整が必要です。顧客データや他社データを含むログの取り扱いは、所属組織の規程に従ってください。誤記・不正確な情報がございましたら、コメント欄よりご指摘いただければ、確認のうえ修正いたします。

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