0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

License System Day 24: トラブルシューティングとFAQ

Last updated at Posted at 2025-12-23

🎄 科学と神々株式会社 アドベントカレンダー 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"}

原因

  1. 公開鍵と秘密鍵のペアが一致していない
  2. JWT署名アルゴリズムの不一致
  3. トークンが改ざんされている

解決策

# 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"}

原因

  1. JWTトークンの有効期限が切れた
  2. システム時刻のずれ

解決策

# 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}

原因

  1. プランの制限を超えた(Free: 10 req/hour)
  2. レート制限カウンターが正しくリセットされていない

解決策

# 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

原因

  1. ポート8080が既に使用されている
  2. データベースファイルが存在しない/権限がない
  3. 環境変数が設定されていない

解決策

# ポート競合の確認
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"

原因

  1. サーバーが起動していない
  2. CORS設定の問題
  3. 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

原因

  1. SQLiteファイルが存在しない
  2. 複数プロセスが同時書き込み
  3. パーミッション不足

解決策

# 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:

  1. 有効期限まで使用可能
  2. 期限後は無料プランに自動ダウングレード
  3. データは保持(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まで報告してください(バグバウンティプログラムあり)。


🌟 まとめ

トラブルシューティングの要点:

  1. エラー診断

    • エラーメッセージの読み方
    • ログ分析手法
    • 再現性の確認
  2. パフォーマンス

    • レスポンスタイム測定
    • スロークエリ診断
    • インデックス最適化
  3. セキュリティ

    • 監査チェックリスト
    • 侵入検知
    • 脆弱性スキャン
  4. FAQ

    • 有効期限管理
    • デバイス制限
    • サブスクリプション管理

前回: Day 23: 追加機能の実装アイデア
次回: Day 25: 総括とまとめ

Happy Learning! 🎉

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?