Source code for swh.core.logger
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
import datetime
import logging
from typing import Any, Dict, Generator, List, Tuple
from systemd.journal import JournalHandler as _JournalHandler
from systemd.journal import send
try:
from celery import current_task
except ImportError:
current_task = None
EXTRA_LOGDATA_PREFIX = "swh_"
LOGGED_TASK_KWARGS = ("url", "instance")
[docs]
def db_level_of_py_level(lvl):
"""convert a log level of the logging module to a log level suitable for the
logging Postgres DB
"""
return logging.getLevelName(lvl).lower()
[docs]
def flatten(data: Any, separator: str = "_") -> Generator[Tuple[str, Any], None, None]:
"""Flatten the data dictionary into a flat structure"""
def inner_flatten(
data: Any, prefix: List[str]
) -> Generator[Tuple[List[str], Any], None, None]:
if isinstance(data, dict):
if all(isinstance(key, str) for key in data):
for key, value in data.items():
yield from inner_flatten(value, prefix + [key])
else:
yield prefix, str(data)
elif isinstance(data, (list, tuple)):
for key, value in enumerate(data):
yield from inner_flatten(value, prefix + [str(key)])
else:
yield prefix, data
for path, value in inner_flatten(data, []):
yield separator.join(path), value
[docs]
def stringify(value: Any) -> str:
"""Convert value to string"""
if isinstance(value, datetime.datetime):
return value.isoformat()
return str(value)
[docs]
class JournalHandler(_JournalHandler):
[docs]
def emit(self, record):
"""Write `record` as a journal event.
MESSAGE is taken from the message provided by the user, and PRIORITY,
LOGGER, THREAD_NAME, CODE_{FILE,LINE,FUNC} fields are appended
automatically. In addition, record.MESSAGE_ID will be used if present.
This also records all the extra data fetched by `get_extra_data`.
"""
try:
extra_data = flatten(get_extra_data(record))
extra_data = {
(EXTRA_LOGDATA_PREFIX + key).upper(): stringify(value)
for key, value in extra_data
}
msg = self.format(record)
pri = self.mapPriority(record.levelno)
send(
msg,
PRIORITY=format(pri),
LOGGER=record.name,
THREAD_NAME=record.threadName,
CODE_FILE=record.pathname,
CODE_LINE=record.lineno,
CODE_FUNC=record.funcName,
**extra_data,
)
except Exception:
self.handleError(record)