監視ツールのCLIコマンドを実装していたとき、妙な挙動に遭遇しました。

Jenkinsでpython -m monitoring_sdk.core.cli metabase-checkを実行すると、エラーも出ず、終了コード0で正常終了する。でも、実行されるはずの処理が何も動いていない。レポートファイルも生成されない。

+ python -m monitoring_sdk.core.cli metabase-check --config config.yaml --output reports
+ echo === チェック完了 ===
=== チェック完了 ===
+ ls -la reports/
total 0
drwxr-xr-x. 2 root root  40 Jan 28 02:23 .

正常終了しているのに、何も起きていない。この「静かな失敗」に1時間以上悩まされました。

正直、こんな初歩的な問題にハマるとは思っていませんでした。


関連書籍(学習用)

実例で出てきたPythonの基本や実行周りを理解するのに強力なのがこちら:

[📦 商品リンク: moshimo-card-EdE1y]

『みんなのPython 第5版』では、Pythonの基礎からモジュール設計・実行方法まで丁寧に解説されています。CLI周りの落とし穴を未然に防げます。

AI活用の前提となるPython基礎を固めたい方にもおすすめです。


AI駆動開発のスピード感と脆さ

このプロジェクトは、AIを使ってかなりのスピードで開発していました。CLIコマンドの実装、ログ設定、監視ロジック、レポート生成。コード自体はどんどん書けます。

でも、振り返ってみると、きちんとレビューをしないで「動くだろう」という前提でコードを書き続けていました。テストは後回し。実際に動かしてみるのは最後の最後。

そして、テスト段階でこの問題にぶつかりました。

コーディング自体のスピードは確かに速い。でも、こういうトラブルにハマると意外と脆い。原因が分からず、ログを追いかけ、仮説を立て、検証を繰り返す。気づけば1時間以上経っていました。

最初の仮説:ログ出力の問題?

最初はPythonの標準出力バッファリングを疑いました。Jenkinsfileにpython -uフラグを追加してバッファリングを無効化しようとしましたが、「それよりPython側のログ出力を強化したほうがいいのでは?」と気づき、方向転換。

各モジュールにロガーを追加し、処理の各段階でINFOログを出すようにしました。

from monitoring_sdk.utils.logging_config import get_logger

logger = get_logger(__name__)

def metabase_check(config: str, output: str):
    logger.info("metabase-checkコマンドを開始します: config=%s, output=%s", config, output)
    # ... 処理 ...
    logger.info("metabase-checkコマンドが完了しました")

※ここからは開発体験の話になります。

ログ出力を追加することにしましたが、対象は32ファイル。monitors、CLI commands、reporters、HTTP API関連のファイル全てです。

手作業でやっていたら、半日はかかる作業です。ファイルを開いて、インポート文を追加して、各関数にログを追加して、動作確認して...を32回繰り返す。

でも、今回は違いました。私が開発しているDevLoop Runnerを使ったからです。これは、issueを書いたら自動でPRまで持っていってくれるツールです。

issue: 「monitoring_sdk内の全32ファイルにget_loggerを使ったログ出力を追加」
↓
自動でコード修正(約2時間)
↓
PR作成

実際にかかった時間は2時間ほど。でも、私が手を動かしたのはissue作成とジョブ実行の3〜5分だけです。残りの2時間は、自動処理が走っている間に他の作業ができます。

手作業なら半日べったり作業に張り付くところが、実質的な拘束時間は5分で済む。この差は大きいです。

32ファイル分のログ追加が完了し、マージ。デバッグログも充実しました。

デバッグログで見えた不自然な挙動

ログレベルをDEBUGに上げて再実行。すると、こんな出力が:

2026-01-28 09:01:37,029 - monitoring_sdk.core - DEBUG - monitoring_sdk.core パッケージを初期化しました
2026-01-28 09:01:37,066 - monitoring_sdk.monitors - DEBUG - monitoring_sdk.monitors パッケージを初期化しました
2026-01-28 09:01:37,518 - monitoring_sdk.cloud - DEBUG - monitoring_sdk.cloud パッケージを初期化しました
+ echo === チェック完了 ===

パッケージの__init__.pyが実行されている証拠はある。でも、metabase_check関数内のログが一切出ない。"metabase-checkコマンドを開始します"というログが出ていない。

結論から言うと、main()関数が一度も呼ばれていませんでした。

終了コード0の罠

念のため終了コードを明示的に確認してみました。

+ set +e
+ python -m monitoring_sdk.core.cli metabase-check --config config.yaml --output reports
+ EXIT_CODE=$?
+ echo "終了コード: $EXIT_CODE"
終了コード: 0

やはり0。エラーではない。じゃあ何が起きているのか?

さらに--helpを試してみました。

+ python -m monitoring_sdk.core.cli --help
+ echo "ヘルプ表示完了"
ヘルプ表示完了

ヘルプの内容が何も出力されない。コマンドは実行されている(+記号が表示されている)のに、出力が完全に空。

