スポンサーリンク

Pythonプロジェクトの「設定・ログ・例外」役割分担マップ|設計で差がつく保守性の基本

クラス設計・OOP入門

Pythonである程度の規模のコードを書いていると、こんな経験はありませんか?
「設定値があちこちに散らばっている」「ログが多すぎて本当に必要な情報が見つからない」「try/exceptを書いたはずなのに、なぜか原因が追えない」――。

最初は小さなスクリプトだったのに、機能追加や修正を重ねるうちに、
設定・ログ・例外がごちゃ混ぜになって、触るのが怖いコードになってしまう。 これは珍しい話ではなく、多くのPythonプロジェクトが一度は通る道です。

その原因の多くは、テクニック不足ではなく、
「それぞれの役割をどう分けるか」を最初に整理していないことにあります。

設定は何のためにあるのか。 ログはどこで、何を、どこまで書くべきなのか。 例外はその場で処理すべきか、それとも上に投げるべきなのか。

この記事では、Pythonプロジェクトにおける
「設定・ログ・例外」の役割分担を一枚のマップとして整理し、 設計の判断に迷わなくなる考え方を解説します。

コード例を大量に並べるのではなく、
「なぜそう分けるのか」「どこまで責任を持つのか」という設計の軸を中心に話を進めていきます。

読み終わる頃には、 「とりあえず動くコード」から一段進んだ、 あとから変更しやすく、壊れにくいPythonコードを組み立てるための視点が手に入るはずです 😊


  1. 1. なぜ「設定・ログ・例外」は混ざりやすいのか
    1. ハードコードが増えていく理由
    2. printデバッグが残すもの
    3. try / except が広がりすぎると起きること
    4. 共通する根本原因
  2. 2. 設計思想としての「関心の分離」をどう考えるか
    1. 「関心の分離」とは何か
    2. 設定・ログ・例外は「外側の関心」
    3. Clean Architectureが示していること
  3. 3. 役割分担マップ全体像(1枚で俯瞰)
    1. まずは全体像を整理する
    2. やってはいけない混ざり方
    3. 責任の境界線を引く
    4. マップとして捉えるメリット
  4. 4. 設定(Configuration)の責務と実装指針
    1. 設定に含めるべきもの・含めるべきでないもの
    2. 環境依存・アプリ設定・シークレットを分ける
    3. 設定は「起動時に解決」する
    4. Pydanticを使う理由
  5. 5. 例外(Exceptions)の責務と設計判断
    1. raiseする側の責務:判断しない、ただ報告する
    2. catchする側の責務:どう対処するかを決める
    3. try / except を書いていい場所・悪い場所
    4. ログとの役割分担を意識する
  6. 6. ログ(Logging)の責務と運用設計
    1. ログは制御しない、判断しない
    2. ログレベルは「重要度」ではなく「用途」で考える
    3. logger.exception() を使うべき場面
    4. ログ設定は集中管理する
    5. 実務設計を深めたい人へ
  7. 7. 実装手順まとめ(設定・ログ・例外を統合する)
    1. ① エントリポイントで「外側の準備」を済ませる
    2. ② ロジックには「解決済みの値」だけを渡す
    3. ③ 例外は「境界」で捕まえる
    4. ④ 例外を処理すると決めた場所でログを書く
    5. ⑤ リソース管理は仕組みに任せる
  8. 8. 運用・セキュリティ・可観測性の補足
    1. ログに機密情報を残さない
    2. パフォーマンスへの配慮
    3. 構造化ログという選択肢
    4. 可観測性を意識した拡張
  9. まとめ
    1. あわせて読みたい
  10. よくある質問(Q&A)
    1. 関連投稿:

1. なぜ「設定・ログ・例外」は混ざりやすいのか

Pythonプロジェクトで設定・ログ・例外が混ざってしまうのは、 書き方が悪いからでも、スキルが足りないからでもありません。

多くの場合、原因はとてもシンプルで、
「まずは動かすこと」を優先する判断が、あとから積み重なっていくからです。

