Django的结构化日志记录
django-structlog的Python项目详细描述
django结构日志
django structlog是django项目的结构化日志集成,使用structlog
然后,日志记录将在每个日志上生成附加的内聚元数据,从而更容易跟踪事件或事件。
测井对比
标准日志:
>>>importlogging>>>logger=logging.get_logger(__name__)>>>logger.info("An error occurred")
An error occurred
好吧…好吧
带django structlog和扁平线:
>>>importstructlog>>>logger=structlog.get_logger(__name__)>>>logger.info("an_error_occurred",bar="Buz")
timestamp='2019-04-13T19:39:31.089925Z'level='info'event='an_error_occurred'logger='my_awesome_project.my_awesome_module'request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'user_id=1ip='0.0.0.0'bar='Buz'
然后您可以使用以下命令进行搜索:
$ cat logs/flat_line.log | grep request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'
使用django structlog和json
>>>importstructlog>>>logger=structlog.get_logger(__name__)>>>logger.info("an_error_occurred",bar="Buz")
{"request_id":"3a8f801c-072b-4805-8f38-e1337f363ed4","user_id":1,"ip":"0.0.0.0","event":"an_error_occurred","timestamp":"2019-04-13T19:39:31.089925Z","logger":"my_awesome_project.my_awesome_module","level":"info","bar":"Buz"}
然后您可以使用以下命令进行搜索:
$ cat logs/json.log | jq '.[] | select(.request_id="3a8f801c-072b-4805-8f38-e1337f363ed4")' -s
开始
这些步骤将展示如何将中间件集成到您出色的应用程序中。
安装
安装库
pip install django-structlog
添加中间件
MIDDLEWARE=[# ...'django_structlog.middlewares.RequestMiddleware',]
将适当的structlog配置添加到settings.py
importstructlogLOGGING={"version":1,"disable_existing_loggers":False,"formatters":{"json_formatter":{"()":structlog.stdlib.ProcessorFormatter,"processor":structlog.processors.JSONRenderer(),},"plain_console":{"()":structlog.stdlib.ProcessorFormatter,"processor":structlog.dev.ConsoleRenderer(),},"key_value":{"()":structlog.stdlib.ProcessorFormatter,"processor":structlog.processors.KeyValueRenderer(key_order=['timestamp','level','event','logger']),},},"handlers":{"console":{"class":"logging.StreamHandler","formatter":"plain_console",},"json_file":{"class":"logging.handlers.WatchedFileHandler","filename":"logs/json.log","formatter":"json_formatter",},"flat_line_file":{"class":"logging.handlers.WatchedFileHandler","filename":"logs/flat_line.log","formatter":"key_value",},},"loggers":{"django_structlog":{"handlers":["console","flat_line_file","json_file"],"level":"INFO",},"django_structlog_demo_project":{"handlers":["console","flat_line_file","json_file"],"level":"INFO",},}}structlog.configure(processors=[structlog.stdlib.filter_by_level,structlog.processors.TimeStamper(fmt="iso"),structlog.stdlib.add_logger_name,structlog.stdlib.add_log_level,structlog.stdlib.PositionalArgumentsFormatter(),structlog.processors.StackInfoRenderer(),structlog.processors.format_exc_info,structlog.processors.UnicodeDecoder(),structlog.processors.ExceptionPrettyPrinter(),structlog.stdlib.ProcessorFormatter.wrap_for_formatter,],context_class=structlog.threadlocal.wrap_dict(dict),logger_factory=structlog.stdlib.LoggerFactory(),wrapper_class=structlog.stdlib.BoundLogger,cache_logger_on_first_use=True,)
使用structlog而不是logging开始日志记录。
importstructloglogger=structlog.get_logger(__name__)
扩展请求日志元数据
默认情况下,只有一个request_id和user_id从请求绑定,但是相关的日志元数据可能因项目而异。
如果需要从请求中添加更多元数据,可以实现一个方便的信号接收器来绑定它们。
fromdjango.dispatchimportreceiverfromdjango_structlog.signalsimportbind_extra_request_metadata@receiver(bind_extra_request_metadata)defbind_user_email(request,logger,**kwargs):logger.bind(user_email=getattr(request.user,'email',''))
示例输出
平面线文件(logs/flat_lines.log)
timestamp='2019-04-13T19:39:29.321453Z'level='info'event='request_started'logger='django_structlog.middlewares.request'request_id='c53dff1d-3fc5-4257-a78a-9a567c937561'user_id=1ip='0.0.0.0'request=<WSGIRequest: GET '/'> user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'timestamp='2019-04-13T19:39:29.345207Z'level='info'event='request_finished'logger='django_structlog.middlewares.request'request_id='c53dff1d-3fc5-4257-a78a-9a567c937561'user_id=1ip='0.0.0.0'code=200timestamp='2019-04-13T19:39:31.086155Z'level='info'event='request_started'logger='django_structlog.middlewares.request'request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'user_id=1ip='0.0.0.0'request=<WSGIRequest: POST '/success_task'> user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'timestamp='2019-04-13T19:39:31.089925Z'level='info'event='Enqueuing successful task'logger='django_structlog_demo_project.home.views'request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'user_id=1ip='0.0.0.0'timestamp='2019-04-13T19:39:31.147590Z'level='info'event='task_enqueued'logger='django_structlog.middlewares.celery'request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'user_id=1ip='0.0.0.0'child_task_id='6b11fd80-3cdf-4de5-acc2-3fd4633aa654'timestamp='2019-04-13T19:39:31.153081Z'level='info'event='This is a successful task'logger='django_structlog_demo_project.taskapp.celery'task_id='6b11fd80-3cdf-4de5-acc2-3fd4633aa654'request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'user_id=1ip='0.0.0.0'timestamp='2019-04-13T19:39:31.160043Z'level='info'event='request_finished'logger='django_structlog.middlewares.request'request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'user_id=1ip='0.0.0.0'code=201timestamp='2019-04-13T19:39:31.162372Z'level='info'event='task_succeed'logger='django_structlog.middlewares.celery'task_id='6b11fd80-3cdf-4de5-acc2-3fd4633aa654'request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'user_id=1ip='0.0.0.0'result='None'
json文件(logs/json.log)
{"request_id":"c53dff1d-3fc5-4257-a78a-9a567c937561","user_id":1,"ip":"0.0.0.0","request":"<WSGIRequest: GET '/'>","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36","event":"request_started","timestamp":"2019-04-13T19:39:29.321453Z","logger":"django_structlog.middlewares.request","level":"info"}{"request_id":"c53dff1d-3fc5-4257-a78a-9a567c937561","user_id":1,"ip":"0.0.0.0","code":200,"event":"request_finished","timestamp":"2019-04-13T19:39:29.345207Z","logger":"django_structlog.middlewares.request","level":"info"}{"request_id":"3a8f801c-072b-4805-8f38-e1337f363ed4","user_id":1,"ip":"0.0.0.0","request":"<WSGIRequest: POST '/success_task'>","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36","event":"request_started","timestamp":"2019-04-13T19:39:31.086155Z","logger":"django_structlog.middlewares.request","level":"info"}{"request_id":"3a8f801c-072b-4805-8f38-e1337f363ed4","user_id":1,"ip":"0.0.0.0","event":"Enqueuing successful task","timestamp":"2019-04-13T19:39:31.089925Z","logger":"django_structlog_demo_project.home.views","level":"info"}{"request_id":"3a8f801c-072b-4805-8f38-e1337f363ed4","user_id":1,"ip":"0.0.0.0","child_task_id":"6b11fd80-3cdf-4de5-acc2-3fd4633aa654","event":"task_enqueued","timestamp":"2019-04-13T19:39:31.147590Z","logger":"django_structlog.middlewares.celery","level":"info"}{"task_id":"6b11fd80-3cdf-4de5-acc2-3fd4633aa654","request_id":"3a8f801c-072b-4805-8f38-e1337f363ed4","user_id":1,"ip":"0.0.0.0","event":"This is a successful task","timestamp":"2019-04-13T19:39:31.153081Z","logger":"django_structlog_demo_project.taskapp.celery","level":"info"}{"request_id":"3a8f801c-072b-4805-8f38-e1337f363ed4","user_id":1,"ip":"0.0.0.0","code":201,"event":"request_finished","timestamp":"2019-04-13T19:39:31.160043Z","logger":"django_structlog.middlewares.request","level":"info"}{"task_id":"6b11fd80-3cdf-4de5-acc2-3fd4633aa654","request_id":"3a8f801c-072b-4805-8f38-e1337f363ed4","user_id":1,"ip":"0.0.0.0","result":"None","event":"task_succeed","timestamp":"2019-04-13T19:39:31.162372Z","logger":"django_structlog.middlewares.celery","level":"info"}
运行测试
注意:目前需要redis来运行测试。最简单的方法开始Docker的演示。
docker-compose up --build
在另一个壳中
pip install -r requirements/base.txt pytest
演示应用程序
docker-compose up --build
在浏览器中打开http://0.0.0.0:8000/。
浏览日志文件和shell的输出。
致谢
- 非常感谢@ferd他bad opinions激发了作者足够的时间花在这个图书馆上。
- This issue帮助作者找出如何在django中集成structlog。
- This stack overflow question也有帮助。
许可证
这个项目是根据麻省理工学院的许可证授权的-请参见LICENSE文件以了解详细信息