第18章:エラー率を見る:失敗の数え方 🧯🧾
「落ちた!💥」って時、ログで原因を追うのは大事だけど… “今どれだけヤバいか” を一瞬で判断するなら、やっぱり エラー率 が強いです😎📈
① 今日のゴール 🎯
- 成功/失敗を“割合”で話せるようになる(例:5xx率 2%)
- HTTPステータス別にカウントして、/metrics に出せる
/boomを叩いて 5xxが増えるのを確認できる ✅
prom-client(Node向けPrometheusクライアント)は、npm上で最新 15.1.3 が案内されています(本日時点の表示)。(npm) Node.jsは v24 が Active LTS、v25が Current として公開されています(本日時点)。(Node.js)
② 図(1枚)🖼️
エラー率は「失敗の数」÷「全部の数」ってだけ!超シンプル😆
┌──────────────┐
request → │ API (Express) │ → response(status)
└──────┬───────┘
│
▼
http_requests_total (Counter)
labels: method / route / status / status_class
│
▼
5xx率 = (5xxの増え方) / (全体の増え方)
③ 手を動かす(手順 5〜10個)🛠️
ここでは 「ステータス別のカウンタ」 を追加していくよ〜🧱✨
(前章までで /metrics が出てる前提でOK!)
A. まずは“設計の型”を1つ決める 🧠📌
エラー率に使う“失敗”は何? を先に決めるのが超大事!
- 5xx:だいたい「サーバ側の失敗」=アラート対象になりやすい 🔥
- 4xx:ユーザー操作ミスや入力ミスも多い(全部を障害扱いにすると疲れる😵💫)
この章では、まず 5xx を “失敗” として数える でいきます🫡
B. 追加するメトリクスを作る(Counter)🧾➕
ファイル例:src/metrics/httpRequestsTotal.ts
import client from "prom-client";
// 例:HTTPリクエスト総数(ステータス別に分ける)
export const httpRequestsTotal = new client.Counter({
name: "http_requests_total",
help: "Total number of HTTP requests",
labelNames: ["method", "route", "status", "status_class"] as const,
});
✅ Prometheusでは「メトリクス名にラベル名を埋め込まず、ラベルで次元を分ける」のが推奨されます。(prometheus.io)
C. middlewareで「レスポンスの結果」を数える 🧩🔢
Expressは 処理の最後に statusCode が決まるから、finish イベントで数えるのがコツだよ😊
ファイル例:src/middlewares/metricsCounter.ts
import type { Request, Response, NextFunction } from "express";
import { httpRequestsTotal } from "../metrics/httpRequestsTotal";
function statusClass(statusCode: number) {
return `${Math.floor(statusCode / 100)}xx`;
}
function routeLabel(req: Request) {
// ルートがマッチした時は route.path が取れることが多い(例: "/users/:id")
// 404などは取れないので固定ラベルにする
const r = (req as any).route?.path;
return typeof r === "string" ? r : "(unmatched)";
}
export function metricsCounter(req: Request, res: Response, next: NextFunction) {
// /metrics 自体を数えたくない場合は除外(好みでOK)
if (req.path === "/metrics") return next();
res.on("finish", () => {
const status = String(res.statusCode);
httpRequestsTotal.inc({
method: req.method,
route: routeLabel(req),
status,
status_class: statusClass(res.statusCode),
});
});
next();
}
D. appにmiddlewareを差し込む 🚪🧷
例:src/app.ts(または server.ts)
import express from "express";
import { metricsCounter } from "./middlewares/metricsCounter";
const app = express();
app.use(metricsCounter);
// 例:成功
app.get("/ping", (_req, res) => {
res.status(200).send("pong");
});
// 例:わざと失敗(5xx)
app.get("/boom", (_req, res) => {
res.status(500).json({ message: "boom 💥" });
});
export default app;
E. 動かして叩く(Windows向け)🪟💥
- 起動(Compose運用ならこれ)
docker compose up --build
- まず成功を増やす(PowerShell)
1..20 | % { iwr http://localhost:3000/ping -UseBasicParsing | Out-Null }
- つぎに失敗を増やす(500は例外扱いになるので try/catch)
1..5 | % {
try { iwr http://localhost:3000/boom -UseBasicParsing | Out-Null }
catch { }
}
- /metrics を見て、増えてるか確認 👀
(iwr http://localhost:3000/metrics -UseBasicParsing).Content `
| Select-String "http_requests_total"
F. 期待する出力(例)✅
/metrics の中に、こんな感じで出てきたら勝ち!🏆
## HELP http_requests_total Total number of HTTP requests
## TYPE http_requests_total counter
http_requests_total{method="GET",route="/ping",status="200",status_class="2xx"} 20
http_requests_total{method="GET",route="/boom",status="500",status_class="5xx"} 5
G. エラー率を“手計算”で感じる 🧮😆
この例だと…
- 全体:20 + 5 = 25
- 5xx:5
- 5xx率:5/25 = 0.2(20%) 😱
もちろん実運用はPrometheus/Grafanaで“自動計算”するけど、 最初にこの感覚を身体に入れるのが大事〜💪✨
H. (予告)Prometheusでの計算はこうなる 🕸️📥
Counters(カウンタ)は基本「増えるだけ」なので、Prometheus側では rate()(増え方)で見るのが王道だよ📈
rate() は カウンタに適用するのが前提として説明されています。(prometheus.io)
- 全体のRPS(1秒あたりリクエスト数)
sum(rate(http_requests_total[5m]))
- 5xxのRPS
sum(rate(http_requests_total{status_class="5xx"}[5m]))
- 5xxエラー率(割合)
sum(rate(http_requests_total{status_class="5xx"}[5m]))
/
sum(rate(http_requests_total[5m]))
ラベルは次元を増やすほど「時系列」が増えます(=重くなりがち)。(prometheus.io)
④ つまづきポイント(3つ)🪤😵💫
-
routeラベルが爆発する問題 💣
/users/1/users/2みたいに“値入りパス”をそのまま入れると、時系列が無限に増えます😇 ➡️req.route.path(/users/:idみたいな形)を優先して使うのが安全寄り! -
コンテナ再起動でカウンタが0に戻る 🔁 だから「生の数字」じゃなく、Prometheusでは rate()/increase() で見るのが基本!(prometheus.io)
-
4xxを全部“障害”にすると運用が死ぬ ☠️📣 最初は 5xxだけ をアラート対象にするのが平和です🕊️ (4xxは「増え方」を別グラフで見るのはアリ👍)
⑤ ミニ課題(15分)⏳✍️
status_class="4xx"の増え方も見られるように、PromQL(予告の式)を自分で組み立ててみてね🧩/metricsを除外した時と、除外しない時でhttp_requests_totalの伸びがどう変わるか確認👀- 404(存在しないURL)を叩いて、
route="(unmatched)"が増えるのを見てみよう🚪❌
⑥ AIに投げるプロンプト例(コピペOK)🤖📋✨
1) middleware生成(Express + TS)
Express(TypeScript)で、レスポンス完了時に statusCode を取得して
prom-client の Counter に method/route/status/status_class をラベルとして inc する middleware を書いて。
route は req.route.path を優先し、取れない場合は "(unmatched)" にして。
/metrics はカウントしないようにして。
2) PromQL(エラー率)
Prometheusのメトリクス http_requests_total{status_class="2xx|4xx|5xx"} があるとして、
5xxエラー率を 5分窓で計算する PromQL を3種類出して:
(1) 全体の5xx率 (2) route別の5xx率 (3) method別の5xx率
3) ラベル設計レビュー
Prometheusのラベル設計レビューをして。
route に req.path を入れたいけど危険?なぜ?
安全な代替案(正規化・固定ラベル・ルートテンプレ化)の提案もして。
チェック ✅🎉
/pingを叩くとstatus_class="2xx"が増える/boomを叩くとstatus_class="5xx"が増える- /metrics で
http_requests_totalが見えてる
ここまで来たら、次は CPU/メモリ/イベントループみたいな「アプリ以外のボトルネック」に進める準備が整ってるよ〜🧠⚙️✨