update: now logger.py based on loguru.

feature: now logs will output to file if set app.log_format to file. default path: logs/kohakuhub.log
This commit is contained in:
lxy
2025-10-24 23:02:02 +08:00
parent 88a2e3c328
commit 980c1462c1
7 changed files with 481 additions and 151 deletions

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ config.toml
docker-compose.yml
kohakuhub.conf
lint-reports/
/logs
# Byte-compiled / optimized / DLL files
__pycache__/

View File

@@ -73,3 +73,7 @@ download_keep_sessions_days = 30 # Keep sessions from last N days
debug_log_payloads = false # Log commit payloads (development only)
# Site identification
site_name = "KohakuHub" # Customizable site name (e.g., "MyCompany Hub")
# Log Settings
log_level = "INFO" # Low level logs will be filted (avaiable options: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" )
log_format = "terminal" # Output logs to "file" or "terminal" (maybe sql in future)
log_dir = "logs/" # Path to log file (if log_format is "file")

View File

@@ -24,6 +24,7 @@ dependencies = [
"fastapi",
"fsspec",
"httpx",
"loguru",
"numpy",
"peewee",
"psycopg2-binary",

View File

@@ -360,6 +360,9 @@ def generate_hub_api_service(config: dict) -> str:
- KOHAKU_HUB_LFS_KEEP_VERSIONS=5
- KOHAKU_HUB_LFS_AUTO_GC=true
- KOHAKU_HUB_AUTO_MIGRATE=true # Auto-confirm database migrations (required for Docker)
- KOHAKU_HUB_LOG_LEVEL=INFO
- KOHAKU_HUB_LOG_FORMAT=terminal
- KOHAKU_HUB_LOG_DIR=logs/
## ===== Auth & SMTP Configuration =====
- KOHAKU_HUB_REQUIRE_EMAIL_VERIFICATION=false

View File

@@ -149,6 +149,10 @@ class AppConfig(BaseModel):
]
# Site identification
site_name: str = "KohakuHub" # Configurable site name (e.g., "MyCompany Hub")
# Log settings
log_level: str = "INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
log_format: str = "file" # Output logs to "file" or "terminal" (maybe sql in future)
log_dir: str = "logs/" # Path to log file (if log_format is "file")
class Config(BaseModel):
@@ -423,6 +427,12 @@ def load_config(path: str = None) -> Config:
app_env["debug_log_payloads"] = (
os.environ["KOHAKU_HUB_DEBUG_LOG_PAYLOADS"].lower() == "true"
)
if "KOHAKU_HUB_LOG_LEVEL" in os.environ:
app_env["log_level"] = os.environ["KOHAKU_HUB_LOG_LEVEL"]
if "KOHAKU_HUB_LOG_FORMAT" in os.environ:
app_env["log_format"] = os.environ["KOHAKU_HUB_LOG_FORMAT"]
if "KOHAKU_HUB_LOG_DIR" in os.environ:
app_env["log_dir"] = os.environ["KOHAKU_HUB_LOG_DIR"]
if app_env:
config_from_env["app"] = app_env

View File

