Python loggingでDiscordに出力するカスタムハンドラ作ってみた

こんにちはRcatです。
前回の記事ではHTTPを使って任意の入力を受け付けるDiscordのBOTを作成しました。
これを利用することで、プログラム内からHTTPリクエストをすることでログ用BOTとしても使うことができます。

さて、今回はそれをさらに発展させて、Pythonの標準ライブラリでログを管理する"logging"ライブラリと組み合わせてさらに使い勝手を向上させていきます!

前回はこちら



はじめに

利用規約

情報や作品の活用時は事前に利用規約をご確認ください。

https://note.com/rcat999/n/nb6a601a36ef5

コメントについて

利用規約のガイドラインを確認の上コメントしてください


概要

Loggingライブラリとは

loggingライブラリはPythonの標準ライブラリの1つです。これを使うことでログの出力を管理することができます。

最も簡単な使用例は以下の通りです。

>>> import logging
>>> logging.basicConfig(level=logging.INFO,format='%(asctime)s %(levelname)s %(message)s')
>>> logging.debug("デバッグ")
>>> logging.info("情報")
2024-11-09 22:39:47,931 INFO 情報
>>> logging.warning("警告")
2024-11-09 22:39:47,972 WARNING 警告
>>> logging.critical("致命的")
2024-11-09 22:39:48,015 CRITICAL 致命的

ポイントとしては大きく分けて2つあります。

1つ目は表示が綺麗になる。
最初のコンフィグで表示形式を設定していて必ずそれに沿った出力が行われます。
次にレベルの設定です。
コンフィグを見ると"INFO"なっていますよね。
つまり、これはINFOよりレベルの低いログは表示しないでくれという設定になります。
なので、プログラム作成中にいろんなところにデバッグレベルのログをたくさん書いておいたとしても、配る前にレベルを直せばいちいち消さなくてもいいということです。

なお、色々調べてみると、この最も簡単な使い方は推奨されておらず、きちんとロガーを作成して使うことが推奨されているようです。
今駆け足で作業しているので、ここで説明できるレベルまで落とし込めていないので、とりあえずは省略とさせてください。

で、何やるの?

上で説明は省くと言いましたが、ロガーを使った出力方法をまず説明します。

>>> import logging
>>> loger = logging.getLogger("Rcat.Rlog") #自由に名前をつけてロガーを作成
>>> loger.setLevel(logging.WARNING) #レベルはここでセット
>>> handler = logging.StreamHandler() #ハンドラーを作成。ストリームハンドラーは画面に出力するということ
>>> handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) #出力フォーマットはここで設定
>>> loger.addHandler(handler) #ロガーに対してハンドラーを設定する
>>> loger.debug("デバッグ")
>>> loger.info("インフォ")
>>> loger.warning("ワーニング")
2024-11-09 22:47:58,165 WARNING ワーニング
>>> loger.critical("致命的")
2024-11-09 22:47:58,871 CRITICAL 致命的

はい、こんな感じです。
もっと分かりやすく説明してくれているところがたくさんありますので、ロガーとかハンドラーとかについては、ここでは省略します。
今要するにロギングライブラリは、部品ごとに分解して使うことができることがわかっていただければOKです。

部品ごとに分解して使うというのが、どういうことかというと次の通りです。

>>> import logging
>>> loger = logging.getLogger("Rcat.loggingbot")
>>> shandler = logging.StreamHandler()
>>> shandler.setLevel(logging.INFO)
>>> shandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
>>> fhandler = logging.handlers.RotatingFileHandler(filename="log.log",encoding="UTF-8",maxBytes=1024^2*100,backupCount=10)
>>> fhandler.setLevel(logging.WARNING)
>>> fhandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
>>> loger.addHandler(shandler)
>>> loger.addHandler(fhandler)
>>> loger.debug("デバッグ")
>>> loger.info("インフォ")
>>> loger.warning("ワーニング")
2024-11-09 22:52:37,701 WARNING ワーニング
>>> loger.critical("致命的")
2024-11-09 22:52:38,324 CRITICAL 致命的

