🎄 科学と神々株式会社 アドベントカレンダー 2025
License System Day 24: トラブルシューティングとFAQ
📖 今日のテーマ
今日は、よくある問題と解決策を学びます。
エラーメッセージの診断、ログ分析、パフォーマンス問題、セキュリティ診断、本番環境での対応まで、実践的なトラブルシューティングを解説します。
🎯 トラブルシューティングの基本
デバッグ手順
1. 問題の特定
→ エラーメッセージ、ログ、症状の確認
2. 再現性の確認
→ 一貫して発生するか、特定条件下のみか
3. 原因の調査
→ ログ分析、デバッグ出力、ネットワーク診断
4. 解決策の実施
→ 段階的に修正、テストで確認
5. 予防策の実装
→ エラーハンドリング強化、監視追加
❓ よくある問題と解決策
問題1: "Invalid signature" エラー
症状
curl -X POST http://localhost:8080/api/v1/license/activate \
-d '{"email": "test@example.com", "password": "test123"}'
# レスポンス:
# {"error": "Invalid signature"}
原因
- 公開鍵と秘密鍵のペアが一致していない
- JWT署名アルゴリズムの不一致
- トークンが改ざんされている
解決策
# 1. 鍵ペアの確認
openssl ec -in keys/private_key.pem -text -noout
openssl ec -in keys/public_key.pem -pubin -text -noout
# 2. 鍵ペアの再生成
rm keys/*.pem
./generate_keys.sh
# 3. サーバー再起動
pkill -f main
cd server && ./src/main
# 4. 再テスト
curl -X POST http://localhost:8080/api/v1/license/activate \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "test123"}'
問題2: "Token expired" エラー
症状
# ライセンス検証
curl -X POST http://localhost:8080/api/v1/license/validate \
-H "X-Activation-Key: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
# レスポンス:
# {"error": "Token expired"}
原因
- JWTトークンの有効期限が切れた
- システム時刻のずれ
解決策
# 1. システム時刻の確認
date
timedatectl status # Linux
# 2. NTPで時刻同期
sudo ntpdate -s time.nist.gov # Linux
sudo sntp -sS time.apple.com # macOS
# 3. トークンの有効期限確認
# JWT Debugger (https://jwt.io/)でデコード
# 4. 再認証
curl -X POST http://localhost:8080/api/v1/license/activate \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "test123"}'
問題3: "Rate limit exceeded" エラー
症状
# 連続リクエスト後
curl -X POST http://localhost:8080/api/v1/echo \
-H "X-Activation-Key: VALID_TOKEN" \
-d '{"message": "Test"}'
# レスポンス:
# {"error": "Rate limit exceeded", "retry_after": 3540}
原因
- プランの制限を超えた(Free: 10 req/hour)
- レート制限カウンターが正しくリセットされていない
解決策
# 1. 現在のレート制限カウント確認
sqlite3 server/data/license_system.db <<EOF
SELECT user_id, endpoint, COUNT(*) AS request_count
FROM rate_limits
WHERE request_time >= datetime('now', '-1 hour')
GROUP BY user_id, endpoint;
EOF
# 2. レート制限カウンターをリセット(開発環境のみ)
sqlite3 server/data/license_system.db <<EOF
DELETE FROM rate_limits WHERE user_id = 'test-user-001';
EOF
# 3. プランのアップグレード
curl -X POST http://localhost:8080/api/v1/subscription/upgrade \
-H "X-Activation-Key: VALID_TOKEN" \
-d '{"plan_type": "premium_monthly"}'
# 4. 待機時間の確認
# retry_after秒後に再試行
問題4: サーバーが起動しない
症状
./server/src/main
# エラー:
# Error: Address already in use (port 8080)
# または
# Error: unable to open database file
原因
- ポート8080が既に使用されている
- データベースファイルが存在しない/権限がない
- 環境変数が設定されていない
解決策
# ポート競合の確認
lsof -i :8080
# PIDを確認して終了
kill -9 <PID>
# データベース確認
ls -la server/data/license_system.db
chmod 644 server/data/license_system.db
chmod 755 server/data/
# データベース再作成
cd server
sqlite3 data/license_system.db < schema.sql
# 環境変数確認
cat .env
export SERVER_PORT=8080
export DATABASE_PATH=data/license_system.db
# サーバー再起動
./src/main
問題5: ブラウザ拡張機能が動作しない
症状
拡張機能アイコンをクリックしても反応なし
またはエラー: "Failed to fetch"
原因
- サーバーが起動していない
- CORS設定の問題
- JavaScriptエラー
解決策
# 1. サーバー起動確認
curl http://localhost:8080/health
# {"status": "ok"}
# 2. CORS設定確認
# server/src/main.nimで以下を確認:
resp.setHeader("Access-Control-Allow-Origin", "*")
resp.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
resp.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Activation-Key")
# 3. ブラウザコンソールでエラー確認
# Chrome: F12 → Console
# Firefox: F12 → コンソール
# 4. 拡張機能の再読み込み
# chrome://extensions/ → 拡張機能の「再読み込み」ボタン
問題6: データベース接続エラー
症状
# サーバー起動時
Error: unable to open database file
# またはクエリ実行時
Error: database is locked
原因
- SQLiteファイルが存在しない
- 複数プロセスが同時書き込み
- パーミッション不足
解決策
# SQLiteファイル作成
cd server
mkdir -p data
sqlite3 data/license_system.db < schema.sql
# パーミッション修正
chmod 755 data/
chmod 644 data/license_system.db
# ロックの確認(他プロセスを終了)
lsof | grep license_system.db
kill -9 <PID>
# PostgreSQLへの移行(本番環境推奨)
# schema_postgresql.sqlを使用
psql -U postgres -d license_system -f server/schema_postgresql.sql
🔍 ログ分析とデバッグ
構造化ログの分析
# アプリケーションログの確認
tail -f server/logs/application.log
# エラーログの抽出
cat server/logs/application.log | jq 'select(.level == "ERROR")'
# 特定ユーザーのログ
cat server/logs/application.log | jq 'select(.user_id == "test-user-001")'
# 特定時間範囲のログ
cat server/logs/application.log | jq 'select(.timestamp >= "2025-06-15T10:00:00Z" and .timestamp <= "2025-06-15T11:00:00Z")'
# エラー頻度の集計
cat server/logs/application.log | jq -r 'select(.level == "ERROR") | .message' | sort | uniq -c | sort -rn
デバッグ出力の追加
# server/src/main.nim
# デバッグログ追加
import std/logging
# ロガー初期化
var logger = newConsoleLogger(lvlDebug)
routes:
post "/api/v1/license/activate":
logger.log(lvlDebug, "Activation request received")
logger.log(lvlDebug, "Email: " & body["email"].getStr())
let user = db.getUserByEmail(email)
logger.log(lvlDebug, "User found: " & $user.isSome)
if user.isNone:
logger.log(lvlWarn, "User not found: " & email)
resp Http401, %*{"error": "Invalid credentials"}
logger.log(lvlDebug, "Password verification starting")
# ... 処理続行
⚡ パフォーマンス問題のトラブルシューティング
レスポンスタイムの測定
# curlでレスポンスタイム計測
curl -X POST http://localhost:8080/api/v1/echo \
-H "Content-Type: application/json" \
-H "X-Activation-Key: VALID_TOKEN" \
-d '{"message": "Performance test"}' \
-w "\nTime: %{time_total}s\n" \
-o /dev/null -s
# Apache Benchで負荷テスト
ab -n 1000 -c 50 -p payload.json -T application/json \
http://localhost:8080/api/v1/echo
# 出力例:
# Requests per second: 850.32 [#/sec]
# Time per request: 58.81 [ms] (mean)
スロークエリの診断
-- PostgreSQLでスロークエリログ有効化
ALTER DATABASE license_system SET log_min_duration_statement = 100; -- 100ms以上のクエリをログ
-- スロークエリの確認
SELECT
calls,
total_time,
mean_time,
query
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
パフォーマンス最適化
# server/src/database_postgresql.nim
# インデックス追加でクエリ高速化
proc createIndexes*(db: PostgresDatabase) =
## パフォーマンス最適化インデックス
# メールアドレス検索高速化
db.exec(sql"CREATE INDEX IF NOT EXISTS idx_users_email ON users (email)")
# アクティブなサブスクリプション検索
db.exec(sql"CREATE INDEX IF NOT EXISTS idx_subscriptions_active ON subscriptions (user_id, status) WHERE status = 'active'")
# レート制限クエリ高速化
db.exec(sql"CREATE INDEX IF NOT EXISTS idx_rate_limits_recent ON rate_limits (user_id, endpoint, request_time DESC)")
echo "✅ Performance indexes created"
🔒 セキュリティ問題の診断
セキュリティ監査チェックリスト
# 1. 鍵ファイルのパーミッション確認
ls -la keys/
# private_key.pem: -r-------- (400)
# public_key.pem: -rw-r--r-- (644)
# 2. 環境変数の漏洩チェック
grep -r "password" . --exclude-dir=node_modules --exclude-dir=.git
# ハードコードされたパスワードがないか確認
# 3. SQLインジェクション脆弱性チェック
# プリペアドステートメント使用を確認
grep -n "db.exec(sql\"" server/src/*.nim
# 4. HTTPS強制確認(本番環境)
curl -I http://api.license-system.com
# Location: https://api.license-system.com へリダイレクト
# 5. セキュリティヘッダー確認
curl -I https://api.license-system.com
# X-Content-Type-Options: nosniff
# X-Frame-Options: DENY
# X-XSS-Protection: 1; mode=block
侵入検知
# 不正なアクセス試行のログ分析
cat server/logs/application.log | \
jq -r 'select(.level == "WARNING" and .message | contains("Invalid credentials"))' | \
jq -r '.metadata.email' | \
sort | uniq -c | sort -rn
# 出力例:
# 42 attacker@example.com ← ブルートフォース攻撃
# 5 test@example.com
🌟 FAQ(よくある質問)
Q1: ライセンスキーの有効期限はどのように設定しますか?
A: JWT生成時にexpiresInパラメータで日数を指定します。
let token = cryptoService.generateJWT(
userId = userId,
email = email,
planType = "premium_monthly",
expiresIn = 365 # 365日間有効
)
Q2: 複数デバイスでの同時使用は可能ですか?
A: プランによって異なります。
- Free: 1デバイス
- Premium: 3デバイス
- Enterprise: 無制限
デバイス管理は/api/v1/devicesエンドポイントで確認できます。
Q3: オフライン環境で動作しますか?
A: 部分的に可能です。
- 認証: オフライン不可(初回認証必須)
- 検証: キャッシュされたトークンで短期間可能
- 完全オフライン: オフラインライセンスキー実装が必要(Day 23参照)
Q4: サブスクリプションをキャンセルするとどうなりますか?
A:
- 有効期限まで使用可能
- 期限後は無料プランに自動ダウングレード
- データは保持(90日間)
curl -X POST http://localhost:8080/api/v1/subscription/cancel \
-H "X-Activation-Key: VALID_TOKEN"
Q5: レート制限を一時的に解除できますか?
A: 管理者権限で可能です(本番環境非推奨)。
# server/src/admin_api.nim
routes:
post "/api/v1/admin/ratelimit/reset":
let userId = body["user_id"].getStr()
db.exec(sql"DELETE FROM rate_limits WHERE user_id = $1", userId)
resp Http200, %*{"status": "reset"}
Q6: どのプランが自分に適していますか?
A:
- Free: 個人利用、軽い使用(10 req/hour)
- Premium: 中規模プロジェクト(100 req/hour)
- Enterprise: 商用・大規模(無制限)
Q7: セキュリティ上の問題を発見した場合は?
A: security@license-system.comまで報告してください(バグバウンティプログラムあり)。
🌟 まとめ
トラブルシューティングの要点:
-
エラー診断
- エラーメッセージの読み方
- ログ分析手法
- 再現性の確認
-
パフォーマンス
- レスポンスタイム測定
- スロークエリ診断
- インデックス最適化
-
セキュリティ
- 監査チェックリスト
- 侵入検知
- 脆弱性スキャン
-
FAQ
- 有効期限管理
- デバイス制限
- サブスクリプション管理
前回: Day 23: 追加機能の実装アイデア
次回: Day 25: 総括とまとめ
Happy Learning! 🎉