Colorlog windows fix (#13929)

* Fix colorlog on windows

Modified the way logging is initialized to fix two things.
1. If the import of `colorlog` fails the logs will still be formatted
   using the expected HASS log format.
2. Ensure that `logging.basicConfig` is called AFTER `colorlog` is
   imported so that the default handler generated will be writing to the
   wrapped stream generated when `colorama` is initialized.  This allows
   colored logging to work on Windows.

Added support for a `--log-no-color` command line switch in the event
that someone just wants to disable colored log output entirely.

* Fix line lengths

* Switch default value
This commit is contained in:
Ben Randall 2018-04-18 07:18:44 -07:00 committed by Paulus Schoutsen
parent b589dbf26c
commit 7d43ad6a37
3 changed files with 54 additions and 33 deletions

View file

@ -126,6 +126,10 @@ def get_arguments() -> argparse.Namespace:
default=None, default=None,
help='Log file to write to. If not set, CONFIG/home-assistant.log ' help='Log file to write to. If not set, CONFIG/home-assistant.log '
'is used') 'is used')
parser.add_argument(
'--log-no-color',
action='store_true',
help="Disable color logs")
parser.add_argument( parser.add_argument(
'--runner', '--runner',
action='store_true', action='store_true',
@ -259,13 +263,14 @@ def setup_and_run_hass(config_dir: str,
hass = bootstrap.from_config_dict( hass = bootstrap.from_config_dict(
config, config_dir=config_dir, verbose=args.verbose, config, config_dir=config_dir, verbose=args.verbose,
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days, skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days,
log_file=args.log_file) log_file=args.log_file, log_no_color=args.log_no_color)
else: else:
config_file = ensure_config_file(config_dir) config_file = ensure_config_file(config_dir)
print('Config directory:', config_dir) print('Config directory:', config_dir)
hass = bootstrap.from_config_file( hass = bootstrap.from_config_file(
config_file, verbose=args.verbose, skip_pip=args.skip_pip, config_file, verbose=args.verbose, skip_pip=args.skip_pip,
log_rotate_days=args.log_rotate_days, log_file=args.log_file) log_rotate_days=args.log_rotate_days, log_file=args.log_file,
log_no_color=args.log_no_color)
if hass is None: if hass is None:
return None return None

View file

@ -42,7 +42,8 @@ def from_config_dict(config: Dict[str, Any],
verbose: bool = False, verbose: bool = False,
skip_pip: bool = False, skip_pip: bool = False,
log_rotate_days: Any = None, log_rotate_days: Any = None,
log_file: Any = None) \ log_file: Any = None,
log_no_color: bool = False) \
-> Optional[core.HomeAssistant]: -> Optional[core.HomeAssistant]:
"""Try to configure Home Assistant from a configuration dictionary. """Try to configure Home Assistant from a configuration dictionary.
@ -60,7 +61,7 @@ def from_config_dict(config: Dict[str, Any],
hass = hass.loop.run_until_complete( hass = hass.loop.run_until_complete(
async_from_config_dict( async_from_config_dict(
config, hass, config_dir, enable_log, verbose, skip_pip, config, hass, config_dir, enable_log, verbose, skip_pip,
log_rotate_days, log_file) log_rotate_days, log_file, log_no_color)
) )
return hass return hass
@ -74,7 +75,8 @@ def async_from_config_dict(config: Dict[str, Any],
verbose: bool = False, verbose: bool = False,
skip_pip: bool = False, skip_pip: bool = False,
log_rotate_days: Any = None, log_rotate_days: Any = None,
log_file: Any = None) \ log_file: Any = None,
log_no_color: bool = False) \
-> Optional[core.HomeAssistant]: -> Optional[core.HomeAssistant]:
"""Try to configure Home Assistant from a configuration dictionary. """Try to configure Home Assistant from a configuration dictionary.
@ -84,7 +86,8 @@ def async_from_config_dict(config: Dict[str, Any],
start = time() start = time()
if enable_log: if enable_log:
async_enable_logging(hass, verbose, log_rotate_days, log_file) async_enable_logging(hass, verbose, log_rotate_days, log_file,
log_no_color)
core_config = config.get(core.DOMAIN, {}) core_config = config.get(core.DOMAIN, {})
@ -164,7 +167,8 @@ def from_config_file(config_path: str,
verbose: bool = False, verbose: bool = False,
skip_pip: bool = True, skip_pip: bool = True,
log_rotate_days: Any = None, log_rotate_days: Any = None,
log_file: Any = None): log_file: Any = None,
log_no_color: bool = False):
"""Read the configuration file and try to start all the functionality. """Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter if given, Will add functionality to 'hass' parameter if given,
@ -176,7 +180,8 @@ def from_config_file(config_path: str,
# run task # run task
hass = hass.loop.run_until_complete( hass = hass.loop.run_until_complete(
async_from_config_file( async_from_config_file(
config_path, hass, verbose, skip_pip, log_rotate_days, log_file) config_path, hass, verbose, skip_pip,
log_rotate_days, log_file, log_no_color)
) )
return hass return hass
@ -188,7 +193,8 @@ def async_from_config_file(config_path: str,
verbose: bool = False, verbose: bool = False,
skip_pip: bool = True, skip_pip: bool = True,
log_rotate_days: Any = None, log_rotate_days: Any = None,
log_file: Any = None): log_file: Any = None,
log_no_color: bool = False):
"""Read the configuration file and try to start all the functionality. """Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter. Will add functionality to 'hass' parameter.
@ -199,7 +205,8 @@ def async_from_config_file(config_path: str,
hass.config.config_dir = config_dir hass.config.config_dir = config_dir
yield from async_mount_local_lib_path(config_dir, hass.loop) yield from async_mount_local_lib_path(config_dir, hass.loop)
async_enable_logging(hass, verbose, log_rotate_days, log_file) async_enable_logging(hass, verbose, log_rotate_days, log_file,
log_no_color)
try: try:
config_dict = yield from hass.async_add_job( config_dict = yield from hass.async_add_job(
@ -216,40 +223,51 @@ def async_from_config_file(config_path: str,
@core.callback @core.callback
def async_enable_logging(hass: core.HomeAssistant, verbose: bool = False, def async_enable_logging(hass: core.HomeAssistant,
log_rotate_days=None, log_file=None) -> None: verbose: bool = False,
log_rotate_days=None,
log_file=None,
log_no_color: bool = False) -> None:
"""Set up the logging. """Set up the logging.
This method must be run in the event loop. This method must be run in the event loop.
""" """
logging.basicConfig(level=logging.INFO)
fmt = ("%(asctime)s %(levelname)s (%(threadName)s) " fmt = ("%(asctime)s %(levelname)s (%(threadName)s) "
"[%(name)s] %(message)s") "[%(name)s] %(message)s")
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
datefmt = '%Y-%m-%d %H:%M:%S' datefmt = '%Y-%m-%d %H:%M:%S'
if not log_no_color:
try:
from colorlog import ColoredFormatter
# basicConfig must be called after importing colorlog in order to
# ensure that the handlers it sets up wraps the correct streams.
logging.basicConfig(level=logging.INFO)
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
colorfmt,
datefmt=datefmt,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red',
}
))
except ImportError:
pass
# If the above initialization failed for any reason, setup the default
# formatting. If the above succeeds, this wil result in a no-op.
logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO)
# Suppress overly verbose logs from libraries that aren't helpful # Suppress overly verbose logs from libraries that aren't helpful
logging.getLogger('requests').setLevel(logging.WARNING) logging.getLogger('requests').setLevel(logging.WARNING)
logging.getLogger('urllib3').setLevel(logging.WARNING) logging.getLogger('urllib3').setLevel(logging.WARNING)
logging.getLogger('aiohttp.access').setLevel(logging.WARNING) logging.getLogger('aiohttp.access').setLevel(logging.WARNING)
try:
from colorlog import ColoredFormatter
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
colorfmt,
datefmt=datefmt,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red',
}
))
except ImportError:
pass
# Log errors to a file if we have write access to file or config dir # Log errors to a file if we have write access to file or config dir
if log_file is None: if log_file is None:
err_log_path = hass.config.path(ERROR_LOG_FILENAME) err_log_path = hass.config.path(ERROR_LOG_FILENAME)

View file

@ -76,8 +76,6 @@ def send_message(sender, password, recipient, use_tls,
"""Initialize the Jabber Bot.""" """Initialize the Jabber Bot."""
super(SendNotificationBot, self).__init__(sender, password) super(SendNotificationBot, self).__init__(sender, password)
logging.basicConfig(level=logging.ERROR)
self.use_tls = use_tls self.use_tls = use_tls
self.use_ipv6 = False self.use_ipv6 = False
self.add_event_handler('failed_auth', self.check_credentials) self.add_event_handler('failed_auth', self.check_credentials)