Logging Guidelines(ロギング規約)
目的
Sagebaseプロジェクトにおけるロギングの一貫性と品質を確保するためのガイドラインを提供します。DEBUG用のprint文の混入を防ぎ、適切なloggerの使用を促進します。
いつアクティベートするか
このスキルは以下の場合に自動的にアクティベートされます:
- •ログ出力を追加・修正する時
- •デバッグ用コードを書く時
- •新しいモジュールを作成する時(loggerの初期化)
- •
print(をコードに追加しようとする時 - •エラーハンドリングを実装する時
クイックチェックリスト
コード追加前
- • print文を使用していない(絶対禁止)
- • 適切なログレベルを選択している
- • loggerがモジュールに定義されている
コード追加中
- • ログメッセージが明確で有用
- • 機密情報をログに含めていない
- • 既存のlogger出力と重複していない
デバッグ時
- • 一時的なprint文を追加していない
- • logger.debugを使用している
🚫 絶対禁止: print文の使用
本番コードでprint文を使用することは絶対に禁止です
禁止される理由
- •
ログ管理の一貫性が失われる
- •ログレベル制御ができない
- •ログローテーションの対象外
- •構造化ログの恩恵を受けられない
- •
本番環境での問題
- •標準出力への不要な出力
- •パフォーマンス監視ツールとの非互換
- •Sentry等のエラー追跡が機能しない
- •
デバッグの困難さ
- •出力元の特定が困難
- •タイムスタンプがない
- •コンテキスト情報がない
❌ 悪い例
python
# ❌ DEBUG用のprint
print(f"DEBUG: Processing {len(items)} items")
# ❌ 進捗表示のprint
print(f"Processing {i}/{total}...")
# ❌ エラー表示のprint
print(f"Error: {e}")
# ❌ 警告のprint
print(f"警告: キーワードが見つかりません")
✅ 良い例
python
# ✅ DEBUG用のログ
logger.debug(f"Processing {len(items)} items")
# ✅ 進捗表示のログ
logger.info(f"Processing {i}/{total}...")
# ✅ エラー表示のログ
logger.error(f"Error: {e}")
# ✅ 警告のログ
logger.warning("キーワードが見つかりません")
ログレベルの使い分け
DEBUG
用途: 開発時のデバッグ情報、詳細な内部状態
python
# 変数の値を確認
logger.debug(f"Sample links = {sample_urls}")
# 処理の詳細ステップ
logger.debug(f"After filtering - child_links={len(child_links)}")
# 内部状態のトレース
logger.debug(f"size_in_bytes: {size_in_bytes}")
特徴:
- •本番環境では通常出力されない
- •開発者向けの詳細情報
- •パフォーマンスに影響する可能性がある大量出力OK
INFO
用途: 正常な処理の進捗、重要なイベント
python
# 処理の開始・完了
logger.info(f"Analyzing links for URL: {input_dto.current_url}")
# 処理結果のサマリー
logger.info(f"Extracted {len(all_links)} total links")
# 重要な状態変化
logger.info("LLMベースの正規化を使用")
特徴:
- •本番環境でも出力される
- •処理の流れを追跡可能
- •適度な頻度で出力
WARNING
用途: 予期せぬ状況だが処理は継続可能
python
# データが見つからない
logger.warning(f"キーワード '{keyword}' が見つかりません。スキップします。")
# フォールバック処理
logger.warning("LLM正規化失敗、ルールベースを使用", error=str(e))
# 設定の問題
logger.warning("API key not set, using default configuration")
特徴:
- •問題の兆候を示す
- •即座の対応は不要だが注意が必要
- •監視対象とすべき
ERROR
用途: エラーが発生したが、アプリケーションは継続可能
python
# 処理の失敗
logger.error(f"Failed to save data: {e}")
# 外部サービスのエラー
logger.error(f"API call failed: {response.status_code}")
# バリデーションエラー
logger.error(f"Invalid input: {validation_errors}")
特徴:
- •問題の発生を示す
- •調査が必要
- •Sentryに自動送信される
CRITICAL
用途: 致命的なエラー、システムが機能不能
python
# データベース接続失敗
logger.critical("Database connection failed, shutting down")
# 必須設定の欠落
logger.critical("Required API key not configured")
特徴:
- •即座の対応が必要
- •アラート発報
- •稀にしか使用しない
loggerの初期化パターン
標準パターン
python
"""モジュールの説明."""
import logging
# モジュールレベルでloggerを定義
logger = logging.getLogger(__name__)
class MyService:
"""サービスクラス."""
def process(self) -> None:
"""処理を実行."""
logger.info("Processing started")
# ...
logger.debug(f"Processed {count} items")
重要なポイント
- •
__name__を使用するpython# ✅ 良い例 logger = logging.getLogger(__name__) # ❌ 悪い例 logger = logging.getLogger("my_module") # ハードコード logger = logging.getLogger() # ルートロガー - •
モジュールレベルで定義する
python# ✅ 良い例:モジュールレベル logger = logging.getLogger(__name__) class MyClass: pass # ❌ 悪い例:クラス内で定義 class MyClass: logger = logging.getLogger(__name__) - •
import文の後、クラス定義の前に配置
python"""Docstring.""" import logging from typing import Any from src.domain.entities import Entity logger = logging.getLogger(__name__) # ここに配置 class MyService: pass
構造化ログの活用
Sagebaseではstructlogを使用した構造化ログをサポートしています。
キーワード引数でコンテキストを追加
python
# ✅ 良い例:構造化されたコンテキスト
logger.info(
"Processing completed",
item_count=len(items),
duration_ms=elapsed_time,
status="success"
)
# ❌ 悪い例:f-stringで全て埋め込み
logger.info(f"Processing completed: {len(items)} items in {elapsed_time}ms, status=success")
コンテキストマネージャーの使用
python
from src.common.logging import LogContext
# リクエスト全体でコンテキストを共有
with LogContext(request_id=request_id, user_id=user_id):
logger.info("Processing request")
# このブロック内のログにrequest_idとuser_idが自動的に含まれる
よくあるアンチパターン
1. logger.infoと重複するprint
python
# ❌ 悪い例:重複出力
logger.info(f"Extracted {len(all_links)} total links")
print(f"DEBUG: Extracted {len(all_links)} total links") # 重複!
# ✅ 良い例:loggerのみ
logger.info(f"Extracted {len(all_links)} total links")
2. 絵文字をログに含める
python
# ❌ 悪い例:絵文字
print(f"➕ 新規追加: {name}") # ログシステムとの互換性問題
# ✅ 良い例:プレーンテキスト
logger.debug(f"新規追加: {name}")
3. 機密情報のログ出力
python
# ❌ 悪い例:機密情報
logger.info(f"User logged in with password: {password}")
logger.debug(f"API key: {api_key}")
# ✅ 良い例:機密情報をマスク
logger.info(f"User logged in: {username}")
logger.debug("API key configured: ***")
4. 過剰なログ出力
python
# ❌ 悪い例:ループ内で毎回ログ
for item in items: # 10000件
logger.info(f"Processing item: {item}")
# ✅ 良い例:サマリーのみ
logger.info(f"Processing {len(items)} items")
for item in items:
# 処理
pass
logger.info("Processing completed")
デバッグ時のベストプラクティス
一時的なデバッグ出力
python
# ❌ 絶対禁止:print文
print(f"DEBUG: value = {value}")
# ✅ 推奨:logger.debug
logger.debug(f"value = {value}")
デバッグ後のクリーンアップ
デバッグが完了したら:
- •不要な
logger.debugを削除 - •残すべきログは適切なレベルに変更
- •コミット前に
printの残留をチェック
bash
# print文の残留チェック
grep -rn "print(" src/ | grep -v "docstring\|#"
関連スキル
- •project-conventions: プロジェクト全体の規約
- •test-writer: テスト作成ガイド
まとめ
重要な原則
✅ print文は絶対に使用しない ✅ logger.getLogger(name)でloggerを初期化 ✅ 適切なログレベルを選択 ✅ 構造化ログでコンテキストを追加 ✅ 機密情報をログに含めない
これらの原則に従うことで、一貫性があり、運用しやすいロギングを実現できます。