Skip to content

Commit e6941fe

Browse files
committed
Use SysLogProcessor and SysLogRenderer to format the logs
Use a custom renderer to match the structure we want: ``` %programname[%processid] [%level] %message [%logger:%linenumber] %structureddata ``` By default, rsyslog will prepend ``` %timestamp %hostname ``` and it will look exactly as we want.
1 parent e04aa5e commit e6941fe

File tree

1 file changed

+127
-1
lines changed

1 file changed

+127
-1
lines changed

readthedocs/core/logs.py

+127-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
from io import StringIO
2+
13
import structlog
4+
from structlog.dev import _pad
25

36
from django_structlog.middlewares.request import RequestMiddleware
47

@@ -70,8 +73,131 @@ def __call__(self, logger, method_name, event_dict):
7073
return event_dict
7174

7275

76+
class SysLogRenderer(structlog.dev.ConsoleRenderer):
77+
def __call__(self, logger, name, event_dict):
78+
sio = StringIO()
79+
80+
# ``readthedocs`` as programname is required because it's used by
81+
# syslog to filter messages and send them to different files.
82+
# https://www.rsyslog.com/doc/master/configuration/properties.html#message-properties
83+
sio.write("readthedocs")
84+
85+
process_id = event_dict.pop("process_id", None)
86+
if process_id is not None:
87+
sio.write(
88+
"["
89+
+ str(process_id)
90+
+ "]"
91+
)
92+
93+
# syslog tag delimiter
94+
sio.write(": ")
95+
96+
ts = event_dict.pop("timestamp", None)
97+
if ts is not None:
98+
sio.write(
99+
# can be a number if timestamp is UNIXy
100+
self._styles.timestamp
101+
+ str(ts)
102+
+ self._styles.reset
103+
+ " "
104+
)
105+
106+
level = event_dict.pop("level", None)
107+
if level is not None:
108+
sio.write(
109+
"["
110+
+ self._level_to_color.get(level, "")
111+
+ _pad(level, self._longest_level)
112+
+ self._styles.reset
113+
+ "] "
114+
)
115+
116+
# force event to str for compatibility with standard library
117+
event = event_dict.pop("event", None)
118+
if not isinstance(event, str):
119+
event = str(event)
120+
121+
if event_dict:
122+
event = _pad(event, self._pad_event) + self._styles.reset + " "
123+
else:
124+
event += self._styles.reset
125+
sio.write(self._styles.bright + event)
126+
127+
logger_name = event_dict.pop("logger", None)
128+
if logger_name is None:
129+
logger_name = event_dict.pop("logger_name", None)
130+
131+
line_number = event_dict.pop("line_number", None)
132+
if logger_name is not None:
133+
sio.write(
134+
"["
135+
+ self._styles.logger_name
136+
+ self._styles.bright
137+
+ logger_name
138+
+ self._styles.reset
139+
+ ":"
140+
+ str(line_number)
141+
+ "] "
142+
)
143+
144+
stack = event_dict.pop("stack", None)
145+
exc = event_dict.pop("exception", None)
146+
exc_info = event_dict.pop("exc_info", None)
147+
148+
event_dict_keys = event_dict.keys()
149+
if self._sort_keys:
150+
event_dict_keys = sorted(event_dict_keys)
151+
152+
sio.write(
153+
" ".join(
154+
self._styles.kv_key
155+
+ key
156+
+ self._styles.reset
157+
+ "="
158+
+ self._styles.kv_value
159+
+ self._repr(event_dict[key])
160+
+ self._styles.reset
161+
for key in event_dict_keys
162+
)
163+
)
164+
165+
if stack is not None:
166+
sio.write("\n" + stack)
167+
if exc_info or exc is not None:
168+
sio.write("\n\n" + "=" * 79 + "\n")
169+
170+
if exc_info:
171+
if not isinstance(exc_info, tuple):
172+
exc_info = sys.exc_info()
173+
174+
self._exception_formatter(sio, exc_info)
175+
elif exc is not None:
176+
if self._exception_formatter is not plain_traceback:
177+
warnings.warn(
178+
"Remove `format_exc_info` from your processor chain "
179+
"if you want pretty exceptions."
180+
)
181+
sio.write("\n" + exc)
182+
183+
return sio.getvalue()
184+
185+
186+
class SysLogProcessor:
187+
188+
def __call__(self, logger, method_name, event_dict):
189+
record = event_dict.get('_record', None)
190+
if record is None:
191+
return event_dict
192+
193+
event_dict.update({
194+
'process_id': record.process,
195+
'line_number': record.lineno,
196+
})
197+
return event_dict
198+
199+
73200
shared_processors = [
74-
structlog.processors.TimeStamper(fmt='iso'),
75201
structlog.stdlib.add_logger_name,
76202
structlog.stdlib.add_log_level,
77203
structlog.stdlib.PositionalArgumentsFormatter(),

0 commit comments

Comments
 (0)