ClearNets

Navigation

ホーム

トップ / プロダクト一覧

会社紹介

ClearNets の考え方

ブログ

開発ノート・設計思想

コツコツ。

和モダン習慣トラッカー

Merci

チップ決済 SaaS

Kotodama

AI 占い × キャラ

Paircon

家族見守り

家族おでかけガイド

週末のおでかけ先

コツコツ。を試す

← ブログ·運営·

1 人開発のオブザーバビリティ実務 (日本・2026 年版)

ClearNets は 1 人 + AI エージェント体制で Merci / Kotsukotsu / Kotodama / OSHITABI / スキ活マーケット の 5 プロダクトを本番運用しています。24 時間 365 日の on-call は無理なので、代わりに『壊れたら Slack と Discord に自動で叫び声が飛んでくる』観測レイヤに投資し続けてきました。本記事は Sentry / Logtail / UptimeRobot / Vercel Analytics / GA4 / Mixpanel / Cost 監視 / Cron 監視 / SLO 設計 / postmortem テンプレの現時点の全構成を、無料枠を最大限使うコスト明細と実際の設定コード付きで整理した実務ノートです。

1. なぜオブザーバビリティに投資するか — 1 人体制の物理制約

1 人開発では「バグは 1 人しか直せない、しかし本番は 24h 動いている」 という非対称性が最大の敵です。エンタープライズの監視スタックは 「on-call 3 人交代で PagerDuty から起こされる」設計ですが、1 人 SaaS で 毎晩 3 時に PagerDuty で起こされたら 1 週間で燃え尽きます。ClearNets の 監視スタックは以下の 4 原則で設計されています。

  • 致命傷だけ通知: 5xx / 決済失敗 / DB 到達不能 / SSL 失効 など「収益 or 信用が今すぐ崩れる」ものだけを Slack / Discord に流す。 それ以外は日次ダイジェストで OK。
  • 通知は 1 チャンネルに集約: Sentry / UptimeRobot / Vercel / GitHub / Cost の通知を全部同じ Discord チャンネルに送る。 「何を見ればいいか」の選択負荷を最小化。
  • Runbook で 5 分復旧を目指す: 各アラートに対して 「これを Slack で見たら、次にこれをコピペしろ」のコマンド列を Runbook として README に貼っておく。深夜の思考停止でも復旧可能。
  • 無料枠 or ¥1,000/月以下: 監視に月 5 万円払うのは 個人プロダクトでは本末転倒。Sentry Free / Logtail Free / UptimeRobot Free の 3 点セットで大体足りる。

2. Sentry の入れ方と実運用ノイズ削減

エラー追跡は Sentry 一択です。無料枠 5,000 error / 月 + 10,000 transaction / 月。小規模プロダクト 5 本合計でも余裕。Next.js は 公式 wizard で 3 分でセットアップ完了します。

# インストール
npx @sentry/wizard@latest -i nextjs

# 環境変数 (Vercel に投入)
SENTRY_ORG=clearnets
SENTRY_PROJECT=merci-web
SENTRY_AUTH_TOKEN=sntrys_xxxx  # source map upload 用
NEXT_PUBLIC_SENTRY_DSN=https://xxxx@oNNN.ingest.sentry.io/PPPP

Sentry を本番投入した時に必ず踏むのが 「ノイズが多すぎて アラートを見なくなる」 罠です。ClearNets が Merci 本番 1 週目に踏んだ実例: bot からの 404 / prefetch キャンセルの AbortError / 拡張機能起因の ResizeObserver loop 警告 で Sentry が 800 event/日 まで膨らみ、本物のバグが埋もれました。対処:

// sentry.client.config.ts — ノイズフィルタ
import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1,  // トランザクションの 10% だけサンプリング
  ignoreErrors: [
    "AbortError",                                  // fetch cancel
    "ResizeObserver loop limit exceeded",          // 拡張機能起因
    "ResizeObserver loop completed with undelivered notifications",
    "Non-Error promise rejection captured",        // 3rd party script
    /Cannot read properties of null \(reading 'querySelector'\)/,
  ],
  denyUrls: [
    /extensions\//i,        // Chrome extension
    /^chrome:\/\//i,
    /^moz-extension:\/\//i,
  ],
  beforeSend(event, hint) {
    // 4xx は無視 (5xx とハンドリングされない例外だけ通す)
    const status = event.contexts?.response?.status_code;
    if (typeof status === "number" && status < 500) return null;
    return event;
  },
});

