diff --git a/borgmatic/borg/check.py b/borgmatic/borg/check.py index 96f0fe2..7051138 100644 --- a/borgmatic/borg/check.py +++ b/borgmatic/borg/check.py @@ -3,13 +3,14 @@ import os import subprocess from borgmatic.borg import extract +from borgmatic.logger import get_logger DEFAULT_CHECKS = ('repository', 'archives') DEFAULT_PREFIX = '{hostname}-' -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def _parse_checks(consistency_config): diff --git a/borgmatic/borg/create.py b/borgmatic/borg/create.py index d420cde..ccd9e3f 100644 --- a/borgmatic/borg/create.py +++ b/borgmatic/borg/create.py @@ -5,9 +5,10 @@ import os import tempfile from borgmatic.borg.execute import execute_command +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def _expand_directory(directory): diff --git a/borgmatic/borg/execute.py b/borgmatic/borg/execute.py index 064557a..890af38 100644 --- a/borgmatic/borg/execute.py +++ b/borgmatic/borg/execute.py @@ -1,8 +1,9 @@ -import logging import subprocess +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) + +logger = get_logger(__name__) def execute_command(full_command, capture_output=False): diff --git a/borgmatic/borg/extract.py b/borgmatic/borg/extract.py index 1b5bce8..7d0b2a8 100644 --- a/borgmatic/borg/extract.py +++ b/borgmatic/borg/extract.py @@ -2,8 +2,10 @@ import logging import sys import subprocess +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) + +logger = get_logger(__name__) def extract_last_archive_dry_run(repository, lock_wait=None, local_path='borg', remote_path=None): diff --git a/borgmatic/borg/info.py b/borgmatic/borg/info.py index 79bfb5a..8711747 100644 --- a/borgmatic/borg/info.py +++ b/borgmatic/borg/info.py @@ -1,9 +1,10 @@ import logging from borgmatic.borg.execute import execute_command +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def display_archives_info( diff --git a/borgmatic/borg/init.py b/borgmatic/borg/init.py index 9ce4198..bf11687 100644 --- a/borgmatic/borg/init.py +++ b/borgmatic/borg/init.py @@ -1,8 +1,9 @@ import logging import subprocess +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def initialize_repository( diff --git a/borgmatic/borg/list.py b/borgmatic/borg/list.py index 0c2b8bc..bd18ded 100644 --- a/borgmatic/borg/list.py +++ b/borgmatic/borg/list.py @@ -1,9 +1,10 @@ import logging from borgmatic.borg.execute import execute_command +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def list_archives( diff --git a/borgmatic/borg/prune.py b/borgmatic/borg/prune.py index 54e4ba5..ef4f8b8 100644 --- a/borgmatic/borg/prune.py +++ b/borgmatic/borg/prune.py @@ -1,8 +1,10 @@ import logging import subprocess +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) + +logger = get_logger(__name__) def _make_prune_flags(retention_config): diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 31e810e..fe12443 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -1,5 +1,6 @@ from argparse import ArgumentParser import collections +import colorama import json import logging import os @@ -20,12 +21,12 @@ from borgmatic.borg import ( ) from borgmatic.commands import hook from borgmatic.config import checks, collect, convert, validate +from borgmatic.logger import should_do_markup, get_logger from borgmatic.signals import configure_signals from borgmatic.verbosity import verbosity_to_log_level -logger = logging.getLogger(__name__) - +logger = get_logger(__name__) LEGACY_CONFIG_PATH = '/etc/borgmatic/config' @@ -169,6 +170,9 @@ def parse_arguments(*arguments): action='store_true', help='Go through the motions, but do not actually write to any repositories', ) + common_group.add_argument( + '-nc', '--no-color', dest='no_color', action='store_true', help='Disable colored output' + ) common_group.add_argument( '-v', '--verbosity', @@ -472,6 +476,8 @@ def main(): # pragma: no cover logger.critical(error) exit_with_help_link() + colorama.init(autoreset=True, strip=not should_do_markup(args.no_color)) + logging.basicConfig(level=verbosity_to_log_level(args.verbosity), format='%(message)s') if args.version: diff --git a/borgmatic/commands/hook.py b/borgmatic/commands/hook.py index 77cbcc9..0e915c8 100644 --- a/borgmatic/commands/hook.py +++ b/borgmatic/commands/hook.py @@ -1,8 +1,9 @@ -import logging import subprocess +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) + +logger = get_logger(__name__) def execute_hook(commands, config_filename, description, dry_run): diff --git a/borgmatic/commands/validate_config.py b/borgmatic/commands/validate_config.py index a53c146..606b793 100644 --- a/borgmatic/commands/validate_config.py +++ b/borgmatic/commands/validate_config.py @@ -3,8 +3,9 @@ import sys import logging from borgmatic.config import collect, validate +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def parse_arguments(*arguments): diff --git a/borgmatic/config/load.py b/borgmatic/config/load.py index 3ebe2cf..a3df4c4 100644 --- a/borgmatic/config/load.py +++ b/borgmatic/config/load.py @@ -1,10 +1,11 @@ import os -import logging import ruamel.yaml +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) + +logger = get_logger(__name__) def load_configuration(filename): diff --git a/borgmatic/config/validate.py b/borgmatic/config/validate.py index 7818a74..64b96f5 100644 --- a/borgmatic/config/validate.py +++ b/borgmatic/config/validate.py @@ -6,9 +6,10 @@ import pykwalify.errors import ruamel.yaml from borgmatic.config import load +from borgmatic.logger import get_logger -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def schema_filename(): diff --git a/borgmatic/logger.py b/borgmatic/logger.py new file mode 100644 index 0000000..cd71c21 --- /dev/null +++ b/borgmatic/logger.py @@ -0,0 +1,75 @@ +import logging +import os +import sys + +import colorama + + +def to_bool(arg): + ''' + Return a boolean value based on `arg`. + ''' + if arg is None or isinstance(arg, bool): + return arg + + if isinstance(arg, str): + arg = arg.lower() + + if arg in ('yes', 'on', '1', 'true', 1): + return True + + return False + + +def should_do_markup(no_colour): + ''' + Determine if we should enable colorama marking up. + ''' + if no_colour: + return False + + py_colors = os.environ.get('PY_COLORS', None) + + if py_colors is not None: + return to_bool(py_colors) + + return sys.stdout.isatty() and os.environ.get('TERM') != 'dumb' + + +class BorgmaticLogger(logging.Logger): + def warn(self, msg, *args, **kwargs): + return super(BorgmaticLogger, self).warn( + color_text(colorama.Fore.YELLOW, msg), *args, **kwargs + ) + + def info(self, msg, *args, **kwargs): + return super(BorgmaticLogger, self).info( + color_text(colorama.Fore.GREEN, msg), *args, **kwargs + ) + + def debug(self, msg, *args, **kwargs): + return super(BorgmaticLogger, self).debug( + color_text(colorama.Fore.CYAN, msg), *args, **kwargs + ) + + def critical(self, msg, *args, **kwargs): + return super(BorgmaticLogger, self).critical( + color_text(colorama.Fore.RED, msg), *args, **kwargs + ) + + +def get_logger(name=None): + ''' + Build a logger with the given name. + ''' + logging.setLoggerClass(BorgmaticLogger) + logger = logging.getLogger(name) + logger.propagate = False + return logger + + +def color_text(color, msg): + ''' + Give colored text. + ''' + return '{}{}{}'.format(color, msg, colorama.Style.RESET_ALL)