diff --git a/docs/administration/error-reporting.md b/docs/administration/error-reporting.md new file mode 100644 index 000000000..b2977bf39 --- /dev/null +++ b/docs/administration/error-reporting.md @@ -0,0 +1,27 @@ +# Error Reporting + +## Sentry + +NetBox v3.2.3 and later support native integration with [Sentry](https://sentry.io/) for automatic error reporting. To enable this feature, begin by creating a new project in Sentry to represent your NetBox deployment and obtain its corresponding data source name (DSN). This looks like a URL similar to the example below: + +``` +https://examplePublicKey@o0.ingest.sentry.io/0 +``` + +Once you have obtained a DSN, configure Sentry in NetBox's `configuration.py` file with the following parameters: + +```python +SENTRY_ENABLED = True +SENTRY_DSN = "https://YourDSNgoesHere@o0.ingest.sentry.io/0" +``` + +You can optionally attach one or more arbitrary tags to the outgoing error reports if desired by setting the `SENTRY_TAGS` parameter: + +```python +SENTRY_TAGS = { + "custom.foo": "123", + "custom.bar": "abc", +} +``` + +Once the configuration has been saved, restart the NetBox service. diff --git a/docs/configuration/optional-settings.md b/docs/configuration/optional-settings.md index 76fd0a12c..4a2d62e91 100644 --- a/docs/configuration/optional-settings.md +++ b/docs/configuration/optional-settings.md @@ -404,6 +404,39 @@ The file path to the location where [custom scripts](../customization/custom-scr --- +## SENTRY_DSN + +Default: None + +Defines a Sentry data source name (DSN) for automated error reporting. `SENTRY_ENABLED` must be True for this parameter to take effect. For example: + +``` +SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0" +``` + +--- + +## SENTRY_ENABLED + +Default: False + +Set to True to enable automatic error reporting via [Sentry](https://sentry.io/). Requires `SENTRY_DSN` to be defined. + +--- + +## SENTRY_TAGS + +An optional dictionary of tags to apply to Sentry error reports. `SENTRY_ENABLED` must be True for this parameter to take effect. For example: + +``` +SENTRY_TAGS = { + "custom.foo": "123", + "custom.bar": "abc", +} +``` + +--- + ## SESSION_COOKIE_NAME Default: `sessionid` diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 6eadb3d9e..bfd432741 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -11,6 +11,7 @@ * [#9278](https://github.com/netbox-community/netbox/issues/9278) - Linkify device types count under manufacturers list * [#9280](https://github.com/netbox-community/netbox/issues/9280) - Allow adopting existing components when installing a module * [#9314](https://github.com/netbox-community/netbox/issues/9314) - Add device and VM filters for FHRP group assignments +* [#9340](https://github.com/netbox-community/netbox/issues/9340) - Introduce support for error reporting via Sentry ### Bug Fixes diff --git a/mkdocs.yml b/mkdocs.yml index 225c6d4bf..0b7108cd0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -123,6 +123,7 @@ nav: - Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md' - Okta: 'administration/authentication/okta.md' - Permissions: 'administration/permissions.md' + - Error Reporting: 'administration/error-reporting.md' - Housekeeping: 'administration/housekeeping.md' - Replicating NetBox: 'administration/replicating-netbox.md' - NetBox Shell: 'administration/netbox-shell.md' diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 6dee1081a..e539ecbbe 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -8,9 +8,11 @@ import sys import warnings from urllib.parse import urlsplit +import sentry_sdk from django.contrib.messages import constants as messages from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.validators import URLValidator +from sentry_sdk.integrations.django import DjangoIntegration from netbox.config import PARAMS @@ -113,6 +115,9 @@ REMOTE_AUTH_GROUP_SEPARATOR = getattr(configuration, 'REMOTE_AUTH_GROUP_SEPARATO REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/') RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300) SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/') +SENTRY_DSN = getattr(configuration, 'SENTRY_DSN', None) +SENTRY_ENABLED = getattr(configuration, 'SENTRY_ENABLED', False) +SENTRY_TAGS = getattr(configuration, 'SENTRY_TAGS', {}) SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None) SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid') SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d') @@ -428,6 +433,26 @@ EXEMPT_PATHS = ( ) +# +# Sentry +# + +if SENTRY_ENABLED: + if not SENTRY_DSN: + raise ImproperlyConfigured("SENTRY_ENABLED is True but SENTRY_DSN has not been defined.") + sentry_sdk.init( + dsn=SENTRY_DSN, + release=VERSION, + integrations=[DjangoIntegration()], + traces_sample_rate=1.0, + send_default_pii=True, + http_proxy=HTTP_PROXIES.get('http') if HTTP_PROXIES else None, + https_proxy=HTTP_PROXIES.get('https') if HTTP_PROXIES else None + ) + for k, v in SENTRY_TAGS.items(): + sentry_sdk.set_tag(k, v) + + # # Django social auth #