この設定 3 種を入れた結果、event 数が 800/日 → 12/日 まで削減され、 本物のバグに反応できる状態に。「無視するべきエラーを 明示的に列挙する」 は Sentry 運用の 8 割の工数です。

Sentry の Alert Rules は「1 時間以内に同種 error が 5 件発生」で Discord webhook 発火、というルールを 1 本だけ設定。それ以上細かい アラート設計は 1 人体制では回りません。

3. Logtail (Better Stack) でログを集約

Vercel の Function Logs は 1 時間しか遡れない (Pro でも 24h) ので、 本番トラブル調査で「昨日の 15 時のログ」が見たい時に困ります。 ClearNets は Logtail (現 Better Stack) に全ログを 流していて、無料枠 1 GB/月 + 3 日保持 で回っています。

// lib/logger.ts — Logtail 統合
import { Logtail } from "@logtail/node";

const logtail = new Logtail(process.env.LOGTAIL_SOURCE_TOKEN!);

export const log = {
  info: (msg: string, ctx?: Record<string, unknown>) => {
    console.log(msg, ctx);          // Vercel Function Logs にも残す
    logtail.info(msg, ctx);
  },
  warn: (msg: string, ctx?: Record<string, unknown>) => {
    console.warn(msg, ctx);
    logtail.warn(msg, ctx);
  },
  error: (msg: string, ctx?: Record<string, unknown>) => {
    console.error(msg, ctx);
    logtail.error(msg, ctx);
  },
};

// 使い方
import { log } from "@/lib/logger";
log.info("checkout.session.completed", { userId, amountJpy });

ログには必ず構造化されたコンテキスト (userId / requestId / productId) を付ける。Logtail の Live Tail 画面で userId=xxx でフィルタして「あのユーザの一連の動作」を 追える状態にしておくのは、本番トラブル調査の初速を 10 倍にします。

ClearNets の運用実測: Merci が Stripe Webhook を取りこぼした調査で、 Vercel Logs は 1 時間で消えていたが Logtail に残っていた webhook.received ログから「Vercel が 500 を返し、 Stripe が 3 回リトライして諦めた」経緯を 5 分で特定 → Webhook リトライ処理を実装、というループが可能に。

4. UptimeRobot で外形監視 (無料 50 モニタ)

外部から Ping 打って「本番が生きているか」を監視するのが外形監視。 Sentry は「アプリ内部のエラー」しか見えないので、DNS ダウンや SSL 失効などは外形で捕捉する必要があります。UptimeRobot は 無料 50 モニタ / 5 分間隔 で ClearNets 5 プロダクト × 10 endpoint 監視でも余裕で足ります。

  • Web 前面: https://www.clearnets.org が 200 を返すか。5 分間隔。
  • API endpoint: https://api.kotodama.clearnets.org/health が 200 を 返すか。1 分間隔 (課金 endpoint なので手厚く)。
  • SSL expiry: 各 domain の SSL 証明書が 30 日以内に 切れないか。専用モニタ設定。
  • Cron 動作痕跡: Cron が最後に走った時刻を /api/status/cron-heartbeat で返し、「1 時間以上更新 されなかったら Down」で監視。

UptimeRobot の Alert 先は Discord Webhook + メール 2 系統 に設定。Discord で気づかなかった時のバックアップとしてメール。1 人体制では 「気づかない」が最大リスクなので冗長化は当然。

// app/api/status/cron-heartbeat/route.ts
import { NextResponse } from "next/server";
import { kv } from "@vercel/kv";

export async function GET() {
  const last = await kv.get<number>("cron:last_run");
  if (!last) {
    return NextResponse.json({ status: "unknown" }, { status: 500 });
  }
  const ageMinutes = (Date.now() - last) / 60000;
  if (ageMinutes > 90) {
    // 想定 60 分間隔 + 30 分猶予
    return NextResponse.json(
      { status: "stale", ageMinutes },
      { status: 500 },
    );
  }
  return NextResponse.json({ status: "ok", ageMinutes });
}

5. Vercel Analytics / GA4 / Mixpanel — 3 段構えの使い分け

「アプリの数字を見る」ツールは 3 段で使い分けています。1 つで済ませようと すると必ずどこかで無理が出ます。

  • Vercel Analytics: 手軽なページビュー / 経路 / Cookie 不要 / 25k event/月 まで Hobby 無料。日次ダッシュボード用。
  • GA4: 詳細な流入元 (referrer / UTM) / Search Console 連携 / 無料無制限。SEO 分析・広告分析用。
  • Mixpanel: ユーザー行動フロー / Funnel / Retention。 プロダクト行動分析用。無料枠 100k event/月。

