Sfoglia il codice sorgente

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

Closes #9340: Enable Sentry integration
Jeremy Stretch 3 anni fa
parent
commit
8cc94689d8

+ 4 - 0
base_requirements.txt

@@ -102,6 +102,10 @@ psycopg2-binary
 # https://github.com/yaml/pyyaml
 # https://github.com/yaml/pyyaml
 PyYAML
 PyYAML
 
 
+# Sentry SDK
+# https://github.com/getsentry/sentry-python
+sentry-sdk
+
 # Social authentication framework
 # Social authentication framework
 # https://github.com/python-social-auth/social-core
 # https://github.com/python-social-auth/social-core
 social-auth-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
 ## SESSION_COOKIE_NAME
 
 
 Default: `sessionid`
 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
 * [#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
 * [#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
 * [#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
 ### Bug Fixes
 
 

+ 1 - 0
mkdocs.yml

@@ -123,6 +123,7 @@ nav:
             - Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md'
             - Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md'
             - Okta: 'administration/authentication/okta.md'
             - Okta: 'administration/authentication/okta.md'
         - Permissions: 'administration/permissions.md'
         - Permissions: 'administration/permissions.md'
+        - Error Reporting: 'administration/error-reporting.md'
         - Housekeeping: 'administration/housekeeping.md'
         - Housekeeping: 'administration/housekeeping.md'
         - Replicating NetBox: 'administration/replicating-netbox.md'
         - Replicating NetBox: 'administration/replicating-netbox.md'
         - NetBox Shell: 'administration/netbox-shell.md'
         - NetBox Shell: 'administration/netbox-shell.md'

+ 25 - 0
netbox/netbox/settings.py

@@ -8,9 +8,11 @@ import sys
 import warnings
 import warnings
 from urllib.parse import urlsplit
 from urllib.parse import urlsplit
 
 
+import sentry_sdk
 from django.contrib.messages import constants as messages
 from django.contrib.messages import constants as messages
 from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.core.validators import URLValidator
 from django.core.validators import URLValidator
+from sentry_sdk.integrations.django import DjangoIntegration
 
 
 from netbox.config import PARAMS
 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('/')
 REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/')
 RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300)
 RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300)
 SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/')
 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_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None)
 SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid')
 SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid')
 SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
 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
 # Django social auth
 #
 #

+ 1 - 0
netbox/netbox/urls.py

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

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

@@ -2,7 +2,6 @@ import platform
 import sys
 import sys
 
 
 from django.conf import settings
 from django.conf import settings
-from django.contrib.contenttypes.models import ContentType
 from django.core.cache import cache
 from django.core.cache import cache
 from django.db.models import F
 from django.db.models import F
 from django.http import HttpResponseServerError
 from django.http import HttpResponseServerError
@@ -11,9 +10,10 @@ from django.template import loader
 from django.template.exceptions import TemplateDoesNotExist
 from django.template.exceptions import TemplateDoesNotExist
 from django.urls import reverse
 from django.urls import reverse
 from django.views.decorators.csrf import requires_csrf_token
 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 django.views.generic import View
 from packaging import version
 from packaging import version
+from sentry_sdk import capture_message
 
 
 from circuits.models import Circuit, Provider
 from circuits.models import Circuit, Provider
 from dcim.models import (
 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.
     Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
     """
     """
-
     def get(self, request):
     def get(self, request):
         return render(request, 'media_failure.html', {
         return render(request, 'media_failure.html', {
             'filename': request.GET.get('filename')
             '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
 @requires_csrf_token
 def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
 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
 Pillow==9.1.0
 psycopg2-binary==2.9.3
 psycopg2-binary==2.9.3
 PyYAML==6.0
 PyYAML==6.0
+sentry-sdk==1.5.12
 social-auth-app-django==5.0.0
 social-auth-app-django==5.0.0
 social-auth-core==4.2.0
 social-auth-core==4.2.0
 svgwrite==1.4.2
 svgwrite==1.4.2