Added structured and wrapped logger #187
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 5s
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 5s
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
from contextvars import ContextVar
|
||||
from typing import Optional
|
||||
|
||||
from cpl.auth.auth_logger import AuthLogger
|
||||
from cpl.auth.schema._administration.auth_user import AuthUser
|
||||
|
||||
_user_context: ContextVar[Optional[AuthUser]] = ContextVar("user", default=None)
|
||||
|
||||
_logger = AuthLogger(__name__)
|
||||
|
||||
|
||||
def set_user(user_id: Optional[AuthUser]):
|
||||
_logger.trace("Setting user context", user_id)
|
||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
||||
from cpl.core.log.logger_abc import LoggerABC
|
||||
|
||||
logger = ServiceProviderABC.get_global_service(LoggerABC)
|
||||
logger.trace("Setting user context", user_id)
|
||||
_user_context.set(user_id)
|
||||
|
||||
|
||||
|
||||
@@ -2,3 +2,4 @@ from .logger import Logger
|
||||
from .logger_abc import LoggerABC
|
||||
from .log_level import LogLevel
|
||||
from .log_settings import LogSettings
|
||||
from .structured_logger import StructuredLogger
|
||||
|
||||
80
src/cpl-core/cpl/core/log/structured_logger.py
Normal file
80
src/cpl-core/cpl/core/log/structured_logger.py
Normal file
@@ -0,0 +1,80 @@
|
||||
import asyncio
|
||||
import importlib.util
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from cpl.core.log import Logger, LogLevel
|
||||
from cpl.core.typing import Source, Messages
|
||||
|
||||
|
||||
class StructuredLogger(Logger):
|
||||
|
||||
def __init__(self, source: Source, file_prefix: str = None):
|
||||
Logger.__init__(self, source, file_prefix)
|
||||
|
||||
@property
|
||||
def log_file(self):
|
||||
return f"logs/{self._file_prefix}_{datetime.now().strftime('%Y-%m-%d')}.jsonl"
|
||||
|
||||
def _log(self, level: LogLevel, *messages: Messages):
|
||||
try:
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||
formatted_message = self._format_message(level.value, timestamp, *messages)
|
||||
|
||||
self._write_log_to_file(level, formatted_message)
|
||||
self._write_to_console(level, formatted_message)
|
||||
except Exception as e:
|
||||
print(f"Error while logging: {e} -> {traceback.format_exc()}")
|
||||
|
||||
def _get_structured_message(self, level: str, timestamp: str, messages: str) -> str:
|
||||
structured_message = {
|
||||
"timestamp": timestamp,
|
||||
"level": level.upper(),
|
||||
"source": self._source,
|
||||
"messages": messages,
|
||||
}
|
||||
|
||||
self._enrich_message_with_request(structured_message)
|
||||
self._enrich_message_with_user(structured_message)
|
||||
|
||||
return str(structured_message)
|
||||
|
||||
@staticmethod
|
||||
def _enrich_message_with_request(message: dict):
|
||||
if importlib.util.find_spec("cpl.api") is None:
|
||||
return
|
||||
|
||||
from cpl.api.middleware.request import get_request
|
||||
from starlette.requests import Request
|
||||
|
||||
request = get_request()
|
||||
|
||||
if request is None:
|
||||
return
|
||||
|
||||
message["request"] = {
|
||||
"url": str(request.url),
|
||||
"method": request.method,
|
||||
"scope": request.scope,
|
||||
}
|
||||
if isinstance(request, Request) and request.scope == "http":
|
||||
request: Request = request # fix typing for IDEs
|
||||
|
||||
message["request"]["data"] = asyncio.create_task(request.body())
|
||||
|
||||
@staticmethod
|
||||
def _enrich_message_with_user(message: dict):
|
||||
if importlib.util.find_spec("cpl-auth") is None:
|
||||
return
|
||||
|
||||
from cpl.core.ctx import get_user
|
||||
|
||||
user = get_user()
|
||||
if user is None:
|
||||
return
|
||||
|
||||
message["user"] = {
|
||||
"id": str(user.id),
|
||||
"username": user.username,
|
||||
"email": user.email,
|
||||
}
|
||||
37
src/cpl-core/cpl/core/log/wrapped_logger.py
Normal file
37
src/cpl-core/cpl/core/log/wrapped_logger.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from cpl.core.log import LoggerABC, LogLevel
|
||||
from cpl.core.typing import Messages
|
||||
|
||||
|
||||
class WrappedLogger(LoggerABC):
|
||||
|
||||
def __init__(self, logger: LoggerABC):
|
||||
LoggerABC.__init__(self)
|
||||
assert isinstance(logger, LoggerABC), "logger must be an instance of LoggerABC"
|
||||
self._logger = logger
|
||||
|
||||
def set_level(self, level: LogLevel):
|
||||
self._logger.set_level(level)
|
||||
|
||||
def _format_message(self, level: str, timestamp, *messages: Messages) -> str:
|
||||
return self._logger._format_message(level, timestamp, *messages)
|
||||
|
||||
def header(self, string: str):
|
||||
self._logger.header(string)
|
||||
|
||||
def trace(self, *messages: Messages):
|
||||
self._logger.trace(*messages)
|
||||
|
||||
def debug(self, *messages: Messages):
|
||||
self._logger.debug(*messages)
|
||||
|
||||
def info(self, *messages: Messages):
|
||||
self._logger.info(*messages)
|
||||
|
||||
def warning(self, *messages: Messages):
|
||||
self._logger.warning(*messages)
|
||||
|
||||
def error(self, messages: str, e: Exception = None):
|
||||
self._logger.error(messages, e)
|
||||
|
||||
def fatal(self, messages: str, e: Exception = None):
|
||||
self._logger.fatal(messages, e)
|
||||
Reference in New Issue
Block a user