Kacemath's picture
Update
b43bf80
import functools
import logging
import sys
import os
from dotenv import load_dotenv
from uvicorn.logging import ColourizedFormatter
from typing import Any, Callable
load_dotenv()
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()
# Mapping of string levels to logging constants
LOG_LEVEL_MAPPING = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL,
}
# Set the actual logging level
LOG_LEVEL = LOG_LEVEL_MAPPING.get(LOG_LEVEL, logging.INFO) # Default to INFO if invalid
# Custom colorized formatter to apply colors specifically to log levels
class CustomColourizedFormatter(ColourizedFormatter):
def format(self, record: logging.LogRecord) -> str:
# Define color mappings for different log levels
level_color_map = {
"DEBUG": "\033[34m", # Blue
"INFO": "\033[32m", # Green
"WARNING": "\033[33m", # Yellow
"ERROR": "\033[31m", # Red
"CRITICAL": "\033[41m", # Red background
}
# Reset color
reset = "\033[0m"
# Apply color to the log level name
record.levelname = (
f"{level_color_map.get(record.levelname, '')}{record.levelname}{reset}"
)
# Format the log message using the parent class's format method
return super().format(record)
def get_logger(name: str) -> logging.Logger:
"""Creates a logger object that reads its log level from .env.
Args:
name (str): name given to the logger
Returns:
logging.Logger: logger object to be used for logging
"""
# Create logger
logger = logging.getLogger(name)
logger.setLevel(LOG_LEVEL) # Set level based on .env file
# Prevent adding multiple handlers if already exists
if not logger.hasHandlers():
# Create console handler
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(LOG_LEVEL) # Set handler level
# Create a custom formatter with colored log levels
formatter = CustomColourizedFormatter(
"{asctime} | {levelname:<8} | {message}",
style="{",
datefmt="%Y-%m-%d %H:%M:%S",
use_colors=True,
)
# Add formatter to the handler
ch.setFormatter(formatter)
# Add handler to the logger
logger.addHandler(ch)
return logger
# Logger decorator implementation
def log_function_call(logger: logging.Logger) -> Callable:
"""A decorator that logs the function calls and results.
Args:
logger (logging.Logger): The logger instance to use for logging.
Returns:
Callable: A wrapper function that logs the execution details.
"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs) -> Any:
# Log the function call with arguments
logger.debug(
f"Calling {func.__name__} with args: {args} and kwargs: {kwargs}"
)
result = func(*args, **kwargs)
# Log the function result
logger.debug(f"{func.__name__} returned {result}")
return result
return wrapper
return decorator
logger = logging.getLogger(__name__)