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