Browse Source

Fix logs that interfere with JSON output by making warnings go to stderr instead of stdout (#602).

Dan Helfman 2 years ago
parent
commit
c657764367

+ 3 - 0
NEWS

@@ -1,3 +1,6 @@
+1.7.6.dev0
+ * #602: Fix logs that interfere with JSON output by making warnings go to stderr instead of stdout.
+
 1.7.5
 1.7.5
  * #311: Override PostgreSQL dump/restore commands via configuration options.
  * #311: Override PostgreSQL dump/restore commands via configuration options.
  * #604: Fix traceback when a configuration section is present but lacking any options.
  * #604: Fix traceback when a configuration section is present but lacking any options.

+ 3 - 1
borgmatic/borg/borg.py

@@ -1,5 +1,6 @@
 import logging
 import logging
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, flags
 from borgmatic.borg import environment, flags
 from borgmatic.execute import execute_command
 from borgmatic.execute import execute_command
 
 
@@ -25,6 +26,7 @@ def run_arbitrary_borg(
     sequence of arbitrary command-line Borg options, and an optional archive name, run an arbitrary
     sequence of arbitrary command-line Borg options, and an optional archive name, run an arbitrary
     Borg command on the given repository/archive.
     Borg command on the given repository/archive.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
     lock_wait = storage_config.get('lock_wait', None)
     lock_wait = storage_config.get('lock_wait', None)
 
 
     try:
     try:
@@ -60,7 +62,7 @@ def run_arbitrary_borg(
 
 
     return execute_command(
     return execute_command(
         full_command,
         full_command,
-        output_log_level=logging.WARNING,
+        output_log_level=logging.ANSWER,
         borg_local_path=local_path,
         borg_local_path=local_path,
         extra_environment=environment.make_environment(storage_config),
         extra_environment=environment.make_environment(storage_config),
     )
     )

+ 4 - 2
borgmatic/borg/create.py

@@ -6,6 +6,7 @@ import pathlib
 import stat
 import stat
 import tempfile
 import tempfile
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, feature, flags, state
 from borgmatic.borg import environment, feature, flags, state
 from borgmatic.execute import (
 from borgmatic.execute import (
     DO_NOT_CAPTURE,
     DO_NOT_CAPTURE,
@@ -305,6 +306,7 @@ def create_archive(
     If a sequence of stream processes is given (instances of subprocess.Popen), then execute the
     If a sequence of stream processes is given (instances of subprocess.Popen), then execute the
     create command while also triggering the given processes to produce output.
     create command while also triggering the given processes to produce output.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
     borgmatic_source_directories = expand_directories(
     borgmatic_source_directories = expand_directories(
         collect_borgmatic_source_directories(location_config.get('borgmatic_source_directory'))
         collect_borgmatic_source_directories(location_config.get('borgmatic_source_directory'))
     )
     )
@@ -406,8 +408,8 @@ def create_archive(
 
 
     if json:
     if json:
         output_log_level = None
         output_log_level = None
-    elif (stats or list_files) and logger.getEffectiveLevel() == logging.WARNING:
-        output_log_level = logging.WARNING
+    elif list_files or (stats and not dry_run):
+        output_log_level = logging.ANSWER
     else:
     else:
         output_log_level = logging.INFO
         output_log_level = logging.INFO
 
 

+ 4 - 2
borgmatic/borg/export_tar.py

@@ -1,6 +1,7 @@
 import logging
 import logging
 import os
 import os
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, flags
 from borgmatic.borg import environment, flags
 from borgmatic.execute import DO_NOT_CAPTURE, execute_command
 from borgmatic.execute import DO_NOT_CAPTURE, execute_command
 
 
@@ -30,6 +31,7 @@ def export_tar_archive(
 
 
     If the destination path is "-", then stream the output to stdout instead of to a file.
     If the destination path is "-", then stream the output to stdout instead of to a file.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
     umask = storage_config.get('umask', None)
     umask = storage_config.get('umask', None)
     lock_wait = storage_config.get('lock_wait', None)
     lock_wait = storage_config.get('lock_wait', None)
 
 
@@ -53,8 +55,8 @@ def export_tar_archive(
         + (tuple(paths) if paths else ())
         + (tuple(paths) if paths else ())
     )
     )
 
 
-    if list_files and logger.getEffectiveLevel() == logging.WARNING:
-        output_log_level = logging.WARNING
+    if list_files:
+        output_log_level = logging.ANSWER
     else:
     else:
         output_log_level = logging.INFO
         output_log_level = logging.INFO
 
 

+ 3 - 1
borgmatic/borg/info.py

@@ -1,5 +1,6 @@
 import logging
 import logging
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, feature, flags
 from borgmatic.borg import environment, feature, flags
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 
 
@@ -19,6 +20,7 @@ def display_archives_info(
     arguments to the info action, display summary information for Borg archives in the repository or
     arguments to the info action, display summary information for Borg archives in the repository or
     return JSON summary information.
     return JSON summary information.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
     lock_wait = storage_config.get('lock_wait', None)
     lock_wait = storage_config.get('lock_wait', None)
 
 
     full_command = (
     full_command = (
@@ -62,7 +64,7 @@ def display_archives_info(
     else:
     else:
         execute_command(
         execute_command(
             full_command,
             full_command,
-            output_log_level=logging.WARNING,
+            output_log_level=logging.ANSWER,
             borg_local_path=local_path,
             borg_local_path=local_path,
             extra_environment=environment.make_environment(storage_config),
             extra_environment=environment.make_environment(storage_config),
         )
         )

+ 5 - 2
borgmatic/borg/list.py

@@ -3,6 +3,7 @@ import copy
 import logging
 import logging
 import re
 import re
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, feature, flags, rlist
 from borgmatic.borg import environment, feature, flags, rlist
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 
 
@@ -99,6 +100,8 @@ def list_archive(
     list the files by searching across multiple archives. If neither find_paths nor archive name
     list the files by searching across multiple archives. If neither find_paths nor archive name
     are given, instead list the archives in the given repository.
     are given, instead list the archives in the given repository.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
+
     if not list_arguments.archive and not list_arguments.find_paths:
     if not list_arguments.archive and not list_arguments.find_paths:
         if feature.available(feature.Feature.RLIST, local_borg_version):
         if feature.available(feature.Feature.RLIST, local_borg_version):
             logger.warning(
             logger.warning(
@@ -170,7 +173,7 @@ def list_archive(
 
 
     # For each archive listed by Borg, run list on the contents of that archive.
     # For each archive listed by Borg, run list on the contents of that archive.
     for archive in archive_lines:
     for archive in archive_lines:
-        logger.warning(f'{repository}: Listing archive {archive}')
+        logger.answer(f'{repository}: Listing archive {archive}')
 
 
         archive_arguments = copy.copy(list_arguments)
         archive_arguments = copy.copy(list_arguments)
         archive_arguments.archive = archive
         archive_arguments.archive = archive
@@ -191,7 +194,7 @@ def list_archive(
 
 
         execute_command(
         execute_command(
             main_command,
             main_command,
-            output_log_level=logging.WARNING,
+            output_log_level=logging.ANSWER,
             borg_local_path=local_path,
             borg_local_path=local_path,
             extra_environment=borg_environment,
             extra_environment=borg_environment,
         )
         )

+ 4 - 2
borgmatic/borg/prune.py

@@ -1,5 +1,6 @@
 import logging
 import logging
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, feature, flags
 from borgmatic.borg import environment, feature, flags
 from borgmatic.execute import execute_command
 from borgmatic.execute import execute_command
 
 
@@ -52,6 +53,7 @@ def prune_archives(
     retention config dict, prune Borg archives according to the retention policy specified in that
     retention config dict, prune Borg archives according to the retention policy specified in that
     configuration.
     configuration.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
     umask = storage_config.get('umask', None)
     umask = storage_config.get('umask', None)
     lock_wait = storage_config.get('lock_wait', None)
     lock_wait = storage_config.get('lock_wait', None)
     extra_borg_options = storage_config.get('extra_borg_options', {}).get('prune', '')
     extra_borg_options = storage_config.get('extra_borg_options', {}).get('prune', '')
@@ -75,8 +77,8 @@ def prune_archives(
         + flags.make_repository_flags(repository, local_borg_version)
         + flags.make_repository_flags(repository, local_borg_version)
     )
     )
 
 
-    if (stats or list_archives) and logger.getEffectiveLevel() == logging.WARNING:
-        output_log_level = logging.WARNING
+    if stats or list_archives:
+        output_log_level = logging.ANSWER
     else:
     else:
         output_log_level = logging.INFO
         output_log_level = logging.INFO
 
 

+ 3 - 1
borgmatic/borg/rinfo.py

@@ -1,5 +1,6 @@
 import logging
 import logging
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, feature, flags
 from borgmatic.borg import environment, feature, flags
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 
 
@@ -19,6 +20,7 @@ def display_repository_info(
     arguments to the rinfo action, display summary information for the Borg repository or return
     arguments to the rinfo action, display summary information for the Borg repository or return
     JSON summary information.
     JSON summary information.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
     lock_wait = storage_config.get('lock_wait', None)
     lock_wait = storage_config.get('lock_wait', None)
 
 
     full_command = (
     full_command = (
@@ -53,7 +55,7 @@ def display_repository_info(
     else:
     else:
         execute_command(
         execute_command(
             full_command,
             full_command,
-            output_log_level=logging.WARNING,
+            output_log_level=logging.ANSWER,
             borg_local_path=local_path,
             borg_local_path=local_path,
             extra_environment=extra_environment,
             extra_environment=extra_environment,
         )
         )

+ 3 - 1
borgmatic/borg/rlist.py

@@ -1,5 +1,6 @@
 import logging
 import logging
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, feature, flags
 from borgmatic.borg import environment, feature, flags
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 from borgmatic.execute import execute_command, execute_command_and_capture_output
 
 
@@ -108,6 +109,7 @@ def list_repository(
     arguments to the list action, and local and remote Borg paths, display the output of listing
     arguments to the list action, and local and remote Borg paths, display the output of listing
     Borg archives in the given repository (or return JSON output).
     Borg archives in the given repository (or return JSON output).
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
     borg_environment = environment.make_environment(storage_config)
     borg_environment = environment.make_environment(storage_config)
 
 
     main_command = make_rlist_command(
     main_command = make_rlist_command(
@@ -119,7 +121,7 @@ def list_repository(
     else:
     else:
         execute_command(
         execute_command(
             main_command,
             main_command,
-            output_log_level=logging.WARNING,
+            output_log_level=logging.ANSWER,
             borg_local_path=local_path,
             borg_local_path=local_path,
             extra_environment=borg_environment,
             extra_environment=borg_environment,
         )
         )

+ 4 - 1
borgmatic/borg/transfer.py

@@ -1,5 +1,6 @@
 import logging
 import logging
 
 
+import borgmatic.logger
 from borgmatic.borg import environment, flags
 from borgmatic.borg import environment, flags
 from borgmatic.execute import execute_command
 from borgmatic.execute import execute_command
 
 
@@ -19,6 +20,8 @@ def transfer_archives(
     Given a dry-run flag, a local or remote repository path, a storage config dict, the local Borg
     Given a dry-run flag, a local or remote repository path, a storage config dict, the local Borg
     version, and the arguments to the transfer action, transfer archives to the given repository.
     version, and the arguments to the transfer action, transfer archives to the given repository.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
+
     full_command = (
     full_command = (
         (local_path, 'transfer')
         (local_path, 'transfer')
         + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
         + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
@@ -41,7 +44,7 @@ def transfer_archives(
 
 
     return execute_command(
     return execute_command(
         full_command,
         full_command,
-        output_log_level=logging.WARNING,
+        output_log_level=logging.ANSWER,
         borg_local_path=local_path,
         borg_local_path=local_path,
         extra_environment=environment.make_environment(storage_config),
         extra_environment=environment.make_environment(storage_config),
     )
     )

+ 9 - 8
borgmatic/commands/borgmatic.py

@@ -33,7 +33,7 @@ from borgmatic.borg import version as borg_version
 from borgmatic.commands.arguments import parse_arguments
 from borgmatic.commands.arguments import parse_arguments
 from borgmatic.config import checks, collect, convert, validate
 from borgmatic.config import checks, collect, convert, validate
 from borgmatic.hooks import command, dispatch, dump, monitor
 from borgmatic.hooks import command, dispatch, dump, monitor
-from borgmatic.logger import configure_logging, should_do_markup
+from borgmatic.logger import add_custom_log_levels, configure_logging, should_do_markup
 from borgmatic.signals import configure_signals
 from borgmatic.signals import configure_signals
 from borgmatic.verbosity import verbosity_to_log_level
 from borgmatic.verbosity import verbosity_to_log_level
 
 
@@ -244,6 +244,7 @@ def run_actions(
     action or a hook. Raise ValueError if the arguments or configuration passed to action are
     action or a hook. Raise ValueError if the arguments or configuration passed to action are
     invalid.
     invalid.
     '''
     '''
+    add_custom_log_levels()
     repository = os.path.expanduser(repository_path)
     repository = os.path.expanduser(repository_path)
     global_arguments = arguments['global']
     global_arguments = arguments['global']
     dry_run_label = ' (dry run; not making any changes)' if global_arguments.dry_run else ''
     dry_run_label = ' (dry run; not making any changes)' if global_arguments.dry_run else ''
@@ -651,7 +652,7 @@ def run_actions(
         ):
         ):
             rlist_arguments = copy.copy(arguments['rlist'])
             rlist_arguments = copy.copy(arguments['rlist'])
             if not rlist_arguments.json:  # pragma: nocover
             if not rlist_arguments.json:  # pragma: nocover
-                logger.warning('{}: Listing repository'.format(repository))
+                logger.answer('{}: Listing repository'.format(repository))
             json_output = borg_rlist.list_repository(
             json_output = borg_rlist.list_repository(
                 repository,
                 repository,
                 storage,
                 storage,
@@ -669,9 +670,9 @@ def run_actions(
             list_arguments = copy.copy(arguments['list'])
             list_arguments = copy.copy(arguments['list'])
             if not list_arguments.json:  # pragma: nocover
             if not list_arguments.json:  # pragma: nocover
                 if list_arguments.find_paths:
                 if list_arguments.find_paths:
-                    logger.warning('{}: Searching archives'.format(repository))
+                    logger.answer('{}: Searching archives'.format(repository))
                 elif not list_arguments.archive:
                 elif not list_arguments.archive:
-                    logger.warning('{}: Listing archives'.format(repository))
+                    logger.answer('{}: Listing archives'.format(repository))
             list_arguments.archive = borg_rlist.resolve_archive_name(
             list_arguments.archive = borg_rlist.resolve_archive_name(
                 repository,
                 repository,
                 list_arguments.archive,
                 list_arguments.archive,
@@ -696,7 +697,7 @@ def run_actions(
         ):
         ):
             rinfo_arguments = copy.copy(arguments['rinfo'])
             rinfo_arguments = copy.copy(arguments['rinfo'])
             if not rinfo_arguments.json:  # pragma: nocover
             if not rinfo_arguments.json:  # pragma: nocover
-                logger.warning('{}: Displaying repository summary information'.format(repository))
+                logger.answer('{}: Displaying repository summary information'.format(repository))
             json_output = borg_rinfo.display_repository_info(
             json_output = borg_rinfo.display_repository_info(
                 repository,
                 repository,
                 storage,
                 storage,
@@ -713,7 +714,7 @@ def run_actions(
         ):
         ):
             info_arguments = copy.copy(arguments['info'])
             info_arguments = copy.copy(arguments['info'])
             if not info_arguments.json:  # pragma: nocover
             if not info_arguments.json:  # pragma: nocover
-                logger.warning('{}: Displaying archive summary information'.format(repository))
+                logger.answer('{}: Displaying archive summary information'.format(repository))
             info_arguments.archive = borg_rlist.resolve_archive_name(
             info_arguments.archive = borg_rlist.resolve_archive_name(
                 repository,
                 repository,
                 info_arguments.archive,
                 info_arguments.archive,
@@ -736,7 +737,7 @@ def run_actions(
         if arguments['break-lock'].repository is None or validate.repositories_match(
         if arguments['break-lock'].repository is None or validate.repositories_match(
             repository, arguments['break-lock'].repository
             repository, arguments['break-lock'].repository
         ):
         ):
-            logger.warning(f'{repository}: Breaking repository and cache locks')
+            logger.info(f'{repository}: Breaking repository and cache locks')
             borg_break_lock.break_lock(
             borg_break_lock.break_lock(
                 repository,
                 repository,
                 storage,
                 storage,
@@ -748,7 +749,7 @@ def run_actions(
         if arguments['borg'].repository is None or validate.repositories_match(
         if arguments['borg'].repository is None or validate.repositories_match(
             repository, arguments['borg'].repository
             repository, arguments['borg'].repository
         ):
         ):
-            logger.warning('{}: Running arbitrary Borg command'.format(repository))
+            logger.info('{}: Running arbitrary Borg command'.format(repository))
             archive_name = borg_rlist.resolve_archive_name(
             archive_name = borg_rlist.resolve_archive_name(
                 repository,
                 repository,
                 arguments['borg'].archive,
                 arguments['borg'].archive,

+ 54 - 11
borgmatic/logger.py

@@ -85,18 +85,19 @@ class Multi_stream_handler(logging.Handler):
             handler.setLevel(level)
             handler.setLevel(level)
 
 
 
 
-LOG_LEVEL_TO_COLOR = {
-    logging.CRITICAL: colorama.Fore.RED,
-    logging.ERROR: colorama.Fore.RED,
-    logging.WARN: colorama.Fore.YELLOW,
-    logging.INFO: colorama.Fore.GREEN,
-    logging.DEBUG: colorama.Fore.CYAN,
-}
-
-
 class Console_color_formatter(logging.Formatter):
 class Console_color_formatter(logging.Formatter):
     def format(self, record):
     def format(self, record):
-        color = LOG_LEVEL_TO_COLOR.get(record.levelno)
+        add_custom_log_levels()
+
+        color = {
+            logging.CRITICAL: colorama.Fore.RED,
+            logging.ERROR: colorama.Fore.RED,
+            logging.WARN: colorama.Fore.YELLOW,
+            logging.ANSWER: colorama.Fore.MAGENTA,
+            logging.INFO: colorama.Fore.GREEN,
+            logging.DEBUG: colorama.Fore.CYAN,
+        }.get(record.levelno)
+
         return color_text(color, record.msg)
         return color_text(color, record.msg)
 
 
 
 
@@ -110,6 +111,45 @@ def color_text(color, message):
     return '{}{}{}'.format(color, message, colorama.Style.RESET_ALL)
     return '{}{}{}'.format(color, message, colorama.Style.RESET_ALL)
 
 
 
 
+def add_logging_level(level_name, level_number):
+    '''
+    Globally add a custom logging level based on the given (all uppercase) level name and number.
+    Do this idempotently.
+
+    Inspired by https://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility/35804945#35804945
+    '''
+    method_name = level_name.lower()
+
+    if not hasattr(logging, level_name):
+        logging.addLevelName(level_number, level_name)
+        setattr(logging, level_name, level_number)
+
+    if not hasattr(logging, method_name):
+
+        def log_for_level(self, message, *args, **kwargs):  # pragma: no cover
+            if self.isEnabledFor(level_number):
+                self._log(level_number, message, args, **kwargs)
+
+        setattr(logging.getLoggerClass(), method_name, log_for_level)
+
+    if not hasattr(logging.getLoggerClass(), method_name):
+
+        def log_to_root(message, *args, **kwargs):  # pragma: no cover
+            logging.log(level_number, message, *args, **kwargs)
+
+        setattr(logging, method_name, log_to_root)
+
+
+ANSWER = logging.WARN - 5
+
+
+def add_custom_log_levels():  # pragma: no cover
+    '''
+    Add a custom log level between WARN and INFO for user-requested answers.
+    '''
+    add_logging_level('ANSWER', ANSWER)
+
+
 def configure_logging(
 def configure_logging(
     console_log_level,
     console_log_level,
     syslog_log_level=None,
     syslog_log_level=None,
@@ -130,6 +170,8 @@ def configure_logging(
     if monitoring_log_level is None:
     if monitoring_log_level is None:
         monitoring_log_level = console_log_level
         monitoring_log_level = console_log_level
 
 
+    add_custom_log_levels()
+
     # Log certain log levels to console stderr and others to stdout. This supports use cases like
     # Log certain log levels to console stderr and others to stdout. This supports use cases like
     # grepping (non-error) output.
     # grepping (non-error) output.
     console_error_handler = logging.StreamHandler(sys.stderr)
     console_error_handler = logging.StreamHandler(sys.stderr)
@@ -138,7 +180,8 @@ def configure_logging(
         {
         {
             logging.CRITICAL: console_error_handler,
             logging.CRITICAL: console_error_handler,
             logging.ERROR: console_error_handler,
             logging.ERROR: console_error_handler,
-            logging.WARN: console_standard_handler,
+            logging.WARN: console_error_handler,
+            logging.ANSWER: console_standard_handler,
             logging.INFO: console_standard_handler,
             logging.INFO: console_standard_handler,
             logging.DEBUG: console_standard_handler,
             logging.DEBUG: console_standard_handler,
         }
         }

+ 6 - 2
borgmatic/verbosity.py

@@ -1,7 +1,9 @@
 import logging
 import logging
 
 
+import borgmatic.logger
+
 VERBOSITY_ERROR = -1
 VERBOSITY_ERROR = -1
-VERBOSITY_WARNING = 0
+VERBOSITY_ANSWER = 0
 VERBOSITY_SOME = 1
 VERBOSITY_SOME = 1
 VERBOSITY_LOTS = 2
 VERBOSITY_LOTS = 2
 
 
@@ -10,9 +12,11 @@ def verbosity_to_log_level(verbosity):
     '''
     '''
     Given a borgmatic verbosity value, return the corresponding Python log level.
     Given a borgmatic verbosity value, return the corresponding Python log level.
     '''
     '''
+    borgmatic.logger.add_custom_log_levels()
+
     return {
     return {
         VERBOSITY_ERROR: logging.ERROR,
         VERBOSITY_ERROR: logging.ERROR,
-        VERBOSITY_WARNING: logging.WARNING,
+        VERBOSITY_ANSWER: logging.ANSWER,
         VERBOSITY_SOME: logging.INFO,
         VERBOSITY_SOME: logging.INFO,
         VERBOSITY_LOTS: logging.DEBUG,
         VERBOSITY_LOTS: logging.DEBUG,
     }.get(verbosity, logging.WARNING)
     }.get(verbosity, logging.WARNING)

+ 1 - 1
setup.py

@@ -1,6 +1,6 @@
 from setuptools import find_packages, setup
 from setuptools import find_packages, setup
 
 
-VERSION = '1.7.5'
+VERSION = '1.7.6.dev0'
 
 
 
 
 setup(
 setup(

+ 45 - 14
tests/unit/borg/test_borg.py

@@ -8,12 +8,14 @@ from ..test_verbosity import insert_logging_mock
 
 
 
 
 def test_run_arbitrary_borg_calls_borg_with_parameters():
 def test_run_arbitrary_borg_calls_borg_with_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'break-lock', 'repo'),
         ('borg', 'break-lock', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -24,12 +26,14 @@ def test_run_arbitrary_borg_calls_borg_with_parameters():
 
 
 
 
 def test_run_arbitrary_borg_with_log_info_calls_borg_with_info_parameter():
 def test_run_arbitrary_borg_with_log_info_calls_borg_with_info_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'break-lock', 'repo', '--info'),
         ('borg', 'break-lock', 'repo', '--info'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -41,12 +45,14 @@ def test_run_arbitrary_borg_with_log_info_calls_borg_with_info_parameter():
 
 
 
 
 def test_run_arbitrary_borg_with_log_debug_calls_borg_with_debug_parameter():
 def test_run_arbitrary_borg_with_log_debug_calls_borg_with_debug_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'break-lock', 'repo', '--debug', '--show-rc'),
         ('borg', 'break-lock', 'repo', '--debug', '--show-rc'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -58,6 +64,8 @@ def test_run_arbitrary_borg_with_log_debug_calls_borg_with_debug_parameter():
 
 
 
 
 def test_run_arbitrary_borg_with_lock_wait_calls_borg_with_lock_wait_parameters():
 def test_run_arbitrary_borg_with_lock_wait_calls_borg_with_lock_wait_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     storage_config = {'lock_wait': 5}
     storage_config = {'lock_wait': 5}
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(
     flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(
@@ -66,7 +74,7 @@ def test_run_arbitrary_borg_with_lock_wait_calls_borg_with_lock_wait_parameters(
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'break-lock', 'repo', '--lock-wait', '5'),
         ('borg', 'break-lock', 'repo', '--lock-wait', '5'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -80,6 +88,8 @@ def test_run_arbitrary_borg_with_lock_wait_calls_borg_with_lock_wait_parameters(
 
 
 
 
 def test_run_arbitrary_borg_with_archive_calls_borg_with_archive_parameter():
 def test_run_arbitrary_borg_with_archive_calls_borg_with_archive_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -87,7 +97,7 @@ def test_run_arbitrary_borg_with_archive_calls_borg_with_archive_parameter():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'break-lock', 'repo::archive'),
         ('borg', 'break-lock', 'repo::archive'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -102,12 +112,14 @@ def test_run_arbitrary_borg_with_archive_calls_borg_with_archive_parameter():
 
 
 
 
 def test_run_arbitrary_borg_with_local_path_calls_borg_via_local_path():
 def test_run_arbitrary_borg_with_local_path_calls_borg_via_local_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg1', 'break-lock', 'repo'),
         ('borg1', 'break-lock', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg1',
         borg_local_path='borg1',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -122,6 +134,8 @@ def test_run_arbitrary_borg_with_local_path_calls_borg_via_local_path():
 
 
 
 
 def test_run_arbitrary_borg_with_remote_path_calls_borg_with_remote_path_parameters():
 def test_run_arbitrary_borg_with_remote_path_calls_borg_with_remote_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(
     flexmock(module.flags).should_receive('make_flags').and_return(
         ('--remote-path', 'borg1')
         ('--remote-path', 'borg1')
@@ -129,7 +143,7 @@ def test_run_arbitrary_borg_with_remote_path_calls_borg_with_remote_path_paramet
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'break-lock', 'repo', '--remote-path', 'borg1'),
         ('borg', 'break-lock', 'repo', '--remote-path', 'borg1'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -144,12 +158,14 @@ def test_run_arbitrary_borg_with_remote_path_calls_borg_with_remote_path_paramet
 
 
 
 
 def test_run_arbitrary_borg_passes_borg_specific_parameters_to_borg():
 def test_run_arbitrary_borg_passes_borg_specific_parameters_to_borg():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo', '--progress'),
         ('borg', 'list', 'repo', '--progress'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -163,12 +179,14 @@ def test_run_arbitrary_borg_passes_borg_specific_parameters_to_borg():
 
 
 
 
 def test_run_arbitrary_borg_omits_dash_dash_in_parameters_passed_to_borg():
 def test_run_arbitrary_borg_omits_dash_dash_in_parameters_passed_to_borg():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'break-lock', 'repo'),
         ('borg', 'break-lock', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -182,11 +200,16 @@ def test_run_arbitrary_borg_omits_dash_dash_in_parameters_passed_to_borg():
 
 
 
 
 def test_run_arbitrary_borg_without_borg_specific_parameters_does_not_raise():
 def test_run_arbitrary_borg_without_borg_specific_parameters_does_not_raise():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').never()
     flexmock(module.flags).should_receive('make_repository_flags').never()
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
-        ('borg',), output_log_level=logging.WARNING, borg_local_path='borg', extra_environment=None,
+        ('borg',),
+        output_log_level=module.borgmatic.logger.ANSWER,
+        borg_local_path='borg',
+        extra_environment=None,
     )
     )
 
 
     module.run_arbitrary_borg(
     module.run_arbitrary_borg(
@@ -195,12 +218,14 @@ def test_run_arbitrary_borg_without_borg_specific_parameters_does_not_raise():
 
 
 
 
 def test_run_arbitrary_borg_passes_key_sub_command_to_borg_before_repository():
 def test_run_arbitrary_borg_passes_key_sub_command_to_borg_before_repository():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'key', 'export', 'repo'),
         ('borg', 'key', 'export', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -211,12 +236,14 @@ def test_run_arbitrary_borg_passes_key_sub_command_to_borg_before_repository():
 
 
 
 
 def test_run_arbitrary_borg_passes_debug_sub_command_to_borg_before_repository():
 def test_run_arbitrary_borg_passes_debug_sub_command_to_borg_before_repository():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'debug', 'dump-manifest', 'repo', 'path'),
         ('borg', 'debug', 'dump-manifest', 'repo', 'path'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -230,12 +257,14 @@ def test_run_arbitrary_borg_passes_debug_sub_command_to_borg_before_repository()
 
 
 
 
 def test_run_arbitrary_borg_with_debug_info_command_does_not_pass_borg_repository():
 def test_run_arbitrary_borg_with_debug_info_command_does_not_pass_borg_repository():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').never()
     flexmock(module.flags).should_receive('make_repository_flags').never()
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'debug', 'info'),
         ('borg', 'debug', 'info'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -246,12 +275,14 @@ def test_run_arbitrary_borg_with_debug_info_command_does_not_pass_borg_repositor
 
 
 
 
 def test_run_arbitrary_borg_with_debug_convert_profile_command_does_not_pass_borg_repository():
 def test_run_arbitrary_borg_with_debug_convert_profile_command_does_not_pass_borg_repository():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_flags').never()
     flexmock(module.flags).should_receive('make_repository_flags').never()
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'debug', 'convert-profile', 'in', 'out'),
         ('borg', 'debug', 'convert-profile', 'in', 'out'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )

+ 92 - 86
tests/unit/borg/test_create.py

@@ -413,6 +413,8 @@ REPO_ARCHIVE_WITH_PATHS = (f'repo::{DEFAULT_ARCHIVE_NAME}', 'foo', 'bar')
 
 
 
 
 def test_create_archive_calls_borg_with_parameters():
 def test_create_archive_calls_borg_with_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -452,6 +454,8 @@ def test_create_archive_calls_borg_with_parameters():
 
 
 
 
 def test_create_archive_calls_borg_with_environment():
 def test_create_archive_calls_borg_with_environment():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -492,6 +496,8 @@ def test_create_archive_calls_borg_with_environment():
 
 
 
 
 def test_create_archive_with_patterns_calls_borg_with_patterns_including_converted_source_directories():
 def test_create_archive_with_patterns_calls_borg_with_patterns_including_converted_source_directories():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     pattern_flags = ('--patterns-from', 'patterns')
     pattern_flags = ('--patterns-from', 'patterns')
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -534,6 +540,8 @@ def test_create_archive_with_patterns_calls_borg_with_patterns_including_convert
 
 
 
 
 def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
 def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     exclude_flags = ('--exclude-from', 'excludes')
     exclude_flags = ('--exclude-from', 'excludes')
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -576,6 +584,8 @@ def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
 
 
 
 
 def test_create_archive_with_log_info_calls_borg_with_info_parameter():
 def test_create_archive_with_log_info_calls_borg_with_info_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -616,6 +626,8 @@ def test_create_archive_with_log_info_calls_borg_with_info_parameter():
 
 
 
 
 def test_create_archive_with_log_info_and_json_suppresses_most_borg_output():
 def test_create_archive_with_log_info_and_json_suppresses_most_borg_output():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -654,6 +666,8 @@ def test_create_archive_with_log_info_and_json_suppresses_most_borg_output():
 
 
 
 
 def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
 def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -694,6 +708,8 @@ def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
 
 
 
 
 def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output():
 def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -732,6 +748,8 @@ def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output():
 
 
 
 
 def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
 def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -773,6 +791,8 @@ def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
 def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats_parameter():
 def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats_parameter():
     # --dry-run and --stats are mutually exclusive, see:
     # --dry-run and --stats are mutually exclusive, see:
     # https://borgbackup.readthedocs.io/en/stable/usage/create.html#description
     # https://borgbackup.readthedocs.io/en/stable/usage/create.html#description
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -814,6 +834,8 @@ def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats_paramete
 
 
 
 
 def test_create_archive_with_checkpoint_interval_calls_borg_with_checkpoint_interval_parameters():
 def test_create_archive_with_checkpoint_interval_calls_borg_with_checkpoint_interval_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -853,6 +875,8 @@ def test_create_archive_with_checkpoint_interval_calls_borg_with_checkpoint_inte
 
 
 
 
 def test_create_archive_with_chunker_params_calls_borg_with_chunker_params_parameters():
 def test_create_archive_with_chunker_params_calls_borg_with_chunker_params_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -892,6 +916,8 @@ def test_create_archive_with_chunker_params_calls_borg_with_chunker_params_param
 
 
 
 
 def test_create_archive_with_compression_calls_borg_with_compression_parameters():
 def test_create_archive_with_compression_calls_borg_with_compression_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -936,6 +962,8 @@ def test_create_archive_with_compression_calls_borg_with_compression_parameters(
 def test_create_archive_with_upload_rate_limit_calls_borg_with_upload_ratelimit_parameters(
 def test_create_archive_with_upload_rate_limit_calls_borg_with_upload_ratelimit_parameters(
     feature_available, option_flag
     feature_available, option_flag
 ):
 ):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -975,6 +1003,8 @@ def test_create_archive_with_upload_rate_limit_calls_borg_with_upload_ratelimit_
 
 
 
 
 def test_create_archive_with_working_directory_calls_borg_with_working_directory():
 def test_create_archive_with_working_directory_calls_borg_with_working_directory():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1017,6 +1047,8 @@ def test_create_archive_with_working_directory_calls_borg_with_working_directory
 
 
 
 
 def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_parameter():
 def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1062,6 +1094,8 @@ def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_par
 def test_create_archive_with_numeric_ids_calls_borg_with_numeric_ids_parameter(
 def test_create_archive_with_numeric_ids_calls_borg_with_numeric_ids_parameter(
     feature_available, option_flag
     feature_available, option_flag
 ):
 ):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1102,6 +1136,8 @@ def test_create_archive_with_numeric_ids_calls_borg_with_numeric_ids_parameter(
 
 
 
 
 def test_create_archive_with_read_special_calls_borg_with_read_special_parameter():
 def test_create_archive_with_read_special_calls_borg_with_read_special_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1158,6 +1194,8 @@ def test_create_archive_with_read_special_calls_borg_with_read_special_parameter
 def test_create_archive_with_basic_option_calls_borg_with_corresponding_parameter(
 def test_create_archive_with_basic_option_calls_borg_with_corresponding_parameter(
     option_name, option_value
     option_name, option_value
 ):
 ):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     option_flag = '--no' + option_name.replace('', '') if option_value is False else None
     option_flag = '--no' + option_name.replace('', '') if option_value is False else None
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -1210,6 +1248,8 @@ def test_create_archive_with_basic_option_calls_borg_with_corresponding_paramete
 def test_create_archive_with_atime_option_calls_borg_with_corresponding_parameter(
 def test_create_archive_with_atime_option_calls_borg_with_corresponding_parameter(
     option_value, feature_available, option_flag
     option_value, feature_available, option_flag
 ):
 ):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1261,6 +1301,8 @@ def test_create_archive_with_atime_option_calls_borg_with_corresponding_paramete
 def test_create_archive_with_flags_option_calls_borg_with_corresponding_parameter(
 def test_create_archive_with_flags_option_calls_borg_with_corresponding_parameter(
     option_value, feature_available, option_flag
     option_value, feature_available, option_flag
 ):
 ):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1301,6 +1343,8 @@ def test_create_archive_with_flags_option_calls_borg_with_corresponding_paramete
 
 
 
 
 def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters():
 def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1341,6 +1385,8 @@ def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters(
 
 
 
 
 def test_create_archive_with_local_path_calls_borg_via_local_path():
 def test_create_archive_with_local_path_calls_borg_via_local_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1381,6 +1427,8 @@ def test_create_archive_with_local_path_calls_borg_via_local_path():
 
 
 
 
 def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters():
 def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1421,6 +1469,8 @@ def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters(
 
 
 
 
 def test_create_archive_with_umask_calls_borg_with_umask_parameters():
 def test_create_archive_with_umask_calls_borg_with_umask_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1460,6 +1510,8 @@ def test_create_archive_with_umask_calls_borg_with_umask_parameters():
 
 
 
 
 def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
 def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1498,7 +1550,9 @@ def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
     )
     )
 
 
 
 
-def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_warning_output_log_level():
+def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_answer_output_log_level():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1517,7 +1571,7 @@ def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_warning_o
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS + ('--stats',),
         ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS + ('--stats',),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
         output_file=None,
         borg_local_path='borg',
         borg_local_path='borg',
         working_directory=None,
         working_directory=None,
@@ -1538,48 +1592,9 @@ def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_warning_o
     )
     )
 
 
 
 
-def test_create_archive_with_stats_and_log_info_calls_borg_with_stats_parameter_and_info_output_log_level():
-    flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
-    flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
-    flexmock(module).should_receive('map_directories_to_devices').and_return({})
-    flexmock(module).should_receive('expand_directories').and_return(())
-    flexmock(module).should_receive('pattern_root_directories').and_return([])
-    flexmock(module.os.path).should_receive('expanduser').and_raise(TypeError)
-    flexmock(module).should_receive('expand_home_directories').and_return(())
-    flexmock(module).should_receive('write_pattern_file').and_return(None)
-    flexmock(module.feature).should_receive('available').and_return(True)
-    flexmock(module).should_receive('ensure_files_readable')
-    flexmock(module).should_receive('make_pattern_flags').and_return(())
-    flexmock(module).should_receive('make_exclude_flags').and_return(())
-    flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
-        (f'repo::{DEFAULT_ARCHIVE_NAME}',)
-    )
-    flexmock(module.environment).should_receive('make_environment')
-    flexmock(module).should_receive('execute_command').with_args(
-        ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS + ('--info', '--stats'),
-        output_log_level=logging.INFO,
-        output_file=None,
-        borg_local_path='borg',
-        working_directory=None,
-        extra_environment=None,
-    )
-    insert_logging_mock(logging.INFO)
-
-    module.create_archive(
-        dry_run=False,
-        repository='repo',
-        location_config={
-            'source_directories': ['foo', 'bar'],
-            'repositories': ['repo'],
-            'exclude_patterns': None,
-        },
-        storage_config={},
-        local_borg_version='1.2.3',
-        stats=True,
-    )
-
-
-def test_create_archive_with_files_calls_borg_with_list_parameter_and_warning_output_log_level():
+def test_create_archive_with_files_calls_borg_with_list_parameter_and_answer_output_log_level():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1598,7 +1613,7 @@ def test_create_archive_with_files_calls_borg_with_list_parameter_and_warning_ou
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--list', '--filter', 'AMEx-') + REPO_ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--list', '--filter', 'AMEx-') + REPO_ARCHIVE_WITH_PATHS,
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
         output_file=None,
         borg_local_path='borg',
         borg_local_path='borg',
         working_directory=None,
         working_directory=None,
@@ -1619,48 +1634,9 @@ def test_create_archive_with_files_calls_borg_with_list_parameter_and_warning_ou
     )
     )
 
 
 
 
-def test_create_archive_with_files_and_log_info_calls_borg_with_list_parameter_and_info_output_log_level():
-    flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
-    flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
-    flexmock(module).should_receive('map_directories_to_devices').and_return({})
-    flexmock(module).should_receive('expand_directories').and_return(())
-    flexmock(module).should_receive('pattern_root_directories').and_return([])
-    flexmock(module.os.path).should_receive('expanduser').and_raise(TypeError)
-    flexmock(module).should_receive('expand_home_directories').and_return(())
-    flexmock(module).should_receive('write_pattern_file').and_return(None)
-    flexmock(module.feature).should_receive('available').and_return(True)
-    flexmock(module).should_receive('ensure_files_readable')
-    flexmock(module).should_receive('make_pattern_flags').and_return(())
-    flexmock(module).should_receive('make_exclude_flags').and_return(())
-    flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
-        (f'repo::{DEFAULT_ARCHIVE_NAME}',)
-    )
-    flexmock(module.environment).should_receive('make_environment')
-    flexmock(module).should_receive('execute_command').with_args(
-        ('borg', 'create', '--list', '--filter', 'AMEx-') + REPO_ARCHIVE_WITH_PATHS + ('--info',),
-        output_log_level=logging.INFO,
-        output_file=None,
-        borg_local_path='borg',
-        working_directory=None,
-        extra_environment=None,
-    )
-    insert_logging_mock(logging.INFO)
-
-    module.create_archive(
-        dry_run=False,
-        repository='repo',
-        location_config={
-            'source_directories': ['foo', 'bar'],
-            'repositories': ['repo'],
-            'exclude_patterns': None,
-        },
-        storage_config={},
-        local_borg_version='1.2.3',
-        list_files=True,
-    )
-
-
 def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_parameter_and_no_list():
 def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_parameter_and_no_list():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1702,6 +1678,8 @@ def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_para
 
 
 
 
 def test_create_archive_with_progress_calls_borg_with_progress_parameter():
 def test_create_archive_with_progress_calls_borg_with_progress_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -1742,6 +1720,8 @@ def test_create_archive_with_progress_calls_borg_with_progress_parameter():
 
 
 
 
 def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progress_parameter():
 def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progress_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     processes = flexmock()
     processes = flexmock()
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -1800,6 +1780,8 @@ def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progr
 
 
 
 
 def test_create_archive_with_stream_processes_ignores_read_special_false_and_logs_warnings():
 def test_create_archive_with_stream_processes_ignores_read_special_false_and_logs_warnings():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     processes = flexmock()
     processes = flexmock()
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -1860,6 +1842,8 @@ def test_create_archive_with_stream_processes_ignores_read_special_false_and_log
 
 
 
 
 def test_create_archive_with_stream_processes_adds_special_files_to_excludes():
 def test_create_archive_with_stream_processes_adds_special_files_to_excludes():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     processes = flexmock()
     processes = flexmock()
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -1924,6 +1908,8 @@ def test_create_archive_with_stream_processes_adds_special_files_to_excludes():
 
 
 
 
 def test_create_archive_with_stream_processes_and_read_special_does_not_add_special_files_to_excludes():
 def test_create_archive_with_stream_processes_and_read_special_does_not_add_special_files_to_excludes():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     processes = flexmock()
     processes = flexmock()
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -1985,6 +1971,8 @@ def test_create_archive_with_stream_processes_and_read_special_does_not_add_spec
 
 
 
 
 def test_create_archive_with_json_calls_borg_with_json_parameter():
 def test_create_archive_with_json_calls_borg_with_json_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -2024,6 +2012,8 @@ def test_create_archive_with_json_calls_borg_with_json_parameter():
 
 
 
 
 def test_create_archive_with_stats_and_json_calls_borg_without_stats_parameter():
 def test_create_archive_with_stats_and_json_calls_borg_without_stats_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -2064,6 +2054,8 @@ def test_create_archive_with_stats_and_json_calls_borg_without_stats_parameter()
 
 
 
 
 def test_create_archive_with_source_directories_glob_expands():
 def test_create_archive_with_source_directories_glob_expands():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'food'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'food'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -2104,6 +2096,8 @@ def test_create_archive_with_source_directories_glob_expands():
 
 
 
 
 def test_create_archive_with_non_matching_source_directories_glob_passes_through():
 def test_create_archive_with_non_matching_source_directories_glob_passes_through():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo*',))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo*',))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -2144,6 +2138,8 @@ def test_create_archive_with_non_matching_source_directories_glob_passes_through
 
 
 
 
 def test_create_archive_with_glob_calls_borg_with_expanded_directories():
 def test_create_archive_with_glob_calls_borg_with_expanded_directories():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'food'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'food'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -2183,6 +2179,8 @@ def test_create_archive_with_glob_calls_borg_with_expanded_directories():
 
 
 
 
 def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
 def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -2222,6 +2220,8 @@ def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
 
 
 
 
 def test_create_archive_with_archive_name_format_accepts_borg_placeholders():
 def test_create_archive_with_archive_name_format_accepts_borg_placeholders():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     repository_archive_pattern = 'repo::Documents_{hostname}-{now}'
     repository_archive_pattern = 'repo::Documents_{hostname}-{now}'
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -2262,6 +2262,8 @@ def test_create_archive_with_archive_name_format_accepts_borg_placeholders():
 
 
 
 
 def test_create_archive_with_repository_accepts_borg_placeholders():
 def test_create_archive_with_repository_accepts_borg_placeholders():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     repository_archive_pattern = '{fqdn}::Documents_{hostname}-{now}'
     repository_archive_pattern = '{fqdn}::Documents_{hostname}-{now}'
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
@@ -2302,6 +2304,8 @@ def test_create_archive_with_repository_accepts_borg_placeholders():
 
 
 
 
 def test_create_archive_with_extra_borg_options_calls_borg_with_extra_options():
 def test_create_archive_with_extra_borg_options_calls_borg_with_extra_options():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
     flexmock(module).should_receive('map_directories_to_devices').and_return({})
@@ -2341,6 +2345,8 @@ def test_create_archive_with_extra_borg_options_calls_borg_with_extra_options():
 
 
 
 
 def test_create_archive_with_stream_processes_calls_borg_with_processes_and_read_special():
 def test_create_archive_with_stream_processes_calls_borg_with_processes_and_read_special():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     processes = flexmock()
     processes = flexmock()
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('collect_borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))

+ 27 - 1
tests/unit/borg/test_export_tar.py

@@ -21,6 +21,8 @@ def insert_execute_command_mock(
 
 
 
 
 def test_export_tar_archive_calls_borg_with_path_parameters():
 def test_export_tar_archive_calls_borg_with_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -41,6 +43,8 @@ def test_export_tar_archive_calls_borg_with_path_parameters():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_local_path_parameters():
 def test_export_tar_archive_calls_borg_with_local_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -62,6 +66,8 @@ def test_export_tar_archive_calls_borg_with_local_path_parameters():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_remote_path_parameters():
 def test_export_tar_archive_calls_borg_with_remote_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -83,6 +89,8 @@ def test_export_tar_archive_calls_borg_with_remote_path_parameters():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_umask_parameters():
 def test_export_tar_archive_calls_borg_with_umask_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -103,6 +111,8 @@ def test_export_tar_archive_calls_borg_with_umask_parameters():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_lock_wait_parameters():
 def test_export_tar_archive_calls_borg_with_lock_wait_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -123,6 +133,8 @@ def test_export_tar_archive_calls_borg_with_lock_wait_parameters():
 
 
 
 
 def test_export_tar_archive_with_log_info_calls_borg_with_info_parameter():
 def test_export_tar_archive_with_log_info_calls_borg_with_info_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -142,6 +154,8 @@ def test_export_tar_archive_with_log_info_calls_borg_with_info_parameter():
 
 
 
 
 def test_export_tar_archive_with_log_debug_calls_borg_with_debug_parameters():
 def test_export_tar_archive_with_log_debug_calls_borg_with_debug_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -163,6 +177,8 @@ def test_export_tar_archive_with_log_debug_calls_borg_with_debug_parameters():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_dry_run_parameter():
 def test_export_tar_archive_calls_borg_with_dry_run_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -181,6 +197,8 @@ def test_export_tar_archive_calls_borg_with_dry_run_parameter():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_tar_filter_parameters():
 def test_export_tar_archive_calls_borg_with_tar_filter_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -202,13 +220,15 @@ def test_export_tar_archive_calls_borg_with_tar_filter_parameters():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_list_parameter():
 def test_export_tar_archive_calls_borg_with_list_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
     flexmock(module.os.path).should_receive('abspath').and_return('repo')
     flexmock(module.os.path).should_receive('abspath').and_return('repo')
     insert_execute_command_mock(
     insert_execute_command_mock(
         ('borg', 'export-tar', '--list', 'repo::archive', 'test.tar'),
         ('borg', 'export-tar', '--list', 'repo::archive', 'test.tar'),
-        output_log_level=logging.WARNING,
+        output_log_level=logging.ANSWER,
     )
     )
 
 
     module.export_tar_archive(
     module.export_tar_archive(
@@ -224,6 +244,8 @@ def test_export_tar_archive_calls_borg_with_list_parameter():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_strip_components_parameter():
 def test_export_tar_archive_calls_borg_with_strip_components_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )
@@ -245,6 +267,8 @@ def test_export_tar_archive_calls_borg_with_strip_components_parameter():
 
 
 
 
 def test_export_tar_archive_skips_abspath_for_remote_repository_parameter():
 def test_export_tar_archive_skips_abspath_for_remote_repository_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('server:repo::archive',)
         ('server:repo::archive',)
     )
     )
@@ -263,6 +287,8 @@ def test_export_tar_archive_skips_abspath_for_remote_repository_parameter():
 
 
 
 
 def test_export_tar_archive_calls_borg_with_stdout_destination_path():
 def test_export_tar_archive_calls_borg_with_stdout_destination_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
     flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
         ('repo::archive',)
         ('repo::archive',)
     )
     )

+ 33 - 9
tests/unit/borg/test_info.py

@@ -9,13 +9,15 @@ from ..test_verbosity import insert_logging_mock
 
 
 
 
 def test_display_archives_info_calls_borg_with_parameters():
 def test_display_archives_info_calls_borg_with_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', '--repo', 'repo'),
         ('borg', 'info', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -29,13 +31,15 @@ def test_display_archives_info_calls_borg_with_parameters():
 
 
 
 
 def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
 def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', '--info', '--repo', 'repo'),
         ('borg', 'info', '--info', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -49,6 +53,8 @@ def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
 
 
 
 
 def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output():
 def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
@@ -69,13 +75,15 @@ def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_outpu
 
 
 
 
 def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
 def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', '--debug', '--show-rc', '--repo', 'repo'),
         ('borg', 'info', '--debug', '--show-rc', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -90,6 +98,8 @@ def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
 
 
 
 
 def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output():
 def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
@@ -110,6 +120,8 @@ def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_outp
 
 
 
 
 def test_display_archives_info_with_json_calls_borg_with_json_parameter():
 def test_display_archives_info_with_json_calls_borg_with_json_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
@@ -129,6 +141,8 @@ def test_display_archives_info_with_json_calls_borg_with_json_parameter():
 
 
 
 
 def test_display_archives_info_with_archive_calls_borg_with_match_archives_parameter():
 def test_display_archives_info_with_archive_calls_borg_with_match_archives_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args(
     flexmock(module.flags).should_receive('make_flags').with_args(
         'match-archives', 'archive'
         'match-archives', 'archive'
@@ -138,7 +152,7 @@ def test_display_archives_info_with_archive_calls_borg_with_match_archives_param
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', '--repo', 'repo', '--match-archives', 'archive'),
         ('borg', 'info', '--repo', 'repo', '--match-archives', 'archive'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -152,13 +166,15 @@ def test_display_archives_info_with_archive_calls_borg_with_match_archives_param
 
 
 
 
 def test_display_archives_info_with_local_path_calls_borg_via_local_path():
 def test_display_archives_info_with_local_path_calls_borg_via_local_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg1', 'info', '--repo', 'repo'),
         ('borg1', 'info', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg1',
         borg_local_path='borg1',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -173,6 +189,8 @@ def test_display_archives_info_with_local_path_calls_borg_via_local_path():
 
 
 
 
 def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters():
 def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args(
     flexmock(module.flags).should_receive('make_flags').with_args(
         'remote-path', 'borg1'
         'remote-path', 'borg1'
@@ -182,7 +200,7 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', '--remote-path', 'borg1', '--repo', 'repo'),
         ('borg', 'info', '--remote-path', 'borg1', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -197,6 +215,8 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
 
 
 
 
 def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
 def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return(
     flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return(
         ('--lock-wait', '5')
         ('--lock-wait', '5')
@@ -207,7 +227,7 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', '--lock-wait', '5', '--repo', 'repo'),
         ('borg', 'info', '--lock-wait', '5', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -221,6 +241,8 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
 
 
 
 
 def test_display_archives_info_with_prefix_calls_borg_with_match_archives_parameters():
 def test_display_archives_info_with_prefix_calls_borg_with_match_archives_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args(
     flexmock(module.flags).should_receive('make_flags').with_args(
         'match-archives', 'sh:foo*'
         'match-archives', 'sh:foo*'
@@ -230,7 +252,7 @@ def test_display_archives_info_with_prefix_calls_borg_with_match_archives_parame
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', '--match-archives', 'sh:foo*', '--repo', 'repo'),
         ('borg', 'info', '--match-archives', 'sh:foo*', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -245,6 +267,8 @@ def test_display_archives_info_with_prefix_calls_borg_with_match_archives_parame
 
 
 @pytest.mark.parametrize('argument_name', ('match_archives', 'sort_by', 'first', 'last'))
 @pytest.mark.parametrize('argument_name', ('match_archives', 'sort_by', 'first', 'last'))
 def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
 def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flag_name = f"--{argument_name.replace('_', ' ')}"
     flag_name = f"--{argument_name.replace('_', ' ')}"
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(
@@ -254,7 +278,7 @@ def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', flag_name, 'value', '--repo', 'repo'),
         ('borg', 'info', flag_name, 'value', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )

+ 35 - 8
tests/unit/borg/test_list.py

@@ -254,6 +254,9 @@ def test_make_find_paths_adds_globs_to_path_fragments():
 
 
 
 
 def test_list_archive_calls_borg_with_parameters():
 def test_list_archive_calls_borg_with_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     list_arguments = argparse.Namespace(
     list_arguments = argparse.Namespace(
         archive='archive',
         archive='archive',
         paths=None,
         paths=None,
@@ -279,7 +282,7 @@ def test_list_archive_calls_borg_with_parameters():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         ('borg', 'list', 'repo::archive'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
@@ -293,6 +296,9 @@ def test_list_archive_calls_borg_with_parameters():
 
 
 
 
 def test_list_archive_with_archive_and_json_errors():
 def test_list_archive_with_archive_and_json_errors():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     list_arguments = argparse.Namespace(archive='archive', paths=None, json=True, find_paths=None)
     list_arguments = argparse.Namespace(archive='archive', paths=None, json=True, find_paths=None)
 
 
     flexmock(module.feature).should_receive('available').and_return(False)
     flexmock(module.feature).should_receive('available').and_return(False)
@@ -307,6 +313,9 @@ def test_list_archive_with_archive_and_json_errors():
 
 
 
 
 def test_list_archive_calls_borg_with_local_path():
 def test_list_archive_calls_borg_with_local_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     list_arguments = argparse.Namespace(
     list_arguments = argparse.Namespace(
         archive='archive',
         archive='archive',
         paths=None,
         paths=None,
@@ -332,7 +341,7 @@ def test_list_archive_calls_borg_with_local_path():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg2', 'list', 'repo::archive'),
         ('borg2', 'list', 'repo::archive'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg2',
         borg_local_path='borg2',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
@@ -347,6 +356,9 @@ def test_list_archive_calls_borg_with_local_path():
 
 
 
 
 def test_list_archive_calls_borg_multiple_times_with_find_paths():
 def test_list_archive_calls_borg_multiple_times_with_find_paths():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     glob_paths = ('**/*foo.txt*/**',)
     glob_paths = ('**/*foo.txt*/**',)
     list_arguments = argparse.Namespace(
     list_arguments = argparse.Namespace(
         archive=None,
         archive=None,
@@ -371,13 +383,13 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive1') + glob_paths,
         ('borg', 'list', 'repo::archive1') + glob_paths,
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive2') + glob_paths,
         ('borg', 'list', 'repo::archive2') + glob_paths,
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
@@ -391,6 +403,9 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
 
 
 
 
 def test_list_archive_calls_borg_with_archive():
 def test_list_archive_calls_borg_with_archive():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     list_arguments = argparse.Namespace(
     list_arguments = argparse.Namespace(
         archive='archive',
         archive='archive',
         paths=None,
         paths=None,
@@ -416,7 +431,7 @@ def test_list_archive_calls_borg_with_archive():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         ('borg', 'list', 'repo::archive'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
@@ -430,6 +445,9 @@ def test_list_archive_calls_borg_with_archive():
 
 
 
 
 def test_list_archive_without_archive_delegates_to_list_repository():
 def test_list_archive_without_archive_delegates_to_list_repository():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     list_arguments = argparse.Namespace(
     list_arguments = argparse.Namespace(
         archive=None,
         archive=None,
         short=None,
         short=None,
@@ -457,6 +475,9 @@ def test_list_archive_without_archive_delegates_to_list_repository():
 
 
 
 
 def test_list_archive_with_borg_features_without_archive_delegates_to_list_repository():
 def test_list_archive_with_borg_features_without_archive_delegates_to_list_repository():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     list_arguments = argparse.Namespace(
     list_arguments = argparse.Namespace(
         archive=None,
         archive=None,
         short=None,
         short=None,
@@ -487,6 +508,9 @@ def test_list_archive_with_borg_features_without_archive_delegates_to_list_repos
     'archive_filter_flag', ('prefix', 'match_archives', 'sort_by', 'first', 'last',),
     'archive_filter_flag', ('prefix', 'match_archives', 'sort_by', 'first', 'last',),
 )
 )
 def test_list_archive_with_archive_ignores_archive_filter_flag(archive_filter_flag,):
 def test_list_archive_with_archive_ignores_archive_filter_flag(archive_filter_flag,):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     default_filter_flags = {
     default_filter_flags = {
         'prefix': None,
         'prefix': None,
         'match_archives': None,
         'match_archives': None,
@@ -513,7 +537,7 @@ def test_list_archive_with_archive_ignores_archive_filter_flag(archive_filter_fl
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         ('borg', 'list', 'repo::archive'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
@@ -534,6 +558,9 @@ def test_list_archive_with_archive_ignores_archive_filter_flag(archive_filter_fl
 def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes_it_to_rlist(
 def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes_it_to_rlist(
     archive_filter_flag,
     archive_filter_flag,
 ):
 ):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.logger).answer = lambda message: None
     default_filter_flags = {
     default_filter_flags = {
         'prefix': None,
         'prefix': None,
         'match_archives': None,
         'match_archives': None,
@@ -600,13 +627,13 @@ def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', '--repo', 'repo', 'archive1') + glob_paths,
         ('borg', 'list', '--repo', 'repo', 'archive1') + glob_paths,
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', '--repo', 'repo', 'archive2') + glob_paths,
         ('borg', 'list', '--repo', 'repo', 'archive2') + glob_paths,
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()

+ 26 - 36
tests/unit/borg/test_prune.py

@@ -78,6 +78,8 @@ PRUNE_COMMAND = ('borg', 'prune', '--keep-daily', '1', '--keep-weekly', '2', '--
 
 
 
 
 def test_prune_archives_calls_borg_with_parameters():
 def test_prune_archives_calls_borg_with_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     insert_execute_command_mock(PRUNE_COMMAND + ('repo',), logging.INFO)
     insert_execute_command_mock(PRUNE_COMMAND + ('repo',), logging.INFO)
@@ -92,6 +94,8 @@ def test_prune_archives_calls_borg_with_parameters():
 
 
 
 
 def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
 def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     insert_execute_command_mock(PRUNE_COMMAND + ('--info', 'repo'), logging.INFO)
     insert_execute_command_mock(PRUNE_COMMAND + ('--info', 'repo'), logging.INFO)
@@ -107,6 +111,8 @@ def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
 
 
 
 
 def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
 def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     insert_execute_command_mock(PRUNE_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO)
     insert_execute_command_mock(PRUNE_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO)
@@ -122,6 +128,8 @@ def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
 
 
 
 
 def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
 def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     insert_execute_command_mock(PRUNE_COMMAND + ('--dry-run', 'repo'), logging.INFO)
     insert_execute_command_mock(PRUNE_COMMAND + ('--dry-run', 'repo'), logging.INFO)
@@ -136,6 +144,8 @@ def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
 
 
 
 
 def test_prune_archives_with_local_path_calls_borg_via_local_path():
 def test_prune_archives_with_local_path_calls_borg_via_local_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     insert_execute_command_mock(('borg1',) + PRUNE_COMMAND[1:] + ('repo',), logging.INFO)
     insert_execute_command_mock(('borg1',) + PRUNE_COMMAND[1:] + ('repo',), logging.INFO)
@@ -151,6 +161,8 @@ def test_prune_archives_with_local_path_calls_borg_via_local_path():
 
 
 
 
 def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters():
 def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     insert_execute_command_mock(PRUNE_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO)
     insert_execute_command_mock(PRUNE_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO)
@@ -165,10 +177,12 @@ def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters(
     )
     )
 
 
 
 
-def test_prune_archives_with_stats_calls_borg_with_stats_parameter_and_warning_output_log_level():
+def test_prune_archives_with_stats_calls_borg_with_stats_parameter_and_answer_output_log_level():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
-    insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), logging.WARNING)
+    insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), module.borgmatic.logger.ANSWER)
 
 
     module.prune_archives(
     module.prune_archives(
         dry_run=False,
         dry_run=False,
@@ -180,42 +194,12 @@ def test_prune_archives_with_stats_calls_borg_with_stats_parameter_and_warning_o
     )
     )
 
 
 
 
-def test_prune_archives_with_stats_and_log_info_calls_borg_with_stats_parameter_and_info_output_log_level():
+def test_prune_archives_with_files_calls_borg_with_list_parameter_and_answer_output_log_level():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
-    insert_logging_mock(logging.INFO)
-    insert_execute_command_mock(PRUNE_COMMAND + ('--stats', '--info', 'repo'), logging.INFO)
-
-    module.prune_archives(
-        dry_run=False,
-        repository='repo',
-        storage_config={},
-        retention_config=flexmock(),
-        local_borg_version='1.2.3',
-        stats=True,
-    )
-
-
-def test_prune_archives_with_files_calls_borg_with_list_parameter_and_warning_output_log_level():
-    flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
-    flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
-    insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), logging.WARNING)
-
-    module.prune_archives(
-        dry_run=False,
-        repository='repo',
-        storage_config={},
-        retention_config=flexmock(),
-        local_borg_version='1.2.3',
-        list_archives=True,
-    )
-
-
-def test_prune_archives_with_files_and_log_info_calls_borg_with_list_parameter_and_info_output_log_level():
-    flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
-    flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
-    insert_logging_mock(logging.INFO)
-    insert_execute_command_mock(PRUNE_COMMAND + ('--info', '--list', 'repo'), logging.INFO)
+    insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), module.borgmatic.logger.ANSWER)
 
 
     module.prune_archives(
     module.prune_archives(
         dry_run=False,
         dry_run=False,
@@ -228,6 +212,8 @@ def test_prune_archives_with_files_and_log_info_calls_borg_with_list_parameter_a
 
 
 
 
 def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
 def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     storage_config = {'umask': '077'}
     storage_config = {'umask': '077'}
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@@ -243,6 +229,8 @@ def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
 
 
 
 
 def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
 def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     storage_config = {'lock_wait': 5}
     storage_config = {'lock_wait': 5}
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@@ -258,6 +246,8 @@ def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
 
 
 
 
 def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options():
 def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     insert_execute_command_mock(PRUNE_COMMAND + ('--extra', '--options', 'repo'), logging.INFO)
     insert_execute_command_mock(PRUNE_COMMAND + ('--extra', '--options', 'repo'), logging.INFO)

+ 27 - 7
tests/unit/borg/test_rinfo.py

@@ -8,12 +8,14 @@ from ..test_verbosity import insert_logging_mock
 
 
 
 
 def test_display_repository_info_calls_borg_with_parameters():
 def test_display_repository_info_calls_borg_with_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'rinfo', '--repo', 'repo'),
         ('borg', 'rinfo', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -27,12 +29,14 @@ def test_display_repository_info_calls_borg_with_parameters():
 
 
 
 
 def test_display_repository_info_without_borg_features_calls_borg_with_info_sub_command():
 def test_display_repository_info_without_borg_features_calls_borg_with_info_sub_command():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(False)
     flexmock(module.feature).should_receive('available').and_return(False)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', 'repo'),
         ('borg', 'info', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -46,12 +50,14 @@ def test_display_repository_info_without_borg_features_calls_borg_with_info_sub_
 
 
 
 
 def test_display_repository_info_with_log_info_calls_borg_with_info_parameter():
 def test_display_repository_info_with_log_info_calls_borg_with_info_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'rinfo', '--info', '--repo', 'repo'),
         ('borg', 'rinfo', '--info', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -65,6 +71,8 @@ def test_display_repository_info_with_log_info_calls_borg_with_info_parameter():
 
 
 
 
 def test_display_repository_info_with_log_info_and_json_suppresses_most_borg_output():
 def test_display_repository_info_with_log_info_and_json_suppresses_most_borg_output():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
@@ -84,12 +92,14 @@ def test_display_repository_info_with_log_info_and_json_suppresses_most_borg_out
 
 
 
 
 def test_display_repository_info_with_log_debug_calls_borg_with_debug_parameter():
 def test_display_repository_info_with_log_debug_calls_borg_with_debug_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'rinfo', '--debug', '--show-rc', '--repo', 'repo'),
         ('borg', 'rinfo', '--debug', '--show-rc', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -104,6 +114,8 @@ def test_display_repository_info_with_log_debug_calls_borg_with_debug_parameter(
 
 
 
 
 def test_display_repository_info_with_log_debug_and_json_suppresses_most_borg_output():
 def test_display_repository_info_with_log_debug_and_json_suppresses_most_borg_output():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
@@ -123,6 +135,8 @@ def test_display_repository_info_with_log_debug_and_json_suppresses_most_borg_ou
 
 
 
 
 def test_display_repository_info_with_json_calls_borg_with_json_parameter():
 def test_display_repository_info_with_json_calls_borg_with_json_parameter():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
@@ -141,12 +155,14 @@ def test_display_repository_info_with_json_calls_borg_with_json_parameter():
 
 
 
 
 def test_display_repository_info_with_local_path_calls_borg_via_local_path():
 def test_display_repository_info_with_local_path_calls_borg_via_local_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg1', 'rinfo', '--repo', 'repo'),
         ('borg1', 'rinfo', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg1',
         borg_local_path='borg1',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -161,12 +177,14 @@ def test_display_repository_info_with_local_path_calls_borg_via_local_path():
 
 
 
 
 def test_display_repository_info_with_remote_path_calls_borg_with_remote_path_parameters():
 def test_display_repository_info_with_remote_path_calls_borg_with_remote_path_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'rinfo', '--remote-path', 'borg1', '--repo', 'repo'),
         ('borg', 'rinfo', '--remote-path', 'borg1', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -181,13 +199,15 @@ def test_display_repository_info_with_remote_path_calls_borg_with_remote_path_pa
 
 
 
 
 def test_display_repository_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
 def test_display_repository_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     storage_config = {'lock_wait': 5}
     storage_config = {'lock_wait': 5}
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.feature).should_receive('available').and_return(True)
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'rinfo', '--lock-wait', '5', '--repo', 'repo'),
         ('borg', 'rinfo', '--lock-wait', '5', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )

+ 5 - 1
tests/unit/borg/test_rlist.py

@@ -325,6 +325,8 @@ def test_make_rlist_command_includes_additional_flags(argument_name):
 
 
 
 
 def test_list_repository_calls_borg_with_parameters():
 def test_list_repository_calls_borg_with_parameters():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     rlist_arguments = argparse.Namespace(json=False)
     rlist_arguments = argparse.Namespace(json=False)
 
 
     flexmock(module.feature).should_receive('available').and_return(False)
     flexmock(module.feature).should_receive('available').and_return(False)
@@ -339,7 +341,7 @@ def test_list_repository_calls_borg_with_parameters():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'rlist', 'repo'),
         ('borg', 'rlist', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     ).once()
     ).once()
@@ -353,6 +355,8 @@ def test_list_repository_calls_borg_with_parameters():
 
 
 
 
 def test_list_repository_with_json_returns_borg_output():
 def test_list_repository_with_json_returns_borg_output():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     rlist_arguments = argparse.Namespace(json=True)
     rlist_arguments = argparse.Namespace(json=True)
     json_output = flexmock()
     json_output = flexmock()
 
 

+ 32 - 11
tests/unit/borg/test_transfer.py

@@ -9,13 +9,15 @@ from ..test_verbosity import insert_logging_mock
 
 
 
 
 def test_transfer_archives_calls_borg_with_flags():
 def test_transfer_archives_calls_borg_with_flags():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--repo', 'repo'),
         ('borg', 'transfer', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -30,6 +32,8 @@ def test_transfer_archives_calls_borg_with_flags():
 
 
 
 
 def test_transfer_archives_with_dry_run_calls_borg_with_dry_run_flag():
 def test_transfer_archives_with_dry_run_calls_borg_with_dry_run_flag():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args('dry-run', True).and_return(
     flexmock(module.flags).should_receive('make_flags').with_args('dry-run', True).and_return(
         ('--dry-run',)
         ('--dry-run',)
@@ -39,7 +43,7 @@ def test_transfer_archives_with_dry_run_calls_borg_with_dry_run_flag():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--repo', 'repo', '--dry-run'),
         ('borg', 'transfer', '--repo', 'repo', '--dry-run'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -54,13 +58,15 @@ def test_transfer_archives_with_dry_run_calls_borg_with_dry_run_flag():
 
 
 
 
 def test_transfer_archives_with_log_info_calls_borg_with_info_flag():
 def test_transfer_archives_with_log_info_calls_borg_with_info_flag():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--info', '--repo', 'repo'),
         ('borg', 'transfer', '--info', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -75,13 +81,15 @@ def test_transfer_archives_with_log_info_calls_borg_with_info_flag():
 
 
 
 
 def test_transfer_archives_with_log_debug_calls_borg_with_debug_flag():
 def test_transfer_archives_with_log_debug_calls_borg_with_debug_flag():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--debug', '--show-rc', '--repo', 'repo'),
         ('borg', 'transfer', '--debug', '--show-rc', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -97,6 +105,8 @@ def test_transfer_archives_with_log_debug_calls_borg_with_debug_flag():
 
 
 
 
 def test_transfer_archives_with_archive_calls_borg_with_match_archives_flag():
 def test_transfer_archives_with_archive_calls_borg_with_match_archives_flag():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args(
     flexmock(module.flags).should_receive('make_flags').with_args(
         'match-archives', 'archive'
         'match-archives', 'archive'
@@ -106,7 +116,7 @@ def test_transfer_archives_with_archive_calls_borg_with_match_archives_flag():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--match-archives', 'archive', '--repo', 'repo'),
         ('borg', 'transfer', '--match-archives', 'archive', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -121,6 +131,8 @@ def test_transfer_archives_with_archive_calls_borg_with_match_archives_flag():
 
 
 
 
 def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_flag():
 def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_flag():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args(
     flexmock(module.flags).should_receive('make_flags').with_args(
         'match-archives', 'sh:foo*'
         'match-archives', 'sh:foo*'
@@ -130,7 +142,7 @@ def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_fl
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--match-archives', 'sh:foo*', '--repo', 'repo'),
         ('borg', 'transfer', '--match-archives', 'sh:foo*', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -145,13 +157,15 @@ def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_fl
 
 
 
 
 def test_transfer_archives_with_local_path_calls_borg_via_local_path():
 def test_transfer_archives_with_local_path_calls_borg_via_local_path():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg2', 'transfer', '--repo', 'repo'),
         ('borg2', 'transfer', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg2',
         borg_local_path='borg2',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -167,6 +181,8 @@ def test_transfer_archives_with_local_path_calls_borg_via_local_path():
 
 
 
 
 def test_transfer_archives_with_remote_path_calls_borg_with_remote_path_flags():
 def test_transfer_archives_with_remote_path_calls_borg_with_remote_path_flags():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args(
     flexmock(module.flags).should_receive('make_flags').with_args(
         'remote-path', 'borg2'
         'remote-path', 'borg2'
@@ -176,7 +192,7 @@ def test_transfer_archives_with_remote_path_calls_borg_with_remote_path_flags():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--remote-path', 'borg2', '--repo', 'repo'),
         ('borg', 'transfer', '--remote-path', 'borg2', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -192,6 +208,8 @@ def test_transfer_archives_with_remote_path_calls_borg_with_remote_path_flags():
 
 
 
 
 def test_transfer_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
 def test_transfer_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return(
     flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return(
         ('--lock-wait', '5')
         ('--lock-wait', '5')
@@ -202,7 +220,7 @@ def test_transfer_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--lock-wait', '5', '--repo', 'repo'),
         ('borg', 'transfer', '--lock-wait', '5', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -218,6 +236,8 @@ def test_transfer_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
 
 
 @pytest.mark.parametrize('argument_name', ('upgrader', 'sort_by', 'first', 'last'))
 @pytest.mark.parametrize('argument_name', ('upgrader', 'sort_by', 'first', 'last'))
 def test_transfer_archives_passes_through_arguments_to_borg(argument_name):
 def test_transfer_archives_passes_through_arguments_to_borg(argument_name):
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flag_name = f"--{argument_name.replace('_', ' ')}"
     flag_name = f"--{argument_name.replace('_', ' ')}"
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(
     flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(
@@ -227,7 +247,7 @@ def test_transfer_archives_passes_through_arguments_to_borg(argument_name):
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', flag_name, 'value', '--repo', 'repo'),
         ('borg', 'transfer', flag_name, 'value', '--repo', 'repo'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )
@@ -244,6 +264,7 @@ def test_transfer_archives_passes_through_arguments_to_borg(argument_name):
 
 
 
 
 def test_transfer_archives_with_source_repository_calls_borg_with_other_repo_flags():
 def test_transfer_archives_with_source_repository_calls_borg_with_other_repo_flags():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').and_return(())
     flexmock(module.flags).should_receive('make_flags').with_args('other-repo', 'other').and_return(
     flexmock(module.flags).should_receive('make_flags').with_args('other-repo', 'other').and_return(
         ('--other-repo', 'other')
         ('--other-repo', 'other')
@@ -253,7 +274,7 @@ def test_transfer_archives_with_source_repository_calls_borg_with_other_repo_fla
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'transfer', '--repo', 'repo', '--other-repo', 'other'),
         ('borg', 'transfer', '--repo', 'repo', '--other-repo', 'other'),
-        output_log_level=logging.WARNING,
+        output_log_level=module.borgmatic.logger.ANSWER,
         borg_local_path='borg',
         borg_local_path='borg',
         extra_environment=None,
         extra_environment=None,
     )
     )

+ 48 - 1
tests/unit/commands/test_borgmatic.py

@@ -9,6 +9,7 @@ from borgmatic.commands import borgmatic as module
 
 
 
 
 def test_run_configuration_runs_actions_for_each_repository():
 def test_run_configuration_runs_actions_for_each_repository():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     expected_results = [flexmock(), flexmock()]
     expected_results = [flexmock(), flexmock()]
     flexmock(module).should_receive('run_actions').and_return(expected_results[:1]).and_return(
     flexmock(module).should_receive('run_actions').and_return(expected_results[:1]).and_return(
@@ -23,6 +24,7 @@ def test_run_configuration_runs_actions_for_each_repository():
 
 
 
 
 def test_run_configuration_with_invalid_borg_version_errors():
 def test_run_configuration_with_invalid_borg_version_errors():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
     flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
     flexmock(module.command).should_receive('execute_hook').never()
     flexmock(module.command).should_receive('execute_hook').never()
     flexmock(module.dispatch).should_receive('call_hooks').never()
     flexmock(module.dispatch).should_receive('call_hooks').never()
@@ -34,6 +36,7 @@ def test_run_configuration_with_invalid_borg_version_errors():
 
 
 
 
 def test_run_configuration_logs_monitor_start_error():
 def test_run_configuration_logs_monitor_start_error():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.dispatch).should_receive('call_hooks').and_raise(OSError).and_return(
     flexmock(module.dispatch).should_receive('call_hooks').and_raise(OSError).and_return(
         None
         None
@@ -50,6 +53,7 @@ def test_run_configuration_logs_monitor_start_error():
 
 
 
 
 def test_run_configuration_bails_for_monitor_start_soft_failure():
 def test_run_configuration_bails_for_monitor_start_soft_failure():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
     flexmock(module.dispatch).should_receive('call_hooks').and_raise(error)
     flexmock(module.dispatch).should_receive('call_hooks').and_raise(error)
@@ -64,6 +68,7 @@ def test_run_configuration_bails_for_monitor_start_soft_failure():
 
 
 
 
 def test_run_configuration_logs_actions_error():
 def test_run_configuration_logs_actions_error():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.dispatch).should_receive('call_hooks')
     flexmock(module.dispatch).should_receive('call_hooks')
@@ -79,6 +84,7 @@ def test_run_configuration_logs_actions_error():
 
 
 
 
 def test_run_configuration_bails_for_actions_soft_failure():
 def test_run_configuration_bails_for_actions_soft_failure():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.dispatch).should_receive('call_hooks')
     flexmock(module.dispatch).should_receive('call_hooks')
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
@@ -94,6 +100,7 @@ def test_run_configuration_bails_for_actions_soft_failure():
 
 
 
 
 def test_run_configuration_logs_monitor_finish_error():
 def test_run_configuration_logs_monitor_finish_error():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
     flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
         None
         None
@@ -110,6 +117,7 @@ def test_run_configuration_logs_monitor_finish_error():
 
 
 
 
 def test_run_configuration_bails_for_monitor_finish_soft_failure():
 def test_run_configuration_bails_for_monitor_finish_soft_failure():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
     flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
     flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
@@ -127,6 +135,7 @@ def test_run_configuration_bails_for_monitor_finish_soft_failure():
 
 
 
 
 def test_run_configuration_logs_on_error_hook_error():
 def test_run_configuration_logs_on_error_hook_error():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook').and_raise(OSError)
     flexmock(module.command).should_receive('execute_hook').and_raise(OSError)
     expected_results = [flexmock(), flexmock()]
     expected_results = [flexmock(), flexmock()]
@@ -143,6 +152,7 @@ def test_run_configuration_logs_on_error_hook_error():
 
 
 
 
 def test_run_configuration_bails_for_on_error_hook_soft_failure():
 def test_run_configuration_bails_for_on_error_hook_soft_failure():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
     error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
     flexmock(module.command).should_receive('execute_hook').and_raise(error)
     flexmock(module.command).should_receive('execute_hook').and_raise(error)
@@ -159,6 +169,7 @@ def test_run_configuration_bails_for_on_error_hook_soft_failure():
 
 
 def test_run_configuration_retries_soft_error():
 def test_run_configuration_retries_soft_error():
     # Run action first fails, second passes
     # Run action first fails, second passes
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module).should_receive('run_actions').and_raise(OSError).and_return([])
     flexmock(module).should_receive('run_actions').and_raise(OSError).and_return([])
@@ -171,6 +182,7 @@ def test_run_configuration_retries_soft_error():
 
 
 def test_run_configuration_retries_hard_error():
 def test_run_configuration_retries_hard_error():
     # Run action fails twice
     # Run action fails twice
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
@@ -190,7 +202,8 @@ def test_run_configuration_retries_hard_error():
     assert results == error_logs
     assert results == error_logs
 
 
 
 
-def test_run_repos_ordered():
+def test_run_configuration_repos_ordered():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
@@ -208,6 +221,7 @@ def test_run_repos_ordered():
 
 
 
 
 def test_run_configuration_retries_round_robbin():
 def test_run_configuration_retries_round_robbin():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
@@ -238,6 +252,7 @@ def test_run_configuration_retries_round_robbin():
 
 
 
 
 def test_run_configuration_retries_one_passes():
 def test_run_configuration_retries_one_passes():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
     flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
@@ -266,6 +281,7 @@ def test_run_configuration_retries_one_passes():
 
 
 
 
 def test_run_configuration_retry_wait():
 def test_run_configuration_retry_wait():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
     flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
@@ -304,6 +320,7 @@ def test_run_configuration_retry_wait():
 
 
 
 
 def test_run_configuration_retries_timeout_multiple_repos():
 def test_run_configuration_retries_timeout_multiple_repos():
+    flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module.command).should_receive('execute_hook')
     flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
     flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
@@ -341,6 +358,8 @@ def test_run_configuration_retries_timeout_multiple_repos():
 
 
 
 
 def test_run_actions_does_not_raise_for_rcreate_action():
 def test_run_actions_does_not_raise_for_rcreate_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.borg_rcreate).should_receive('create_repository')
     flexmock(module.borg_rcreate).should_receive('create_repository')
     arguments = {
     arguments = {
         'global': flexmock(monitoring_verbosity=1, dry_run=False),
         'global': flexmock(monitoring_verbosity=1, dry_run=False),
@@ -372,6 +391,8 @@ def test_run_actions_does_not_raise_for_rcreate_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_transfer_action():
 def test_run_actions_does_not_raise_for_transfer_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.borg_transfer).should_receive('transfer_archives')
     flexmock(module.borg_transfer).should_receive('transfer_archives')
     arguments = {
     arguments = {
         'global': flexmock(monitoring_verbosity=1, dry_run=False),
         'global': flexmock(monitoring_verbosity=1, dry_run=False),
@@ -396,6 +417,8 @@ def test_run_actions_does_not_raise_for_transfer_action():
 
 
 
 
 def test_run_actions_calls_hooks_for_prune_action():
 def test_run_actions_calls_hooks_for_prune_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.borg_prune).should_receive('prune_archives')
     flexmock(module.borg_prune).should_receive('prune_archives')
     flexmock(module.command).should_receive('execute_hook').times(
     flexmock(module.command).should_receive('execute_hook').times(
         4
         4
@@ -423,6 +446,8 @@ def test_run_actions_calls_hooks_for_prune_action():
 
 
 
 
 def test_run_actions_calls_hooks_for_compact_action():
 def test_run_actions_calls_hooks_for_compact_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.borg_feature).should_receive('available').and_return(True)
     flexmock(module.borg_feature).should_receive('available').and_return(True)
     flexmock(module.borg_compact).should_receive('compact_segments')
     flexmock(module.borg_compact).should_receive('compact_segments')
     flexmock(module.command).should_receive('execute_hook').times(
     flexmock(module.command).should_receive('execute_hook').times(
@@ -451,6 +476,8 @@ def test_run_actions_calls_hooks_for_compact_action():
 
 
 
 
 def test_run_actions_executes_and_calls_hooks_for_create_action():
 def test_run_actions_executes_and_calls_hooks_for_create_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.borg_create).should_receive('create_archive')
     flexmock(module.borg_create).should_receive('create_archive')
     flexmock(module.command).should_receive('execute_hook').times(
     flexmock(module.command).should_receive('execute_hook').times(
         4
         4
@@ -482,6 +509,8 @@ def test_run_actions_executes_and_calls_hooks_for_create_action():
 
 
 
 
 def test_run_actions_calls_hooks_for_check_action():
 def test_run_actions_calls_hooks_for_check_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(True)
     flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(True)
     flexmock(module.borg_check).should_receive('check_archives')
     flexmock(module.borg_check).should_receive('check_archives')
     flexmock(module.command).should_receive('execute_hook').times(
     flexmock(module.command).should_receive('execute_hook').times(
@@ -512,6 +541,8 @@ def test_run_actions_calls_hooks_for_check_action():
 
 
 
 
 def test_run_actions_calls_hooks_for_extract_action():
 def test_run_actions_calls_hooks_for_extract_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_extract).should_receive('extract_archive')
     flexmock(module.borg_extract).should_receive('extract_archive')
     flexmock(module.command).should_receive('execute_hook').times(
     flexmock(module.command).should_receive('execute_hook').times(
@@ -547,6 +578,8 @@ def test_run_actions_calls_hooks_for_extract_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_export_tar_action():
 def test_run_actions_does_not_raise_for_export_tar_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_export_tar).should_receive('export_tar_archive')
     flexmock(module.borg_export_tar).should_receive('export_tar_archive')
     arguments = {
     arguments = {
@@ -580,6 +613,8 @@ def test_run_actions_does_not_raise_for_export_tar_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_mount_action():
 def test_run_actions_does_not_raise_for_mount_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_mount).should_receive('mount_archive')
     flexmock(module.borg_mount).should_receive('mount_archive')
     arguments = {
     arguments = {
@@ -612,6 +647,8 @@ def test_run_actions_does_not_raise_for_mount_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_rlist_action():
 def test_run_actions_does_not_raise_for_rlist_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_rlist).should_receive('list_repository')
     flexmock(module.borg_rlist).should_receive('list_repository')
     arguments = {
     arguments = {
@@ -637,6 +674,8 @@ def test_run_actions_does_not_raise_for_rlist_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_list_action():
 def test_run_actions_does_not_raise_for_list_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_rlist).should_receive('resolve_archive_name').and_return(flexmock())
     flexmock(module.borg_rlist).should_receive('resolve_archive_name').and_return(flexmock())
     flexmock(module.borg_list).should_receive('list_archive')
     flexmock(module.borg_list).should_receive('list_archive')
@@ -663,6 +702,8 @@ def test_run_actions_does_not_raise_for_list_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_rinfo_action():
 def test_run_actions_does_not_raise_for_rinfo_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_rinfo).should_receive('display_repository_info')
     flexmock(module.borg_rinfo).should_receive('display_repository_info')
     arguments = {
     arguments = {
@@ -688,6 +729,8 @@ def test_run_actions_does_not_raise_for_rinfo_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_info_action():
 def test_run_actions_does_not_raise_for_info_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_rlist).should_receive('resolve_archive_name').and_return(flexmock())
     flexmock(module.borg_rlist).should_receive('resolve_archive_name').and_return(flexmock())
     flexmock(module.borg_info).should_receive('display_archives_info')
     flexmock(module.borg_info).should_receive('display_archives_info')
@@ -714,6 +757,8 @@ def test_run_actions_does_not_raise_for_info_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_break_lock_action():
 def test_run_actions_does_not_raise_for_break_lock_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_break_lock).should_receive('break_lock')
     flexmock(module.borg_break_lock).should_receive('break_lock')
     arguments = {
     arguments = {
@@ -739,6 +784,8 @@ def test_run_actions_does_not_raise_for_break_lock_action():
 
 
 
 
 def test_run_actions_does_not_raise_for_borg_action():
 def test_run_actions_does_not_raise_for_borg_action():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logger).answer = lambda message: None
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.validate).should_receive('repositories_match').and_return(True)
     flexmock(module.borg_rlist).should_receive('resolve_archive_name').and_return(flexmock())
     flexmock(module.borg_rlist).should_receive('resolve_archive_name').and_return(flexmock())
     flexmock(module.borg_borg).should_receive('run_arbitrary_borg')
     flexmock(module.borg_borg).should_receive('run_arbitrary_borg')

+ 48 - 0
tests/unit/test_logger.py

@@ -1,4 +1,5 @@
 import logging
 import logging
+import sys
 
 
 import pytest
 import pytest
 from flexmock import flexmock
 from flexmock import flexmock
@@ -125,6 +126,8 @@ def test_multi_stream_handler_logs_to_handler_for_log_level():
 
 
 
 
 def test_console_color_formatter_format_includes_log_message():
 def test_console_color_formatter_format_includes_log_message():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     plain_message = 'uh oh'
     plain_message = 'uh oh'
     record = flexmock(levelno=logging.CRITICAL, msg=plain_message)
     record = flexmock(levelno=logging.CRITICAL, msg=plain_message)
 
 
@@ -142,7 +145,38 @@ def test_color_text_without_color_does_not_raise():
     module.color_text(None, 'hi')
     module.color_text(None, 'hi')
 
 
 
 
+def test_add_logging_level_adds_level_name_and_sets_global_attributes_and_methods():
+    logger = flexmock()
+    flexmock(module.logging).should_receive('getLoggerClass').and_return(logger)
+    flexmock(module.logging).should_receive('addLevelName').with_args(99, 'PLAID')
+    builtins = flexmock(sys.modules['builtins'])
+    builtins.should_call('setattr')
+    builtins.should_receive('setattr').with_args(module.logging, 'PLAID', 99).once()
+    builtins.should_receive('setattr').with_args(logger, 'plaid', object).once()
+    builtins.should_receive('setattr').with_args(logging, 'plaid', object).once()
+
+    module.add_logging_level('PLAID', 99)
+
+
+def test_add_logging_level_skips_global_setting_if_already_set():
+    logger = flexmock()
+    flexmock(module.logging).should_receive('getLoggerClass').and_return(logger)
+    flexmock(module.logging).PLAID = 99
+    flexmock(logger).plaid = flexmock()
+    flexmock(logging).plaid = flexmock()
+    flexmock(module.logging).should_receive('addLevelName').never()
+    builtins = flexmock(sys.modules['builtins'])
+    builtins.should_call('setattr')
+    builtins.should_receive('setattr').with_args(module.logging, 'PLAID', 99).never()
+    builtins.should_receive('setattr').with_args(logger, 'plaid', object).never()
+    builtins.should_receive('setattr').with_args(logging, 'plaid', object).never()
+
+    module.add_logging_level('PLAID', 99)
+
+
 def test_configure_logging_probes_for_log_socket_on_linux():
 def test_configure_logging_probes_for_log_socket_on_linux():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )
@@ -161,6 +195,8 @@ def test_configure_logging_probes_for_log_socket_on_linux():
 
 
 
 
 def test_configure_logging_probes_for_log_socket_on_macos():
 def test_configure_logging_probes_for_log_socket_on_macos():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )
@@ -180,6 +216,8 @@ def test_configure_logging_probes_for_log_socket_on_macos():
 
 
 
 
 def test_configure_logging_probes_for_log_socket_on_freebsd():
 def test_configure_logging_probes_for_log_socket_on_freebsd():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )
@@ -200,6 +238,8 @@ def test_configure_logging_probes_for_log_socket_on_freebsd():
 
 
 
 
 def test_configure_logging_sets_global_logger_to_most_verbose_log_level():
 def test_configure_logging_sets_global_logger_to_most_verbose_log_level():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )
@@ -213,6 +253,8 @@ def test_configure_logging_sets_global_logger_to_most_verbose_log_level():
 
 
 
 
 def test_configure_logging_skips_syslog_if_not_found():
 def test_configure_logging_skips_syslog_if_not_found():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )
@@ -227,6 +269,8 @@ def test_configure_logging_skips_syslog_if_not_found():
 
 
 
 
 def test_configure_logging_skips_syslog_if_interactive_console():
 def test_configure_logging_skips_syslog_if_interactive_console():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )
@@ -242,6 +286,8 @@ def test_configure_logging_skips_syslog_if_interactive_console():
 
 
 
 
 def test_configure_logging_to_logfile_instead_of_syslog():
 def test_configure_logging_to_logfile_instead_of_syslog():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )
@@ -264,6 +310,8 @@ def test_configure_logging_to_logfile_instead_of_syslog():
 
 
 
 
 def test_configure_logging_skips_logfile_if_argument_is_none():
 def test_configure_logging_skips_logfile_if_argument_is_none():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
     flexmock(module).should_receive('Multi_stream_handler').and_return(
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
         flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
     )
     )

+ 8 - 1
tests/unit/test_verbosity.py

@@ -15,10 +15,17 @@ def insert_logging_mock(log_level):
 
 
 
 
 def test_verbosity_to_log_level_maps_known_verbosity_to_log_level():
 def test_verbosity_to_log_level_maps_known_verbosity_to_log_level():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+
+    assert module.verbosity_to_log_level(module.VERBOSITY_ERROR) == logging.ERROR
+    assert module.verbosity_to_log_level(module.VERBOSITY_ANSWER) == module.borgmatic.logger.ANSWER
     assert module.verbosity_to_log_level(module.VERBOSITY_SOME) == logging.INFO
     assert module.verbosity_to_log_level(module.VERBOSITY_SOME) == logging.INFO
     assert module.verbosity_to_log_level(module.VERBOSITY_LOTS) == logging.DEBUG
     assert module.verbosity_to_log_level(module.VERBOSITY_LOTS) == logging.DEBUG
-    assert module.verbosity_to_log_level(module.VERBOSITY_ERROR) == logging.ERROR
 
 
 
 
 def test_verbosity_to_log_level_maps_unknown_verbosity_to_warning_level():
 def test_verbosity_to_log_level_maps_unknown_verbosity_to_warning_level():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+
     assert module.verbosity_to_log_level('my pants') == logging.WARNING
     assert module.verbosity_to_log_level('my pants') == logging.WARNING