emb-explorer-demo / shared /utils /logging_config.py
netzhang's picture
Deploy precalculated app (manual, from demo branch)
51545af verified
Raw
History Blame Contribute Delete
3.08 kB
"""
Centralized logging configuration for emb-explorer.
Usage:
from shared.utils.logging_config import get_logger
logger = get_logger(__name__)
logger.info("Message")
"""
import logging
import os
import sys
from typing import Optional
# Module-level flag to track if logging has been configured
_logging_configured = False
# Default log directory (relative to working directory)
_LOG_DIR = os.environ.get("EMB_EXPLORER_LOG_DIR", "logs")
_LOG_FILE = "emb_explorer.log"
def configure_logging(
level: int = logging.INFO,
log_format: Optional[str] = None,
log_to_file: bool = True,
):
"""
Configure the root logger for the application.
Args:
level: Logging level (default: INFO)
log_format: Custom log format string (optional)
log_to_file: Whether to also write logs to a file (default: True)
"""
global _logging_configured
if _logging_configured:
return
if log_format is None:
log_format = (
"[%(asctime)s] %(levelname)s "
"[%(name)s.%(funcName)s:%(lineno)d] %(message)s"
)
# Configure root logger
root_logger = logging.getLogger()
root_logger.setLevel(level)
# Remove existing handlers to avoid duplicates
for handler in root_logger.handlers[:]:
root_logger.removeHandler(handler)
formatter = logging.Formatter(log_format, datefmt="%Y-%m-%d %H:%M:%S")
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_handler.setFormatter(formatter)
root_logger.addHandler(console_handler)
# File handler (append mode, rotates implicitly by date via log dir)
file_handler = None
if log_to_file:
try:
os.makedirs(_LOG_DIR, exist_ok=True)
file_handler = logging.FileHandler(
os.path.join(_LOG_DIR, _LOG_FILE), mode="a", encoding="utf-8"
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
root_logger.addHandler(file_handler)
except OSError:
# Non-fatal: skip file logging if directory can't be created
pass
# Prevent duplicate messages from Streamlit's root logger handler.
# Our app loggers (shared.*, apps.*) get their own handlers and don't
# propagate to root, avoiding double output from Streamlit's handler.
for prefix in ("shared", "apps"):
pkg_logger = logging.getLogger(prefix)
pkg_logger.propagate = False
if not pkg_logger.handlers:
pkg_logger.addHandler(console_handler)
if file_handler:
pkg_logger.addHandler(file_handler)
_logging_configured = True
def get_logger(name: str) -> logging.Logger:
"""
Get a logger instance for the given module name.
Automatically configures logging if not already done.
Args:
name: Logger name (typically __name__)
Returns:
Logger instance
"""
configure_logging()
return logging.getLogger(name)