ハードコードが増えていく理由

開発初期は、データベースの接続先やAPIキー、タイムアウト値などを、 その場で分かりやすくコードに直接書いてしまいがちです。

この時点では問題なく動きますし、修正も簡単です。 でも、環境が増えた瞬間に状況が変わります。

  • 開発環境と本番環境で値を切り替えたい
  • APIキーをGit管理から外したい
  • 設定変更のたびにコードを触りたくない

こうした要望が出てきたとき、
設定とロジックが結びついているコードは、一気に扱いづらくなるのです。

printデバッグが残すもの

エラーや挙動確認のために print() を使うのは、決して悪いことではありません。 むしろ、Pythonではとても有効な手段です。

ただし、printは「その瞬間を見る」ための道具です。

プロジェクトが成長すると、こんな問題が起きます。

  • どのprintが本当に必要なのか分からない
  • 本番環境で不要な出力が大量に流れる
  • 過去に何が起きたのか後から追えない

ここで必要になるのが、 「残す情報」と「捨てる情報」を意識したログの設計です。

try / except が広がりすぎると起きること

エラーでプログラムが止まらないように、 try / except を書くのも自然な流れです。

ところが、例外処理の責任を考えずに書いていくと、

  • どこで何が起きたのか分からない
  • 例外を握りつぶしてしまう
  • 本来止めるべき処理が静かに続行される

という状態に陥ります。

これは「例外処理が足りない」のではなく、 「誰が判断するのか」が決まっていないことが原因です。

共通する根本原因

設定・ログ・例外が混ざる現象には、共通点があります。

それは、
それぞれが「何の責任を持つのか」を明確に決めないまま使われていることです。

次の章では、この混乱を整理するために、 設計の土台となる考え方――「関心の分離」について見ていきます。




2. 設計思想としての「関心の分離」をどう考えるか

設定・ログ・例外が混ざってしまう問題を根本から解くには、 「どこに書くか」より先に、どう考えるかを整理する必要があります。

そこで登場するのが、設計の基本思想である関心の分離です。

「関心の分離」とは何か

関心の分離とは、ひとことで言うと、 「それぞれのコードに、ひとつの責任だけを持たせる」という考え方です。

Pythonプロジェクトに当てはめると、次のように整理できます。

  • ビジネスロジック:何をするアプリなのか
  • 設定:どういう条件で動かすのか
  • ログ:実行中に何が起きたのか
  • 例外:想定外が起きたときにどう知らせるか

重要なのは、 ビジネスロジックが「設定の置き場所」や「ログの出力形式」を知らなくていいという点です。

設定・ログ・例外は「外側の関心」

設定・ログ・例外は、アプリケーションの動作を支える重要な要素ですが、 それ自体が「アプリの本質」ではありません。

たとえば、

  • 設定は「すでに決まった値」として渡されればよい
  • ログは「必要な情報を渡せば、どう出力するかは任せたい」
  • 例外は「何が起きたかを伝え、判断は上位に委ねたい」

という立場に立つと、 ロジックはどんどんシンプルになっていきます。

Clean Architectureが示していること

この考え方を、より明確な構造として示しているのが Clean Architectureです。

Clean Architectureでは、 ビジネスルールを中心に置き、 設定・フレームワーク・I/O・外部サービスを「外側」に配置します。

つまり、

  • 設定値の取得方法
  • ログの出力先や形式
  • 例外をどう通知するか

といった変わりやすい要素に、 ロジックが引きずられない構造を作る、という発想です。

この視点を一度しっかり押さえておくと、 設定・ログ・例外を「どこに書くべきか」で迷いにくくなります。

設計思想を体系的に理解したい場合は、次の一冊がとても参考になります。

Clean Architecture
Amazonでチェックする楽天でチェックする

次の章では、ここまでの考え方を踏まえて、 設定・ログ・例外の役割分担を「マップ」として一気に俯瞰していきます。




