Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions dvc/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,10 @@ def get_parent_parser():

log_level_group = parent_parser.add_mutually_exclusive_group()
log_level_group.add_argument(
"-q", "--quiet", action="store_true", default=False, help="Be quiet."
"-q", "--quiet", action="count", default=0, help="Be quiet."
)
log_level_group.add_argument(
"-v",
"--verbose",
action="store_true",
default=False,
help="Be verbose.",
"-v", "--verbose", action="count", default=0, help="Be verbose."
)

return parent_parser
Expand Down
60 changes: 50 additions & 10 deletions dvc/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,45 @@
)


def addLoggingLevel(levelName, levelNum, methodName=None):
"""
Adds a new logging level to the `logging` module and the
currently configured logging class.

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)

def logForLevel(self, message, *args, **kwargs):
if self.isEnabledFor(levelNum):
self._log(levelNum, 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)


class LoggingException(Exception):
def __init__(self, record):
msg = "failed to log {}".format(str(record))
super().__init__(msg)


class ExcludeErrorsFilter(logging.Filter):
def filter(self, record):
return record.levelno < logging.WARNING

def excludeFilter(level):
class ExcludeLevelFilter(logging.Filter):
def filter(self, record):
return record.levelno < level

class ExcludeInfoFilter(logging.Filter):
def filter(self, record):
return record.levelno < logging.INFO
return ExcludeLevelFilter


class ColorFormatter(logging.Formatter):
Expand All @@ -47,6 +72,7 @@ class ColorFormatter(logging.Formatter):
"""

color_code = {
"TRACE": colorama.Fore.GREEN,
"DEBUG": colorama.Fore.BLUE,
"WARNING": colorama.Fore.YELLOW,
"ERROR": colorama.Fore.RED,
Comment on lines +75 to 78
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not particularly fussed about colours - feel free to suggest others

Expand Down Expand Up @@ -116,7 +142,11 @@ def emit(self, record):


def _is_verbose():
return logging.getLogger("dvc").getEffectiveLevel() == logging.DEBUG
return (
logging.NOTSET
< logging.getLogger("dvc").getEffectiveLevel()
<= logging.DEBUG
)


def _iter_causes(exc):
Expand Down Expand Up @@ -152,12 +182,14 @@ def disable_other_loggers():
def setup(level=logging.INFO):
colorama.init()

addLoggingLevel("TRACE", logging.DEBUG - 5)
logging.config.dictConfig(
{
"version": 1,
"filters": {
"exclude_errors": {"()": ExcludeErrorsFilter},
"exclude_info": {"()": ExcludeInfoFilter},
"exclude_errors": {"()": excludeFilter(logging.WARNING)},
"exclude_info": {"()": excludeFilter(logging.INFO)},
"exclude_debug": {"()": excludeFilter(logging.DEBUG)},
},
"formatters": {"color": {"()": ColorFormatter}},
"handlers": {
Expand All @@ -175,6 +207,13 @@ def setup(level=logging.INFO):
"stream": "ext://sys.stdout",
"filters": ["exclude_info"],
},
"console_trace": {
"class": "dvc.logger.LoggerHandler",
"level": "TRACE",
"formatter": "color",
"stream": "ext://sys.stdout",
"filters": ["exclude_debug"],
},
"console_errors": {
"class": "dvc.logger.LoggerHandler",
"level": "WARNING",
Expand All @@ -188,6 +227,7 @@ def setup(level=logging.INFO):
"handlers": [
"console_info",
"console_debug",
"console_trace",
"console_errors",
],
},
Expand Down
17 changes: 11 additions & 6 deletions dvc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

"".encode("idna")


logger = logging.getLogger("dvc")


Expand All @@ -38,11 +37,17 @@ def main(argv=None):
try:
args = parse_args(argv)

if args.quiet:
logger.setLevel(logging.CRITICAL)

elif args.verbose:
logger.setLevel(logging.DEBUG)
verbosity = args.verbose - args.quiet
if verbosity:
logger.setLevel(
{
-2: logging.CRITICAL,
-1: logging.ERROR,
1: logging.DEBUG,
2: logging.TRACE,
}[max(-2, min(verbosity, 2))]
)
logger.trace(args)

cmd = args.func(args)
ret = cmd.run()
Expand Down
2 changes: 1 addition & 1 deletion dvc/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def __exit__(self, typ, value, tbck):
self.dump()

def _execute(self, cmd, parameters=()):
logger.debug(cmd)
logger.trace(cmd)
return self.cursor.execute(cmd, parameters)

def _fetchall(self):
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,17 +222,19 @@ def test_progress_awareness(self, mocker, capsys, caplog):


def test_handlers():
out, deb, err = logger.handlers
out, deb, vrb, err = logger.handlers

assert out.level == logging.INFO
assert deb.level == logging.DEBUG
assert vrb.level == logging.TRACE
assert err.level == logging.WARNING


def test_logging_debug_with_datetime(caplog, dt):
with caplog.at_level(logging.DEBUG, logger="dvc"):
logger.warning("WARNING")
logger.debug("DEBUG")
logger.trace("TRACE")
logger.error("ERROR")

for record in caplog.records:
Expand Down