ds_common_logger_py_lib ======================= .. py:module:: ds_common_logger_py_lib .. autoapi-nested-parse:: **File:** ``__init__.py`` **Region:** ``ds_common_logger_py_lib`` Description ----------- Package entrypoint that exposes the public API (``Logger``, ``LoggerFilter``) and the installed package version (``__version__``). .. rubric:: Example >>> from ds_common_logger_py_lib import Logger >>> import logging >>> >>> Logger.configure( ... prefix="Application", ... level=logging.DEBUG ... ) >>> logger = Logger.get_logger(__name__) >>> >>> logger.info("Hello from ds_common_logger_py_lib") [2024-01-15T10:30:45][__main__][INFO][__init__.py:16]: Hello from ds_common_logger_py_lib Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/ds_common_logger_py_lib/core/index /autoapi/ds_common_logger_py_lib/formatter/index Attributes ---------- .. autoapisummary:: ds_common_logger_py_lib.__version__ Classes ------- .. autoapisummary:: ds_common_logger_py_lib.Logger ds_common_logger_py_lib.LoggerFilter Package Contents ---------------- .. py:class:: Logger Logger class for the application with static methods only. Configure the logger using Logger.configure() before using Logger.get_logger(). The default format can be customized by calling set_log_format() or by passing a format_string to configure(). .. rubric:: Example >>> Logger.configure(level=logging.DEBUG) >>> logger = Logger.get_logger(__name__) >>> logger.info("Test message") [2024-01-15T10:30:45][__main__][INFO][core.py:59]: Test message >>> >>> Logger.set_log_format("%(levelname)s: %(message)s") >>> logger.info("Formatted message") INFO: Formatted message >>> >>> Logger.configure(level=logging.INFO, handlers=[logging.FileHandler("app.log")]) >>> Logger.configure(level=logging.DEBUG, force=True) .. py:attribute:: DEFAULT_FORMAT :value: '[%(asctime)s][%(levelname)s][%(name)s][%(filename)s:%(lineno)d]: %(message)s' .. py:attribute:: DEFAULT_FORMAT_WITH_PREFIX :value: '[%(asctime)s][%(levelname)s][{prefix}][%(name)s][%(filename)s:%(lineno)d]: %(message)s' .. py:attribute:: DEFAULT_DATE_FORMAT :value: '%Y-%m-%dT%H:%M:%S' .. py:attribute:: _configured :type: bool :value: False .. py:attribute:: _prefix :type: str :value: '' .. py:attribute:: _format_string :type: str | None :value: '[%(asctime)s][%(levelname)s][{prefix}][%(name)s][%(filename)s:%(lineno)d]: %(message)s' .. py:attribute:: _date_format :type: str | None :value: '%Y-%m-%dT%H:%M:%S' .. py:attribute:: _level :type: int :value: 20 .. py:attribute:: _handlers :type: ClassVar[list[logging.Handler]] :value: [] .. py:attribute:: _default_handler :type: logging.Handler | None :value: None .. py:attribute:: _managed_loggers :type: ClassVar[set[str]] .. py:attribute:: _logger_levels :type: ClassVar[dict[str, int]] .. py:attribute:: _filter :type: ds_common_logger_py_lib.formatter.LoggerFilter .. py:method:: configure(prefix: str = '', format_string: str = DEFAULT_FORMAT_WITH_PREFIX, date_format: str = DEFAULT_DATE_FORMAT, level: int = logging.INFO, handlers: list[logging.Handler] | None = None, default_handler: logging.Handler | None = None, allowed_prefixes: set[str] | None = None, logger_levels: dict[str, int] | None = None, force: bool = False) -> None :staticmethod: Configure application-level logging settings. This should be called once at application startup, before any packages start using the logger. The configuration will be applied to all loggers created via Logger.get_logger(). :param prefix: Prefix to inject into log messages (via {prefix} in format). Can be updated later with set_prefix(). :param format_string: Format string for log messages. Uses {prefix} to include the prefix. Uses DEFAULT_FORMAT_WITH_PREFIX by default. :param date_format: Date format string. Uses DEFAULT_DATE_FORMAT by default. :param level: Default logging level. :param handlers: List of handlers to add to all loggers. If None, uses default StreamHandler. :param default_handler: Single default handler to use for all loggers. If provided, this replaces the default StreamHandler. :param allowed_prefixes: Set of logger name prefixes to allow in addition to library-created loggers. Default is None, which means only loggers created via Logger.get_logger() are allowed. To include third-party library logs, add their prefixes: {"sqlalchemy", "boto3"} to see SQLAlchemy and boto3 logs. :param logger_levels: Optional mapping of logger names to logging levels. If provided, levels are applied to the parent loggers for those names (e.g., setting "myapp" sets the parent logger for "myapp.*"). Pass an empty dict to clear existing rules. :param force: If True, force reconfiguration even if already configured. .. rubric:: Example >>> from ds_common_logger_py_lib import Logger >>> import logging >>> Logger.configure( ... prefix="MyService", ... format_string="[%(asctime)s][{prefix}][%(name)s]: %(message)s", ... level=logging.DEBUG ... logger_levels={ ... "ds": logging.WARNING, ... }, ... ) >>> logger = Logger.get_logger(__name__) >>> logger.info("Service started") [2024-01-15T10:30:45][MyService][__main__]: Service started .. py:method:: get_logger(name: str, package: bool = False) -> logging.Logger :staticmethod: Get a configured logger instance. If Logger.configure() is called, the logger will use application-level settings (prefix, format, handlers). Otherwise, uses default settings. :param name: The logger name (usually __name__). :param package: If True, normalize internal package names into a shared namespace (e.g., ds_common_* -> ds.common.*). :returns: Configured logger instance. .. rubric:: Example >>> Logger.configure() >>> logger = Logger.get_logger(__name__) >>> logger.info("Test message") [2024-01-15T10:30:45][__main__][INFO][core.py:232]: Test message .. py:method:: set_prefix(prefix: str) -> None :staticmethod: Update the prefix at runtime. This allows you to change the prefix dynamically, for example when a session starts or when context changes. The new prefix will be applied to all existing and future loggers. If Logger.configure() hasn't been called yet, this will automatically configure it with default settings that include {prefix} in the format (using the provided prefix). :param prefix: New prefix value to use in log messages. .. rubric:: Example >>> from ds_common_logger_py_lib import Logger >>> import logging >>> Logger.set_prefix("MyApp") >>> logger = Logger.get_logger(__name__) >>> logger.info("Log with MyApp prefix") [2024-01-15T10:30:45][MyApp][__main__][INFO][core.py:158]: Log with MyApp prefix >>> >>> session_id = "session_12345" >>> Logger.set_prefix(f"[{session_id}]") >>> logger.info("Log with session prefix") [2024-01-15T10:30:46][session_12345][__main__][INFO][core.py:162]: Log with session prefix .. py:method:: set_log_format(format_string: str | None = None, date_format: str | None = None) -> None :staticmethod: Set or update the default log format for all loggers. :param format_string: Format string to set. If None, resets to DEFAULT_FORMAT. :param date_format: Date format string to set. If None, resets to DEFAULT_DATE_FORMAT. .. rubric:: Example >>> Logger.configure() >>> Logger.set_log_format("%(levelname)s: %(message)s") >>> logger = Logger.get_logger(__name__) >>> logger.info("This will use the custom format") INFO: This will use the custom format .. py:method:: add_handler(handler: logging.Handler) -> None :staticmethod: Add a handler to the root logger. When Logger.configure() is called, handlers are on the root logger only. Package loggers propagate to root, so they will use this handler automatically. :param handler: Handler to add. .. rubric:: Example >>> from ds_common_logger_py_lib import Logger >>> import logging >>> Logger.configure() >>> file_handler = logging.FileHandler("app.log") >>> Logger.add_handler(file_handler) >>> logger = Logger.get_logger(__name__) >>> logger.info("Message goes to file via root logger") .. py:method:: remove_handler(handler: logging.Handler) -> None :staticmethod: Remove a handler from the root logger. :param handler: Handler to remove. .. rubric:: Example >>> from ds_common_logger_py_lib import Logger >>> import logging >>> Logger.configure() >>> file_handler = logging.FileHandler("app.log") >>> Logger.add_handler(file_handler) >>> Logger.remove_handler(file_handler) .. py:method:: set_default_handler(handler: logging.Handler) -> None :staticmethod: Set the default handler for all loggers, replacing the current default. :param handler: Handler to use as default. .. rubric:: Example >>> from ds_common_logger_py_lib import Logger >>> import logging >>> import sys >>> Logger.configure() >>> custom_handler = logging.StreamHandler(sys.stderr) >>> Logger.set_default_handler(custom_handler) >>> logger = Logger.get_logger(__name__) >>> logger.info("Message goes to stderr via root logger") .. py:method:: is_configured() -> bool :staticmethod: Check if Logger has been configured. :returns: True if Logger has been configured, False otherwise. .. py:method:: get_managed_loggers() -> set[str] :staticmethod: Get the set of managed loggers. :returns: The set of registered loggers. .. py:method:: get_prefix() -> str :staticmethod: Get the configured prefix. :returns: The configured prefix. .. py:method:: get_format_string() -> str | None :staticmethod: Get the configured format string. :returns: The configured format string. .. py:method:: get_date_format() -> str | None :staticmethod: Get the configured date format. :returns: The configured date format. .. py:method:: _register_managed_logger(logger_name: str) -> None :staticmethod: Register a logger name as being managed by this helper. This ensures that loggers created via Logger.get_logger() are automatically allowed by the filter, regardless of allowed_prefixes. :param logger_name: The name of the logger to register. .. py:method:: _normalize_logger_name(name: str) -> str :staticmethod: Normalize internal package names into a dotted namespace. :param name: The logger name to normalize. :returns: The normalized logger name. .. py:method:: _create_formatter() -> ds_common_logger_py_lib.formatter.ExtraFieldsFormatter :staticmethod: Create a formatter with current configuration. :returns: ExtraFieldsFormatter instance with current configuration. .. py:method:: _setup_filter() -> None :staticmethod: Apply filter to existing handlers managed by Logger. .. py:method:: _update_existing_loggers() -> None :staticmethod: Update root logger handlers managed by Logger with current configuration. .. py:method:: _apply_logger_levels(previous_levels: dict[str, int] | None = None) -> None :staticmethod: Apply logger-level rules to logger hierarchy. :param previous_levels: The previous logger levels. .. py:class:: LoggerFilter(allowed_prefixes: set[str] | None = None, managed_loggers: set[str] | None = None) Bases: :py:obj:`logging.Filter` Filter to only allow logs from specified logger name prefixes. Simple whitelist approach: if a logger name starts with (or equals) any allowed prefix, it's allowed. Everything else is filtered out. Loggers in the managed_loggers set are automatically allowed, regardless of allowed_prefixes. .. py:attribute:: allowed_prefixes :value: None .. py:attribute:: managed_loggers .. py:method:: filter(record: logging.LogRecord) -> bool Filter log records - return True to allow, False to exclude. Loggers in managed_loggers are automatically allowed. If allowed_prefixes is None or empty, only managed loggers are allowed. Otherwise, managed loggers plus logs whose name starts with (or equals) an allowed prefix are allowed. :param record: The log record to filter. :returns: True if the log should be shown, False if it should be excluded. .. py:data:: __version__