logging: Add support for per logger colors (fixes: #808)
This commit is contained in:
parent
b11d89d72f
commit
5c833e106b
@ -53,6 +53,8 @@ v0.20.0 (UNRELEASED)
|
|||||||
- Add custom log level ``TRACE`` (numerical level 5), which can be used by
|
- Add custom log level ``TRACE`` (numerical level 5), which can be used by
|
||||||
Mopidy and extensions to log at an even more detailed level than ``DEBUG``.
|
Mopidy and extensions to log at an even more detailed level than ``DEBUG``.
|
||||||
|
|
||||||
|
- Add support for per logger color overrides. (Fixes: :issue:`808`)
|
||||||
|
|
||||||
**Local backend**
|
**Local backend**
|
||||||
|
|
||||||
- Add cover URL to all scanned files with MusicBrainz album IDs. (Fixes:
|
- Add cover URL to all scanned files with MusicBrainz album IDs. (Fixes:
|
||||||
|
|||||||
@ -128,6 +128,14 @@ Logging configuration
|
|||||||
The ``loglevels`` config section can be used to change the log level for
|
The ``loglevels`` config section can be used to change the log level for
|
||||||
specific parts of Mopidy during development or debugging. Each key in the
|
specific parts of Mopidy during development or debugging. Each key in the
|
||||||
config section should match the name of a logger. The value is the log
|
config section should match the name of a logger. The value is the log
|
||||||
|
level to use for that logger, one of ``black``, ``red``, ``green``,
|
||||||
|
``yellow``, ``blue``, ``magenta``, ``cyan`` or ``white``.
|
||||||
|
|
||||||
|
.. confval:: logcolors/*
|
||||||
|
|
||||||
|
The ``logcolors`` config section can be used to change the log color for
|
||||||
|
specific parts of Mopidy during development or debugging. Each key in the
|
||||||
|
config section should match the name of a logger. The value is the log
|
||||||
level to use for that logger, one of ``debug``, ``info``, ``warning``,
|
level to use for that logger, one of ``debug``, ``info``, ``warning``,
|
||||||
``error``, or ``critical``.
|
``error``, or ``critical``.
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ _logging_schema['debug_file'] = Path()
|
|||||||
_logging_schema['config_file'] = Path(optional=True)
|
_logging_schema['config_file'] = Path(optional=True)
|
||||||
|
|
||||||
_loglevels_schema = MapConfigSchema('loglevels', LogLevel())
|
_loglevels_schema = MapConfigSchema('loglevels', LogLevel())
|
||||||
|
_logcolors_schema = MapConfigSchema('logcolors', LogColor())
|
||||||
|
|
||||||
_audio_schema = ConfigSchema('audio')
|
_audio_schema = ConfigSchema('audio')
|
||||||
_audio_schema['mixer'] = String()
|
_audio_schema['mixer'] = String()
|
||||||
@ -42,7 +43,8 @@ _proxy_schema['password'] = Secret(optional=True)
|
|||||||
# NOTE: if multiple outputs ever comes something like LogLevelConfigSchema
|
# NOTE: if multiple outputs ever comes something like LogLevelConfigSchema
|
||||||
# _outputs_schema = config.AudioOutputConfigSchema()
|
# _outputs_schema = config.AudioOutputConfigSchema()
|
||||||
|
|
||||||
_schemas = [_logging_schema, _loglevels_schema, _audio_schema, _proxy_schema]
|
_schemas = [_logging_schema, _loglevels_schema, _logcolors_schema,
|
||||||
|
_audio_schema, _proxy_schema]
|
||||||
|
|
||||||
_INITIAL_HELP = """
|
_INITIAL_HELP = """
|
||||||
# For further information about options in this file see:
|
# For further information about options in this file see:
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import socket
|
|||||||
|
|
||||||
from mopidy import compat
|
from mopidy import compat
|
||||||
from mopidy.config import validators
|
from mopidy.config import validators
|
||||||
from mopidy.utils import path
|
from mopidy.utils import log, path
|
||||||
|
|
||||||
|
|
||||||
def decode(value):
|
def decode(value):
|
||||||
@ -197,6 +197,17 @@ class List(ConfigValue):
|
|||||||
return b'\n ' + b'\n '.join(encode(v) for v in value if v)
|
return b'\n ' + b'\n '.join(encode(v) for v in value if v)
|
||||||
|
|
||||||
|
|
||||||
|
class LogColor(ConfigValue):
|
||||||
|
def deserialize(self, value):
|
||||||
|
validators.validate_choice(value.lower(), log.COLORS)
|
||||||
|
return value.lower()
|
||||||
|
|
||||||
|
def serialize(self, value, display=False):
|
||||||
|
if value.lower() in log.COLORS:
|
||||||
|
return value.lower()
|
||||||
|
return b''
|
||||||
|
|
||||||
|
|
||||||
class LogLevel(ConfigValue):
|
class LogLevel(ConfigValue):
|
||||||
"""Log level value.
|
"""Log level value.
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,7 @@ def setup_console_logging(config, verbosity_level):
|
|||||||
formatter = logging.Formatter(log_format)
|
formatter = logging.Formatter(log_format)
|
||||||
|
|
||||||
if config['logging']['color']:
|
if config['logging']['color']:
|
||||||
handler = ColorizingStreamHandler()
|
handler = ColorizingStreamHandler(config.get('logcolors', {}))
|
||||||
else:
|
else:
|
||||||
handler = logging.StreamHandler()
|
handler = logging.StreamHandler()
|
||||||
handler.addFilter(verbosity_filter)
|
handler.addFilter(verbosity_filter)
|
||||||
@ -117,6 +117,11 @@ class VerbosityFilter(logging.Filter):
|
|||||||
return record.levelno >= required_log_level
|
return record.levelno >= required_log_level
|
||||||
|
|
||||||
|
|
||||||
|
#: Available log colors.
|
||||||
|
COLORS = [b'black', b'red', b'green', b'yellow', b'blue', b'magenta', b'cyan',
|
||||||
|
b'white']
|
||||||
|
|
||||||
|
|
||||||
class ColorizingStreamHandler(logging.StreamHandler):
|
class ColorizingStreamHandler(logging.StreamHandler):
|
||||||
"""
|
"""
|
||||||
Stream handler which colorizes the log using ANSI escape sequences.
|
Stream handler which colorizes the log using ANSI escape sequences.
|
||||||
@ -130,17 +135,6 @@ class ColorizingStreamHandler(logging.StreamHandler):
|
|||||||
Licensed under the new BSD license.
|
Licensed under the new BSD license.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
color_map = {
|
|
||||||
'black': 0,
|
|
||||||
'red': 1,
|
|
||||||
'green': 2,
|
|
||||||
'yellow': 3,
|
|
||||||
'blue': 4,
|
|
||||||
'magenta': 5,
|
|
||||||
'cyan': 6,
|
|
||||||
'white': 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Map logging levels to (background, foreground, bold/intense)
|
# Map logging levels to (background, foreground, bold/intense)
|
||||||
level_map = {
|
level_map = {
|
||||||
TRACE_LOG_LEVEL: (None, 'blue', False),
|
TRACE_LOG_LEVEL: (None, 'blue', False),
|
||||||
@ -150,11 +144,18 @@ class ColorizingStreamHandler(logging.StreamHandler):
|
|||||||
logging.ERROR: (None, 'red', False),
|
logging.ERROR: (None, 'red', False),
|
||||||
logging.CRITICAL: ('red', 'white', True),
|
logging.CRITICAL: ('red', 'white', True),
|
||||||
}
|
}
|
||||||
|
# Map logger name to foreground colors
|
||||||
|
logger_map = {}
|
||||||
|
|
||||||
csi = '\x1b['
|
csi = '\x1b['
|
||||||
reset = '\x1b[0m'
|
reset = '\x1b[0m'
|
||||||
|
|
||||||
is_windows = platform.system() == 'Windows'
|
is_windows = platform.system() == 'Windows'
|
||||||
|
|
||||||
|
def __init__(self, logger_colors):
|
||||||
|
super(ColorizingStreamHandler, self).__init__()
|
||||||
|
self.logger_map = logger_colors
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_tty(self):
|
def is_tty(self):
|
||||||
isatty = getattr(self.stream, 'isatty', None)
|
isatty = getattr(self.stream, 'isatty', None)
|
||||||
@ -173,19 +174,23 @@ class ColorizingStreamHandler(logging.StreamHandler):
|
|||||||
message = logging.StreamHandler.format(self, record)
|
message = logging.StreamHandler.format(self, record)
|
||||||
if not self.is_tty or self.is_windows:
|
if not self.is_tty or self.is_windows:
|
||||||
return message
|
return message
|
||||||
return self.colorize(message, record)
|
for name, color in self.logger_map.iteritems():
|
||||||
|
if record.name.startswith(name):
|
||||||
def colorize(self, message, record):
|
return self.colorize(message, fg=color)
|
||||||
if record.levelno in self.level_map:
|
if record.levelno in self.level_map:
|
||||||
bg, fg, bold = self.level_map[record.levelno]
|
bg, fg, bold = self.level_map[record.levelno]
|
||||||
params = []
|
return self.colorize(message, bg=bg, fg=fg, bold=bold)
|
||||||
if bg in self.color_map:
|
return message
|
||||||
params.append(str(self.color_map[bg] + 40))
|
|
||||||
if fg in self.color_map:
|
def colorize(self, message, bg=None, fg=None, bold=False):
|
||||||
params.append(str(self.color_map[fg] + 30))
|
params = []
|
||||||
if bold:
|
if bg in COLORS:
|
||||||
params.append('1')
|
params.append(str(COLORS.index(bg) + 40))
|
||||||
if params:
|
if fg in COLORS:
|
||||||
message = ''.join((
|
params.append(str(COLORS.index(fg) + 30))
|
||||||
self.csi, ';'.join(params), 'm', message, self.reset))
|
if bold:
|
||||||
|
params.append('1')
|
||||||
|
if params:
|
||||||
|
message = ''.join((
|
||||||
|
self.csi, ';'.join(params), 'm', message, self.reset))
|
||||||
return message
|
return message
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user