@@ -1,63 +1,56 @@
"""Custom logging module for KohakuHub with colored output and formatted tracebacks."""
"""Loguru-based logging implementation for KohakuHub."""
import os
import sys
import logging
import traceback as tb
from datetime import datetime
from loguru import logger
from enum import Enum
from typing import Optional
class Color:
"""ANSI color codes for terminal output."""
# Text colors
RESET = "\033[0m"
BLACK = "\033[30m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"
# Bright colors
BRIGHT_BLACK = "\033[90m"
BRIGHT_RED = "\033[91m"
BRIGHT_GREEN = "\033[92m"
BRIGHT_YELLOW = "\033[93m"
BRIGHT_BLUE = "\033[94m"
BRIGHT_MAGENTA = "\033[95m"
BRIGHT_CYAN = "\033[96m"
BRIGHT_WHITE = "\033[97m"
# Background colors
BG_BLACK = "\033[40m"
BG_RED = "\033[41m"
BG_GREEN = "\033[42m"
BG_YELLOW = "\033[43m"
BG_BLUE = "\033[44m"
# Text styles
BOLD = "\033[1m"
DIM = "\033[2m"
UNDERLINE = "\033[4m"
from kohakuhub.config import cfg
class LogLevel(Enum):
"""Log levels with associated colors."""
"""Log levels mapping to loguru levels."""
DEBUG = "DEBUG"
INFO = "INFO"
SUCCESS = "SUCCESS"
WARNING = "WARNING"
ERROR = "ERROR"
CRITICAL = "CRITICAL"
TRACE = "TRACE"
DEBUG = ("DEBUG", Color.BRIGHT_BLACK)
INFO = ("INFO", Color.BRIGHT_CYAN)
SUCCESS = ("SUCCESS", Color.BRIGHT_GREEN)
WARNING = ("WARNING", Color.BRIGHT_YELLOW)
ERROR = ("ERROR", Color.BRIGHT_RED)
CRITICAL = ("CRITICAL", Color.BG_RED + Color.WHITE + Color.BOLD)
class InterceptHandler(logging.Handler):
"""
Logger InterceptorRedirects standard library logs to Loguru.
"""
def emit(self, record: logging.LogRecord) -> None:
# Get Level Name and API name from LogRecord
try:
level = logger.level(record.levelname).name
api_name = record.name.upper()
if "UVICORN" in api_name:
api_name = "UVICORN"
except ValueError:
level = record.levelno
frame, depth = logging.currentframe(), 2
while frame.f_code.co_filename == logging.__file__:
frame = frame.f_back
depth += 1
logger.bind(api_name=api_name).opt(depth=depth, exception=record.exc_info).log(
level,
record.getMessage()
)
class Logger:
"""Custom logger with API name prefix and colored output."""
"""Loguru-based logger"""
def __init__(self, api_name: str = "APP"):
"""Initialize logger with API name.
@@ -66,74 +59,49 @@ class Logger:
api_name: Name of the API/module (e.g., "AUTH", "FILE", "LFS")
"""
self.api_name = api_name.upper()
def _get_timestamp(self) -> str:
"""Get current timestamp in HH:MM:SS format."""
return datetime.now().strftime("%H:%M:%S")
def _format_message(self, level: LogLevel, message: str) -> str:
"""Format log message with colors and structure.
Format: [LEVEL][API-NAME][Worker:PID][HH:MM:SS] message
Args:
level: Log level
message: Message to log
Returns:
Formatted colored string
"""
level_name, level_color = level.value
timestamp = self._get_timestamp()
worker_id = os.getpid() # Process ID identifies the worker
# Build formatted message
parts = [
f"{level_color}[{level_name}]{Color.RESET}",
f"{Color.BRIGHT_MAGENTA}[{self.api_name}]{Color.RESET}",
f"{Color.BLUE}[W:{worker_id}]{Color.RESET}",
f"{Color.BRIGHT_BLACK}[{timestamp}]{Color.RESET}",
message,
]
return " ".join(parts)
self._logger = logger.bind(api_name=api_name)
def _log(self, level: LogLevel, message: str):
"""Internal log method."""
formatted = self._format_message(level, message)
print(
formatted,
file=(
sys.stderr
if level in [LogLevel.ERROR, LogLevel.CRITICAL]
else sys.stdout
),
)
match level:
case LogLevel.DEBUG:
self._logger.debug(message)
case LogLevel.INFO:
self._logger.info(message)
case LogLevel.SUCCESS:
self._logger.success(message)
case LogLevel.WARNING:
self._logger.warning(message)
case LogLevel.ERROR:
self._logger.error(message)
case LogLevel.CRITICAL:
self._logger.critical(message)
case LogLevel.TRACE:
self._logger.trace(message)
case _:
self._logger.log(level, message)
def debug(self, message: str):
"""Log debug message."""
self._log(LogLevel.DEBUG, message)
def info(self, message: str):
"""Log info message."""
self._log(LogLevel.INFO, message)
def success(self, message: str):
"""Log success message."""
self._log(LogLevel.SUCCESS, message)
def warning(self, message: str):
"""Log warning message."""
self._log(LogLevel.WARNING, message)
def error(self, message: str):
"""Log error message."""
self._log(LogLevel.ERROR, message)
def critical(self, message: str):
"""Log critical error message."""
self._log(LogLevel.CRITICAL, message)
def trace(self, message: str):
self._log(LogLevel.TRACE, message)
def exception(self, message: str, exc: Optional[Exception] = None):
"""Log exception with formatted traceback.
@@ -168,9 +136,9 @@ class Logger:
frames = tb.extract_tb(exc_tb)
# Print header
print(f"\n{Color.BRIGHT_RED}{'' * 100}{Color.RESET}", file=sys.stderr)
print(f"{Color.BRIGHT_RED}{Color.BOLD}TRACEBACK{Color.RESET}", file=sys.stderr)
print(f"{Color.BRIGHT_RED}{'' * 100}{Color.RESET}\n", file=sys.stderr)
self.trace(f"{'=' * 50}")
self.trace("TRACEBACK")
self.trace(f"{'=' * 50}")
# Print stack frames as tables
for i, frame in enumerate(frames, 1):
@@ -179,7 +147,7 @@ class Logger:
# Print final error table
self._print_error_table(exc_type, exc_value, frames[-1] if frames else None)
print(f"{Color.BRIGHT_RED}{'' * 100}{Color.RESET}\n", file=sys.stderr)
self.trace(f"{'=' * 50}")
def _print_frame_table(self, index: int, frame: tb.FrameSummary, is_last: bool):
"""Print single stack frame as a table.
@@ -189,39 +157,27 @@ class Logger:
frame: Frame summary
is_last: Whether this is the last frame (error location)
"""
color = Color.BRIGHT_RED if is_last else Color.BRIGHT_BLACK
# loguru only renders colors in format
# color = "ff0000" if is_last else "09D0EF"
# Table header
print(
f"{color}┌─ Frame #{index} {' (ERROR HERE)' if is_last else ''}",
file=sys.stderr,
)
self.trace(f"┌─ Frame #{index} {' (ERROR HERE)' if is_last else ''}")
# File
print(
f"{color}{Color.CYAN}File:{Color.RESET} {frame.filename}", file=sys.stderr
)
self.trace(f"│ File: {frame.filename}")
# Line number
print(
f"{color}{Color.YELLOW}Line:{Color.RESET} {frame.lineno}", file=sys.stderr
)
self.trace(f"│ Line: {frame.lineno}")
# Function/Code
if frame.name:
print(
f"{color}{Color.GREEN}In:{Color.RESET} {frame.name}()",
file=sys.stderr,
)
self.trace(f"│ In:{frame.name}()")
if frame.line:
# Show the actual code line
print(
f"{color}{Color.BRIGHT_WHITE}Code:{Color.RESET} {frame.line.strip()}",
file=sys.stderr,
)
self.trace(f"│ Code: {frame.line.strip()}")
print(f"{color}{'' * 99}{Color.RESET}\n", file=sys.stderr)
self.trace(f"{'' * 99}")
def _print_error_table(
self, exc_type, exc_value, last_frame: Optional[tb.FrameSummary]
@@ -234,46 +190,83 @@ class Logger:
last_frame: Last stack frame (error location)
"""
# Table header
print(
f"{Color.BG_RED}{Color.WHITE}{Color.BOLD} EXCEPTION DETAILS {Color.RESET}",
file=sys.stderr,
)
print(f"{Color.BRIGHT_RED}{'' * 99}", file=sys.stderr)
self.trace(" EXCEPTION DETAILS ")
self.trace(f"{'' * 99}")
# Error type
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Type:{Color.RESET} {Color.BRIGHT_RED}{exc_type.__name__}{Color.RESET}",
file=sys.stderr,
)
self.trace(f"│ Type: {exc_type.__name__}")
# Error message
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Message:{Color.RESET} {Color.WHITE}{str(exc_value)}{Color.RESET}",
file=sys.stderr,
)
self.trace(f"│ Message: {str(exc_value)}")
if last_frame:
# Error location
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Location:{Color.RESET} {Color.CYAN}{last_frame.filename}{Color.RESET}:{Color.YELLOW}{last_frame.lineno}{Color.RESET}",
file=sys.stderr,
)
self.trace(f"│ Location: {last_frame.filename}:{last_frame.lineno}")
if last_frame.line:
# Code that caused error
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Code:{Color.RESET} {Color.BRIGHT_WHITE}{last_frame.line.strip()}{Color.RESET}",
file=sys.stderr,
)
self.trace(f"│ Code: {last_frame.line.strip()}")
print(f"{Color.BRIGHT_RED}{'' * 99}{Color.RESET}", file=sys.stderr)
self.trace(f"{'' * 99}")
class LoggerFactory:
"""Factory to create loggers with different API names."""
"""Factory to create loguru loggers."""
_loggers = {}
@classmethod
def init_logger_settings(cls):
log_dir = cfg.app.log_dir
if not os.path.exists(log_dir):
os.mkdir(log_dir)
log_path = os.path.join(log_dir, "kohakuhub.log")
log_level = cfg.app.log_level.upper()
log_format = cfg.app.log_format.lower()
# Configure loguru format to match existing style
# Remove default handler
logger.remove()
""" About Color codes for loguru.
Actually, it also support 8bit , Hex , RGB color codes.
See https://loguru.readthedocs.io/en/stable/api/logger.html#color
"""
logger.level("DEBUG", color="<fg #222024>")
logger.level("INFO", color="<fg #09D0EF>")
logger.level("SUCCESS", color="<fg #66FF00>")
logger.level("WARNING", color="<fg #FFEB2A>")
logger.level("ERROR", color="<fg #FF160C>")
logger.level("CRITICAL", color="<white><bg #FF160C><bold>")
logger.level("TRACE", color="<fg #FF160C>")
"""Add Defualt Terminal logger"""
logger.add(
sys.stderr,
format="<level>[{level}]</level><fg #FF00CD>[{extra[api_name]}]</fg #FF00CD><blue>[W:{process}]</blue>[{time:HH:mm:ss}] {message}",
level=log_level,
colorize=True
)
"""Add File logger"""
if log_format == "file":
logger.add(
log_path,
format="<level>[{level}]</level><fg #FF00CD>[{extra[api_name]}]</fg #FF00CD><blue>[W:{process}]</blue>[{time:HH:mm:ss}] {message}",
level=log_level,
rotation="2 MB"
)
logger_name_list = [name for name in logging.root.manager.loggerDict]
for logger_name in logger_name_list:
_logger = logging.getLogger(logger_name)
_logger.setLevel(logging.INFO)
_logger.handlers = []
if '.' not in logger_name:
_logger.addHandler(InterceptHandler())
@classmethod
def get_logger(cls, api_name: str) -> Logger:
"""Get or create logger for API name.
@@ -289,22 +282,16 @@ class LoggerFactory:
return cls._loggers[api_name]
# Convenience function for getting logger
def init_logger_settings():
"""Initialize logger settings."""
LoggerFactory.init_logger_settings()
def get_logger(api_name: str) -> Logger:
"""Get logger for specific API.
Usage:
logger = get_logger("AUTH")
logger.info("User logged in")
logger.error("Login failed")
try:
# ... code ...
except Exception as e:
logger.exception("Failed to process request", e)
Args:
api_name: Name of the API/module (e.g., "AUTH", "FILE", "LFS")
api_name: Name of the API/module
Returns:
Logger instance
@@ -312,6 +299,7 @@ def get_logger(api_name: str) -> Logger:
return LoggerFactory.get_logger(api_name)
init_logger_settings()
# Pre-create common loggers
logger_auth = get_logger("AUTH")
logger_file = get_logger("FILE")

