From 7d43ad6a37ef80c06890933113f5a9eace46a938 Mon Sep 17 00:00:00 2001 From: Ben Randall Date: Wed, 18 Apr 2018 07:18:44 -0700 Subject: [PATCH] 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 --- homeassistant/__main__.py | 9 ++- homeassistant/bootstrap.py | 76 +++++++++++++++---------- homeassistant/components/notify/xmpp.py | 2 - 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/homeassistant/__main__.py b/homeassistant/__main__.py index aa966027922..deb1746c167 100644 --- a/homeassistant/__main__.py +++ b/homeassistant/__main__.py @@ -126,6 +126,10 @@ def get_arguments() -> argparse.Namespace: default=None, help='Log file to write to. If not set, CONFIG/home-assistant.log ' 'is used') + parser.add_argument( + '--log-no-color', + action='store_true', + help="Disable color logs") parser.add_argument( '--runner', action='store_true', @@ -259,13 +263,14 @@ def setup_and_run_hass(config_dir: str, hass = bootstrap.from_config_dict( config, config_dir=config_dir, verbose=args.verbose, 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: config_file = ensure_config_file(config_dir) print('Config directory:', config_dir) hass = bootstrap.from_config_file( 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: return None diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 00822d93299..e0962568a66 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -42,7 +42,8 @@ def from_config_dict(config: Dict[str, Any], verbose: bool = False, skip_pip: bool = False, log_rotate_days: Any = None, - log_file: Any = None) \ + log_file: Any = None, + log_no_color: bool = False) \ -> Optional[core.HomeAssistant]: """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( async_from_config_dict( config, hass, config_dir, enable_log, verbose, skip_pip, - log_rotate_days, log_file) + log_rotate_days, log_file, log_no_color) ) return hass @@ -74,7 +75,8 @@ def async_from_config_dict(config: Dict[str, Any], verbose: bool = False, skip_pip: bool = False, log_rotate_days: Any = None, - log_file: Any = None) \ + log_file: Any = None, + log_no_color: bool = False) \ -> Optional[core.HomeAssistant]: """Try to configure Home Assistant from a configuration dictionary. @@ -84,7 +86,8 @@ def async_from_config_dict(config: Dict[str, Any], start = time() 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, {}) @@ -164,7 +167,8 @@ def from_config_file(config_path: str, verbose: bool = False, skip_pip: bool = True, 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. Will add functionality to 'hass' parameter if given, @@ -176,7 +180,8 @@ def from_config_file(config_path: str, # run task hass = hass.loop.run_until_complete( 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 @@ -188,7 +193,8 @@ def async_from_config_file(config_path: str, verbose: bool = False, skip_pip: bool = True, 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. Will add functionality to 'hass' parameter. @@ -199,7 +205,8 @@ def async_from_config_file(config_path: str, hass.config.config_dir = config_dir 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: config_dict = yield from hass.async_add_job( @@ -216,40 +223,51 @@ def async_from_config_file(config_path: str, @core.callback -def async_enable_logging(hass: core.HomeAssistant, verbose: bool = False, - log_rotate_days=None, log_file=None) -> None: +def async_enable_logging(hass: core.HomeAssistant, + verbose: bool = False, + log_rotate_days=None, + log_file=None, + log_no_color: bool = False) -> None: """Set up the logging. This method must be run in the event loop. """ - logging.basicConfig(level=logging.INFO) fmt = ("%(asctime)s %(levelname)s (%(threadName)s) " "[%(name)s] %(message)s") - colorfmt = "%(log_color)s{}%(reset)s".format(fmt) 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 logging.getLogger('requests').setLevel(logging.WARNING) logging.getLogger('urllib3').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 if log_file is None: err_log_path = hass.config.path(ERROR_LOG_FILENAME) diff --git a/homeassistant/components/notify/xmpp.py b/homeassistant/components/notify/xmpp.py index 806acdb6d09..12ddf49fca8 100644 --- a/homeassistant/components/notify/xmpp.py +++ b/homeassistant/components/notify/xmpp.py @@ -76,8 +76,6 @@ def send_message(sender, password, recipient, use_tls, """Initialize the Jabber Bot.""" super(SendNotificationBot, self).__init__(sender, password) - logging.basicConfig(level=logging.ERROR) - self.use_tls = use_tls self.use_ipv6 = False self.add_event_handler('failed_auth', self.check_credentials)