Files
Medios-Macina/SYS/logger.py

105 lines
2.9 KiB
Python
Raw Normal View History

2025-11-25 20:09:33 -08:00
"""Unified logging utility for automatic file and function name tracking."""
import sys
import inspect
2025-12-11 12:47:30 -08:00
import threading
2025-11-25 20:09:33 -08:00
from pathlib import Path
_DEBUG_ENABLED = False
2025-12-11 12:47:30 -08:00
_thread_local = threading.local()
def set_thread_stream(stream):
"""Set a custom output stream for the current thread."""
_thread_local.stream = stream
def get_thread_stream():
"""Get the custom output stream for the current thread, if any."""
return getattr(_thread_local, 'stream', None)
2025-11-25 20:09:33 -08:00
def set_debug(enabled: bool) -> None:
"""Enable or disable debug logging."""
global _DEBUG_ENABLED
_DEBUG_ENABLED = enabled
2025-12-11 12:47:30 -08:00
def is_debug_enabled() -> bool:
"""Check if debug logging is enabled."""
return _DEBUG_ENABLED
2025-11-25 20:09:33 -08:00
def debug(*args, **kwargs) -> None:
"""Print debug message if debug logging is enabled.
Automatically prepends [filename.function_name] to all output.
"""
if not _DEBUG_ENABLED:
return
2025-12-11 12:47:30 -08:00
# Check if stderr has been redirected to /dev/null (quiet mode)
# If so, skip output to avoid queuing in background worker's capture
try:
stderr_name = getattr(sys.stderr, 'name', '')
if 'nul' in str(stderr_name).lower() or '/dev/null' in str(stderr_name):
return
except Exception:
pass
2025-11-25 20:09:33 -08:00
2025-12-11 12:47:30 -08:00
# Check for thread-local stream first
stream = get_thread_stream()
if stream:
kwargs['file'] = stream
2025-11-25 20:09:33 -08:00
# Set default to stderr for debug messages
2025-12-11 12:47:30 -08:00
elif 'file' not in kwargs:
2025-11-25 20:09:33 -08:00
kwargs['file'] = sys.stderr
# Prepend DEBUG label
args = ("DEBUG:", *args)
# Use the same logic as log()
log(*args, **kwargs)
def log(*args, **kwargs) -> None:
"""Print with automatic file.function prefix.
Automatically prepends [filename.function_name] to all output.
Defaults to stdout if not specified.
Example:
log("Upload started") # Output: [add_file.run] Upload started
"""
2025-12-05 03:42:57 -08:00
# When debug is disabled, suppress the automatic prefix for cleaner user-facing output.
add_prefix = _DEBUG_ENABLED
2025-11-25 20:09:33 -08:00
# Get the calling frame
frame = inspect.currentframe()
if frame is None:
print(*args, **kwargs)
return
caller_frame = frame.f_back
if caller_frame is None:
print(*args, **kwargs)
return
try:
# Get file name without extension
file_name = Path(caller_frame.f_code.co_filename).stem
# Get function name
func_name = caller_frame.f_code.co_name
2025-12-11 12:47:30 -08:00
# Check for thread-local stream first
stream = get_thread_stream()
if stream:
kwargs['file'] = stream
2025-11-25 20:09:33 -08:00
# Set default to stdout if not specified
2025-12-11 12:47:30 -08:00
elif 'file' not in kwargs:
2025-11-25 20:09:33 -08:00
kwargs['file'] = sys.stdout
2025-12-05 03:42:57 -08:00
if add_prefix:
prefix = f"[{file_name}.{func_name}]"
print(prefix, *args, **kwargs)
else:
print(*args, **kwargs)
2025-11-25 20:09:33 -08:00
finally:
del frame
del caller_frame