Просмотр исходного кода

Merge pull request #9342 from netbox-community/9340-sentry

Closes #9340: Enable Sentry integration
Jeremy Stretch 3 лет назад
Родитель
Сommit
8cc94689d8

+ 4 - 0
base_requirements.txt

@@ -102,6 +102,10 @@ psycopg2-binary
 # https://github.com/yaml/pyyaml
 PyYAML
 
+# Sentry SDK
+# https://github.com/getsentry/sentry-python
+sentry-sdk
+
 # Social authentication framework
 # https://github.com/python-social-auth/social-core
 social-auth-core

+ 29 - 0
docs/administration/error-reporting.md

@@ -0,0 +1,29 @@
+# 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.
+
+To test Sentry operation, try generating a 404 (page not found) error by navigating to an invalid URL, such as `https://netbox/404-error-testing`. After receiving a 404 response from the NetBox server, you should see the issue appear shortly in Sentry.

+ 33 - 0
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`

+ 1 - 0
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
 

+ 1 - 0
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'

+ 25 - 0
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
 #

+ 1 - 0
netbox/netbox/urls.py

@@ -100,4 +100,5 @@ urlpatterns = [
     path('{}'.format(settings.BASE_PATH), include(_patterns))
 ]
 
+handler404 = 'netbox.views.handler_404'
 handler500 = 'netbox.views.server_error'

+ 11 - 3
netbox/netbox/views/__init__.py

@@ -2,7 +2,6 @@ import platform
 import sys
 
 from django.conf import settings
-from django.contrib.contenttypes.models import ContentType
 from django.core.cache import cache
 from django.db.models import F
 from django.http import HttpResponseServerError
@@ -11,9 +10,10 @@ from django.template import loader
 from django.template.exceptions import TemplateDoesNotExist
 from django.urls import reverse
 from django.views.decorators.csrf import requires_csrf_token
-from django.views.defaults import ERROR_500_TEMPLATE_NAME
+from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found
 from django.views.generic import View
 from packaging import version
+from sentry_sdk import capture_message
 
 from circuits.models import Circuit, Provider
 from dcim.models import (
@@ -190,13 +190,21 @@ class StaticMediaFailureView(View):
     """
     Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
     """
-
     def get(self, request):
         return render(request, 'media_failure.html', {
             'filename': request.GET.get('filename')
         })
 
 
+def handler_404(request, exception):
+    """
+    Wrap Django's default 404 handler to enable Sentry reporting.
+    """
+    capture_message("Page not found", level="error")
+
+    return page_not_found(request, exception)
+
+
 @requires_csrf_token
 def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
     """

+ 1 - 0
requirements.txt

@@ -24,6 +24,7 @@ netaddr==0.8.0
 Pillow==9.1.0
 psycopg2-binary==2.9.3
 PyYAML==6.0
+sentry-sdk==1.5.12
 social-auth-app-django==5.0.0
 social-auth-core==4.2.0
 svgwrite==1.4.2