ClearNets の「どれを何に使ってるか」実運用マップ:

  • 「今日 LP 何 PV あった?」 → Vercel Analytics (Dashboard 開いて 5 秒)
  • 「先週の SNS 経由の CV 数は?」 → GA4 (Referrer 分析、UTM タグ)
  • 「サインアップ後 3 日以内に習慣を 1 個作った率は?」 → Mixpanel (Funnel)
  • 「D+7 継続率」 → Mixpanel (Retention レポート)
  • 「Search Console の掲載順位」 → GA4 に Search Console を紐付け

Mixpanel の Event 設計は最初から丁寧にやる価値があります。「命名が 雑」だと 3 ヶ月後に分析できません。ClearNets の命名規則:

# {domain}.{object}.{verb} の 3 部構成
auth.signup.completed
habit.item.created
habit.log.checked
tip.charge.succeeded
tip.charge.failed

# properties は snake_case、value は enum で列挙
mixpanel.track("habit.item.created", {
  habit_kind: "daily",        // enum: daily | weekly
  color_theme: "aizome",      // enum: aizome | matcha | sakura
  from_screen: "onboarding",  // enum: onboarding | home | settings
});

6. Cost 監視 — 「月末に破産する」を防ぐ

個人開発の隠れた地雷は「気づいたら課金が跳ねていた」です。ClearNets は各サービスに Budget Alert を仕込んでいます。

  • Vercel: Team Settings > Billing > Spending Management で $50/月 上限。50%/80%/100% で Discord 通知。100% 到達で Function Concurrency 制限 (アプリは 動くが Image Optimization がキャッシュ返し)。
  • Google Cloud (Cloud Run + Anthropic 経由): Budget $30/月、50%/90% でメール通知。Anthropic API のトークン単価が 高いので、リクエスト前に想定 token 数計算 → 超過なら 429 を返す 自前 rate limit を アプリ層 に入れる。
  • Neon / Supabase: Free tier で運用中は課金 0 だが、 Free 制限 (Compute Hours) を超過した瞬間に「一時停止」なので、 Supabase Dashboard の Usage を UptimeRobot で監視するのは意味なし。 週次で自分で確認する運用に。
  • Stripe: Radar rule で「1 IP から連続決済失敗」で block。決済失敗の量産で fee (¥30 × 3D Secure 認証料) が積むのを防ぐ。
  • OpenAI / Anthropic API: Dashboard に Usage limit を $50/月 で設定 + Slack Webhook 通知。過去にプロンプトインジェクション で長文出力を誘発され $30/日消費した事例 (Kotodama β 期) がある ので、必須。

Cost Alert の実効性を上げる工夫: 「50% で 1 回目、80% で 2 回目」を必ず設定。100% だけだと「気づいた時にもう遅い」に なる。ClearNets は Vercel の 80% Alert (=$40) で Discord に通知が 飛んできて「あれ、今月 Image Optimization 走りすぎ?」と気づいて 修正 (前記事の事例 3) というループを回しています。

7. SLO 設計 — 1 人体制のリアルな指標

大企業の SLO (Service Level Objective) は「99.99% uptime、error budget 0.01%」など厳密ですが、1 人体制では 「99% uptime = 月 7h 停止許容」 くらいで 現実的です。ClearNets の 5 プロダクトの SLO:

  • Web 前面 (LP / Blog): 99.5% uptime (月 3.6h まで 許容)。5xx が 1 時間 5 件超えたら Sentry Alert。
  • Merci 決済 API: 99.9% uptime (月 43 min まで許容)。 Stripe Webhook 失敗率 < 1% を維持。決済失敗はメールで即通知。
  • Kotodama AI 生成 API: 99% uptime (月 7.2h)。 Anthropic API がダウン時は「Gemini フォールバック」で継続、 両方ダウンなら 503 を返しユーザーには「AI が休憩中です」表示。
  • Cron ジョブ (日次集計): 24h 以内に 1 回成功。 3 日連続失敗で警報。

SLO の実運用ポイント: 「p99 レイテンシ」より 「p50 レイテンシ + 成功率」 で追う方が 1 人 オペレーションに向いています。p99 は 1 件のスロークエリで暴れやすく、 優先度低いバグに時間を溶かす原因になる。Merci の場合、SLO は 「p50 < 400ms + 決済成功率 > 99%」で 6 ヶ月安定運用。

8. インシデント対応の 5 段階手順

