Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Loggingモジュールではじめるログ出力入門 / Introduction to Pytho...
Search
Toshifumi Tsutsumi
October 15, 2021
Programming
17k
33
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Loggingモジュールではじめるログ出力入門 / Introduction to Python Logging
PyCon JP 2021 登壇資料:
https://2021.pycon.jp/time-table/?id=272259
Toshifumi Tsutsumi
October 15, 2021
More Decks by Toshifumi Tsutsumi
See All by Toshifumi Tsutsumi
ModuleNotFoundErrorの傾向と対策:仕組みから学ぶImport / Unpacking ModuleNotFoundError
tosh2230
3
6.7k
CDCデータパイプラインを止めないために / One Stream of the CDC
tosh2230
0
1.8k
ニアリアルタイム分析の実現に向けたChange Data Captureの導入 / Change data capture for near realtime analytics
tosh2230
3
2.5k
データリネージの組織導入事例と今後の戦略 / Introduction to an example of data lineage in GMO Pepabo
tosh2230
0
1.3k
SQLクエリ解析によるE2Eデータリネージの実現 / E2E-data-lineage
tosh2230
0
4.3k
データ抽出基盤 Yeti をつくっている話 / Yeti - Yet another Extract-Transfer Infrastructure
tosh2230
1
5.6k
データ基盤チームの設立と直近の取り組み / the-establishment-of-pepabo-data-platform-team
tosh2230
5
4.8k
Other Decks in Programming
See All in Programming
Press start. Python's next generation.
willingc
PRO
3
320
フロントエンドUIフレームワークのこれまでとこれから
ssssota
2
1.3k
XP祭りでしか伝わらないフリップネタ #xpjug
murabayashi
0
110
What We Talk About When We Talk About XP
m_seki
2
440
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.5k
PHPプロジェクトの結合バランスを可視化する #php_night
kajitack
0
230
新人はどこまで自力でやり、どこからAIに頼るべきか/エンジニア育成に向き合う_先輩たちの悩みと知見共有会
toppan_digital_dev
1
580
不幸な GC
chencmd
0
890
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
1
190
まだ間に合う!今年の夏こそSchemeのマクロ展開器を完全理解!
omasanori
0
640
Webプラットフォームで議論されているセキュリティ課題 / Security issues being discussed on Web Platforms
petamoriken
0
320
ALB ログから Trace を気合で繋げる技術
fohte
7
880
Featured
See All Featured
Chasing Engaging Ingredients in Design
codingconduct
0
300
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.1k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
35
2.8k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
680
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
230
Docker and Python
trallard
47
4.2k
GitHub's CSS Performance
jonrohan
1033
470k
Fireside Chat
paigeccino
42
4k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
4
610
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
260
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Transcript
#pyconjp_3 Loggingモジュールではじめる ログ出力入門 PyCon JP 2021 2021-10-16
#pyconjp_3 堤 利史 / Toshifumi Tsutsumi GMO Pepabo, inc. Twitter:
@tosh2230 #風来のシレン #囲碁 #pyconjp_3 2
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 3. パブリッククラウドサービスでのログ出力 4.
まとめ 3
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 3. パブリッククラウドサービスでのログ出力 4.
まとめ 4
#pyconjp_3 ゴール - 利用するかどうかの判断基準を知る - 主な構成要素を知る - ログ出力方法と実装例を知る - ソースコードは
GitHub にあります - https://github.com/tosh223/pycon-jp-2021 5
#pyconjp_3 スコープ - Python 3.9 アプリケーション・ライブラリを対象とします - ログを出力する話をします - ログを受け取ったあとの話はほとんどしない予定です
出力 >> [収集, 蓄積, 加工] >> 分析 6 このあたり
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 2.1. Logging モジュールの概要
2.2. Logging モジュールの構成要素 2.3. Logger の性質 2.4. Logger の設定と出力 (アプリケーション編) 2.5. Logger の設定と出力 (ライブラリ編) 3. パブリッククラウドサービスでのログ出力 4. まとめ 7
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 2.1. Logging モジュールの概要
2.2. Logging モジュールの構成要素 2.3. Logger の性質 2.4. Logger の設定と出力 (アプリケーション編) 2.5. Logger の設定と出力 (ライブラリ編) 3. パブリッククラウドサービスでのログ出力 4. まとめ 8
#pyconjp_3 - Python に標準で搭載されている - アプリケーションやライブラリで発生した イベントを記録するための道具 - Five Ws
(who, what, when, where, why) Logging モジュールとは 9
#pyconjp_3 - 処理が (開始|終了) した - 処理の待ち合わせに成功した - 注意すべき条件分岐に入った -
期待した状態になっていない - エラーになった イベント例 10
#pyconjp_3 >>> from logging import getLogger >>> logger = getLogger(__name__)
>>> logger.error('This is an error!') This is an error! 11 (print でよいのでは...?) シンプルに出力してみる
#pyconjp_3 - プログラムとログのライフタイムが短い - 局所的・バッチ的なテキスト出力 - 目的がイベントの記録以外 - デバッグ -
ちょっとした挙動の確認 print でも良いケース 12
#pyconjp_3 - イベント時刻・発生箇所の記録 - ログレベルの指定 - 出力フォーマットの統一 - 柔軟な出力設定 -
スレッドセーフ Logging モジュールの便利ポイント 13
#pyconjp_3 標準ライブラリモジュールとしてログ記録 API が提供される利点 は、すべての Python モジュールがログ記録に参加できることで あり、これによってあなたが書くアプリケーションのログにサー ドパーティーのモジュールが出力するメッセージを含ませること ができます。
最大のメリット 14 https://docs.python.org/ja/3/library/logging.html
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 2.1. Logging モジュールの概要
2.2. Logging モジュールの構成要素 2.3. Logger の性質 2.4. Logger の設定と出力 (アプリケーション編) 2.5. Logger の設定と出力 (ライブラリ編) 3. パブリッククラウドサービスでのログ出力 4. まとめ 15
#pyconjp_3 ① Logger ⑤ Handler ⑤ Handler ⑤ Handler 16
propagate ⑥ Formatter ④ Filter ④ Filter ② Level ② Level ③ Log Record Required Optional String Message String String logger.error() 主な要素は 6つ
#pyconjp_3 ① Logger ⑤ Handler ⑤ Handler ⑤ Handler 17
propagate ⑥ Formatter ④ Filter ④ Filter ② Level ② Level ③ Log Record Required Optional String Message String String logger.error() ① Logger
#pyconjp_3 - ログを出力する主体 - メッセージを LogRecord に変換する - Handler を使って、LogRecord
を指定した書式の文字列に変 換して出力する - propagate という仕組みがある (後述) ① Logger 18
#pyconjp_3 ① Logger ⑤ Handler ⑤ Handler ⑤ Handler 19
propagate ⑥ Formatter ④ Filter ④ Filter ② Level ② Level ③ Log Record Required Optional String Message String String logger.error() ② Level
#pyconjp_3 - Logger や Handler に設定する - ログメッセージ出力時にイベントの重要度に応じて指定する - 以下の条件が
True のときにログが出力される (Message level) >= (Logger level) and (Message level) >= (Handler level) ② Level 20
#pyconjp_3 21 番号 名称 イベントの目安 0 NOTSET 上位ロガーに依存する。NOTSET 以外のレベルをもつ上位ロガーを 見つけるか、ルートロガーに到達するまで辿っていく。
10 DEBUG おもに問題を診断するときにのみ関心があるような、詳細な情報。 20 INFO 想定された通りのことが起こったことの確認。 30 WARNING 想定外のことが起こった、または問題が近く起こりそうである (例 えば、'disk space low') ことの表示。 40 ERROR より重大な問題により、ソフトウェアがある機能を実行できないこ と。 50 CRITICAL プログラム自体が実行を続けられないことを表す、重大なエラー。 https://docs.python.org/ja/3/howto/logging.html
#pyconjp_3 ① Logger ⑤ Handler ⑤ Handler ⑤ Handler 22
propagate ⑥ Formatter ④ Filter ④ Filter ② Level ② Level ③ Log Record Required Optional String Message String String logger.error() ③ LogRecord
#pyconjp_3 - Logger に渡されたメッセージを変換したオブジェクト - メッセージだけでなく、イベントが発生したソースコードや 実行環境に関するコンテキストを保持する - ログに出力される時刻は、LogRecord が作成された時刻
③ LogRecord 23 https://docs.python.org/ja/3/library/logging.html#logrecord-attributes
#pyconjp_3 ① Logger ⑤ Handler ⑤ Handler ⑤ Handler 24
propagate ⑥ Formatter ④ Filter ④ Filter ② Level ② Level ③ Log Record Required Optional String Message String String logger.error() ④ Filter
#pyconjp_3 - 出力対象 Level と判定された LogRecord が対象 - 設定対象によってフィルタリングのタイミングが異なる -
Logger : LogRecord が できた直後 - Handler : Handler Level 判定の後 ④ Filter 25
#pyconjp_3 26 from logging import Filter class CredentialsFilter(Filter): # Filter
クラスを継承 def __init__(self): pass def filter(self, record) -> bool: # filter() の返り値で出力判定 return self.__check_message(record.getMessage()) @staticmethod def __check_message(message) -> bool: return not message.startswith('Credentials') # 除外するログを False にする 👉
#pyconjp_3 ① Logger ⑤ Handler ⑤ Handler ⑤ Handler 27
propagate ⑥ Formatter ④ Filter ④ Filter ② Level ② Level ③ Log Record Required Optional String Message String String logger.error() ⑤ Handler
#pyconjp_3 - LogRecord を特定の方法・書式で出力するための道具 - 多様な Handler が実装されている - StreamHandler:
ストリームへの出力 - FileHandler: ファイルへの出力 - ひとつの Logger に複数の Handler を設定できる ⑤ Handler 28
#pyconjp_3 ① Logger ⑤ Handler ⑤ Handler ⑤ Handler 29
propagate ⑥ Formatter ④ Filter ④ Filter ② Level ② Level ③ Log Record Required Optional String Message String String logger.error() ⑥ Formatter
#pyconjp_3 - LogRecord を任意の文字列・日付書式に変更する - タイムゾーンは Formatter.converter で設定 - デフォルトは
time.localtime - UTC に変更するには time.gmtime を設定 - メッセージフォーマットの Style は 3種類ある(次ページ) ⑥ Formatter 30
#pyconjp_3 - ‘%’: %(<dict key>)s 形式の置換文字列 - dict key で指定できるのは
LogRecord 属性 - ‘{’: str.format() 互換 - ‘$’: string.Template.substitute() 互換 Message format style 31 https://docs.python.org/ja/3/howto/logging.html#formatters
#pyconjp_3 32 >>> from logging import Formatter, StreamHandler, getLogger >>>
fmt = '%(asctime)s.%(msecs)03d %(filename)s:%(funcName)s:%(lineno)d [%(levelname)s]%(message)s' >>> datefmt = '%Y-%m-%d %H:%M:%S' >>> formatter = Formatter(fmt=fmt, datefmt=datefmt, style='%') >>> sh = StreamHandler() >>> sh.setFormatter(formatter) # ハンドラにフォーマッタ設定 >>> logger = getLogger() # ロガーの参照取得 >>> logger.addHandler(sh) # ロガーにハンドラ設定 >>> logger.error('This is an error!') # ロガーで記録 2021-10-16 13:50:00.000 <stdin>:<module>:1 [ERROR]This is an error! 👉
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 2.1. Logging モジュールの概要
2.2. Logging モジュールの構成要素 2.3. Logger の性質 2.4. Logger の設定と出力 (アプリケーション編) 2.5. Logger の設定と出力 (ライブラリ編) 3. パブリッククラウドサービスでのログ出力 4. まとめ 33
#pyconjp_3 - Logger はモジュールごとにつくるとよい - logger = getLogger(__name__) とすると、モジュールの完 全修飾名と同じ名前の
Logger をつくれる - Logger がわかる = イベントがどこで起きたのかわかる - モジュールやパッケージと同じく階層構造がある Logger の作成と命名 34 https://docs.python.org/ja/3/library/logging.html#logger-objects
#pyconjp_3 app 35 Logger の階層構造 root lib app.main lib.db lib.util
#pyconjp_3 36 root logger - すべての Logger の祖先 - logging
モジュールが読み込まれたタイミングで自動生成 - logging.error など logging 直接記録は root logger を使用 - 記録時に Handler がなければ、logging.basicConfig (後述) が呼ばれる
#pyconjp_3 37 階層構造を活かしたイベントの伝播 root lib lib.util StreamHandler lib.util で メッセージを記録すると
root まで伝播する (すべての下位 Logger が propagate=true である場合) Handler 未設定 FileHandler logger.error()
#pyconjp_3 38 root lib lib.util Handler 未設定 StreamHandler 下位 Logger
に Handler がなくても、同じく上位に伝播する Handler 未設定 logger.error() 階層構造を活かしたイベントの伝播
#pyconjp_3 - エントリポイントで、root logger に Logger 全体に適用した い設定 (=デフォルト設定) を行う
- getLogger(__name__) で作成した個別の Logger は “propagate=true” にする - 特定のLogger で挙動を変えたいときだけ、個別に設定する 39 設定は root logger にまとめると便利
#pyconjp_3 上位 Logger (root 含む) Handler 40 注意: Propagate 対象は
Handler のみ 上位 Logger 自体に設定された Level, Filter は作用しない 下位 Logger Handler Formatter Filter Filter Level Level Log Record Log Record
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 2.1. Logging モジュールの概要
2.2. Logging モジュールの構成要素 2.3. Logger の性質 2.4. Logger の設定と出力 (アプリケーション編) 2.5. Logger の設定と出力 (ライブラリ編) 3. パブリッククラウドサービスでのログ出力 4. まとめ 41
#pyconjp_3 設定方法は4種類 42 いずれかを エントリポイント付近で一度だけ 行う 1. コードで直接指定 2. logging.basicConfig
3. logging.config.dictConfig 4. logging.config.fileConfig (非推奨のため今回は省略)
#pyconjp_3 sh_fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
default_fmt = Formatter(fmt=sh_fmt, style='%') # Formatter default_fmt.converter = gmtime # 時刻をUTC表示に統一 sh = StreamHandler() # Handler 作成 sh.setFormatter(default_fmt) # Formatter を設定 logger = getLogger() # ルートロガー参照を取得 logger.setLevel(DEBUG) # Level を設定 logger.addHandler(sh) # Handler を設定 方法1: コードで直接指定 43
#pyconjp_3 - root logger に対する基本設定ができる - デフォルトでは、root logger に何らかの Handler
設定がな されている場合は反映されない(引数 force で変更可) 方法2: logging.basicConfig 44 https://docs.python.org/ja/3/library/logging.html#logging.basicConfig
#pyconjp_3 - Dict から設定を読み込む - YAML や JSON で設定ファイルをつくることができる -
デフォルトでは、呼び出す前に存在している非ルートロガー を無効化するので注意 (disable_existing_loggers=true) 方法3: logging.config.dictConfig 45
#pyconjp_3 version: 1 disable_existing_loggers: False root: level: WARNING handlers: [console]
loggers: __main__: propagate: yes handlers: console: class: logging.StreamHandler level: DEBUG formatter: default stream: ext://sys.stdout ... 46 import os from logging import config from yaml import safe_load class DictConfig(): def __init__(self, config_file=None): self.__config_file = config_file def set(self): current_dir = os.path.dirname(__file__) with open(current_dir + '/' + self.__config_file) as file: _, ext = os.path.splitext(self.__config_file) if ext in ['.yaml', '.yml']: conf = safe_load(file) config.dictConfig(conf) 👉 Load a yaml file
#pyconjp_3 from logging import getLogger logger = getLogger(__name__) # ルートロガーに設定済なのでこれだけ
logger.debug('Debug') logger.info('Info') logger.warning('Warning') logger.error(exception) # logger.exception(e, exc_info=False) と同じ logger.exception(exception) # logger.error(e, exc_info=True) と同じ 設定したら Logger を作って出力 47
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 2.1. Logging モジュールの概要
2.2. Logging モジュールの構成要素 2.3. Logger の性質 2.4. Logger の設定と出力 (アプリケーション編) 2.5. Logger の設定と出力 (ライブラリ編) 3. パブリッククラウドサービスでのログ出力 4. まとめ 48
#pyconjp_3 - Handler/Level/Formatter/Filter の選択権は 呼び出し側のアプリケーションにある - ライブラリが決めるのは タイミング と メッセージ内容
ライブラリでは詳細設定をしない 49
#pyconjp_3 - ライブラリのトップレベル Logger に NullHandler のみを設 定するのがお作法 - NullHandler
は、何もせず上位 Logger に伝播させる - この仕組みにより 3rd party ライブラリもログ出力対象に加 えることができる NullHandler を設定 50 https://docs.python.org/ja/3/howto/logging.html#configuring-logging-for-a-library
#pyconjp_3 # 設定 from logging import getLogger, NullHandler logger =
getLogger(__name__) logger.addHandler(NullHandler()) ライブラリでの設定・出力例 51 # 出力 logger.warning('Warning') 👉
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 3. パブリッククラウドサービスでのログ出力 3.1.
実行環境とその背景 3.2. Amazon Web Services 3.3. Google Cloud Platform 4. まとめ 52
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 3. パブリッククラウドサービスでのログ出力 3.1.
実行環境とその背景 3.2. Amazon Web Services 3.3. Google Cloud Platform 4. まとめ 53
#pyconjp_3 - パブリッククラウドサービスでの PaaS / FaaS では、あらか じめロギング設定がなされている - Python
の Logging モジュールを応用している - 凝ったことをするには Logging モジュールの知識が不可欠 実行環境は用意されている 54
#pyconjp_3 - モダンなアプリケーションとは、を述べたもの - ステートレス かつ シェアードナッシング を志向 - Factor
Ⅸ: “ログをイベントストリームとして扱う” - ログはローカルに出さず 標準出力に書き出して収集 前提: The Twelve-Factor App 55 https://12factor.net/ja/logs
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 3. パブリッククラウドサービスでのログ出力 3.1.
実行環境とその背景 3.2. Amazon Web Services 3.3. Google Cloud Platform 4. まとめ 56
#pyconjp_3 - サーバーやアプリケーションのログを収集・保存できるサー ビス - AWS サービスが標準出力すると、多くは CloudWatch Logs に書き込みにいく
- AWSの各サービスがロギング設定を隠蔽している Amazon CloudWatch Logs 57
#pyconjp_3 - root logger に LambdaLoggerHandler という カスタムハンドラが 設定されている -
Formatter で aws_request_id を指定できる - Filter で LogRecord の属性を追加するという技 - https://github.com/aws/aws-lambda-python-runtime-interface-client/blob/mai n/awslambdaric/bootstrap.py AWS Lambda (Zip/Container) 58
#pyconjp_3 - Logging モジュールに則った設定になっているので、これまで お話しした方法で変更できる - root logger に伝播させると(propagate=true)、前述の aws_request_id
出力を維持できるのでちょっと便利 Lambda の設定変更は標準と同じ 59
#pyconjp_3 60 def set_logger(): level = logging.getLevelName(os.environ.get('LOG_LEVEL')) FMT = '%(asctime)s.%(msecs)03d\t%(aws_request_id)s\t%(filename)s:%(funcName)s:
%(lineno)d\t[%(levelname)s]%(message)s' # フォーマット変更 DATE_FMT = '%Y-%m-%d %H:%M:%S' fmt = logging.Formatter(fmt=FMT, datefmt=DATE_FMT, style='%') fmt.converter = time.gmtime root_logger = logging.getLogger() # ルートロガー取得 root_logger.setLevel(level) root_logger.handlers[0].setLevel(level) # handlers[0] でハンドラを指定 root_logger.handlers[0].setFormatter(fmt) 👉
#pyconjp_3 2021-10-01 13:22:26.84618a54970-32ad-4484-9650-167ed4c7b777 lambda_function.py:main:25 [INFO]{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
2021-10-01 13:22:26.84618a54970-32ad-4484-9650-167ed4c7b777 lambda_function.py:main:26 [INFO]{'aws_request_id': '18a54970-32ad-4484-9650-167ed4c7b777', 'log_group_name': '/aws/lambda/pycon-jp-2021_test_logging', 'log_stream_name': '2021/10/01/[$LATEST]0f2d935e588b4cf1be2a734049dca4e5', 'function_name': 'pycon-jp-2021_test_logging', 'memory_limit_in_mb': '128', 'function_version': '$LATEST', 'invoked_function_arn': 'arn:aws:lambda:ap-northeast-1:123456789012:function:pycon-jp-2021_test_logging', 'client_context': None, 'identity': <__main__.CognitoIdentity object at 0x7f4836e986d0>, '_epoch_deadline_time_in_ms': 1633094556845} 61 def main(event, context): logger = logging.getLogger(__name__) logger.info(event) logger.info(vars(context)) # コンテキストを出力して aws_request_id を確認 👉
#pyconjp_3 62 Lambda powertools python https://awslabs.github.io/aws-lambda-powertools-python/latest/core/logger/ - Logging モジュールでは物足りない人に -
context を含めた形で JSON で出力できる - xray_trace_id を簡単に出力できる - CloudWatch Logs Insights と相性がよい
#pyconjp_3 - Spark Job ではないほう - ログ出力先が二箇所に分かれている - /aws-glue/python-jobs/output -
/aws-glue/python-jobs/error - ログが複数行まとまって出力されることがある AWS Glue: Python Script 63
#pyconjp_3 - /aws-glue/python-jobs/output - print() で出力するメッセージ - /aws-glue/python-jobs/error - logging
モジュールで出力するメッセージ - スタックトレース Glue python script のデフォルト設定 64
#pyconjp_3 - /aws-glue/python-jobs/output - print() で出力するメッセージ - logging モジュールで出力するメッセージ -
/aws-glue/python-jobs/error - スタックトレース こうしたくなる気持ち 65
#pyconjp_3 args = getResolvedOptions(sys.argv, ['log_level']) level = logging.getLevelName(args['log_level']) FMT =
'%(asctime)s.%(msecs)03d %(filename)s:%(funcName)s:%(lineno)d [%(levelname)s]%(message)s' DATE_FMT = '%Y-%m-%d %H:%M:%S' fmt = logging.Formatter(fmt=FMT, datefmt=DATE_FMT, style='%') sh = logging.StreamHandler(stream=sys.stdout) # stdout に向いた StreamHandler sh.setLevel(level) # Level を変更 sh.setFormatter(fmt) # Formatter も変更 root_logger = logging.getLogger() root_logger.handlers.clear() # stderr に向いているハンドラを削除 root_logger.setLevel(level) root_logger.addHandler(sh) 66 👉
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 3. パブリッククラウドサービスでのログ出力 3.1.
実行環境とその背景 3.2. Amazon Web Services 3.3. Google Cloud Platform 4. まとめ 67
#pyconjp_3 - サーバーやアプリケーションのログを収集・保存できるサー ビス - Cloud Logging クライアントライブラリを使って出力 - https://github.com/googleapis/python-logging
- サービスに応じて対応する Handler が異なる - GCPの各サービスを拡張するツールを提供している Cloud Logging 68
#pyconjp_3 - Python 標準の logging モジュールそのまま使うと Cloud Logging 独自のログレベルである ‘Severity’
を設定できない - それをカバーするためのライブラリ - 使い方は2通り、直接使用 か Logging モジュールに接続 - デフォルトのレベルは INFO googleapis/python-logging 69
#pyconjp_3 from google.cloud.logging import Client client = Client() client.setup_logging() #
実行サービスに応じたハンドラを ルートロガーにセットしてくれる デフォルト設定で出力してみる 70 from logging import getLogger logger = logging.getLogger(__name__) logger.info(‘Information!’) # INFO 以上のログが出力対象 👉
#pyconjp_3 71 驚きの長さ(1/3) { "textPayload": "2021-10-02 14:33:29.246 main.py:handler:30 [INFO]Information!", "insertId":
"000001-6b9ed058-409f-4178-947b-444b580997c1", "httpRequest": { "requestMethod": "POST", "requestUrl": "http://5caf1872csaljdhnrk8d3e68398bad9e25-dot-pcb9e111e60bda6d2p-tp.appspot.com/", "userAgent": "Go-http-client/1.1", "protocol": "HTTP/1.1" },
#pyconjp_3 72 驚きの長さ(2/3) "resource": { "type": "cloud_function", "labels": { "function_name":
"test_logging", "project_id": "my-project-id", "region": "us-east1" } }, "timestamp": "2021-10-02T14:33:29.246Z", "severity": "INFO",
#pyconjp_3 "labels": { "execution_id": "ayhzxzby8ee1" }, "logName": "projects/my-project-id/logs/cloudfunctions.googleapis.com%2Fcloud-functions", "trace": "projects/my-project-id/traces/5cb0d2b4e7997590659930dcfdd4c001",
"sourceLocation": { "file": "/workspace/main.py", "line": "30", "function": "handler" }, "receiveTimestamp": "2021-10-02T14:33:39.442665753Z" } 73 驚きの長さ(3/3)
#pyconjp_3 - それでも Level や Formatter にこだわりたい あなたへ 74 デフォルトでいいじゃん
#pyconjp_3 import os from logging import Formatter, getLevelName, getLogger from
google.cloud.logging.handlers import StructuredLogHandler, setup_logging FMT = '%(asctime)s.%(msecs)03d\t%(filename)s:%(funcName)s:%(lineno)d\t[%(levelname)s]%(messag e)s' DATE_FMT = '%Y-%m-%d %H:%M:%S' fmt = Formatter(fmt=FMT, datefmt=DATE_FMT, style='%') level = getLevelName(os.environ.get('LOG_LEVEL')) handler = StructuredLogHandler() handler.setLevel(level) handler.setFormatter(fmt) setup_logging(handler, log_level=level) # ルートロガーへのハンドラ設定(詳細は次ページ) 75 👉
#pyconjp_3 76 def setup_logging( handler, *, excluded_loggers=EXCLUDED_LOGGER_DEFAULTS, log_level=logging.INFO ): all_excluded_loggers
= set(excluded_loggers + EXCLUDED_LOGGER_DEFAULTS) logger = logging.getLogger() if detect_resource().type in _CLEAR_HANDLER_RESOURCE_TYPES: logger.handlers.clear() # ルートロガーの既存ハンドラをクリア logger.setLevel(log_level) logger.addHandler(handler) # 新しいハンドラを追加 for logger_name in all_excluded_loggers: # google.cloud.logging 関連のログ伝播を止める logger = logging.getLogger(logger_name) logger.propagate = False https://github.com/googleapis/python-logging/blob/main/google/cloud/logging_v2/handler s/handlers.py (当スライドではコメントを一部編集) 👉
#pyconjp_3 - requirements.txt に google-cloud-logging を指定 Cloud Functions 77 def
handler(request): request_json = request.get_json(silent=True) logger = getLogger(__name__) logger.info(f'Info {request_json}') try: raise ValueError('Error test') except Exception as e: traceback_str = traceback.format_exc().splitlines() err_msg = json.dumps({ "errorType": e.__class__.__name__, "errorMessage": e.__str__(), "stackTrace": traceback_str }) logger.error(err_msg) return err_msg
#pyconjp_3 - requirements.txt に google-cloud-logging を指定 - Flask を使う場合は app.logger.xxx
経由で出力 Cloud Run 78 @app.errorhandler(InternalServerError) def error_handler(e): return 'InternalServerError has occured.', e.code @app.route("/") def test_logging(): app.logger.debug('Debug') try: raise InternalServerError('Error test') except Exception as e: traceback_str = traceback.format_exc().splitlines() err_msg = json.dumps({ "errorType": e.__class__.__name__, "errorMessage": e.__str__(), "stackTrace": traceback_str }) app.logger.error(err_msg) abort(e.code)
#pyconjp_3 Contents📝 1. ゴールとスコープ 2. Logging モジュール探訪 3. パブリッククラウドサービスでのログ出力 4.
まとめ 79
#pyconjp_3 伝えたかったこと 80 - 利用するかどうかの判断基準を知る - 書き捨てでなければ使うと便利 - 構成要素がわかる -
主要な要素を図にまとめました - 出力方法と実装例を知る - アプリケーション と ライブラリ でお作法が異なる - AWS と GCP で思想が異なる
#pyconjp_3 - 目的が明確である - 伝えている内容が正確な事実である - 流量が適切である - ネクストアクションを促している こんな風にログを出したい
81
#pyconjp_3 - python.org, logging --- Python 用ロギング機能 - python.org, logging
HOWTO - Adam Wiggins, The Twelve-Factor App - @amedama, ログ出力のための print と import logging はやめてほしい, Qiita - @yomon8, AWS Glue Python Shellでloggingを使ったログ出力につ いて - DevelopersIO, Lambda + Python3.7のログをJSON形式で出力して みる(行ごとに分割されない形で) 参考資料 82
#pyconjp_3 Thank you! 83
#pyconjp_3 (おまけ) Loggingモジュール関連の バージョン別変更点 まとめ 84
#pyconjp_3 - logging.Logger - pickle 化と unpickle 化ができるようになった - logging.disable
- パラメータ level のデフォルト値が CRITICAL になった - logging.StreamHandler - stream の Setter である setStream が追加された Python 3.7 85
#pyconjp_3 - logging.Formatter - パラメータ validate が追加された (style と fmt
のチェック) - logging.basicConfig - 引数 force が追加された Python 3.8 86
#pyconjp_3 - logger.Formatter - default_msec_format に None を指定できるようになった - logging.basicConfig
- 引数 encoding, errors が追加された - logging.FileHandler (子クラスも対象) - パラメータ errors が追加された Python 3.9 87