TL;DR
- djangoの標準ログは質素すぎて足りない部分が多かった
- djangoのログ出力をjson形式にするformatterを作成
- healthcheck pathなど、特定URIをログ出力させないfilterを作成
Why?
筆者はdjango初心者ですが、自社サービスで一部djangoを使っており、
「datadog logsにjsonログを流したい」という要件が出てきました。
標準のログ機能じゃ要件を満たせず。。またdjangoのloggingはclassが設定できるので、
「標準でダメだったら自分たちで作ってね」という仕様なのかなぁと思いました。
Why formatter?
formatterを作ろうと思ったのは2つ理由があります。
- ログを出力させても質素なログしかGETできず、http request/response周りのデータが欲しかった点です。 _
- 理由は、json型で出力させて、datadog logsにフィールド認識させたかった点です。
https://b0uh.github.io/django-json-logging.html のようにやるとjsonは出力され、2つ目は解決しそうですが、1つ目が解決しません。
https://docs.datadoghq.com/ja/logs/log_collection/python/?tab=jsonlogformatter をみると
https://github.com/namespace-ee/django-datadog-logger が推奨となってたので使ってみるとこのような出力になりました。
{ "message": "HTTP 200 OK", "logger.name": "django_datadog_logger.middleware.request_log", "logger.thread_name": "Thread-2", "logger.method_name": "log_request", "syslog.timestamp": "2021-12-02T09:10:00.541469+00:00", "syslog.severity": "INFO", "network.client.ip": "172.23.0.1", "http.url": "/hoge", "http.url_details.host": "localhost", "http.url_details.port": 8000, "http.url_details.path": "/hoge", "http.url_details.queryString": {}, "http.url_details.scheme": "http", "http.method": "GET", "http.referer": null, "http.useragent": "curl/7.77.0", "http.request_version": null, "http.request_id": "f44778b6-0e78-433a-a2ca-24d1ae1736a3", "duration": 1774787.9028320312, "http.status_code": 200 }
とても良さそうですが、自由にフィールドを追加したい/フィールド名を変えれたら便利
(その他サービスのログフィールドと一緒にすると検索しやすいから)
だと思って、一からformatterを作成することにしました。
Why filter
healthcheck pathなどはノイズになるため、リクエストが来てもログを出力しないようにしたかったです。
djangoにはfilter機能があったので、それも一から作ってみました。
実装したものを適用した後のログ出力
$ curl -X POST -H "Content-Type: application/json" \ -d '{"Name":"hogehoge", "fugarfuga": "fugaaaaaa"}' localhost:8000/hoge { "middleware": "django_datadog_logger.middleware.request_log", "levelname": "WARNING", "filename": "request_log.py", "status": 404, "duration": 28986692.428588867, "error.message": "Not Found", "host": "localhost", "port": 8000, "protocol": "http", "method": "POST", "path": "/hoge", "query_string": {}, "request_body": "{\"Name\":\"hogehoge\", \"fugarfuga\": \"fugaaaaaa\"}", "user-agent": "curl/7.77.0", "remote_ip": "172.23.0.1", "referer": null, "time": "2021-12-08T12:44:50.447804+00:00" }
How
こちらにプログラムを置いてます。
設定方法
required
httpリクエストを解析するために下記が必要です。
# requirements.txt django-datadog-logger==0.5.0
config/base.py
MIDDLEWARE = [ "django_datadog_logger.middleware.request_id.RequestIdMiddleware", . . . "django_datadog_logger.middleware.error_log.ErrorLoggingMiddleware", "django_datadog_logger.middleware.request_log.RequestLoggingMiddleware", ]
のようにして、django-datadog-loggerをmiddlewareに登録させます。
これは、 logging_formatter.py
にhttpの詳細ログを送るためです。
config/logging_formatter.py
format用のプログラムです。 django-datadog-loggerでも足りない必要なログを追加したり、
datadog logsに合わせてフィールド名を変更できます。
最後にjson型に変更して出力して終わり。
config/logging_filter.py
filter用です。
ログの出力をするorしないをジャッジします。主にヘルスチェックなど、有効なURIだがロギングしたくないものに対して行ってます
config/environemnt名.py
LOGGING = { "version": 1, "disable_existing_loggers": False, 'filters': { 'custom': { '()': 'config.logging_filter.Main', }, }, "formatters": { "json": {"()": "config.logging_formatter.Main"}, }, "handlers": { "console": {"level": "INFO", "class": "logging.StreamHandler", "formatter": "json", 'filters': ['custom']}, }, "loggers": { 'django.server': {'level': 'ERROR'}, "django_datadog_logger.middleware.request_log": {"handlers": ["console"], "level": "INFO", "propagate": False}, }, } DEBUG: False
formatにlogging_formatter.pyを設定
filterはlogging_filter.pyを設定
handlerはconsole(コンソールにstdoutとして出力用)の1つだけ作成し、filter/formatterは上記を選択
loggersは2つです
django.server
- djangoが生成する標準ログ。質素で使えないため(
"GET / HTTP/1.1" 404 179
のようなログ)出力させないようにしてます - level: ERRORにすることで、 上記のログを出さないようにしています
- handlerも設定不要
- djangoが生成する標準ログ。質素で使えないため(
django_datadog_logger.middleware.request_log
- httpリクエストを解析したログも出力されるやつ。これをベースにログを生成します
またDEBUG: True
にすることで、ルーティングしてないURIアクセス時の無駄なログ( Not Found: /
)を消しています。
developmentだけTrue推奨です。