ghosttrap-sdk 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: ghosttrap-sdk
3
+ Version: 0.4.0
4
+ Summary: Drop-in error reporter for ghosttrap.io
5
+ Project-URL: Homepage, https://github.com/arowley-predictive-power/ghosttrap-sdk
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+
9
+ # ghosttrap-sdk
10
+
11
+ Drop-in error reporter for Python apps.
12
+
13
+ ## Install
14
+
15
+ ```
16
+ pip install ghosttrap-sdk
17
+ ```
18
+
19
+ ## Use
20
+
21
+ ```python
22
+ import ghosttrap
23
+
24
+ # with a token (get one from `ghosttrap watch`):
25
+ ghosttrap.init("t_abc123def456")
26
+
27
+ # or with a full URL:
28
+ ghosttrap.init("https://ghosttrap.io/trap/owner/repo/")
29
+ ```
30
+
31
+ Unhandled exceptions are POSTed to ghosttrap.io with full tracebacks.
32
+ The original `sys.excepthook` is preserved so errors still print to
33
+ stderr as normal.
34
+
35
+ For caught exceptions inside web frameworks:
36
+
37
+ ```python
38
+ try:
39
+ do_something()
40
+ except Exception as exc:
41
+ ghosttrap.report(exc)
42
+ raise
43
+ ```
@@ -0,0 +1,35 @@
1
+ # ghosttrap-sdk
2
+
3
+ Drop-in error reporter for Python apps.
4
+
5
+ ## Install
6
+
7
+ ```
8
+ pip install ghosttrap-sdk
9
+ ```
10
+
11
+ ## Use
12
+
13
+ ```python
14
+ import ghosttrap
15
+
16
+ # with a token (get one from `ghosttrap watch`):
17
+ ghosttrap.init("t_abc123def456")
18
+
19
+ # or with a full URL:
20
+ ghosttrap.init("https://ghosttrap.io/trap/owner/repo/")
21
+ ```
22
+
23
+ Unhandled exceptions are POSTed to ghosttrap.io with full tracebacks.
24
+ The original `sys.excepthook` is preserved so errors still print to
25
+ stderr as normal.
26
+
27
+ For caught exceptions inside web frameworks:
28
+
29
+ ```python
30
+ try:
31
+ do_something()
32
+ except Exception as exc:
33
+ ghosttrap.report(exc)
34
+ raise
35
+ ```
@@ -0,0 +1,3 @@
1
+ from ghosttrap.client import init, report
2
+
3
+ __all__ = ["init", "report"]
@@ -0,0 +1,118 @@
1
+ """Drop-in error reporter for ghosttrap.io.
2
+
3
+ Usage:
4
+ import ghosttrap
5
+
6
+ # with a token (recommended):
7
+ ghosttrap.init("t_abc123def456")
8
+
9
+ # or with a full URL:
10
+ ghosttrap.init("https://ghosttrap.io/trap/owner/repo/")
11
+
12
+ Hooks into sys.excepthook (unhandled exceptions), Python logging
13
+ (logger.exception / logger.error with exc_info), and Celery task
14
+ failures (if Celery is installed). For additional coverage, use the
15
+ Django middleware: ghosttrap.middleware.GhostTrapMiddleware.
16
+ """
17
+
18
+ import json
19
+ import logging
20
+ import sys
21
+ import traceback
22
+ import urllib.request
23
+
24
+ GHOSTTRAP_SERVER = "https://ghosttrap.io"
25
+
26
+ _endpoint = None
27
+ _original_excepthook = None
28
+
29
+
30
+ def init(dsn, server=None):
31
+ """Configure the reporter.
32
+
33
+ Args:
34
+ dsn: Either a token (e.g. "t_abc123") or a full URL
35
+ (e.g. "https://ghosttrap.io/trap/owner/repo/")
36
+ server: Base server URL. Only needed if dsn is a token and
37
+ you're not using ghosttrap.io.
38
+ """
39
+ global _endpoint, _original_excepthook
40
+ if dsn.startswith("http://") or dsn.startswith("https://"):
41
+ _endpoint = dsn.rstrip("/") + "/"
42
+ else:
43
+ base = (server or GHOSTTRAP_SERVER).rstrip("/")
44
+ _endpoint = f"{base}/trap/{dsn}/"
45
+ _original_excepthook = sys.excepthook
46
+ sys.excepthook = _error_hook
47
+ _install_logging_handler()
48
+ _install_celery_hook()
49
+
50
+
51
+ def report(exc):
52
+ """Report a caught exception to the ghosttrap server.
53
+
54
+ Args:
55
+ exc: the exception instance from an `except Exception as exc` block
56
+ """
57
+ if _endpoint is None:
58
+ return
59
+ _post(_build_payload(type(exc), exc, exc.__traceback__))
60
+
61
+
62
+ def _error_hook(exc_type, exc_value, exc_tb):
63
+ _post(_build_payload(exc_type, exc_value, exc_tb))
64
+ _original_excepthook(exc_type, exc_value, exc_tb)
65
+
66
+
67
+ def _install_logging_handler():
68
+ handler = _GhostTrapLogHandler()
69
+ handler.setLevel(logging.ERROR)
70
+ logging.getLogger().addHandler(handler)
71
+
72
+
73
+ def _install_celery_hook():
74
+ try:
75
+ from celery.signals import task_failure
76
+ task_failure.connect(_on_task_failure)
77
+ except ImportError:
78
+ pass
79
+
80
+
81
+ def _on_task_failure(sender, exception, traceback, **kwargs):
82
+ report(exception)
83
+
84
+
85
+ class _GhostTrapLogHandler(logging.Handler):
86
+ def emit(self, record):
87
+ if record.exc_info and record.exc_info[1]:
88
+ report(record.exc_info[1])
89
+
90
+
91
+ def _build_payload(exc_type, exc_value, exc_tb):
92
+ frames = traceback.extract_tb(exc_tb) if exc_tb else []
93
+ return {
94
+ "type": exc_type.__name__,
95
+ "message": str(exc_value),
96
+ "traceback": traceback.format_exception(exc_type, exc_value, exc_tb) if exc_tb else [],
97
+ "frames": [
98
+ {
99
+ "file": f.filename,
100
+ "line": f.lineno,
101
+ "function": f.name,
102
+ "code": f.line,
103
+ }
104
+ for f in frames
105
+ ],
106
+ }
107
+
108
+
109
+ def _post(payload):
110
+ try:
111
+ req = urllib.request.Request(
112
+ _endpoint,
113
+ data=json.dumps(payload).encode(),
114
+ headers={"Content-Type": "application/json"},
115
+ )
116
+ urllib.request.urlopen(req, timeout=5)
117
+ except Exception:
118
+ pass
@@ -0,0 +1,43 @@
1
+ """Django integration for ghosttrap.
2
+
3
+ Add to INSTALLED_APPS:
4
+
5
+ INSTALLED_APPS = [
6
+ ...
7
+ "ghosttrap.django.GhostTrapApp",
8
+ ]
9
+
10
+ This re-attaches the ghosttrap logging handler after Django's
11
+ dictConfig runs (which typically clobbers handlers added during
12
+ init()). Also provides the GhostTrapMiddleware for catching
13
+ unhandled view exceptions.
14
+ """
15
+
16
+ import logging
17
+
18
+ from django.apps import AppConfig
19
+
20
+
21
+ class GhostTrapApp(AppConfig):
22
+ name = "ghosttrap.django"
23
+ label = "ghosttrap_django"
24
+ verbose_name = "Ghosttrap"
25
+
26
+ def ready(self):
27
+ from ghosttrap.client import _install_logging_handler
28
+ _install_logging_handler()
29
+
30
+
31
+ class GhostTrapMiddleware:
32
+ """Catches unhandled view exceptions and reports them."""
33
+
34
+ def __init__(self, get_response):
35
+ self.get_response = get_response
36
+
37
+ def __call__(self, request):
38
+ return self.get_response(request)
39
+
40
+ def process_exception(self, request, exception):
41
+ from ghosttrap.client import report
42
+ report(exception)
43
+ return None
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: ghosttrap-sdk
3
+ Version: 0.4.0
4
+ Summary: Drop-in error reporter for ghosttrap.io
5
+ Project-URL: Homepage, https://github.com/arowley-predictive-power/ghosttrap-sdk
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+
9
+ # ghosttrap-sdk
10
+
11
+ Drop-in error reporter for Python apps.
12
+
13
+ ## Install
14
+
15
+ ```
16
+ pip install ghosttrap-sdk
17
+ ```
18
+
19
+ ## Use
20
+
21
+ ```python
22
+ import ghosttrap
23
+
24
+ # with a token (get one from `ghosttrap watch`):
25
+ ghosttrap.init("t_abc123def456")
26
+
27
+ # or with a full URL:
28
+ ghosttrap.init("https://ghosttrap.io/trap/owner/repo/")
29
+ ```
30
+
31
+ Unhandled exceptions are POSTed to ghosttrap.io with full tracebacks.
32
+ The original `sys.excepthook` is preserved so errors still print to
33
+ stderr as normal.
34
+
35
+ For caught exceptions inside web frameworks:
36
+
37
+ ```python
38
+ try:
39
+ do_something()
40
+ except Exception as exc:
41
+ ghosttrap.report(exc)
42
+ raise
43
+ ```
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ ghosttrap/__init__.py
4
+ ghosttrap/client.py
5
+ ghosttrap/django.py
6
+ ghosttrap_sdk.egg-info/PKG-INFO
7
+ ghosttrap_sdk.egg-info/SOURCES.txt
8
+ ghosttrap_sdk.egg-info/dependency_links.txt
9
+ ghosttrap_sdk.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ ghosttrap
@@ -0,0 +1,17 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ghosttrap-sdk"
7
+ version = "0.4.0"
8
+ description = "Drop-in error reporter for ghosttrap.io"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = []
12
+
13
+ [project.urls]
14
+ Homepage = "https://github.com/arowley-predictive-power/ghosttrap-sdk"
15
+
16
+ [tool.setuptools.packages.find]
17
+ include = ["ghosttrap*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+