Skip to content

Commit

Permalink
Merge pull request #619 from minrk/logging-adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Carreau committed Sep 7, 2020
2 parents e3b26fa + 9bb4702 commit 43f6761
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 3 deletions.
9 changes: 8 additions & 1 deletion traitlets/config/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import pprint
import re
import sys
import warnings

from traitlets.config.configurable import Configurable, SingletonConfigurable
from traitlets.config.loader import (
Expand Down Expand Up @@ -198,7 +199,13 @@ def _log_level_changed(self, change):
@observe_compat
def _log_format_changed(self, change):
"""Change the log formatter when log_format is set."""
_log_handler = self.log.handlers[0]
_log_handler = self._get_log_handler()
if not _log_handler:
warnings.warn(
f"No Handler found on {self.log}, setting log_format will have no effect",
RuntimeWarning,
)
return
_log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
_log_handler.setFormatter(_log_formatter)

Expand Down
31 changes: 29 additions & 2 deletions traitlets/config/configurable.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@


from copy import deepcopy
import logging
import warnings

from .loader import Config, LazyConfigValue, DeferredConfig, _is_section_key
from traitlets.traitlets import (
Any,
HasTraits,
Instance,
Container,
Dict,
observe,
observe_compat,
default,
validate,
)
from ipython_genutils.text import indent, dedent, wrap_paragraphs

Expand Down Expand Up @@ -433,14 +436,38 @@ class LoggingConfigurable(Configurable):
is to get the logger from the currently running Application.
"""

log = Instance('logging.Logger')
@default('log')
log = Any(help="Logger or LoggerAdapter instance")

@validate("log")
def _validate_log(self, proposal):
if not isinstance(proposal.value, (logging.Logger, logging.LoggerAdapter)):
# warn about unsupported type, but be lenient to allow for duck typing
warnings.warn(
f"{self.__class__.__name__}.log should be a Logger or LoggerAdapter,"
f" got {proposal.value}."
)
return proposal.value

@default("log")
def _log_default(self):
if isinstance(self.parent, LoggingConfigurable):
return self.parent.log
from traitlets import log
return log.get_logger()

def _get_log_handler(self):
"""Return the default Handler
Returns None if none can be found
"""
logger = self.log
if isinstance(logger, logging.LoggerAdapter):
logger = logger.logger
if not getattr(logger, "handlers", None):
# no handlers attribute or empty handlers list
return None
return logger.handlers[0]


class SingletonConfigurable(LoggingConfigurable):
"""A configurable that only allows one instance.
Expand Down
12 changes: 12 additions & 0 deletions traitlets/config/tests/test_configurable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from pytest import mark

from traitlets.config.application import Application
from traitlets.config.configurable import (
Configurable,
LoggingConfigurable,
Expand Down Expand Up @@ -666,3 +667,14 @@ def test_warn_match(self):
self.assertIn('Config option `totally_wrong` not recognized by `A`.', output)
self.assertNotIn('Did you mean', output)

def test_logger_adapter(self):
logger = logging.getLogger("test_logger_adapter")
adapter = logging.LoggerAdapter(logger, {"key": "adapted"})

with self.assertLogs(logger, logging.INFO) as captured:
app = Application(log=adapter, log_level=logging.INFO)
app.log_format = "%(key)s %(message)s"
app.log.info("test message")

output = "\n".join(captured.output)
assert "adapted test message" in output

0 comments on commit 43f6761

Please sign in to comment.