logs.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import os
  4. import logging
  5. import json
  6. from pathlib import Path
  7. from django.http import HttpRequest
  8. BUILDDIR = Path(os.environ.get('BUILDDIR', '/tmp'))
  9. def log_api_request(request, response, view, logger_name='api'):
  10. """Helper function for LogAPIMixin"""
  11. repjson = {
  12. 'view': view,
  13. 'path': request.path,
  14. 'method': request.method,
  15. 'status': response.status_code
  16. }
  17. logger = logging.getLogger(logger_name)
  18. logger.info(
  19. json.dumps(repjson, indent=4, separators=(", ", " : "))
  20. )
  21. def log_view_mixin(view):
  22. def log_view_request(*args, **kwargs):
  23. # get request from args else kwargs
  24. request = None
  25. if len(args) > 0:
  26. for req in args:
  27. if isinstance(req, HttpRequest):
  28. request = req
  29. break
  30. elif request is None:
  31. request = kwargs.get('request')
  32. response = view(*args, **kwargs)
  33. view_name = 'unknown'
  34. if hasattr(request, 'resolver_match'):
  35. if hasattr(request.resolver_match, 'view_name'):
  36. view_name = request.resolver_match.view_name
  37. log_api_request(
  38. request, response, view_name, 'toaster')
  39. return response
  40. return log_view_request
  41. class LogAPIMixin:
  42. """Logs API requests
  43. tested with:
  44. - APIView
  45. - ModelViewSet
  46. - ReadOnlyModelViewSet
  47. - GenericAPIView
  48. Note: you can set `view_name` attribute in View to override get_view_name()
  49. """
  50. def get_view_name(self):
  51. if hasattr(self, 'view_name'):
  52. return self.view_name
  53. return super().get_view_name()
  54. def finalize_response(self, request, response, *args, **kwargs):
  55. log_api_request(request, response, self.get_view_name())
  56. return super().finalize_response(request, response, *args, **kwargs)
  57. LOGGING_SETTINGS = {
  58. 'version': 1,
  59. 'disable_existing_loggers': False,
  60. 'filters': {
  61. 'require_debug_false': {
  62. '()': 'django.utils.log.RequireDebugFalse'
  63. }
  64. },
  65. 'formatters': {
  66. 'datetime': {
  67. 'format': '%(asctime)s %(levelname)s %(message)s'
  68. },
  69. 'verbose': {
  70. 'format': '{levelname} {asctime} {module} {name}.{funcName} {process:d} {thread:d} {message}',
  71. 'datefmt': "%d/%b/%Y %H:%M:%S",
  72. 'style': '{',
  73. },
  74. 'api': {
  75. 'format': '\n{levelname} {asctime} {name}.{funcName}:\n{message}',
  76. 'style': '{'
  77. }
  78. },
  79. 'handlers': {
  80. 'mail_admins': {
  81. 'level': 'ERROR',
  82. 'filters': ['require_debug_false'],
  83. 'class': 'django.utils.log.AdminEmailHandler'
  84. },
  85. 'console': {
  86. 'level': 'DEBUG',
  87. 'class': 'logging.StreamHandler',
  88. 'formatter': 'datetime',
  89. },
  90. 'file_django': {
  91. 'level': 'INFO',
  92. 'class': 'logging.handlers.TimedRotatingFileHandler',
  93. 'filename': BUILDDIR / 'toaster_logs/django.log',
  94. 'when': 'D', # interval type
  95. 'interval': 1, # defaults to 1
  96. 'backupCount': 10, # how many files to keep
  97. 'formatter': 'verbose',
  98. },
  99. 'file_api': {
  100. 'level': 'INFO',
  101. 'class': 'logging.handlers.TimedRotatingFileHandler',
  102. 'filename': BUILDDIR / 'toaster_logs/api.log',
  103. 'when': 'D',
  104. 'interval': 1,
  105. 'backupCount': 10,
  106. 'formatter': 'verbose',
  107. },
  108. 'file_toaster': {
  109. 'level': 'INFO',
  110. 'class': 'logging.handlers.TimedRotatingFileHandler',
  111. 'filename': BUILDDIR / 'toaster_logs/web.log',
  112. 'when': 'D',
  113. 'interval': 1,
  114. 'backupCount': 10,
  115. 'formatter': 'verbose',
  116. },
  117. },
  118. 'loggers': {
  119. 'django.request': {
  120. 'handlers': ['file_django', 'console'],
  121. 'level': 'WARN',
  122. 'propagate': True,
  123. },
  124. 'django': {
  125. 'handlers': ['file_django', 'console'],
  126. 'level': 'WARNING',
  127. 'propogate': True,
  128. },
  129. 'toaster': {
  130. 'handlers': ['file_toaster'],
  131. 'level': 'INFO',
  132. 'propagate': False,
  133. },
  134. 'api': {
  135. 'handlers': ['file_api'],
  136. 'level': 'INFO',
  137. 'propagate': False,
  138. }
  139. }
  140. }