From 31b6e2113958c91dcd321d005f12e2f71d60e572 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Sun, 26 Jan 2025 19:03:40 -0800 Subject: [PATCH] Fix end-to-end tests and update more log messages (#635). --- borgmatic/commands/borgmatic.py | 5 +++ borgmatic/hooks/monitoring/apprise.py | 8 ++-- borgmatic/hooks/monitoring/cronhub.py | 10 ++--- borgmatic/hooks/monitoring/cronitor.py | 10 ++--- borgmatic/hooks/monitoring/healthchecks.py | 12 +++--- borgmatic/hooks/monitoring/loki.py | 4 +- borgmatic/hooks/monitoring/ntfy.py | 16 ++++---- borgmatic/hooks/monitoring/pagerduty.py | 10 ++--- borgmatic/hooks/monitoring/pushover.py | 10 ++--- borgmatic/hooks/monitoring/sentry.py | 14 +++---- borgmatic/hooks/monitoring/uptime_kuma.py | 8 ++-- borgmatic/hooks/monitoring/zabbix.py | 32 +++++++-------- borgmatic/logger.py | 40 +++++++++++++------ tests/end-to-end/test_override.py | 2 +- .../hooks/monitoring/test_apprise.py | 4 +- .../hooks/monitoring/test_healthchecks.py | 4 +- .../integration/hooks/monitoring/test_loki.py | 6 +-- tests/unit/hooks/monitoring/test_apprise.py | 1 - 18 files changed, 108 insertions(+), 88 deletions(-) diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 112a7691..1bcdbb7f 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -108,6 +108,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'initialize_monitor', config, dispatch.Hook_type.MONITORING, + config_filename, monitoring_log_level, global_arguments.dry_run, ) @@ -116,6 +117,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, dispatch.Hook_type.MONITORING, + config_filename, monitor.State.START, monitoring_log_level, global_arguments.dry_run, @@ -187,6 +189,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, dispatch.Hook_type.MONITORING, + config_filename, monitor.State.LOG, monitoring_log_level, global_arguments.dry_run, @@ -203,6 +206,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, dispatch.Hook_type.MONITORING, + config_filename, monitor.State.FINISH, monitoring_log_level, global_arguments.dry_run, @@ -237,6 +241,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, dispatch.Hook_type.MONITORING, + config_filename, monitor.State.FAIL, monitoring_log_level, global_arguments.dry_run, diff --git a/borgmatic/hooks/monitoring/apprise.py b/borgmatic/hooks/monitoring/apprise.py index 9592d787..b873c6cd 100644 --- a/borgmatic/hooks/monitoring/apprise.py +++ b/borgmatic/hooks/monitoring/apprise.py @@ -66,12 +66,12 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev ) if not hook_config.get('services'): - logger.info(f'{config_filename}: No Apprise services to ping') + logger.info('No Apprise services to ping') return dry_run_string = ' (dry run; not actually pinging)' if dry_run else '' labels_string = ', '.join(map(operator.itemgetter('label'), hook_config.get('services'))) - logger.info(f'{config_filename}: Pinging Apprise services: {labels_string}{dry_run_string}') + logger.info(f'Pinging Apprise services: {labels_string}{dry_run_string}') apprise_object = apprise.Apprise() apprise_object.add(list(map(operator.itemgetter('url'), hook_config.get('services')))) @@ -100,10 +100,10 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev ) if result is False: - logger.warning(f'{config_filename}: Error sending some Apprise notifications') + logger.warning('Error sending some Apprise notifications') -def destroy_monitor(hook_config, config, config_filename, monitoring_log_level, dry_run): +def destroy_monitor(hook_config, config, monitoring_log_level, dry_run): ''' Remove the monitor handler that was added to the root logger. This prevents the handler from getting reused by other instances of this monitor. diff --git a/borgmatic/hooks/monitoring/cronhub.py b/borgmatic/hooks/monitoring/cronhub.py index fbb84c8b..dfc4892f 100644 --- a/borgmatic/hooks/monitoring/cronhub.py +++ b/borgmatic/hooks/monitoring/cronhub.py @@ -29,7 +29,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev ''' if state not in MONITOR_STATE_TO_CRONHUB: logger.debug( - f'{config_filename}: Ignoring unsupported monitoring {state.name.lower()} in Cronhub hook' + f'Ignoring unsupported monitoring {state.name.lower()} in Cronhub hook' ) return @@ -41,8 +41,8 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev .replace('/ping/', formatted_state) ) - logger.info(f'{config_filename}: Pinging Cronhub {state.name.lower()}{dry_run_label}') - logger.debug(f'{config_filename}: Using Cronhub ping URL {ping_url}') + logger.info(f'Pinging Cronhub {state.name.lower()}{dry_run_label}') + logger.debug(f'Using Cronhub ping URL {ping_url}') if not dry_run: logging.getLogger('urllib3').setLevel(logging.ERROR) @@ -51,11 +51,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Cronhub error: {error}') + logger.warning(f'Cronhub error: {error}') def destroy_monitor( - ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + ping_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/hooks/monitoring/cronitor.py b/borgmatic/hooks/monitoring/cronitor.py index 87a2072e..994dab31 100644 --- a/borgmatic/hooks/monitoring/cronitor.py +++ b/borgmatic/hooks/monitoring/cronitor.py @@ -29,15 +29,15 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev ''' if state not in MONITOR_STATE_TO_CRONITOR: logger.debug( - f'{config_filename}: Ignoring unsupported monitoring {state.name.lower()} in Cronitor hook' + f'Ignoring unsupported monitoring {state.name.lower()} in Cronitor hook' ) return dry_run_label = ' (dry run; not actually pinging)' if dry_run else '' ping_url = f"{hook_config['ping_url']}/{MONITOR_STATE_TO_CRONITOR[state]}" - logger.info(f'{config_filename}: Pinging Cronitor {state.name.lower()}{dry_run_label}') - logger.debug(f'{config_filename}: Using Cronitor ping URL {ping_url}') + logger.info(f'Pinging Cronitor {state.name.lower()}{dry_run_label}') + logger.debug(f'Using Cronitor ping URL {ping_url}') if not dry_run: logging.getLogger('urllib3').setLevel(logging.ERROR) @@ -46,11 +46,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Cronitor error: {error}') + logger.warning(f'Cronitor error: {error}') def destroy_monitor( - ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + ping_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/hooks/monitoring/healthchecks.py b/borgmatic/hooks/monitoring/healthchecks.py index 8483f98b..1e145106 100644 --- a/borgmatic/hooks/monitoring/healthchecks.py +++ b/borgmatic/hooks/monitoring/healthchecks.py @@ -56,7 +56,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if 'states' in hook_config and state.name.lower() not in hook_config['states']: logger.info( - f'{config_filename}: Skipping Healthchecks {state.name.lower()} ping due to configured states' + f'Skipping Healthchecks {state.name.lower()} ping due to configured states' ) return @@ -69,13 +69,13 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if hook_config.get('create_slug'): if ping_url_is_uuid: logger.warning( - f'{config_filename}: Healthchecks UUIDs do not support auto provisionning; ignoring' + 'Healthchecks UUIDs do not support auto provisionning; ignoring' ) else: ping_url = f'{ping_url}?create=1' - logger.info(f'{config_filename}: Pinging Healthchecks {state.name.lower()}{dry_run_label}') - logger.debug(f'{config_filename}: Using Healthchecks ping URL {ping_url}') + logger.info(f'Pinging Healthchecks {state.name.lower()}{dry_run_label}') + logger.debug(f'Using Healthchecks ping URL {ping_url}') if state in (monitor.State.FINISH, monitor.State.FAIL, monitor.State.LOG): payload = borgmatic.hooks.monitoring.logs.format_buffered_logs_for_payload( @@ -93,10 +93,10 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Healthchecks error: {error}') + logger.warning(f'Healthchecks error: {error}') -def destroy_monitor(hook_config, config, config_filename, monitoring_log_level, dry_run): +def destroy_monitor(hook_config, config, monitoring_log_level, dry_run): ''' Remove the monitor handler that was added to the root logger. This prevents the handler from getting reused by other instances of this monitor. diff --git a/borgmatic/hooks/monitoring/loki.py b/borgmatic/hooks/monitoring/loki.py index 51e287c7..2c0405b1 100644 --- a/borgmatic/hooks/monitoring/loki.py +++ b/borgmatic/hooks/monitoring/loki.py @@ -139,10 +139,10 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev for handler in tuple(logging.getLogger().handlers): if isinstance(handler, Loki_log_handler): if state in MONITOR_STATE_TO_LOKI.keys(): - handler.raw(f'{config_filename}: {MONITOR_STATE_TO_LOKI[state]} backup') + handler.raw(f'{MONITOR_STATE_TO_LOKI[state]} backup') -def destroy_monitor(hook_config, config, config_filename, monitoring_log_level, dry_run): +def destroy_monitor(hook_config, config, monitoring_log_level, dry_run): ''' Remove the monitor handler that was added to the root logger. ''' diff --git a/borgmatic/hooks/monitoring/ntfy.py b/borgmatic/hooks/monitoring/ntfy.py index d3a37ca6..9fa28c80 100644 --- a/borgmatic/hooks/monitoring/ntfy.py +++ b/borgmatic/hooks/monitoring/ntfy.py @@ -37,8 +37,8 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev base_url = hook_config.get('server', 'https://ntfy.sh') topic = hook_config.get('topic') - logger.info(f'{config_filename}: Pinging ntfy topic {topic}{dry_run_label}') - logger.debug(f'{config_filename}: Using Ntfy ping URL {base_url}/{topic}') + logger.info(f'Pinging ntfy topic {topic}{dry_run_label}') + logger.debug(f'Using Ntfy ping URL {base_url}/{topic}') headers = { 'X-Title': state_config.get('title'), @@ -55,19 +55,19 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if access_token is not None: if username or password: logger.warning( - f'{config_filename}: ntfy access_token is set but so is username/password, only using access_token' + 'ntfy access_token is set but so is username/password, only using access_token' ) auth = requests.auth.HTTPBasicAuth('', access_token) elif (username and password) is not None: auth = requests.auth.HTTPBasicAuth(username, password) - logger.info(f'{config_filename}: Using basic auth with user {username} for ntfy') + logger.info(f'Using basic auth with user {username} for ntfy') elif username is not None: logger.warning( - f'{config_filename}: Password missing for ntfy authentication, defaulting to no auth' + 'Password missing for ntfy authentication, defaulting to no auth' ) elif password is not None: logger.warning( - f'{config_filename}: Username missing for ntfy authentication, defaulting to no auth' + 'Username missing for ntfy authentication, defaulting to no auth' ) if not dry_run: @@ -77,11 +77,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: ntfy error: {error}') + logger.warning(f'ntfy error: {error}') def destroy_monitor( - ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + ping_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/hooks/monitoring/pagerduty.py b/borgmatic/hooks/monitoring/pagerduty.py index 2d5325de..5e4aea4f 100644 --- a/borgmatic/hooks/monitoring/pagerduty.py +++ b/borgmatic/hooks/monitoring/pagerduty.py @@ -29,12 +29,12 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev ''' if state != monitor.State.FAIL: logger.debug( - f'{config_filename}: Ignoring unsupported monitoring {state.name.lower()} in PagerDuty hook', + f'Ignoring unsupported monitoring {state.name.lower()} in PagerDuty hook', ) return dry_run_label = ' (dry run; not actually sending)' if dry_run else '' - logger.info(f'{config_filename}: Sending failure event to PagerDuty {dry_run_label}') + logger.info(f'Sending failure event to PagerDuty {dry_run_label}') if dry_run: return @@ -61,7 +61,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev }, } ) - logger.debug(f'{config_filename}: Using PagerDuty payload: {payload}') + logger.debug(f'Using PagerDuty payload: {payload}') logging.getLogger('urllib3').setLevel(logging.ERROR) try: @@ -69,11 +69,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: PagerDuty error: {error}') + logger.warning(f'PagerDuty error: {error}') def destroy_monitor( - ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + ping_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/hooks/monitoring/pushover.py b/borgmatic/hooks/monitoring/pushover.py index 739283bd..40f93d93 100644 --- a/borgmatic/hooks/monitoring/pushover.py +++ b/borgmatic/hooks/monitoring/pushover.py @@ -35,14 +35,14 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev token = hook_config.get('token') user = hook_config.get('user') - logger.info(f'{config_filename}: Updating Pushover{dry_run_label}') + logger.info(f'Updating Pushover{dry_run_label}') if state_config.get('priority') == EMERGENCY_PRIORITY: if 'expire' not in state_config: - logger.info(f'{config_filename}: Setting expire to default (10 min).') + logger.info('Setting expire to default (10 min)') state_config['expire'] = 600 if 'retry' not in state_config: - logger.info(f'{config_filename}: Setting retry to default (30 sec).') + logger.info('Setting retry to default (30 sec)') state_config['retry'] = 30 else: if 'expire' in state_config or 'retry' in state_config: @@ -75,11 +75,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Pushover error: {error}') + logger.warning(f'Pushover error: {error}') def destroy_monitor( - ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + ping_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/hooks/monitoring/sentry.py b/borgmatic/hooks/monitoring/sentry.py index 5a561760..dbc8f014 100644 --- a/borgmatic/hooks/monitoring/sentry.py +++ b/borgmatic/hooks/monitoring/sentry.py @@ -16,7 +16,7 @@ def initialize_monitor( DATA_SOURCE_NAME_URL_PATTERN = re.compile( - '^(?P.*)://(?P.*)@(?P.*)/(?P.*)$' + '^(?P.+)://(?P.+)@(?P.+)/(?P.+)$' ) @@ -39,14 +39,14 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not match: logger.warning( - 'f{config_filename}: Invalid Sentry data source name URL: {data_source_name_url}' + f'Invalid Sentry data source name URL: {data_source_name_url}' ) return cron_url = f'{match.group("protocol")}://{match.group("hostname")}/api/{match.group("project_id")}/cron/{monitor_slug}/{match.group("username")}/' - logger.info(f'{config_filename}: Pinging Sentry {state.name.lower()}{dry_run_label}') - logger.debug(f'{config_filename}: Using Sentry cron URL {cron_url}') + logger.info(f'Pinging Sentry {state.name.lower()}{dry_run_label}') + logger.debug(f'Using Sentry cron URL {cron_url}') status = { 'start': 'in_progress', @@ -55,7 +55,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev }.get(state.name.lower()) if not status: - logger.warning('f{config_filename}: Invalid Sentry state') + logger.warning('Invalid Sentry state') return if dry_run: @@ -67,11 +67,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Sentry error: {error}') + logger.warning(f'Sentry error: {error}') def destroy_monitor( - ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + ping_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/hooks/monitoring/uptime_kuma.py b/borgmatic/hooks/monitoring/uptime_kuma.py index 46fa5247..9b06b750 100644 --- a/borgmatic/hooks/monitoring/uptime_kuma.py +++ b/borgmatic/hooks/monitoring/uptime_kuma.py @@ -29,9 +29,9 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev push_url = hook_config.get('push_url', 'https://example.uptime.kuma/api/push/abcd1234') query = f'status={status}&msg={state.name.lower()}' logger.info( - f'{config_filename}: Pushing Uptime Kuma push_url {push_url}?{query} {dry_run_label}' + f'Pushing Uptime Kuma push_url {push_url}?{query} {dry_run_label}' ) - logger.debug(f'{config_filename}: Full Uptime Kuma state URL {push_url}?{query}') + logger.debug(f'Full Uptime Kuma state URL {push_url}?{query}') if dry_run: return @@ -43,11 +43,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Uptime Kuma error: {error}') + logger.warning(f'Uptime Kuma error: {error}') def destroy_monitor( - push_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + push_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/hooks/monitoring/zabbix.py b/borgmatic/hooks/monitoring/zabbix.py index a33d77b4..17744a21 100644 --- a/borgmatic/hooks/monitoring/zabbix.py +++ b/borgmatic/hooks/monitoring/zabbix.py @@ -44,16 +44,16 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev value = state_config.get('value') headers = {'Content-Type': 'application/json-rpc'} - logger.info(f'{config_filename}: Updating Zabbix{dry_run_label}') - logger.debug(f'{config_filename}: Using Zabbix URL: {server}') + logger.info(f'Updating Zabbix{dry_run_label}') + logger.debug(f'Using Zabbix URL: {server}') if server is None: - logger.warning(f'{config_filename}: Server missing for Zabbix') + logger.warning('Server missing for Zabbix') return # Determine the Zabbix method used to store the value: itemid or host/key if itemid is not None: - logger.info(f'{config_filename}: Updating {itemid} on Zabbix') + logger.info(f'Updating {itemid} on Zabbix') data = { 'jsonrpc': '2.0', 'method': 'history.push', @@ -62,7 +62,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev } elif (host and key) is not None: - logger.info(f'{config_filename}: Updating Host:{host} and Key:{key} on Zabbix') + logger.info(f'Updating Host:{host} and Key:{key} on Zabbix') data = { 'jsonrpc': '2.0', 'method': 'history.push', @@ -71,23 +71,23 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev } elif host is not None: - logger.warning(f'{config_filename}: Key missing for Zabbix') + logger.warning('Key missing for Zabbix') return elif key is not None: - logger.warning(f'{config_filename}: Host missing for Zabbix.') + logger.warning('Host missing for Zabbix') return else: - logger.warning(f'{config_filename}: No zabbix itemid or host/key provided.') + logger.warning('No Zabbix itemid or host/key provided') return # Determine the authentication method: API key or username/password if api_key is not None: - logger.info(f'{config_filename}: Using API key auth for Zabbix') + logger.info('Using API key auth for Zabbix') headers['Authorization'] = 'Bearer ' + api_key elif (username and password) is not None: - logger.info(f'{config_filename}: Using user/pass auth with user {username} for Zabbix') + logger.info('Using user/pass auth with user {username} for Zabbix') auth_data = { 'jsonrpc': '2.0', 'method': 'user.login', @@ -102,18 +102,18 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Zabbix error: {error}') + logger.warning(f'Zabbix error: {error}') return elif username is not None: - logger.warning(f'{config_filename}: Password missing for Zabbix authentication') + logger.warning('Password missing for Zabbix authentication') return elif password is not None: - logger.warning(f'{config_filename}: Username missing for Zabbix authentication') + logger.warning('Username missing for Zabbix authentication') return else: - logger.warning(f'{config_filename}: Authentication data missing for Zabbix') + logger.warning('Authentication data missing for Zabbix') return if not dry_run: @@ -123,11 +123,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: - logger.warning(f'{config_filename}: Zabbix error: {error}') + logger.warning(f'Zabbix error: {error}') def destroy_monitor( - ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run + ping_url_or_uuid, config, monitoring_log_level, dry_run ): # pragma: no cover ''' No destruction is necessary for this monitor. diff --git a/borgmatic/logger.py b/borgmatic/logger.py index a2eceacd..e6203fcd 100644 --- a/borgmatic/logger.py +++ b/borgmatic/logger.py @@ -186,6 +186,31 @@ def add_custom_log_levels(): # pragma: no cover add_logging_level('DISABLED', DISABLED) +def get_log_prefix(): + ''' + Return the current log prefix from the defaults for the formatter on the first logging handler. + ''' + try: + return next( + handler.formatter._style._defaults.get('prefix').rstrip().rstrip(':') + for handler in logging.getLogger().handlers + ) + except (StopIteration, AttributeError): + return None + + +def set_log_prefix(prefix): + ''' + Given a log prefix as a string, set it into the defaults for the formatters on all logging + handlers. + ''' + for handler in logging.getLogger().handlers: + try: + handler.formatter._style._defaults = {'prefix': f'{prefix}: ' if prefix else ''} + except AttributeError: + pass + + class Log_prefix: ''' A Python context manager for setting a log prefix so that it shows up in every subsequent @@ -215,23 +240,14 @@ class Log_prefix: up in every log message. But first, save off any original prefix so that it can be restored below. ''' - try: - self.original_prefix = next( - handler.formatter._style._defaults.get('prefix').rstrip().rstrip(':') - for handler in logging.getLogger().handlers - ) - except (StopIteration, AttributeError): - self.original_prefix = None - - for handler in logging.getLogger().handlers: - handler.formatter._style._defaults = {'prefix': f'{self.prefix}: ' if self.prefix else ''} + self.original_prefix = get_log_prefix() + set_log_prefix(self.prefix) def __exit__(self, exception, value, traceback): ''' Restore any original prefix. ''' - for handler in logging.getLogger().handlers: - handler.formatter._style._defaults = {'prefix': f'{self.original_prefix}: ' if self.original_prefix else ''} + set_log_prefix(self.original_prefix) def configure_logging( diff --git a/tests/end-to-end/test_override.py b/tests/end-to-end/test_override.py index a4fb08a7..ee8a64fe 100644 --- a/tests/end-to-end/test_override.py +++ b/tests/end-to-end/test_override.py @@ -28,7 +28,7 @@ def generate_configuration(config_path, repository_path): config_file.close() -def test_override_get_normalized(): +def test_override_gets_normalized(): temporary_directory = tempfile.mkdtemp() repository_path = os.path.join(temporary_directory, 'test.borg') diff --git a/tests/integration/hooks/monitoring/test_apprise.py b/tests/integration/hooks/monitoring/test_apprise.py index f1bcdf67..158e697b 100644 --- a/tests/integration/hooks/monitoring/test_apprise.py +++ b/tests/integration/hooks/monitoring/test_apprise.py @@ -14,7 +14,7 @@ def test_destroy_monitor_removes_apprise_handler(): ) ) - module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock(), flexmock()) + module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock()) assert logger.handlers == original_handlers @@ -23,6 +23,6 @@ def test_destroy_monitor_without_apprise_handler_does_not_raise(): logger = logging.getLogger() original_handlers = list(logger.handlers) - module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock(), flexmock()) + module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock()) assert logger.handlers == original_handlers diff --git a/tests/integration/hooks/monitoring/test_healthchecks.py b/tests/integration/hooks/monitoring/test_healthchecks.py index 9d6ceb4a..da9981b1 100644 --- a/tests/integration/hooks/monitoring/test_healthchecks.py +++ b/tests/integration/hooks/monitoring/test_healthchecks.py @@ -14,7 +14,7 @@ def test_destroy_monitor_removes_healthchecks_handler(): ) ) - module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock(), flexmock()) + module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock()) assert logger.handlers == original_handlers @@ -23,6 +23,6 @@ def test_destroy_monitor_without_healthchecks_handler_does_not_raise(): logger = logging.getLogger() original_handlers = list(logger.handlers) - module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock(), flexmock()) + module.destroy_monitor(flexmock(), flexmock(), flexmock(), flexmock()) assert logger.handlers == original_handlers diff --git a/tests/integration/hooks/monitoring/test_loki.py b/tests/integration/hooks/monitoring/test_loki.py index e0f1cbba..a25a325f 100644 --- a/tests/integration/hooks/monitoring/test_loki.py +++ b/tests/integration/hooks/monitoring/test_loki.py @@ -65,8 +65,8 @@ def test_ping_monitor_adds_log_message(): assert any( map( lambda log: log - == f'{config_filename}: {module.MONITOR_STATE_TO_LOKI[module.monitor.State.FINISH]} backup', - map(lambda x: x[1], handler.buffer.root['streams'][0]['values']), + == f'{module.MONITOR_STATE_TO_LOKI[module.monitor.State.FINISH]} backup', + map(lambda value: value[1], handler.buffer.root['streams'][0]['values']), ) ) return @@ -82,7 +82,7 @@ def test_destroy_monitor_removes_log_handler(): config_filename = 'test.yaml' dry_run = True module.initialize_monitor(hook_config, flexmock(), config_filename, flexmock(), dry_run) - module.destroy_monitor(hook_config, flexmock(), config_filename, flexmock(), dry_run) + module.destroy_monitor(hook_config, flexmock(), flexmock(), dry_run) for handler in tuple(logging.getLogger().handlers): if isinstance(handler, module.Loki_log_handler): diff --git a/tests/unit/hooks/monitoring/test_apprise.py b/tests/unit/hooks/monitoring/test_apprise.py index 91118cb0..fd0e9615 100644 --- a/tests/unit/hooks/monitoring/test_apprise.py +++ b/tests/unit/hooks/monitoring/test_apprise.py @@ -379,7 +379,6 @@ def test_destroy_monitor_does_not_raise(): module.destroy_monitor( hook_config={}, config={}, - config_filename='test.yaml', monitoring_log_level=1, dry_run=False, )