3. 役割分担マップ全体像(1枚で俯瞰)

ここまでで、設定・ログ・例外は「外側の関心」であり、 ビジネスロジックとは切り離して考えるべき、という前提を共有しました。

この章では、それを実務で迷わない形に落とし込むため、 設定・ログ・例外の役割分担を一度、まとめて俯瞰してみます。

まずは全体像を整理する

3つの要素は、それぞれ次の役割を持っています。

  • 設定(Configuration):アプリケーションを「どういう条件で動かすか」を決める
  • 例外(Exceptions):通常フローを壊す出来事を「上位に知らせる」
  • ログ(Logging):実行中に起きた事実を「記録として残す」

ポイントは、 どれも「処理そのもの」を担当しないという点です。

やってはいけない混ざり方

役割が曖昧になると、次のようなコードが生まれがちです。

  • 関数の中で環境変数を直接読む
  • ビジネスロジック内でログ設定を変更する
  • 低レイヤーで例外を握りつぶしてログだけ出す

一見すると便利ですが、 後から変更・テスト・再利用がしづらい構造になります。

責任の境界線を引く

役割分担を考えるときの基本ルールはシンプルです。

  • 設定は「起動時に解決」し、ロジックには値だけ渡す
  • 例外は「判断せずに報告」し、上位で意味づけする
  • ログは「判断材料を残す」だけで、制御をしない

この境界線を守るだけで、 コードの見通しは驚くほど良くなります。

マップとして捉えるメリット

設定・ログ・例外を個別のテクニックとして学ぶと、 どうしても「使い方」ばかりに意識が向きます。

ですが、マップとして捉えると、

  • 今書いているコードは、どの責任か
  • ここで判断していいのか
  • 外に追い出すべき処理は何か

立ち止まって考えられるようになります。

次の章からは、このマップをもとに、 まずは「設定(Configuration)」の役割と実装指針を具体的に見ていきましょう。




4. 設定(Configuration)の責務と実装指針

設定は、アプリケーションの挙動を変えるための「つまみ」のような存在です。 コードを書き換えなくても動作を調整できることが、本来の役割です。

にもかかわらず、設定が扱いづらくなるのは、 「どこまでが設定なのか」が曖昧なまま増えていくからです。

設定に含めるべきもの・含めるべきでないもの

まずは、設定の守備範囲をはっきりさせましょう。

  • 含めるべきもの
    • ログレベルやタイムアウト値
    • 実行モード(dev / prod)
    • 外部サービスの接続先URL
  • 含めるべきでないもの
    • ビジネスロジックそのもの
    • 条件分岐のルール
    • 計算式や処理フロー

設定は「振る舞いを切り替える材料」であって、 処理を決める主体ではありません

環境依存・アプリ設定・シークレットを分ける

実務では、設定を一種類として扱わないことが重要です。

  • アプリケーション設定:ログレベル、機能フラグ、リトライ回数
  • 環境依存設定:DB接続先、APIエンドポイント
  • シークレット情報:パスワード、APIキー

特にシークレットは、 物理的にコードやリポジトリから分離する意識が欠かせません。

設定は「起動時に解決」する

設定設計でよくある失敗が、 関数の中でその都度、環境変数や設定ファイルを読むことです。

おすすめなのは、

  • アプリのエントリポイントで設定を一度だけ読み込む
  • 型付きのオブジェクトとしてまとめる
  • ロジックには「解決済みの値」だけを渡す

この形にすると、 テストや環境切り替えが圧倒的に楽になります。

Pydanticを使う理由

Pydantic や pydantic-settings を使うと、 設定を型安全かつ自己説明的に定義できます。

値が足りない、型が違うといった問題も、 起動時に即座に検知できます。

設定を「ただの値」ではなく、 アプリの前提条件を保証する仕組みとして扱えるのが大きな利点です。

次の章では、 この設定を前提にして、例外(Exceptions)をどう設計するかを見ていきます。




5. 例外(Exceptions)の責務と設計判断