これは、python -mでモジュールを実行しても、エントリーポイントが正しく設定されていない状態でした。

原因特定:エントリーポイントの欠如

cli.pyの実装を見直しました。

# monitoring_sdk/core/cli.py
import click

@click.group()
def main():
    """監視SDKのCLIエントリーポイント"""
    pass

@main.command()
@click.option("--config", required=True)
@click.option("--output", default="reports")
def metabase_check(config: str, output: str):
    # ... 実装 ...
    pass

一見問題なさそうです。でも、ファイルの最後を見ると:

# ここで終わり。if __name__ == "__main__": がない

これが原因でした。

python -m monitoring_sdk.core.cliでモジュールを実行すると:

  1. Pythonはmonitoring_sdk/core/cli.pyをインポートする
  2. モジュールレベルのコード(import文、関数定義、デコレータ)は実行される
  3. でもmain()関数は呼ばれない
  4. 何もせずに終了コード0で終了

正直、AIが作ったコードだから「ちゃんと実装されているだろう」と思い込んでいました。だから、そもそもエントリーポイントの有無なんて確認すらしていませんでした。

ここで詰まったのは痛かったです。

Pythonのエントリーポイントについてのおさらい

せっかくなので、Pythonのモジュール実行とエントリーポイントについて整理しておきます。

python -mの挙動

python -m module.pathを実行すると、Pythonは以下の順序でエントリーポイントを探します:

  1. module/path/__main__.pyがあれば実行
  2. なければmodule/path.pyを実行(ただしif __name__ == "__main__"が必要)

重要なのは、モジュールをインポートはするが、自動でmain()を呼んでくれるわけではないという点です。

エントリーポイントの2つの方法

実行方法 必要なファイル/コード 用途
python -m module.path __main__.py パッケージとして配布
python script.py if __name__ == "__main__" スクリプトとして実行

どちらにも対応する場合:
両方実装しておくのが無難です。今回はこの両方が欠けていました。

実装例

# monitoring_sdk/core/__main__.py
from monitoring_sdk.core.cli import main

if __name__ == "__main__":
    main()
# monitoring_sdk/core/cli.py(最後に追加)
if __name__ == "__main__":
    main()

これで、どちらの起動経路でもmain()が呼ばれるようになります。

修正後の動作

2026-01-28 09:49:07,417 - monitoring_sdk.monitors.metabase_monitor - INFO - カードチェックを開始します
2026-01-28 09:49:07,417 - monitoring_sdk.monitors.metabase_monitor - INFO - カードチェックが完了しました: status=OK
2026-01-28 09:49:07,417 - monitoring_sdk.monitors.metabase_monitor - INFO - Metabaseレポート生成が完了しました: file=reports/summary.md
2026-01-28 09:49:07,418 - monitoring_sdk.cli.commands.metabase_check - INFO - metabase-checkコマンドが完了しました: status=OK

期待通りの動作になりました。

学んだこと:「アクセルとブレーキ」の感覚


AI開発全体を俯瞰するなら

AI駆動開発を加速するだけでなく、「どこでアクセルを踏みどこでブレーキを踏むか」を体系的に学ぶならこちらもおすすめです:

[📦 商品リンク: moshimo-card-HD5Yl]

『AI駆動開発入門』では、AIを活かした開発プロセスの設計・実践・評価までを解説。単なるコード生成に留まらない思考法が身につきます。


1. 自動化のおかげで拘束時間は劇的に短縮

issueからPRまでの自動化があれば、32ファイルのログ追加も手作業は3〜5分で終わります。実際の処理に2時間かかりますが、その間は他の作業ができます。

手作業なら半日べったり作業に張り付く必要があるところが、実質的な拘束時間は5分で済む。この差は大きいです。

2. でも「AIが作ったから大丈夫」という過信は危険

「ちゃんと実装されているだろう」という思い込みで、基本的な部分の確認を怠りました。

エントリーポイントの有無なんて、手書きなら絶対に確認するポイントです。でも、AIに任せていると、そもそも意識すらしなくなる。

3. silent failureは気づきにくい

エラーも出ず、終了コード0で正常終了。でも何も動いていない。こういう「静かな失敗」は、ログが充実していないと原因特定に時間がかかります。

今回はデバッグログのおかげで「パッケージ初期化は動いている」「でも関数は呼ばれていない」という状況が見えました。

4. アクセルを踏むところ、ブレーキを踏むところ

アクセルを踏むところ(AI駆動でスピード重視):

ブレーキを踏むところ(丁寧に確認):

意外とここが肝だったりします。

まとめ

AI駆動開発は確実に速い。でも、「AIが作ったから大丈夫」という過信でレビューを省略すると意外と脆い。

大事なのは、どこでアクセルを踏み、どこでブレーキを踏むかの感覚を磨くことです。

今回の問題は初歩的でしたが、こういう初歩的な問題こそ、最初のテスト段階で丁寧に確認すべきでした。「動くだろう」ではなく、「本当に動くか」を確認する。そのバランス感覚が、AI駆動開発では特に重要だと実感しました。