323
src/kohakuhub/old_logger.py Normal file
View File

@@ -0,0 +1,323 @@
"""Custom logging module for KohakuHub with colored output and formatted tracebacks."""
import os
import sys
import traceback as tb
from datetime import datetime
from enum import Enum
from typing import Optional
class Color:
"""ANSI color codes for terminal output."""
# Text colors
RESET = "\033[0m"
BLACK = "\033[30m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"
# Bright colors
BRIGHT_BLACK = "\033[90m"
BRIGHT_RED = "\033[91m"
BRIGHT_GREEN = "\033[92m"
BRIGHT_YELLOW = "\033[93m"
BRIGHT_BLUE = "\033[94m"
BRIGHT_MAGENTA = "\033[95m"
BRIGHT_CYAN = "\033[96m"
BRIGHT_WHITE = "\033[97m"
# Background colors
BG_BLACK = "\033[40m"
BG_RED = "\033[41m"
BG_GREEN = "\033[42m"
BG_YELLOW = "\033[43m"
BG_BLUE = "\033[44m"
# Text styles
BOLD = "\033[1m"
DIM = "\033[2m"
UNDERLINE = "\033[4m"
class LogLevel(Enum):
"""Log levels with associated colors."""
DEBUG = ("DEBUG", Color.BRIGHT_BLACK)
INFO = ("INFO", Color.BRIGHT_CYAN)
SUCCESS = ("SUCCESS", Color.BRIGHT_GREEN)
WARNING = ("WARNING", Color.BRIGHT_YELLOW)
ERROR = ("ERROR", Color.BRIGHT_RED)
CRITICAL = ("CRITICAL", Color.BG_RED + Color.WHITE + Color.BOLD)
class Logger:
"""Custom logger with API name prefix and colored output."""
def __init__(self, api_name: str = "APP"):
"""Initialize logger with API name.
Args:
api_name: Name of the API/module (e.g., "AUTH", "FILE", "LFS")
"""
self.api_name = api_name.upper()
def _get_timestamp(self) -> str:
"""Get current timestamp in HH:MM:SS format."""
return datetime.now().strftime("%H:%M:%S")
def _format_message(self, level: LogLevel, message: str) -> str:
"""Format log message with colors and structure.
Format: [LEVEL][API-NAME][Worker:PID][HH:MM:SS] message
Args:
level: Log level
message: Message to log
Returns:
Formatted colored string
"""
level_name, level_color = level.value
timestamp = self._get_timestamp()
worker_id = os.getpid() # Process ID identifies the worker
# Build formatted message
parts = [
f"{level_color}[{level_name}]{Color.RESET}",
f"{Color.BRIGHT_MAGENTA}[{self.api_name}]{Color.RESET}",
f"{Color.BLUE}[W:{worker_id}]{Color.RESET}",
f"{Color.BRIGHT_BLACK}[{timestamp}]{Color.RESET}",
message,
]
return " ".join(parts)
def _log(self, level: LogLevel, message: str):
"""Internal log method."""
formatted = self._format_message(level, message)
print(
formatted,
file=(
sys.stderr
if level in [LogLevel.ERROR, LogLevel.CRITICAL]
else sys.stdout
),
)
def debug(self, message: str):
"""Log debug message."""
self._log(LogLevel.DEBUG, message)
def info(self, message: str):
"""Log info message."""
self._log(LogLevel.INFO, message)
def success(self, message: str):
"""Log success message."""
self._log(LogLevel.SUCCESS, message)
def warning(self, message: str):
"""Log warning message."""
self._log(LogLevel.WARNING, message)
def error(self, message: str):
"""Log error message."""
self._log(LogLevel.ERROR, message)
def critical(self, message: str):
"""Log critical error message."""
self._log(LogLevel.CRITICAL, message)
def exception(self, message: str, exc: Optional[Exception] = None):
"""Log exception with formatted traceback.
Args:
message: Error message
exc: Exception object (if None, uses sys.exc_info())
"""
self.error(message)
self._print_formatted_traceback(exc)
def _print_formatted_traceback(self, exc: Optional[Exception] = None):
"""Print formatted traceback as tables.
Format:
1. Stack trace table for each frame
2. Final error table with actual error details
Args:
exc: Exception object (if None, uses sys.exc_info())
"""
if exc is None:
exc_type, exc_value, exc_tb = sys.exc_info()
else:
exc_type = type(exc)
exc_value = exc
exc_tb = exc.__traceback__
if exc_tb is None:
return
# Extract traceback frames
frames = tb.extract_tb(exc_tb)
# Print header
print(f"\n{Color.BRIGHT_RED}{'' * 100}{Color.RESET}", file=sys.stderr)
print(f"{Color.BRIGHT_RED}{Color.BOLD}TRACEBACK{Color.RESET}", file=sys.stderr)
print(f"{Color.BRIGHT_RED}{'' * 100}{Color.RESET}\n", file=sys.stderr)
# Print stack frames as tables
for i, frame in enumerate(frames, 1):
self._print_frame_table(i, frame, is_last=(i == len(frames)))
# Print final error table
self._print_error_table(exc_type, exc_value, frames[-1] if frames else None)
print(f"{Color.BRIGHT_RED}{'' * 100}{Color.RESET}\n", file=sys.stderr)
def _print_frame_table(self, index: int, frame: tb.FrameSummary, is_last: bool):
"""Print single stack frame as a table.
Args:
index: Frame index
frame: Frame summary
is_last: Whether this is the last frame (error location)
"""
color = Color.BRIGHT_RED if is_last else Color.BRIGHT_BLACK
# Table header
print(
f"{color}┌─ Frame #{index} {' (ERROR HERE)' if is_last else ''}",
file=sys.stderr,
)
# File
print(
f"{color}{Color.CYAN}File:{Color.RESET} {frame.filename}", file=sys.stderr
)
# Line number
print(
f"{color}{Color.YELLOW}Line:{Color.RESET} {frame.lineno}", file=sys.stderr
)
# Function/Code
if frame.name:
print(
f"{color}{Color.GREEN}In:{Color.RESET} {frame.name}()",
file=sys.stderr,
)
if frame.line:
# Show the actual code line
print(
f"{color}{Color.BRIGHT_WHITE}Code:{Color.RESET} {frame.line.strip()}",
file=sys.stderr,
)
print(f"{color}{'' * 99}{Color.RESET}\n", file=sys.stderr)
def _print_error_table(
self, exc_type, exc_value, last_frame: Optional[tb.FrameSummary]
):
"""Print final error details as a table.
Args:
exc_type: Exception type
exc_value: Exception value
last_frame: Last stack frame (error location)
"""
# Table header
print(
f"{Color.BG_RED}{Color.WHITE}{Color.BOLD} EXCEPTION DETAILS {Color.RESET}",
file=sys.stderr,
)
print(f"{Color.BRIGHT_RED}{'' * 99}", file=sys.stderr)
# Error type
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Type:{Color.RESET} {Color.BRIGHT_RED}{exc_type.__name__}{Color.RESET}",
file=sys.stderr,
)
# Error message
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Message:{Color.RESET} {Color.WHITE}{str(exc_value)}{Color.RESET}",
file=sys.stderr,
)
if last_frame:
# Error location
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Location:{Color.RESET} {Color.CYAN}{last_frame.filename}{Color.RESET}:{Color.YELLOW}{last_frame.lineno}{Color.RESET}",
file=sys.stderr,
)
if last_frame.line:
# Code that caused error
print(
f"{Color.BRIGHT_RED}{Color.BOLD}Code:{Color.RESET} {Color.BRIGHT_WHITE}{last_frame.line.strip()}{Color.RESET}",
file=sys.stderr,
)
print(f"{Color.BRIGHT_RED}{'' * 99}{Color.RESET}", file=sys.stderr)
class LoggerFactory:
"""Factory to create loggers with different API names."""
_loggers = {}
@classmethod
def get_logger(cls, api_name: str) -> Logger:
"""Get or create logger for API name.
Args:
api_name: Name of the API/module
Returns:
Logger instance
"""
if api_name not in cls._loggers:
cls._loggers[api_name] = Logger(api_name)
return cls._loggers[api_name]
# Convenience function for getting logger
def get_logger(api_name: str) -> Logger:
"""Get logger for specific API.
Usage:
logger = get_logger("AUTH")
logger.info("User logged in")
logger.error("Login failed")
try:
# ... code ...
except Exception as e:
logger.exception("Failed to process request", e)
Args:
api_name: Name of the API/module (e.g., "AUTH", "FILE", "LFS")
Returns:
Logger instance
"""
return LoggerFactory.get_logger(api_name)
# Pre-create common loggers
logger_auth = get_logger("AUTH")
logger_file = get_logger("FILE")
logger_lfs = get_logger("LFS")
logger_repo = get_logger("REPO")
logger_org = get_logger("ORG")
logger_settings = get_logger("SETTINGS")
logger_api = get_logger("API")
logger_db = get_logger("DB")