106 lines
2.8 KiB
Python
106 lines
2.8 KiB
Python
import datetime as dt
|
|
import json
|
|
import logging
|
|
|
|
LOG_RECORD_BUILTIN_ATTRS = {
|
|
"args",
|
|
"asctime",
|
|
"created",
|
|
"exc_info",
|
|
"exc_text",
|
|
"filename",
|
|
"funcName",
|
|
"levelname",
|
|
"levelno",
|
|
"lineno",
|
|
"module",
|
|
"msecs",
|
|
"message",
|
|
"msg",
|
|
"name",
|
|
"pathname",
|
|
"process",
|
|
"processName",
|
|
"relativeCreated",
|
|
"stack_info",
|
|
"thread",
|
|
"threadName",
|
|
"taskName",
|
|
}
|
|
|
|
"""
|
|
This isn't being used since the app will be run on gcloud run but this can be used for future apps.
|
|
If you want to test it:
|
|
|
|
formatters:
|
|
|
|
"json": {
|
|
"()": "json_formatter.JSONFormatter",
|
|
"fmt_keys": {
|
|
"level": "levelname",
|
|
"message": "message",
|
|
"timestamp": "timestamp",
|
|
"logger": "name",
|
|
"module": "module",
|
|
"function": "funcName",
|
|
"line": "lineno",
|
|
"thread_name": "threadName"
|
|
}
|
|
}
|
|
|
|
handlers:
|
|
|
|
"file_json": {
|
|
"class": "logging.handlers.RotatingFileHandler",
|
|
"level": "DEBUG",
|
|
"formatter": "json",
|
|
"filename": "logs/log",
|
|
"maxBytes": 1000000,
|
|
"backupCount": 3
|
|
}
|
|
|
|
and add "cfg://handlers.file_json" to queue handler
|
|
"""
|
|
|
|
# From this video https://www.youtube.com/watch?v=9L77QExPmI0
|
|
# Src here: https://github.com/mCodingLLC/VideosSampleCode/blob/master/videos/135_modern_logging/mylogger.py
|
|
class JSONFormatter(logging.Formatter):
|
|
def __init__(
|
|
self,
|
|
*,
|
|
fmt_keys: dict[str, str] | None = None,
|
|
):
|
|
super().__init__()
|
|
self.fmt_keys = fmt_keys if fmt_keys is not None else {}
|
|
|
|
def format(self, record: logging.LogRecord) -> str:
|
|
message = self._prepare_log_dict(record)
|
|
return json.dumps(message, default=str)
|
|
|
|
def _prepare_log_dict(self, record: logging.LogRecord):
|
|
always_fields = {
|
|
"message": record.getMessage(),
|
|
"timestamp": dt.datetime.fromtimestamp(
|
|
record.created, tz=dt.timezone.utc
|
|
).isoformat(),
|
|
}
|
|
if record.exc_info is not None:
|
|
always_fields["exc_info"] = self.formatException(record.exc_info)
|
|
|
|
if record.stack_info is not None:
|
|
always_fields["stack_info"] = self.formatStack(record.stack_info)
|
|
|
|
message = {
|
|
key: msg_val
|
|
if (msg_val := always_fields.pop(val, None)) is not None
|
|
else getattr(record, val)
|
|
for key, val in self.fmt_keys.items()
|
|
}
|
|
message.update(always_fields)
|
|
|
|
for key, val in record.__dict__.items():
|
|
if key not in LOG_RECORD_BUILTIN_ATTRS:
|
|
message[key] = val
|
|
|
|
return message
|