-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsetup_logging.py
118 lines (95 loc) · 4.28 KB
/
setup_logging.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# https://docs.python.org/3.11/howto/logging-cookbook.html#sending-and-receiving-logging-events-across-a-network
# https://docs.python.org/3.11/howto/logging-cookbook.html#running-a-logging-socket-listener-in-production
import json
import logging
import socketserver
import struct
from logging.handlers import DEFAULT_TCP_LOGGING_PORT
from pathlib import Path
from pythonjsonlogger.json import JsonFormatter
BUILTIN_WARNINGS_LOGGER_NAME = 'py.warnings'
INFO_LOGGER_NAME = 'info'
BASE_DIR = Path(__file__).resolve().parent
class CustomFileHandler(logging.FileHandler):
def __init__(self, name, *args, **kwargs):
super(CustomFileHandler, self).__init__(*args, **kwargs)
self.name = name
def handle(self, record):
if record.name == BUILTIN_WARNINGS_LOGGER_NAME:
record.name = INFO_LOGGER_NAME
if record.name == self.name:
return super(CustomFileHandler, self).handle(record)
pass
def configure_handlers() -> [logging.Handler]:
dj_info_handler = CustomFileHandler(name='info', filename=BASE_DIR / "log/django.log")
dj_error_handler = CustomFileHandler(name='django.request', filename=BASE_DIR / "log/django.error.log")
guni_error_handler = CustomFileHandler(name='gunicorn.error', filename=BASE_DIR / "log/gunicorn.error.log")
guni_access_handler = CustomFileHandler(name='gunicorn.access', filename=BASE_DIR / "log/gunicorn.access.log")
handlers = [dj_info_handler,
dj_error_handler,
guni_error_handler,
guni_access_handler]
formatter = JsonFormatter("%(asctime)s"
" - %(process)s"
" - %(name)s"
" - %(filename)s"
" - %(trace_id)s"
" - %(user_id)s"
" - %(impersonating)s"
" - %(threadName)s"
" - %(levelname)s"
" - %(message)s",
rename_fields={
"asctime": "timestamp",
"trace_id": "x_traceId",
"user_id": "x_userId",
"impersonating": "x_impersonating",
"levelname": "level",
"threadName": "thread",
"filename": "logger"
})
for handler in handlers:
handler.setFormatter(formatter)
return handlers
def handle_log_record(record):
logger = logging.getLogger('root')
logger.handle(record)
class LogRecordStreamHandler(socketserver.StreamRequestHandler):
def handle(self):
""" Handle multiple requests - each expected to be a 4-byte length,
followed by the LogRecord in pickle format. Logs the record
according to whatever policy is configured locally.
"""
while True:
chunk = self.connection.recv(4)
if len(chunk) < 4:
break
slen = struct.unpack('>L', chunk)[0]
chunk = self.connection.recv(slen)
while len(chunk) < slen:
chunk = chunk + self.connection.recv(slen - len(chunk))
obj = json.loads(chunk)
record = logging.makeLogRecord(obj)
handle_log_record(record)
class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):
allow_reuse_address = True
def __init__(self, host='0.0.0.0', port=DEFAULT_TCP_LOGGING_PORT, handler=LogRecordStreamHandler):
super(LogRecordSocketReceiver, self).__init__(server_address=(host, port), RequestHandlerClass=handler)
self.abort = 0
self.timeout = 1
self.logname = None
def serve_until_stopped(self):
import select
abort = 0
while not abort:
rd, wr, ex = select.select([self.socket.fileno()], [], [], self.timeout)
if rd:
self.handle_request()
abort = self.abort
def main():
logging.basicConfig(level=logging.INFO,
handlers=configure_handlers())
tcp_server = LogRecordSocketReceiver()
tcp_server.serve_until_stopped()
if __name__ == '__main__':
main()