1
0
Fork 0
mirror of https://github.com/Kozea/Radicale.git synced 2025-06-26 16:45:52 +00:00

Upgrade to journald's native journal protocol

This commit is contained in:
Unrud 2023-03-21 00:03:29 +01:00
parent 360484e2d5
commit 9276c65462

View file

@ -25,11 +25,16 @@ Log messages are sent to the first available target of:
"""
import contextlib
import logging
import os
import socket
import struct
import sys
import threading
from typing import Any, Callable, ClassVar, Dict, Iterator, Union
import time
from typing import (Any, Callable, ClassVar, Dict, Iterator, Optional, Tuple,
Union)
from radicale import types
@ -65,6 +70,9 @@ class IdentLogRecordFactory:
if current_thread.name and main_thread != current_thread:
ident += "/%s" % current_thread.name
record.ident = ident # type:ignore[attr-defined]
record.tid = None # type:ignore[attr-defined]
if sys.version_info >= (3, 8):
record.tid = current_thread.native_id
return record
@ -75,14 +83,76 @@ class ThreadedStreamHandler(logging.Handler):
terminator: ClassVar[str] = "\n"
_streams: Dict[int, types.ErrorStream]
_journal_stream_id: Optional[Tuple[int, int]]
_journal_socket: Optional[socket.socket]
def __init__(self) -> None:
super().__init__()
self._streams = {}
self._journal_stream_id = None
with contextlib.suppress(TypeError, ValueError):
dev, inode = os.environ.get("JOURNAL_STREAM", "").split(":", 1)
self._journal_stream_id = int(dev), int(inode)
self._journal_socket = None
if self._journal_stream_id and hasattr(socket, "AF_UNIX"):
journal_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
try:
journal_socket.connect("/run/systemd/journal/socket")
except OSError:
journal_socket.close()
else:
self._journal_socket = journal_socket
def _detect_journal(self, stream):
if not self._journal_stream_id:
return False
try:
stat = os.fstat(stream.fileno())
except Exception:
return False
return self._journal_stream_id == (stat.st_dev, stat.st_ino)
@staticmethod
def _encode_journal(data):
msg = b""
for key, value in data.items():
if key is None:
continue
key = key.encode()
value = str(value).encode()
if b"\n" in value:
msg += (key + b"\n" +
struct.pack("<Q", len(value)) + value + b"\n")
else:
msg += key + b"=" + value + b"\n"
return msg
def _emit_journal(self, record):
priority = {"DEBUG": 7,
"INFO": 6,
"WARNING": 4,
"ERROR": 3,
"CRITICAL": 2}.get(record.levelname, 4)
timestamp = time.strftime("%Y-%m-%dT%H:%M:%S.%%03dZ",
time.gmtime(record.created)) % record.msecs
data = {"PRIORITY": priority,
"TID": record.tid,
"SYSLOG_IDENTIFIER": record.name,
"SYSLOG_FACILITY": 1,
"SYSLOG_PID": record.process,
"SYSLOG_TIMESTAMP": timestamp,
"CODE_FILE": record.pathname,
"CODE_LINE": record.lineno,
"CODE_FUNC": record.funcName,
"MESSAGE": self.format(record)}
self._journal_socket.sendall(self._encode_journal(data))
def emit(self, record: logging.LogRecord) -> None:
try:
stream = self._streams.get(threading.get_ident(), sys.stderr)
if self._journal_socket and self._detect_journal(stream):
self._emit_journal(record)
return
msg = self.format(record)
stream.write(msg)
stream.write(self.terminator)