Skip to content

Commit

Permalink
add JSON logging support to logging configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
mdekstrand committed Jan 6, 2025
1 parent ff4826e commit ab3e2f0
Showing 1 changed file with 34 additions and 5 deletions.
39 changes: 34 additions & 5 deletions lenskit/lenskit/logging/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import logging
import os
import re
import sys
import warnings
from pathlib import Path
from typing import Literal, TypeAlias

import structlog

Expand All @@ -24,6 +26,7 @@
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.MaybeTimeStamper(),
]
LogFormat: TypeAlias = Literal["json", "logfmt", "text"]

_active_config: LoggingConfig | None = None

Expand Down Expand Up @@ -64,8 +67,10 @@ class LoggingConfig: # pragma: nocover
"""

level: int = logging.INFO
stream_json: bool = False
file: Path | None = None
file_level: int | None = None
file_format: LogFormat = "json"

def __init__(self):
# initialize configuration from environment variables
Expand All @@ -85,6 +90,12 @@ def effective_level(self) -> int:
else:
return self.level

def term_json(self, flag: bool = True):
"""
Configure logging to stream JSON lines to the stderr (useful for web services).
"""
self.stream_json = flag

def set_verbose(self, verbose: bool | int = True):
"""
Enable verbose logging.
Expand All @@ -108,12 +119,15 @@ def set_verbose(self, verbose: bool | int = True):
else:
self.level = logging.INFO

def log_file(self, path: os.PathLike[str], level: int | None = None):
def log_file(
self, path: os.PathLike[str], level: int | None = None, format: LogFormat = "json"
):
"""
Configure a log file.
"""
self.file = Path(path)
self.file_level = level
self.file_format = format

def apply(self):
"""
Expand All @@ -123,8 +137,15 @@ def apply(self):

setup_console()
root = logging.getLogger()
term = ConsoleHandler()
term.setLevel(self.level)

if self.stream_json:
term = logging.StreamHandler(sys.stderr)
term.setLevel(self.level)
proc_fmt = structlog.processors.JSONRenderer()
else:
term = ConsoleHandler()
term.setLevel(self.level)
proc_fmt = structlog.dev.ConsoleRenderer(colors=term.supports_color)

eff_lvl = self.effective_level
structlog.configure(
Expand All @@ -136,7 +157,7 @@ def apply(self):
processors=[
remove_internal,
format_timestamp,
structlog.dev.ConsoleRenderer(colors=term.supports_color),
proc_fmt,
],
foreign_pre_chain=CORE_PROCESSORS,
)
Expand All @@ -147,11 +168,19 @@ def apply(self):
if self.file:
file_level = self.file_level if self.file_level is not None else self.level
file = logging.FileHandler(self.file, mode="w")

if self.file_format == "json":
proc_fmt = structlog.processors.JSONRenderer()
elif self.file_format == "logfmt":
proc_fmt = structlog.processors.LogfmtRenderer(key_order=["event", "timestamp"])
else:
proc_fmt = structlog.processors.KeyValueRenderer(key_order=["event", "timestamp"])

ffmt = structlog.stdlib.ProcessorFormatter(
processors=[
remove_internal,
structlog.processors.ExceptionPrettyPrinter(),
structlog.processors.JSONRenderer(),
proc_fmt,
],
foreign_pre_chain=CORE_PROCESSORS,
)
Expand Down

0 comments on commit ab3e2f0

Please sign in to comment.