例外は「エラー処理のための仕組み」と思われがちですが、 設計の視点で見ると、その本質は「異常を報告する手段」です。

つまり、例外の役割は 問題を解決することではなく、問題が起きた事実を正しく伝えることにあります。

raiseする側の責務:判断しない、ただ報告する

関数やクラスといった低レイヤーのコードでは、 「このエラーをどう扱うか」を考えすぎないことが大切です。

たとえば、

  • 入力値が不正
  • 外部APIが失敗した
  • 想定外の状態に入った

こうした状況に直面したら、 その場で解釈せず、例外として上に投げるのが基本です。

ここでやってはいけないのが、

  • printやログだけ出して処理を続行する
  • 意味の分からない汎用例外に包み直す
  • 何事もなかったかのように return する

例外を投げる側は、 「何が起きたか」を正確に伝えることに専念します。

catchする側の責務:どう対処するかを決める

一方で、例外を捕捉する側―― つまり呼び出し元やアプリケーション境界では、役割が変わります。

ここでは、

  • ログに残すか
  • リトライするか
  • ユーザーにエラーを返すか
  • 安全に停止するか

といった判断を行います。

この「報告」と「判断」を分けるだけで、 try / except は驚くほど読みやすくなります。

try / except を書いていい場所・悪い場所

設計の観点では、 try / except はどこにでも書けばいいものではありません。

  • 書いていい場所
    • アプリのエントリポイント
    • 外部I/Oの境界
    • ユーザー入力やAPIレスポンスの直前
  • 避けたい場所
    • 純粋なビジネスロジックの内部
    • 再利用したい低レイヤー関数

このルールを意識すると、 例外処理が「守り」ではなく、設計の一部として機能し始めます。

ログとの役割分担を意識する

例外とログは密接ですが、同じものではありません。

基本ルールは、

  • 例外:制御フローを変えるための仕組み
  • ログ:起きた事実を後から追うための記録

例外を投げる場所で必ずログを出す必要はありません。 ログを書くのは「判断する側」で十分なケースも多いです。

次の章では、この考え方を踏まえて、 ログ(Logging)の責務と運用設計を整理していきます。




6. ログ(Logging)の責務と運用設計

ログは、デバッグ用の出力ではありません。 設計の視点で見ると、ログの本質は「実行中に起きた事実を、あとから追える形で残すこと」です。

そのため、ログは「今見るため」ではなく、 未来の自分やチームが読む前提で設計する必要があります。

ログは制御しない、判断しない

ログ設計で一番大事な原則はシンプルです。

  • ログは処理を分岐させない
  • ログはエラーを隠さない
  • ログは意思決定をしない

ログはあくまで記録係。 何かを「止める」「続ける」といった判断は、 例外や呼び出し元の責務です。

ログレベルは「重要度」ではなく「用途」で考える

ログレベルは、よく次のように誤解されがちです。

  • ERROR = 致命的
  • WARNING = ちょっと危険
  • INFO = 普通

実務では、用途ベースで考える方がうまく回ります。

  • DEBUG:調査・開発時にだけ必要な詳細情報
  • INFO:正常系の流れが分かるイベント
  • WARNING:処理は続くが注意が必要な状態
  • ERROR:機能として失敗した事実
  • CRITICAL:システム継続が困難な状態

「どのレベルで残すと、あとで役に立つか」を基準にすると、 ログのノイズが一気に減ります。

logger.exception() を使うべき場面

例外とログが交差する代表的な場面が、 例外を捕捉した直後です。

logger.exception() を使うと、 スタックトレースを含めて自動的に記録できます。

重要なのは、 例外を「処理すると決めた場所」でログを残すことです。

低レイヤーで例外を投げ、 上位で catch してログに残す―― この流れができていると、ログは非常に読みやすくなります。

ログ設定は集中管理する

ロガーの設定を各ファイルでバラバラにすると、 出力形式やレベルがすぐに破綻します。

