需求

请求view中手动打印日志时中插入request的如下信息(每个request请求都记录可以使用中间件进行解决,但这里仅仅是在需要的地方手动打印):

  1. #统一附加日志内容
  2. ADD_LOG = r'''{"username": request.user, "path": request.path, "request_id": request.id, "login_id": request.login_id}'''

旧的解决办法

在每次需要打印日志时,通过 loggingextra** 进行额外的打印信息添加:
每次手动添加同样的extra非常的不优雅。
image.png

新的解决方案

django 自带log系统官方文档

1、熟悉python的logging模块结构。

  1. Loggers
  2. Handlers
  3. Filters
  4. Formatters

2、django中间件存储request信息。

  1. class RequestLogMiddleware(MiddlewareMixin):
  2. """
  3. 将request的信息记录在当前的请求线程上
  4. """
  5. def process_request(self, request):
  6. # 统一附加日志内容
  7. # ADD_LOG = r'''{"username": request.user, "path": request.path, "request_id": request.id, "login_id": request.login_id}'''
  8. local.path = request.path
  9. local.request_id = request.id
  10. local.login_id = request.login_id
  11. local.username = request.user.username

3、logging的filters模块添加request信息。

  1. import logging
  2. class RequestLogFilter(logging.Filter):
  3. """
  4. 日志过滤器,将当前请求线程的request信息保存到日志的record上下文
  5. """
  6. def filter(self, record):
  7. record.request_id = getattr(local, 'request_id', "none")
  8. record.path = getattr(local, 'path', "none")
  9. record.login_id = getattr(local, 'login_id', "none")
  10. record.username = getattr(local, 'username', "none")
  11. record.appName = getattr(local, "appName", "none")
  12. return True

4、实现原理及代码

通过 local = threading.local()local对象local 对象上。
middleware-waiwen文件代码:

  1. import threading
  2. import logging
  3. try:
  4. from django.utils.deprecation import MiddlewareMixin # Django 1.10.x
  5. except ImportError:
  6. MiddlewareMixin = object # Django 1.4.x - Django 1.9.x
  7. local = threading.local()
  8. class RequestLogFilter(logging.Filter):
  9. """
  10. 日志过滤器,将当前请求线程的request信息保存到日志的record上下文
  11. record带有formater需要的信息。
  12. """
  13. def filter(self, record):
  14. record.request_id = getattr(local, 'request_id', "none")
  15. record.path = getattr(local, 'path', "none")
  16. record.login_id = getattr(local, 'login_id', "none")
  17. record.username = getattr(local, 'username', "none")
  18. return True
  19. class RequestLogMiddleware(MiddlewareMixin):
  20. """
  21. 将request的信息记录在当前的请求线程上。
  22. """
  23. def process_request(self, request):
  24. # 统一附加日志内容
  25. # ADD_LOG = r'''{"username": request.user, "path": request.path, "request_id": request.id, "login_id": request.login_id}'''
  26. local.path = request.path
  27. local.request_id = request.id
  28. local.login_id = request.login_id
  29. local.username = request.user.username

settings 文件配置

  1. MIDDLEWARE = [
  2. 'django.middleware.security.SecurityMiddleware',
  3. 'django.contrib.sessions.middleware.SessionMiddleware',
  4. 'django.middleware.common.CommonMiddleware',
  5. 'django.middleware.csrf.CsrfViewMiddleware',
  6. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  7. 'django.contrib.messages.middleware.MessageMiddleware',
  8. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  9. 'wcloud.middleware-waiwen.RequestLogMiddleware'
  10. #使用该中间件
  11. #将当前的request信息保存到当前线程供日志打印使用
  12. ]
  13. LOGGING = {
  14. # 日志相关
  15. 'version': 1,
  16. 'disable_existing_loggers': False,
  17. 'formatters': {
  18. 'standard': {
  19. 'format': '{"date": "%(created)f", "level": "%(levelname)s", "funcName": "%(module)s.%(funcName)s:%(lineno)d", "msg": "%(message)s"}'}, # 日志格式
  20. 'custom': {
  21. #该格式化中包含有过滤器record新增的字段
  22. 'format': '{"date": "%(created)f", "level": "%(levelname)s", "funcName": "%(module)s.%(funcName)s:%(lineno)d", "request_id": "%(request_id)s","login_id": "%(login_id)s", "username": "%(username)s", "path": "%(path)s","msg": "%(message)s"}'
  23. },
  24. },
  25. 'filters': {
  26. #注册该过滤器
  27. 'request_info': {'()': 'wcloud.middleware-waiwen.RequestLogFilter'}
  28. },
  29. 'handlers': {
  30. 'log': {
  31. 'level': 'DEBUG',
  32. 'class': 'logging.StreamHandler',
  33. 'formatter': 'custom',
  34. #在该过handler中使用该过滤器
  35. 'filters': ['request_info'],
  36. },
  37. 'console': {
  38. 'level': 'DEBUG',
  39. 'class': 'logging.StreamHandler',
  40. 'formatter': 'standard',
  41. },
  42. },
  43. 'loggers': {
  44. 'django': {
  45. 'handlers': ['console'],
  46. 'level': 'ERROR',
  47. 'propagate': False
  48. },
  49. 'django.request': {
  50. 'handlers': ['console'],
  51. 'level': 'ERROR',
  52. 'propagate': False
  53. },
  54. 'django.db.backens': {
  55. 'handlers': ['console'],
  56. 'level': 'DEBUG',
  57. 'propagate': False
  58. },
  59. 'log': {
  60. 'handlers': ['log'],
  61. 'level': 'INFO',
  62. 'propagate': True
  63. },
  64. }
  65. }

5、效果

image.png

参考:

给Django日志加上request_id