パッと見何やってるか難しいと思いますので、簡単に説明します。
まずハンドラーが2つあるのがお分かりでしょうか?
先ほど使用した"StreamHandler"ともう一つ追加で"RotatingFileHandler"というのをインスタンスしています
これはファイルにログを書き出すためのハンドラーなんですが、一定以上のサイズになると別のファイルに書き出してくれるという優れものです。
そして下の方でaddHandlerを使ってロガーに統合していますよね。
これで何ができるかというと、画面への出力とファイルへの出力の両方ができるということです。

こちらが出力されたファイルです。
ワーニング以上の設定なので、正しく書き出されていることが分かります。

で、ここからが本題なのですが…
このハンドラー自分で作れるみたいなんです。
もうお分かりですね。
Discordbotハンドラーを作成すれば、そのハンドラーも統合してしまうことで、標準ライブラリでログ出力するところにオリジナルの機能を統合することができてしまいます。


Logging BOTハンドラー作ってみた

サンプルコードとコンソール実行結果

というわけで、まずはいきなり完成形をお見せします。

>>> import logging
>>> import loggingbot_client #前回作成したオリジナルのクライアント
>>> loger = logging.getLogger("Rcat.loggingbot")
>>> loger.setLevel(logging.DEBUG)
>>> shandler = logging.StreamHandler()
>>> shandler.setLevel(logging.DEBUG)
>>> shandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
>>> #--------------------------------------
>>> fhandler = logging.handlers.RotatingFileHandler(filename="log.log",encoding="UTF-8",maxBytes=1024^2*100,backupCount=10)
>>> fhandler.setLevel(logging.INFO)
>>> fhandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
>>> #--------------------------------------
>>> bothandler = loggingbot_client.Loggingbot_Handler("Rcat_loggingtest",Server="192.168.0.4")
>>> bothandler.setLevel(logging.WARNING)
>>> bothandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
>>> #--------------------------------------
>>> loger.addHandler(shandler)
>>> loger.addHandler(fhandler)
>>> loger.addHandler(bothandler)
>>> loger.debug("デバッグ")
2024-11-09 23:09:00,593 DEBUG デバッグ
>>> loger.info("インフォ")
2024-11-09 23:09:00,613 INFO インフォ
>>> loger.warning("ワーニング")
2024-11-09 23:09:00,638 WARNING ワーニング
>>> loger.critical("致命的")
2024-11-09 23:09:01,075 CRITICAL 致命的

上から順番に画面に表示、ファイルに出力、オリジナルのDiscord出力でハンドラーを作成しています。画面の出力はデバッグなので全ての出力が出ていますね。

その他の実行結果は次の通りです。

Discord BOT出力結果

こちらはワーニングで設定しましたので、それ以上の出力が行われています。大成功です。

ログファイル出力結果

こちらは情報以上出力で設定しましたので、同じく出力されています。


ソースコード

オリジナルハンドラー部分

オリジナルハンドラのソースコードは以下のようになっています。
はい、Loggingライブラリのハンドラークラスを継承して、前回作成したボットにアクセスするためのクライアントをインスタンスしているだけです。
まさかたった。これだけで統合できてしまうなんて…。

前回の記事はこちら。BOTだけではなく、専用のクライアントも用意しています。

適法方法

では、具体的にスクリプトの中に取り込む方法の例を紹介します。
今回は以下のようにテンプレートを作成しました。

解説

まず、最初にスクリプトの中ではいちいち関数を使わずにdictConfigという仕組みを使って設定をしています。その前提で上から順にお話しします。

  • よく変更する項目の抜粋
    レベルの変更やロガーの名前など、比較的よく変更するものは上の方に持ってきています。

  • 2つの基本ハンドラを作成

    • ストリームハンドラ
      画面に出力するためのハンドラです。

    • ファイルハンドラ
      ファイルに出力するためのハンドラです。
      ローテーションを使っているので、10mbでファイルが切り替わる設定です。

  • Discordbotハンドラーの作成
    次に前回の記事で作成した専用のクライアントをインポートします。
    インポートに成功した場合は、その中にあるハンドルクラスが使えるようになるため、辞書に追記しています。

  • ー括設定
    dictConfigの仕組みを用い、一括で設定を行っています。
    以降はここで作成したロガーに対してログを出力していくだけです。


配布情報

今回の変更内容は、以下の記事で配布しているデータをアップデートして提供します。


いいなと思ったら応援しよう!

Rcat999
情報が役に立ったと思えば、僅かでも投げ銭していただけるとありがたいです。