File size: 2,516 Bytes
0d32769
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cfc00ce
0d32769
 
 
 
 
 
 
 
 
cfc00ce
0d32769
 
 
 
 
 
 
 
 
 
cfc00ce
 
 
433e26f
cfc00ce
 
 
 
 
 
 
 
 
 
 
 
0d32769
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""Centralized logging configuration for LandmarkDiff.

Usage:
    from landmarkdiff.log import get_logger
    logger = get_logger(__name__)
    logger.info("Training started")

Configure globally:
    from landmarkdiff.log import setup_logging
    setup_logging(level="DEBUG")  # affects all LandmarkDiff loggers
"""

from __future__ import annotations

import logging
import sys

_CONFIGURED = False

# Default format
LOG_FORMAT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"


def setup_logging(
    level: str | int = "INFO",
    fmt: str | None = None,
    stream: object = None,
    log_file: str | None = None,
) -> None:
    """Configure logging for the landmarkdiff package.

    Call once at application startup. Subsequent calls update the level.

    Args:
        level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
        fmt: Custom format string. None uses the default.
        stream: Output stream. None defaults to stderr.
        log_file: Optional file path for logging to a file.
    """
    global _CONFIGURED

    if isinstance(level, str):
        level = getattr(logging, level.upper(), logging.INFO)

    root_logger = logging.getLogger("landmarkdiff")
    root_logger.setLevel(level)

    if not _CONFIGURED:
        formatter = logging.Formatter(
            fmt or LOG_FORMAT,
            datefmt=LOG_DATE_FORMAT,
        )

        # Add stream handler
        stream_handler = logging.StreamHandler(stream or sys.stderr)
        stream_handler.setFormatter(formatter)
        root_logger.addHandler(stream_handler)

        # Add file handler if log_file is specified
        if log_file:
            file_handler = logging.FileHandler(log_file)
            file_handler.setFormatter(formatter)
            root_logger.addHandler(file_handler)

        # Prevent propagation to root logger to avoid duplicate messages
        root_logger.propagate = False
        _CONFIGURED = True
    else:
        # Just update the level
        root_logger.setLevel(level)


def get_logger(name: str) -> logging.Logger:
    """Get a logger for a LandmarkDiff module.

    The returned logger is a child of the 'landmarkdiff' root logger,
    so setup_logging() controls its level and formatting.

    Args:
        name: Module name (typically __name__).

    Returns:
        Configured logging.Logger instance.
    """
    # Ensure base configuration exists
    if not _CONFIGURED:
        setup_logging()

    return logging.getLogger(name)