Store logs as dict on server, and send as json, rather than regex from str
Closes #321
This commit is contained in:
parent
330db19827
commit
6b67078129
3 changed files with 43 additions and 67 deletions
|
|
@ -17,7 +17,8 @@ from logging.handlers import RotatingFileHandler
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import JSONResponse, PlainTextResponse
|
||||
|
||||
from labthings_fastapi.logs import inject_invocation_id
|
||||
|
||||
|
|
@ -53,7 +54,7 @@ def configure_logging(log_folder: str, debug: bool = False) -> None:
|
|||
# Add OFM_HANDLER first so it can capture the error log if the
|
||||
# log file can't be accessed.
|
||||
ofm_format_str = "[%(asctime)s] [%(levelname)s] %(message)s"
|
||||
OFM_HANDLER.setFormatter(logging.Formatter(ofm_format_str))
|
||||
OFM_HANDLER.setFormatter(OFMLogFileFormatter(ofm_format_str))
|
||||
OFM_HANDLER.addFilter(UvicornAccessFilter())
|
||||
OFM_HANDLER.level = root_logger.level
|
||||
root_logger.addHandler(OFM_HANDLER)
|
||||
|
|
@ -95,7 +96,7 @@ def retrieve_log() -> PlainTextResponse:
|
|||
All logs, including ``uvicorn.access`` are logged to the OFM_LOG_FILE (see above)
|
||||
this is the best place to get logs about crashes.
|
||||
"""
|
||||
return PlainTextResponse(OFM_HANDLER.log_history)
|
||||
return JSONResponse(content=jsonable_encoder(OFM_HANDLER.log_history))
|
||||
|
||||
|
||||
def retrieve_log_from_file() -> PlainTextResponse:
|
||||
|
|
@ -157,21 +158,36 @@ class OFMHandler(logging.Handler):
|
|||
super().__init__(level=level)
|
||||
self._log: list[str] = []
|
||||
self._max_logs = max_logs
|
||||
# start at -1 so first record is 0
|
||||
self._running_counter = -1
|
||||
|
||||
def _as_record_dict(self, record: logging.LogRecord) -> dict:
|
||||
"""Format the record into the dictionary structure expected by the server."""
|
||||
msg = record.message
|
||||
self._running_counter += 1
|
||||
return {
|
||||
"timestamp": record.asctime,
|
||||
"level": record.levelname,
|
||||
"summary": msg.split("\n")[0],
|
||||
"message": msg,
|
||||
"sequence": self._running_counter,
|
||||
"expanded": False,
|
||||
}
|
||||
|
||||
def append_record(self, record: logging.LogRecord) -> None:
|
||||
"""Format message and append it to a list of records.
|
||||
|
||||
The built in formatter is used to format the record.
|
||||
"""Append the record as a dictionary to a log of records.
|
||||
|
||||
Any records in excess of the maximum number of logs are removed.
|
||||
"""
|
||||
self._log.append(self.format(record))
|
||||
self._log.append(self._as_record_dict(record))
|
||||
while len(self._log) > self._max_logs:
|
||||
self._log.pop(0)
|
||||
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
"""Emit will save the logged record to the log."""
|
||||
try:
|
||||
# Always format before trying to read or the attributes are not set.
|
||||
self.format(record)
|
||||
if record.levelno >= self.level:
|
||||
self.append_record(record)
|
||||
except RecursionError as e:
|
||||
|
|
@ -182,9 +198,9 @@ class OFMHandler(logging.Handler):
|
|||
self.handleError(record)
|
||||
|
||||
@property
|
||||
def log_history(self) -> str:
|
||||
def log_history(self) -> list[dict]:
|
||||
"""Return the log history up to the maximum number of logs."""
|
||||
return "\n".join(self._log)
|
||||
return list(reversed(self._log))
|
||||
|
||||
|
||||
class UvicornAccessFilter(logging.Filter):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue