Skip to content

Commit

Permalink
Using existing logging level if defined in addLoggingLevel (iterative…
Browse files Browse the repository at this point in the history
…#5632)

* Use existing level if defined in addLoggingLevel

Fixes iterative#4546

* Add addLoggingLevel test

* Format fix.
  • Loading branch information
asford authored Mar 22, 2021
1 parent 0a8927e commit b3bfa34
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 7 deletions.
28 changes: 21 additions & 7 deletions dvc/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,19 @@ def addLoggingLevel(levelName, levelNum, methodName=None):
Adds a new logging level to the `logging` module and the
currently configured logging class.
Uses the existing numeric levelNum if already defined.
Based on https://stackoverflow.com/questions/2183233
"""
if methodName is None:
methodName = levelName.lower()

assert not hasattr(logging, levelName)
assert not hasattr(logging, methodName)
assert not hasattr(logging.getLoggerClass(), methodName)
# If the level name is already defined as a top-level `logging`
# constant, then adopt the existing numeric level.
if hasattr(logging, levelName):
existingLevelNum = getattr(logging, levelName)
assert isinstance(existingLevelNum, int)
levelNum = existingLevelNum

def logForLevel(self, message, *args, **kwargs):
if self.isEnabledFor(levelNum):
Expand All @@ -40,10 +45,19 @@ def logForLevel(self, message, *args, **kwargs):
def logToRoot(message, *args, **kwargs):
logging.log(levelNum, message, *args, **kwargs)

logging.addLevelName(levelNum, levelName)
setattr(logging, levelName, levelNum)
setattr(logging.getLoggerClass(), methodName, logForLevel)
setattr(logging, methodName, logToRoot)
# getLevelName resolves the numeric log level if already defined,
# otherwise returns a string
if not isinstance(logging.getLevelName(levelName), int):
logging.addLevelName(levelNum, levelName)

if not hasattr(logging, levelName):
setattr(logging, levelName, levelNum)

if not hasattr(logging.getLoggerClass(), methodName):
setattr(logging.getLoggerClass(), methodName, logForLevel)

if not hasattr(logging, methodName):
setattr(logging, methodName, logToRoot)


class LoggingException(Exception):
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,31 @@ def test_info_with_debug_loglevel_shows_no_datetime(caplog, dt):
logger.info("message")

assert "message" == formatter.format(caplog.records[0])


def test_add_existing_level(caplog, dt):
# Common pattern to configure logging level in external libraries
# eg:
# https://github.com/bokeh/bokeh/blob/04bb30fef2e72e64baaa8b2f330806d5bfdd3b11/
# bokeh/util/logconfig.py#L79-L85
TRACE2 = 4
logging.addLevelName(TRACE2, "TRACE2")
logging.TRACE2 = TRACE2

dvc.logger.addLoggingLevel("TRACE2", 2)

# DVC sets all expected entrypoints, but doesn't override the level
assert logging.TRACE2 == 4
assert hasattr(logging, "trace2")
assert hasattr(logger, "trace2")
assert logging.getLevelName("TRACE2") == 4

# The TRACE2 logging level uses the original, higher logging level
with caplog.at_level(logging.TRACE2, logger="dvc"):
logger.trace2("TRACE2")
assert len(caplog.records) == 1

(record,) = caplog.records
assert record.levelno == 4
assert record.levelname == "TRACE2"
assert record.message == "TRACE2"

0 comments on commit b3bfa34

Please sign in to comment.