Browse Source

Add a Sentry monitoring hook (#855).

Dan Helfman 4 months ago
parent
commit
914c2b17e9

+ 2 - 0
NEWS

@@ -1,4 +1,6 @@
 1.9.7.dev0
 1.9.7.dev0
+ * #855: Add a Sentry monitoring hook. See the documentation for more information:
+   https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#sentry-hook
  * #968: Fix for a "spot" check error when a filename in the most recent archive contains a newline.
  * #968: Fix for a "spot" check error when a filename in the most recent archive contains a newline.
  * #970: Fix for an error when there's a blank line in the configured patterns or excludes.
  * #970: Fix for an error when there's a blank line in the configured patterns or excludes.
  * #971: Fix for "exclude_from" files being completely ignored.
  * #971: Fix for "exclude_from" files being completely ignored.

+ 1 - 0
README.md

@@ -75,6 +75,7 @@ borgmatic is powered by [Borg Backup](https://www.borgbackup.org/).
 <a href="https://grafana.com/oss/loki/"><img src="docs/static/loki.png" alt="Loki" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
 <a href="https://grafana.com/oss/loki/"><img src="docs/static/loki.png" alt="Loki" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
 <a href="https://github.com/caronc/apprise/wiki"><img src="docs/static/apprise.png" alt="Apprise" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
 <a href="https://github.com/caronc/apprise/wiki"><img src="docs/static/apprise.png" alt="Apprise" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
 <a href="https://www.zabbix.com/"><img src="docs/static/zabbix.png" alt="Zabbix" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
 <a href="https://www.zabbix.com/"><img src="docs/static/zabbix.png" alt="Zabbix" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
+<a href="https://sentry.io/"><img src="docs/static/sentry.png" alt="Sentry" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
 <a href="https://www.borgbase.com/?utm_source=borgmatic"><img src="docs/static/borgbase.png" alt="BorgBase" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
 <a href="https://www.borgbase.com/?utm_source=borgmatic"><img src="docs/static/borgbase.png" alt="BorgBase" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
 
 
 
 

+ 39 - 1
borgmatic/config/schema.yaml

@@ -2259,7 +2259,45 @@ properties:
             can send the logs to a self-hosted instance or create an account at
             can send the logs to a self-hosted instance or create an account at
             https://grafana.com/auth/sign-up/create-user. See borgmatic
             https://grafana.com/auth/sign-up/create-user. See borgmatic
             monitoring documentation for details.
             monitoring documentation for details.
-
+    sentry:
+        type: object
+        required: ['data_source_name_url', 'monitor_slug']
+        additionalProperties: false
+        properties:
+            data_source_name_url:
+                type: string
+                description: |
+                    Sentry Data Source Name (DSN) URL, associated with a
+                    particular Sentry project. Used to construct a cron URL,
+                    notified when a backup begins, ends, or errors.
+                example: https://5f80ec@o294220.ingest.us.sentry.io/203069
+            monitor_slug:
+                type: string
+                description: |
+                    Sentry monitor slug, associated with a particular Sentry
+                    project monitor. Used along with the data source name URL to
+                    construct a cron URL.
+                example: mymonitor
+            states:
+                type: array
+                items:
+                    type: string
+                    enum:
+                        - start
+                        - finish
+                        - fail
+                    uniqueItems: true
+                description: |
+                    List of one or more monitoring states to ping for: "start",
+                    "finish", and/or "fail". Defaults to pinging for all states.
+                example:
+                    - start
+                    - finish
+        description: |
+            Configuration for a monitoring integration with Sentry. You can use
+            a self-hosted instance via https://develop.sentry.dev/self-hosted/
+            or create a cloud-hosted account at https://sentry.io. See borgmatic
+            monitoring documentation for details.
     zfs:
     zfs:
         type: ["object", "null"]
         type: ["object", "null"]
         additionalProperties: false
         additionalProperties: false

+ 0 - 1
borgmatic/hooks/monitoring/ntfy.py

@@ -19,7 +19,6 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
     Ping the configured Ntfy topic. Use the given configuration filename in any log entries.
     Ping the configured Ntfy topic. Use the given configuration filename in any log entries.
     If this is a dry run, then don't actually ping anything.
     If this is a dry run, then don't actually ping anything.
     '''
     '''
-
     run_states = hook_config.get('states', ['fail'])
     run_states = hook_config.get('states', ['fail'])
 
 
     if state.name.lower() in run_states:
     if state.name.lower() in run_states:

+ 79 - 0
borgmatic/hooks/monitoring/sentry.py

@@ -0,0 +1,79 @@
+import logging
+import re
+
+import requests
+
+logger = logging.getLogger(__name__)
+
+
+def initialize_monitor(
+    ping_url, config, config_filename, monitoring_log_level, dry_run
+):  # pragma: no cover
+    '''
+    No initialization is necessary for this monitor.
+    '''
+    pass
+
+
+DATA_SOURCE_NAME_URL_PATTERN = re.compile(
+    '^(?P<protocol>.*)://(?P<username>.*)@(?P<hostname>.*)/(?P<project_id>.*)$'
+)
+
+
+def ping_monitor(hook_config, config, config_filename, state, monitoring_log_level, dry_run):
+    '''
+    Construct and ping a Sentry cron URL, based on the configured DSN URL and monitor slug. Use the
+    given configuration filename in any log entries. If this is a dry run, then don't actually ping
+    anything.
+    '''
+    run_states = hook_config.get('states', ['start', 'finish', 'fail'])
+
+    if not state.name.lower() in run_states:
+        return
+
+    dry_run_label = ' (dry run; not actually pinging)' if dry_run else ''
+
+    data_source_name_url = hook_config.get('data_source_name_url')
+    monitor_slug = hook_config.get('monitor_slug')
+    match = DATA_SOURCE_NAME_URL_PATTERN.match(data_source_name_url)
+
+    if not match:
+        logger.warning(
+            'f{config_filename}: 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}')
+
+    status = {
+        'start': 'in_progress',
+        'finish': 'ok',
+        'fail': 'error',
+    }.get(state.name.lower())
+
+    if not status:
+        logger.warning('f{config_filename}: Invalid Sentry state')
+        return
+
+    if dry_run:
+        return
+
+    logging.getLogger('urllib3').setLevel(logging.ERROR)
+    try:
+        response = requests.post(f'{cron_url}?status={status}')
+        if not response.ok:
+            response.raise_for_status()
+    except requests.exceptions.RequestException as error:
+        logger.warning(f'{config_filename}: Sentry error: {error}')
+
+
+def destroy_monitor(
+    ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run
+):  # pragma: no cover
+    '''
+    No destruction is necessary for this monitor.
+    '''
+    pass

+ 39 - 1
docs/how-to/monitor-your-backups.md

@@ -47,6 +47,7 @@ them as backups happen:
  * [ntfy](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#ntfy-hook)
  * [ntfy](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#ntfy-hook)
  * [PagerDuty](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pagerduty-hook)
  * [PagerDuty](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pagerduty-hook)
  * [Pushover](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pushover-hook)
  * [Pushover](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pushover-hook)
+ * [Sentry](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#sentry-hook)
  * [Uptime Kuma](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#uptime-kuma-hook)
  * [Uptime Kuma](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#uptime-kuma-hook)
  * [Zabbix](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#zabbix-hook)
  * [Zabbix](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#zabbix-hook)
 
 
@@ -361,10 +362,47 @@ pushover:
 ```
 ```
 
 
 
 
+## Sentry hook
+
+<span class="minilink minilink-addedin">New in version 1.9.7</span>
+[Sentry](https://sentry.io/) is an application monitoring service that
+includes cron-style monitoring (either cloud-hosted or
+[self-hosted](https://develop.sentry.dev/self-hosted/)).
+
+To get started, create a [Sentry cron
+monitor](https://docs.sentry.io/product/crons/) in the Sentry UI. Under
+"Instrument your monitor," select "Sentry CLI" and copy the URL value for the
+displayed `SENTRY_DSN` environment variable into borgmatic's Sentry
+`data_source_name_url` configuration option. For example:
+
+```
+sentry:
+    data_source_name_url: https://5f80ec@o294220.ingest.us.sentry.io/203069
+    monitor_slug: mymonitor
+```
+
+The `monitor_slug` value comes from the "Monitor Slug" under "Cron Details" on
+the same Sentry monitor page.
+
+With this configuration, borgmatic pings Sentry whenever borgmatic starts,
+finishes, or fails, but only when any of the `create`, `prune`, `compact`, or
+`check` actions are run. You can optionally override the start/finish/fail
+behavior with the `states` configuration option. For instance, to only ping
+Sentry on failure:
+
+```
+sentry:
+    data_source_name_url: https://5f80ec@o294220.ingest.us.sentry.io/203069
+    monitor_slug: mymonitor
+    states:
+      - fail
+```
+
+
 ## ntfy hook
 ## ntfy hook
 
 
 <span class="minilink minilink-addedin">New in version 1.6.3</span>
 <span class="minilink minilink-addedin">New in version 1.6.3</span>
-[ntfy](https://ntfy.sh) is a free, simple, service (either hosted or
+[ntfy](https://ntfy.sh) is a free, simple, service (either cloud-hosted or
 self-hosted) which offers simple pub/sub push notifications to multiple
 self-hosted) which offers simple pub/sub push notifications to multiple
 platforms including [web](https://ntfy.sh/stats),
 platforms including [web](https://ntfy.sh/stats),
 [Android](https://play.google.com/store/apps/details?id=io.heckel.ntfy) and
 [Android](https://play.google.com/store/apps/details?id=io.heckel.ntfy) and