Skip to content

Commit fbde5ca

Browse files
macoboneilkakkar
andauthored
Add python sentry (and sentry & django) integrations (#13)
* Add python sentry (and sentry & django) integrations * example for django-sentry application and PosthogIntegration code Co-authored-by: Neil Kakkar <[email protected]>
1 parent 2e99081 commit fbde5ca

File tree

15 files changed

+361
-1
lines changed

15 files changed

+361
-1
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## 1.3.1 - 2021-05-07
2+
3+
- Add $set and $set_once support (#23)
4+
- Add distinct id to $create_alias event (#27)
5+
- Add UUID to ID_TYPES (#26)
6+
7+
## 1.2.1
8+
9+
Initial release logged in changelog.

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ release_analytics:
2929
e2e_test:
3030
.buildscripts/e2e.sh
3131

32+
django_example:
33+
python -m pip install -e ".[sentry]"
34+
cd sentry_django_example && python manage.py runserver 8080
35+
3236
.PHONY: test lint release e2e_test

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,22 @@ Specifically, the [Python integration](https://posthog.com/docs/integrations/pyt
1919

2020
## Running Locally
2121

22-
Assuming you have a [local version of PostHog](https://posthog.com/docs/developing-locally) running, you can run `python3 example.py` to see the library in action.
22+
Assuming you have a [local version of PostHog](https://posthog.com/docs/developing-locally) running, you can run `python3 example.py` to see the library in action.
23+
24+
## Running the Django Sentry Integration Locally
25+
26+
There's a sample Django project included, called `sentry_django_example`, which explains how to use PostHog with Sentry.
27+
28+
There's 2 places of importance (Changes required are all marked with TODO in the sample project directory)
29+
30+
1. Settings.py
31+
1. Input your Sentry DSN
32+
2. Input your Sentry Org and ProjectID details into `PosthogIntegration()`
33+
3. Add `POSTHOG_DJANGO` to settings.py. This allows the `PosthogDistinctIdMiddleware` to get the distinct_ids
34+
35+
2. urls.py
36+
1. This includes the `sentry-debug/` endpoint, which generates an exception
37+
38+
To run things: `make django_example`. This installs the posthog-python library with the sentry-sdk add-on, and then runs the django app.
39+
Also start the PostHog app locally.
40+
Then navigate to `http://127.0.0.1:8080/sentry-debug/` and you should get an event in both Sentry and PostHog, with links to each other.

posthog/sentry/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
POSTHOG_ID_TAG = "posthog_distinct_id"

posthog/sentry/django.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from django.conf import settings
2+
from sentry_sdk import configure_scope
3+
4+
from posthog.sentry import POSTHOG_ID_TAG
5+
6+
GET_DISTINCT_ID = getattr(settings, "POSTHOG_DJANGO", {}).get("distinct_id")
7+
8+
9+
def get_distinct_id(request):
10+
if not GET_DISTINCT_ID:
11+
return None
12+
try:
13+
return GET_DISTINCT_ID(request)
14+
except:
15+
return None
16+
17+
18+
class PosthogDistinctIdMiddleware:
19+
def __init__(self, get_response):
20+
self.get_response = get_response
21+
22+
def __call__(self, request):
23+
with configure_scope() as scope:
24+
distinct_id = get_distinct_id(request)
25+
if distinct_id:
26+
scope.set_tag(POSTHOG_ID_TAG, distinct_id)
27+
response = self.get_response(request)
28+
return response
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from sentry_sdk._types import MYPY
2+
from sentry_sdk.hub import Hub
3+
from sentry_sdk.integrations import Integration
4+
from sentry_sdk.scope import add_global_event_processor
5+
6+
import posthog
7+
from posthog.request import DEFAULT_HOST
8+
from posthog.sentry import POSTHOG_ID_TAG
9+
10+
if MYPY:
11+
from typing import Any, Dict, Optional
12+
13+
from sentry_sdk._types import Event, Hint
14+
15+
16+
class PostHogIntegration(Integration):
17+
identifier = "posthog-python"
18+
organization = None # The Sentry organization, used to send a direct link from PostHog to Sentry
19+
project_id = None # The Sentry project id, used to send a direct link from PostHog to Sentry
20+
prefix = "https://sentry.io/organizations/" # Url of a self-hosted sentry instance (default: https://sentry.io/organizations/)
21+
22+
@staticmethod
23+
def setup_once():
24+
@add_global_event_processor
25+
def processor(event, hint):
26+
# type: (Event, Optional[Hint]) -> Optional[Event]
27+
if Hub.current.get_integration(PostHogIntegration) is not None:
28+
if event.get("level") != "error":
29+
return event
30+
31+
if event.get("tags", {}).get(POSTHOG_ID_TAG):
32+
posthog_distinct_id = event["tags"][POSTHOG_ID_TAG]
33+
event["tags"]["PostHog URL"] = f"{posthog.host or DEFAULT_HOST}/person/{posthog_distinct_id}"
34+
35+
properties = {
36+
"$sentry_event_id": event["event_id"],
37+
"$sentry_exception": event["exception"],
38+
}
39+
40+
if PostHogIntegration.organization and PostHogIntegration.project_id:
41+
properties[
42+
"$sentry_url"
43+
] = f"{PostHogIntegration.prefix}{PostHogIntegration.organization}/issues/?project={PostHogIntegration.project_id}&query={event['event_id']}"
44+
45+
posthog.capture(posthog_distinct_id, "$exception", properties)
46+
47+
return event

sentry_django_example/db.sqlite3

Whitespace-only changes.

sentry_django_example/manage.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env python
2+
"""Django's command-line utility for administrative tasks."""
3+
import os
4+
import sys
5+
6+
7+
def main():
8+
"""Run administrative tasks."""
9+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sentry_django_example.settings")
10+
try:
11+
from django.core.management import execute_from_command_line
12+
except ImportError as exc:
13+
raise ImportError(
14+
"Couldn't import Django. Are you sure it's installed and "
15+
"available on your PYTHONPATH environment variable? Did you "
16+
"forget to activate a virtual environment?"
17+
) from exc
18+
execute_from_command_line(sys.argv)
19+
20+
21+
if __name__ == "__main__":
22+
main()

sentry_django_example/sentry_django_example/__init__.py

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
ASGI config for sentry_django_example project.
3+
4+
It exposes the ASGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.asgi import get_asgi_application
13+
14+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sentry_django_example.settings")
15+
16+
application = get_asgi_application()

0 commit comments

Comments
 (0)