Skip to content

Commit

Permalink
Determine correct syslog level for ha core, supervisor and host messa…
Browse files Browse the repository at this point in the history
…ges (#6)
  • Loading branch information
mib1185 authored Sep 6, 2024
1 parent bc5ef08 commit f05d8f0
Showing 1 changed file with 60 additions and 2 deletions.
62 changes: 60 additions & 2 deletions syslog/journal2syslog.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import logging
import logging.handlers
from os import environ
Expand All @@ -10,6 +12,42 @@
SYSLOG_PROTO = str(environ["SYSLOG_PROTO"])
HAOS_HOSTNAME = str(environ["HAOS_HOSTNAME"])

LOGGING_NAME_TO_LEVEL_MAPPING = logging.getLevelNamesMapping()
LOGGING_JOURNAL_PRIORITY_TO_LEVEL_MAPPING = [
logging.CRITICAL, # 0 - emerg
logging.CRITICAL, # 1 - alert
logging.CRITICAL, # 2 - crit
logging.ERROR, # 3 - err
logging.WARNING, # 4 - warning
logging.INFO, # 5 - notice
logging.INFO, # 6 - info
logging.DEBUG, # 7 - debug
]
LOGGING_DEFAULT_LEVEL = logging.INFO
PATTERN_LOGLEVEL_HA = re.compile(
r"^\S+ \S+ (?P<level>INFO|WARNING|DEBUG|ERROR|CRITICAL) "
)
CONTAINER_PATTERN_MAPPING = {
"homeassistant": PATTERN_LOGLEVEL_HA,
"hassio_supervisor": PATTERN_LOGLEVEL_HA,
}


def parse_log_level(message: str, container_name: str) -> int:
"""
Try to determine logging level from message
return: logging.<LEVELNAME> if determined
return: logging.NOTSET if not determined
"""
if pattern := CONTAINER_PATTERN_MAPPING.get(container_name):
if (match := pattern.search(message)) is None:
return logging.NOTSET
return LOGGING_NAME_TO_LEVEL_MAPPING.get(
match.group("level").upper(), logging.NOTSET
)
return logging.NOTSET


# start journal reader and seek to end of journal
jr = journal.Reader(path="/var/log/journal")
jr.this_boot()
Expand Down Expand Up @@ -37,13 +75,33 @@
syslog_handler.setFormatter(formatter)
logger.addHandler(syslog_handler)

last_container_log_level: dict[str, int] = {}

# wait for new messages in journal
while True:
change = jr.wait(timeout=None)
for entry in jr:
extra = {"prog": entry.get("SYSLOG_IDENTIFIER")}
if "CONTAINER_NAME" in entry:

# remove shell colors from container messages
if (container_name := entry.get("CONTAINER_NAME")) is not None:
msg = re.sub(r"\x1b\[\d+m", "", entry.get("MESSAGE"))
else:
msg = entry.get("MESSAGE")
logger.log(level=entry.get("PRIORITY", logging.INFO), msg=msg, extra=extra)

# determine syslog level
if not container_name:
log_level = LOGGING_JOURNAL_PRIORITY_TO_LEVEL_MAPPING[
entry.get("PRIORITY", 6)
]
elif container_name not in CONTAINER_PATTERN_MAPPING:
log_level = LOGGING_DEFAULT_LEVEL
elif log_level := parse_log_level(msg, container_name):
last_container_log_level[container_name] = log_level
else: # use last log level if it could not be parsed (eq. for tracebacks)
log_level = last_container_log_level.get(
container_name, LOGGING_DEFAULT_LEVEL
)

# send syslog message
logger.log(level=log_level, msg=msg, extra=extra)

0 comments on commit f05d8f0

Please sign in to comment.