|
| 1 | +from io import StringIO |
| 2 | + |
1 | 3 | import structlog
|
| 4 | +from structlog.dev import _pad |
2 | 5 |
|
3 | 6 | from django_structlog.middlewares.request import RequestMiddleware
|
4 | 7 |
|
@@ -70,8 +73,131 @@ def __call__(self, logger, method_name, event_dict):
|
70 | 73 | return event_dict
|
71 | 74 |
|
72 | 75 |
|
| 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 | + |
73 | 200 | shared_processors = [
|
74 |
| - structlog.processors.TimeStamper(fmt='iso'), |
75 | 201 | structlog.stdlib.add_logger_name,
|
76 | 202 | structlog.stdlib.add_log_level,
|
77 | 203 | structlog.stdlib.PositionalArgumentsFormatter(),
|
|
0 commit comments