おすすめなのは、

  • 起動時に一度だけ logging を初期化する
  • dictConfig で出力先・フォーマットをまとめる
  • 各モジュールでは getLogger(__name__) するだけ

この形にすると、 環境ごとの切り替えや構造化ログへの移行も楽になります。

実務設計を深めたい人へ

ログ・例外・設定を含めた「実務レベルの設計」を 体系的に理解したい場合は、次の一冊がとても参考になります。

プロフェッショナルPython ソフトウェアデザインの原則と実践
Amazonでチェックする楽天でチェックする

次の章では、 これまで整理してきた設定・例外・ログをどう統合するか、 実装手順としてまとめていきます。




7. 実装手順まとめ(設定・ログ・例外を統合する)

ここまでで、設定・例外・ログそれぞれの責務は整理できました。 この章では、それらを実際のプロジェクトでどう組み合わせるかを、 実装手順としてまとめます。

① エントリポイントで「外側の準備」を済ませる

最初にやるべきことは、 アプリケーションの入口で外側の関心をすべて解決しておくことです。

  • 設定を読み込み、型チェック・バリデーションを行う
  • logging を初期化し、出力先やフォーマットを決める
  • 必要であれば依存関係を組み立てる

この段階でエラーが出るなら、 迷わず起動を失敗させてOKです。 設定不備のまま動かす方が、後で大きな事故になります。

② ロジックには「解決済みの値」だけを渡す

ビジネスロジック側では、

  • 環境変数を直接読まない
  • 設定ファイルの存在を知らない
  • ログ設定を変更しない

という状態を目指します。

関数やクラスは、 必要な値や依存オブジェクトを引数として受け取るだけ。 これにより、テストや再利用が一気に楽になります。

③ 例外は「境界」で捕まえる

例外処理の基本方針はシンプルです。

  • 低レイヤーでは raise する
  • アプリの境界で catch する
  • 意味づけ・ログ・ユーザー通知はそこで行う

この形を守ると、 try / except が少数の分かりやすい場所に集まります。

④ 例外を処理すると決めた場所でログを書く

ログを書く場所に迷ったら、 「ここで処理を終わらせるか?」を自分に問いかけてみてください。

処理を終わらせるならログを書く。 上に投げるなら、無理にログを書かなくていい。

このルールを意識するだけで、 ログの重複やノイズが激減します。

⑤ リソース管理は仕組みに任せる

ファイル・DB接続・ネットワークなどのリソースは、 with 文(コンテキストマネージャ)を使って管理します。

これにより、例外が起きても、

  • 確実にクリーンアップされる
  • finally 地獄を避けられる

設計とPythonの言語機能をうまく組み合わせることで、 コードは自然と安全になります。

次の章では、 運用・セキュリティ・可観測性という視点から、 この設計をどう育てていくかを補足します。




8. 運用・セキュリティ・可観測性の補足

設定・ログ・例外をきれいに分離できても、 それで設計が終わり、というわけではありません。

実際のプロジェクトでは、 「どう運用されるか」「事故が起きたときにどう見えるか」がとても重要です。

ログに機密情報を残さない

一番注意すべきなのが、 ログにシークレット情報を出してしまうことです。

  • パスワード
  • APIキー
  • トークン
  • 個人情報

これらは、意図せずログに混ざりがちです。

対策としては、

  • ログに出す前提で値を加工しない
  • 必要なら SensitiveDataFilter を用意してマスクする
  • 例外メッセージに生値を入れない

「ログは外部に漏れる前提」で設計するのが安全です。

パフォーマンスへの配慮

ログは便利ですが、出しすぎると性能に影響します。

特に注意したいのが、 ループ内や高頻度で呼ばれる処理です。

  • DEBUGログは本番では基本オフにする
  • isEnabledFor(logging.DEBUG) を活用する
  • 遅延フォーマット(%s記法)を使う

これだけでも、 無駄な文字列生成をかなり減らせます。

構造化ログという選択肢