アラートが Discord に飛んできてから復旧するまでの標準手順を フォーマット化してあります。深夜 3 時に思考停止でもこれをなぞれば 大体復旧できるようにしてある。

  1. Step 1: Triage (60 秒): Discord のアラート内容を 読む → 影響範囲を判定 (全ユーザー / 一部 / 個別)。全ユーザー影響なら 即 Rollback、それ以外は Step 2。
  2. Step 2: 状況把握 (5 分): Vercel Deployments で 直近 push を確認 → Sentry / Logtail でエラーログ確認 → Uptime で 外形が緑か赤か確認。原因の当たりをつける。
  3. Step 3: 応急処置 (15 分): 直せそうなら hotfix、 時間かかりそうなら Vercel Instant Rollback + status page 更新。 ユーザーへの告知は Twitter (@clearnets) + status page の 2 系統。
  4. Step 4: 恒久対策 (翌日): hotfix / rollback で 止血後、翌日冷静な頭で原因調査 → 再発防止 PR。テスト追加必須。
  5. Step 5: Postmortem (翌々日): postmortem テンプレ (後述) を埋めて Runbook に追記。次回同じことが起きたら 5 分で 復旧できるようにする。

9. Postmortem テンプレ

ClearNets のインシデント postmortem は company/incidents/YYYY-MM-DD-title.md に以下フォーマット で残しています。Google SRE 本の 4 セクションを 1 人体制向けに簡素化。

# インシデント: [1 行タイトル]

## Summary (2-3 行)
[何が起きた / 影響範囲 / 復旧までの時間]

## Timeline (JST)
- 22:03 Sentry alert (5xx spike on merci-web)
- 22:05 Vercel Deployments 確認、22:00 push を疑う
- 22:07 Instant Rollback 実施
- 22:10 状態正常化を UptimeRobot で確認
- 22:15 Twitter + status page で告知

## Root cause
[技術的な原因を 1 段落。「なぜ」を 5 回繰り返した結論]

## Impact
- 影響ユーザー数: 推定 ~30 人 (22:00-22:10 の 10 分間 5xx)
- 収益影響: 決済失敗 0 件 (Merci の場合)
- SLO 消費: 月 43 min 予算の 10 min 消費 → 残 33 min

## What went well
- Sentry alert が 2 分以内に発火した
- Rollback 手順が Runbook にあったので迷わなかった

## What went wrong
- Preview で目視確認せず main merge した
- Test で該当ケースが cover されていなかった

## Action items (期限付き)
- [ ] Preview 目視確認を PR チェックリストに追加 (2026-08-20)
- [ ] 該当ケースの unit test 追加 (2026-08-19)
- [ ] Runbook にこの事例を追記 (2026-08-18)

Postmortem の目的は「犯人探し」ではなく「Runbook 拡充」です。 ClearNets は過去 12 ヶ月で 8 件の postmortem を書き、そのうち 6 件は Action items が Runbook に組み込まれ、以降同種の障害を予防できました。

10. Cron 監視 — 「動かないけどエラーも出ない」対策

Cron の最悪パターンは「エラーは出ないが、そもそも起動していない」 です。Sentry も UptimeRobot も反応しません。ClearNets の対策:

  1. Cron 実行のたびに Vercel KV に timestamp を書く: await kv.set("cron:last_run", Date.now()) を cron ハンドラの冒頭で必ず実行。
  2. Heartbeat endpoint を UptimeRobot で監視: §4 の/api/status/cron-heartbeat が 500 を返すのを検知して Discord 通知。
  3. 週次ダイジェスト: 毎週日曜 9:00 に「今週 Cron 何回動きましたか?」を Discord に自動投稿。7 回動くはずの日次 Cron が 3 回しか動いていなかったら異常検知。

代替として Cronitor.io Healthchecks.io (無料) を使う手もあります。Cron 側から「動いた」を ping し、決められた時間 ping が来なかったら通知。 Vercel Cron に絞るなら KV 方式で十分ですが、GitHub Actions / Cloud Run Jobs も混ざるなら Healthchecks の方が統一感があります。

11. Discord 通知チャンネル設計

ClearNets の Discord サーバは以下のチャンネル構成にしています。 全部 1 人で見るので「見なくていいもの」を絞る設計です。

  • #alerts-critical: 5xx / 決済失敗 / DB 到達不能 / SSL 失効直前。@everyone mention 付き。夜中でも見る。
  • #alerts-warn: 4xx spike / cron 遅延 / cost 50% 到達 / Sentry の低頻度 event。mention なし。翌朝確認で OK。
  • #deploys: Vercel の deploy 成功 / 失敗、GitHub Actions の workflow 結果。ダイジェスト用。
  • #stripe: Stripe Webhook 全件 (成功も含む) を流す。 決済イベントを追いかけたい時のジャーナル。
  • #daily-digest: 毎朝 9:00 に「昨日の DAU / 決済数 / エラー数 / cost」を投稿する自作 bot。

Discord Webhook は「JSON payload を POST するだけ」なので Vercel の API Route から 5 行で送れます。GitHub Actions / Vercel Deploy Hook / Stripe Webhook / Sentry / UptimeRobot / cron ハンドラなど、全部を 1 個の Discord サーバに集約するのがコツ。

// lib/discord.ts — 通知ヘルパ
export async function notifyDiscord(opts: {
  channel: "critical" | "warn" | "digest";
  title: string;
  description: string;
  color?: number;  // 0xff0000 (red) 等
}) {
  const url = {
    critical: process.env.DISCORD_WEBHOOK_CRITICAL!,
    warn: process.env.DISCORD_WEBHOOK_WARN!,
    digest: process.env.DISCORD_WEBHOOK_DIGEST!,
  }[opts.channel];

  await fetch(url, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      embeds: [{
        title: opts.title,
        description: opts.description,
        color: opts.color ?? 0x00d9ff,
        timestamp: new Date().toISOString(),
      }],
      content: opts.channel === "critical" ? "@everyone" : undefined,
    }),
  });
}

12. Runbook — README に貼るコマンド集

Runbook はプロダクトごとの README-runbook.md に貼って あります。「Discord でこう見えたら、こうしろ」の対応表。ClearNets の Merci Runbook 抜粋:

## Discord で「5xx spike on merci-web」を見たら

1. Vercel dashboard で直近 push 確認 (vercel deployments list)
2. Sentry で該当時刻の error stack trace 確認
3. 原因不明なら即 rollback:
   vercel promote <直前の deployment URL>
4. Rollback 後、UptimeRobot が緑に戻るのを 5 min 待つ
5. Twitter (@clearnets) で「一時的な障害があり復旧しました」告知

## Discord で「Stripe Webhook failed」を見たら

1. Stripe Dashboard > Developers > Webhooks で該当 event 探す
2. Event ID を Logtail に貼って処理ログ確認
3. アプリ側で 500 返しているなら fix + redeploy
4. Stripe 側で "Resend event" ボタン押して再処理

## Discord で「Neon cost 80% alert」を見たら

1. Neon dashboard > Usage で「どのプロジェクトが」を確認
2. autosuspend が効いていない project を suspect
3. Compute settings > Autosuspend を 5 min に戻す

## Discord で「SSL cert expiring in 30 days」を見たら
- Vercel が自動更新するので通常は放置で OK
- 45 days 経過してもまだ通知が来る場合のみ、Vercel Dashboard で
  Domain 再検証 (Refresh domain config) を実行

Runbook の効果測定: ClearNets は 2026 年 6 月の Vercel deploy 障害の際、 Runbook のおかげで「Discord 通知 → 復旧」が 8 分でした。Runbook 無かった 時期 (2025) は同種障害に 40 分かかっていたので 復旧時間 1/5 という定量効果。書く価値は非常に高い。

13. まとめ — 監視は「1 人でも寝られる」ための投資

1 人開発でオブザーバビリティに投資する目的は「エンタープライズごっこ」 ではなく、「1 人でも安心して寝られる仕組み」 を 作るためです。Sentry で本物のバグだけ通知し、Logtail で 3 日ログを 保存し、UptimeRobot で外形を監視し、Cost Alert で破産を防ぎ、 Runbook で 5 分復旧する。これで月 ¥0 - ¥1,000 で 5 プロダクトの 24/7 監視が回ります。

「監視は始めないと重要さがわからない」ので、まず Sentry を npx @sentry/wizard するところから始めることを推奨します。 1 週間本番で回すと「あ、こんなエラー出てたんだ」という気づきが 必ずあり、それが監視投資の第一歩になります。ClearNets の監視スタックも 2 年かけて少しずつ育ててきた結果で、最初から全部揃えたわけではありません。 1 プロダクトずつ、1 アラートずつ、増やしていけば良いです。


この記事を書いた背景

ClearNets は 2024 年から本格的に監視スタックを整備し始め、 Sentry ノイズ削減や Runbook 化を 2 年間の運用の中で磨いてきました。 本記事の数字と設定はすべて 2026 年 8 月時点のもので、SaaS の 料金体系や機能は頻繁に更新されるため、最新情報は各サービス公式を 参照ください。


← ブログ一覧に戻る