Source code for smarter.lib.logging.waffle_switched_logger

"""
smarter.lib.logging.waffle_switched_logger
==========================================

Conditional logger wrapper for the Smarter application.

This module provides a logger wrapper, :class:`WaffleSwitchedLoggerWrapper`, that enables conditional logging
based on a user-supplied function. This allows for dynamic control of logging behavior at runtime, such as
toggling detailed logging with a feature flag (e.g., Django Waffle switches).

Classes
-------
WaffleSwitchedLoggerWrapper
    A logger wrapper that only emits log messages if a condition function returns True for the given log level.

Examples
--------
To use the conditional logger wrapper with a Django Waffle switch::

    import logging
    from smarter.lib.django import waffle
    from smarter.lib.django.waffle import SmarterWaffleSwitches
    from smarter.lib.logging.waffle_switched_logger import WaffleSwitchedLoggerWrapper

    def should_log_detailed(level):
        return waffle.switch_is_active(SmarterWaffleSwitches.PROMPT_LOGGING)

    base_logger = logging.getLogger(__name__)
    logger = WaffleSwitchedLoggerWrapper(base_logger, should_log_detailed)
    logger.debug("This is a debug message.")

The logger will only emit messages if the condition function returns True for the log level, or if the log level
is WARNING or higher (forced by REQUIRED_LOG_LEVEL).
"""

import logging
from typing import Any, Callable, Optional

from django.core.exceptions import SynchronousOnlyOperation


[docs] class WaffleSwitchedLoggerWrapper: """ A wrapper around a standard logger that adds conditional logic. Usage: .. code-block:: python import logging from smarter.lib.django import waffle from smarter.lib.django.waffle import SmarterWaffleSwitches from smarter.lib.logging import WaffleSwitchedLoggerWrapper def should_log_detailed(level): return waffle.switch_is_active(SmarterWaffleSwitches.PROMPT_LOGGING) base_logger = logging.getLogger(__name__) logger = WaffleSwitchedLoggerWrapper(base_logger, should_log_detailed) logger.debug("This is a debug message.") """ # log entries will be forced at this level and above REQUIRED_LOG_LEVEL = logging.WARNING
[docs] def __init__(self, logger: logging.Logger, condition_func: Optional[Callable] = None): self._logger = logger self._condition_func = condition_func
[docs] def should_log(self, level: int = logging.DEBUG) -> bool: """Check if we should log based on custom conditions.""" if not self._logger.isEnabledFor(level): return False if self._condition_func: try: return self._condition_func(level) except SynchronousOnlyOperation: return False return True
[docs] def debug(self, msg: Any, *args, **kwargs): if self.should_log(logging.DEBUG) or logging.DEBUG >= self.REQUIRED_LOG_LEVEL: self._logger.debug(msg, *args, **kwargs)
[docs] def info(self, msg: Any, *args, **kwargs): if self.should_log(logging.INFO) or logging.INFO >= self.REQUIRED_LOG_LEVEL: self._logger.info(msg, *args, **kwargs)
[docs] def warning(self, msg: Any, *args, **kwargs): if self.should_log(logging.WARNING) or logging.WARNING >= self.REQUIRED_LOG_LEVEL: self._logger.warning(msg, *args, **kwargs)
[docs] def error(self, msg: Any, *args, **kwargs): if self.should_log(logging.ERROR) or logging.ERROR >= self.REQUIRED_LOG_LEVEL: self._logger.error(msg, *args, **kwargs)
[docs] def critical(self, msg: Any, *args, **kwargs): if self.should_log(logging.CRITICAL) or logging.CRITICAL >= self.REQUIRED_LOG_LEVEL: self._logger.critical(msg, *args, **kwargs)
[docs] def set_condition(self, condition_func: Callable): """Update the condition function.""" self._condition_func = condition_func
__all__ = [ "WaffleSwitchedLoggerWrapper", ]