運用フェーズでは、 ログは「読む」ものというより、 検索・集計・可視化するデータになります。

そのため、本番環境では、

  • JSON形式で出力する
  • キーを固定する
  • イベント単位で意味を持たせる

といった構造化ログが有効です。

これにより、

  • 障害時の原因追跡
  • 発生頻度の分析
  • 将来的な監視自動化

が一気に楽になります。

可観測性を意識した拡張

規模が大きくなってきたら、 ログだけでなく、メトリクスやトレースも視野に入ってきます。

OpenTelemetry を使えば、

  • ログ
  • メトリクス
  • トレース

を横断的に扱えるようになります。

Sentry、CloudWatch、SigNoz などの外部サービスと組み合わせることで、 「何が起きたか」だけでなく「なぜ起きたか」まで追える設計に育てられます。




まとめ

この記事では、Pythonプロジェクトにおける 「設定・ログ・例外」の役割分担を、設計の視点から整理してきました。

大切なのは、特別なテクニックを覚えることではありません。

  • 設定は「どう動かすか」を決めるだけ
  • 例外は「何が起きたか」を伝えるだけ
  • ログは「起きた事実」を残すだけ

それぞれが判断しすぎないことで、 コード全体の責務が自然と分かれ、読みやすく、直しやすくなります。

最初は少し遠回りに見えるかもしれませんが、 この分離を意識して書かれたコードは、あとからの変更や運用で必ず効いてきます。

私自身も、「とりあえず動く」書き方で何度も苦労してきました。 だからこそ、設計は未来の自分を助けるための仕込みだと実感しています。

もしこの記事を読んで、 「設計について、もう少し体系的に学びたいな」と感じたら、 気軽に試せる選択肢として、こんなサービスもあります。

Kindle Unlimited
技術書や設計思想の本も対象になっていることが多く、 気になるテーマを横断的に読むのにとても便利です。

Amazonでチェックする

設定・ログ・例外の関係は、 「舞台の台本・上演記録・トラブル対応マニュアル」のようなもの。

台本(設定)に従って演じ、 何が起きたかを記録係(ログ)が残し、 想定外の事故(例外)が起きたら、舞台監督(呼び出し元)が判断する。

この役割分担を意識するだけで、 Pythonプロジェクトは驚くほど扱いやすくなります 😊


あわせて読みたい

この記事で紹介した「設定・ログ・例外の役割分担」を、 それぞれのテーマごとにもう一段深く理解したい方におすすめの記事です。

これらを順番に読んでいくと、 「なぜそう設計するのか」→「どう実装するのか」が自然につながっていきます。


よくある質問(Q&A)

Q
小規模なスクリプトでも、設定・ログ・例外は分けた方がいいですか?
A

必ずしも最初から完璧に分ける必要はありません。 ただし、分けられる形で書いておくことはとても重要です。

たとえば、

  • 設定値は定数としてまとめておく
  • printの代わりにloggingを使う癖をつける
  • 例外を握りつぶさずにraiseする

これだけでも、後から規模が大きくなったときに 自然に設計を拡張できるようになります。

Q
例外が発生した場所で、必ずログを出すべきですか?
A

必ずしもそうではありません。 ログを書くべきなのは「例外を処理すると決めた場所」です。

低レイヤーの関数でログを書いてしまうと、

  • 同じ例外が何度もログに出る
  • 本当に重要な情報が埋もれる

といった問題が起きがちです。

基本は、例外をraiseして上位に任せ、 境界でcatchしたタイミングでまとめてログに残すのが安全です。

Q
設定ファイルと環境変数は、どちらを使うのが正解ですか?
A

どちらか一方が正解、というわけではありません。

実務では、

  • 環境変数:シークレット情報や環境依存の値
  • 設定ファイル:アプリ全体の挙動を決める値

というように、役割で使い分けるのが一般的です。

Pydanticなどを使って、 最終的には「ひとつの設定オブジェクト」にまとめてしまうと、 ロジック側は取得元を意識せずに済みます。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク