nikkie-ftnextの日記

イベントレポートや読書メモを発信

イベントレポート | (第89回)Python mini Hack-a-thon : Flaskのロギングの調査、LINE Botを少しだけ触る #pyhack

はじめに

だんないよ、nikkieです。
6/9の pyhack 成果発表をもとにブログにしました。
今回はMarkdownで書いているので、いつもと体裁が異なります。

勉強会の概要

(第89回)Python mini Hack-a-thon - connpass

スプリントのゆるい版みたいな感じで各自自分でやりたいことを持ってきて、勝手に開発を進めています。

取り組んだこと

成果

  • Flaskのログ出力について疑問は解決できた
  • LINE Botでオウム返しできた

Flaskのログ出力から書いていきます。

疑問

Flaskインスタンスをrunしているファイル(app.py)では以下のようにしてdebug.logにログ出力ができます。1
app.pyにimportしているファイル(logtest.py)のログをdebug.logに出力するにはどうすればいいのか調べてみました。

# app.py
import logging
from logging.handlers import RotatingFileHandler
import os

from flask import Flask

from logtest import fabulous_message

app = Flask(__name__)
formatter = logging.Formatter(
    '%(asctime)s %(levelname)s: %(message)s '
    '[in %(pathname)s:%(lineno)d]'
)
debug_log = os.path.join(app.root_path, 'debug.log')
debug_file_handler = RotatingFileHandler(
    debug_log, maxBytes=100000, backupCount=1
)
debug_file_handler.setLevel(logging.INFO)
debug_file_handler.setFormatter(formatter)
app.logger.setLevel(logging.INFO)
app.logger.addHandler(debug_file_handler)

@app.route('/')
def hello():
    app.logger.info('GET Access')
    message = fabulous_message()
    return message


if __name__ == '__main__':
    app.run()

わかったこと

  • ロガーの子孫構造を使う
  • 子孫構造を使わない場合、ロガーを設定して使えばよさそう(ロギング設定ファイルも導入したい)

ロガーの子孫構造を使う2

# logtest.py
import logging


logger = logging.getLogger('__main__.'+__name__)

def fabulous_message():
    logger.info('fabulous message create start')
    return 'Hello, World!'
2018-06-09 15:30:34,008 INFO: GET Access [in app.py:25]
2018-06-09 15:30:34,009 INFO: fabulous message create start [in /Users/.../logtest.py:21]

ロガーの子孫構造について https://docs.python.jp/3/howto/logging.html#loggers

たとえば、名前が foo であるロガーがあったとして、 foo.bar, foo.bar.baz, foo.bam といった名前のロガーはすべて foo の子孫になります。

ロガーの子孫構造を使っているため、logtest.pyの中でロガーの設定は不要と理解しました。
app.loggerは標準モジュール logging のロガーを使っていると確認できました。3
python app.pyと実行したとき、app = Flask(__name__)__name____main__が入るため、
logtest.pylogger = logging.getLogger('__main__.'+__name__)としたことで子孫構造が設定できました。4

logtest.pylogger = logging.getLogger(__name__)としたところ
debug.logにはログ出力されませんでした。(子孫構造が設定されていないためと考えています)

ロガーを設定して使う

以下のようにロガーを設定しても、importしたファイルからdebug.logにログ出力できました。5
(これだと「他のファイルにも同じように書かないといけないじゃん!」と思い、同じようなコードを繰り返さない方法を探して、子孫構造に至りました)

# logtest.py
import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger(__name__)
formatter = logging.Formatter(
    '%(asctime)s %(levelname)s: %(message)s '
    '[in %(pathname)s:%(lineno)d]'
)
debug_file_handler = RotatingFileHandler(
    'debug.log', maxBytes=100000, backupCount=1
)
debug_file_handler.setLevel(logging.INFO)
debug_file_handler.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(debug_file_handler)

def fabulous_message():
    logger.info('fabulous message create start')
    return 'Hello, World!'
2018-06-09 15:58:07,321 INFO: GET Access [in app.py:25]
2018-06-09 15:58:07,323 INFO: fabulous message create start [in /Users/.../logtest.py:21]

ロギング設定ファイル

importするファイル全てに書くのは大変そうなので、ロギング設定ファイルを使ってみようと思います。(宿題事項)

https://docs.python.jp/3/howto/logging.html#configuring-logging

ロギング設定ファイルを作り、それを fileConfig() 関数を使って読み込む。

動作環境

LINE Bot でオウム返し

f:id:nikkie-ftnext:20180610105316j:plain:w400

感想

takanoryさんから「Logging HOWTO — Python 3.6.5 ドキュメント」を教えていただき、ロギングの理解を深めることができました。教えていただきありがとうございました!
次の1週間を乗り切る準備ができたかなと思います。(勤務先でソロでPython使い続けるために毎週末に翌週の準備をしています。例外は直近では使わなさそうだから、またの機会に調べよう)

今回ははじめて来られた方がいつもより多かったと思います。
先日の #PyNyumon から来られた方もいて、メンターにトライした身として嬉しかったです。
来月は合宿ということで、私も思い切って飛び込んでみようと思います。(申込みました!)

脚注


  1. ロギングは次のコードを参考にしています。 http://study-flask.readthedocs.io/ja/latest/07.html#logging

  2. StackOverflowの次の回答を参考にしています。 https://stackoverflow.com/a/39863901

  3. http://flask.pocoo.org/docs/0.12/api/#flask.Flask.logger

  4. python app.pyの実行ではない本番環境で子孫構造が想定どおり働くかは検証の必要があります。

  5. RotatingFileHandlerに'debug.log'と直に渡しているのが気になるので、pathlibのPathを使うやり方に今後アップデートします。(ソフトウェアデザイン2018年2月号を参考にします)