almost ready
This commit is contained in:
47
worker/blworker/log.py
Normal file
47
worker/blworker/log.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Stdlib logging setup — one stream handler on stdout, human or JSON.
|
||||
|
||||
Workers used to print() everything; that gives no levels, no timestamps, and nothing
|
||||
Loki can parse. Default is a compact human format for local runs; set LOG_JSON=1 in the
|
||||
container so Grafana Alloy -> Loki gets structured fields (ts, level, logger, msg) plus
|
||||
any `extra=` keys a call site attaches.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
|
||||
# logging.LogRecord built-ins we don't want to echo into a JSON line as "extra" fields.
|
||||
_RESERVED = set(
|
||||
logging.makeLogRecord({}).__dict__
|
||||
) | {"message", "asctime", "taskName"}
|
||||
|
||||
|
||||
class _JsonFormatter(logging.Formatter):
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
payload = {
|
||||
"ts": self.formatTime(record, "%Y-%m-%dT%H:%M:%S%z"),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"msg": record.getMessage(),
|
||||
}
|
||||
for key, value in record.__dict__.items():
|
||||
if key not in _RESERVED and not key.startswith("_"):
|
||||
payload[key] = value
|
||||
if record.exc_info:
|
||||
payload["exc"] = self.formatException(record.exc_info)
|
||||
return json.dumps(payload, default=str)
|
||||
|
||||
|
||||
def configure(level: str = "INFO", json_logs: bool = False) -> None:
|
||||
"""Install a single stdout handler on the root logger (idempotent)."""
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
if json_logs:
|
||||
handler.setFormatter(_JsonFormatter())
|
||||
else:
|
||||
handler.setFormatter(
|
||||
logging.Formatter("%(asctime)s %(levelname)-5s %(name)s | %(message)s", "%H:%M:%S")
|
||||
)
|
||||
root = logging.getLogger()
|
||||
root.handlers.clear()
|
||||
root.addHandler(handler)
|
||||
root.setLevel(level)
|
||||
Reference in New Issue
Block a user