heimdall-sdk 0.1.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,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: heimdall-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for Heimdall observability platform
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: httpx>=0.24
9
+ Provides-Extra: fastapi
10
+ Requires-Dist: fastapi>=0.95; extra == "fastapi"
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=7.0; extra == "dev"
13
+ Requires-Dist: respx>=0.20; extra == "dev"
14
+ Requires-Dist: ruff>=0.4; extra == "dev"
15
+ Requires-Dist: mypy>=1.0; extra == "dev"
16
+ Requires-Dist: fastapi>=0.95; extra == "dev"
17
+ Requires-Dist: httpx>=0.24; extra == "dev"
18
+
19
+ # Heimdall Python SDK
20
+
21
+ Synchronous, fail-safe Python client for the [Heimdall](https://heimdall-ob.com) ingest API. Send telemetry events, errors, and performance metrics from any Python application.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install heimdall-sdk
27
+ ```
28
+
29
+ With FastAPI middleware support:
30
+
31
+ ```bash
32
+ pip install heimdall-sdk[fastapi]
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```python
38
+ from heimdall import HeimdallClient
39
+
40
+ client = HeimdallClient(
41
+ api_key="your-api-key",
42
+ service_name="payments-api",
43
+ environment="production",
44
+ )
45
+
46
+ # Send telemetry events
47
+ client.success("payment_processed", metadata={"amount": 150, "currency": "USD"})
48
+ client.error("payment_failed", metadata={"reason": "insufficient_funds"})
49
+ client.warning("retry_attempt", metadata={"attempt": 2})
50
+ client.info("user_login", tags={"region": "us-east"})
51
+ client.timeout("external_api_call")
52
+ client.canceled("bulk_import")
53
+ ```
54
+
55
+ ## Error Capture
56
+
57
+ ```python
58
+ try:
59
+ process_payment(order)
60
+ except Exception as exc:
61
+ client.capture_exception(exc, endpoint="/checkout")
62
+ # exc is NOT re-raised by the SDK
63
+ ```
64
+
65
+ Manual error reporting:
66
+
67
+ ```python
68
+ client.send_error(
69
+ "PaymentGatewayError",
70
+ "Gateway returned 503",
71
+ endpoint="/api/payments",
72
+ stacktrace=traceback.format_exc(),
73
+ )
74
+ ```
75
+
76
+ ## Performance Tracking
77
+
78
+ Direct call:
79
+
80
+ ```python
81
+ client.send_performance(
82
+ "db_query",
83
+ target_type="database",
84
+ target_name="users",
85
+ duration_ms=45,
86
+ )
87
+ ```
88
+
89
+ Decorator (automatically measures duration):
90
+
91
+ ```python
92
+ @client.track("transfer", target_type="function", include_exceptions=True)
93
+ def process_transfer(amount: float) -> dict:
94
+ ...
95
+ ```
96
+
97
+ Context manager:
98
+
99
+ ```python
100
+ with client.timing("bank_sync", target_type="job", target_name="nightly_sync"):
101
+ sync_with_bank()
102
+ ```
103
+
104
+ ## FastAPI Integration
105
+
106
+ ```python
107
+ from fastapi import FastAPI
108
+ from heimdall import HeimdallClient
109
+ from heimdall.integrations.fastapi import setup_heimdall
110
+
111
+ app = FastAPI()
112
+ client = HeimdallClient(api_key="your-api-key", service_name="my-api")
113
+
114
+ setup_heimdall(app, client)
115
+ ```
116
+
117
+ Every HTTP request automatically gets a `request_duration` performance metric using the route path template (e.g. `GET /users/{user_id}`).
118
+
119
+ ## Configuration
120
+
121
+ | Parameter | Type | Default | Description |
122
+ |-----------|------|---------|-------------|
123
+ | `api_key` | `str` | required | Heimdall API key |
124
+ | `base_url` | `str` | `https://heimdall-ob.com/api/v1` | Override for local development |
125
+ | `enabled` | `bool` | `True` | Set `False` to disable all network calls |
126
+ | `raise_on_error` | `bool` | `False` | Raise `HeimdallTransportError` on failures |
127
+ | `timeout` | `float` | `5.0` | HTTP request timeout in seconds |
128
+ | `service_name` | `str` | `None` | Injected into every event's tags |
129
+ | `environment` | `str` | `None` | Injected into every event's tags |
130
+ | `default_metadata` | `dict` | `None` | Merged into every event's metadata |
131
+ | `default_tags` | `dict` | `None` | Merged into every event's tags |
132
+
133
+ ## Fail-Safe by Default
134
+
135
+ All send methods return `True` on success and `False` on any failure — the SDK never raises into your application unless `raise_on_error=True`.
136
+
137
+ ```python
138
+ result = client.success("order_placed")
139
+ if not result:
140
+ logger.warning("Heimdall telemetry unavailable")
141
+ ```
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ git clone <repo>
147
+ cd heimdall-sdk
148
+ pip install -e ".[dev]"
149
+ pytest
150
+ ```
151
+
152
+ For more examples see [`specs/001-heimdall-python-sdk/quickstart.md`](specs/001-heimdall-python-sdk/quickstart.md).
@@ -0,0 +1,134 @@
1
+ # Heimdall Python SDK
2
+
3
+ Synchronous, fail-safe Python client for the [Heimdall](https://heimdall-ob.com) ingest API. Send telemetry events, errors, and performance metrics from any Python application.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install heimdall-sdk
9
+ ```
10
+
11
+ With FastAPI middleware support:
12
+
13
+ ```bash
14
+ pip install heimdall-sdk[fastapi]
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```python
20
+ from heimdall import HeimdallClient
21
+
22
+ client = HeimdallClient(
23
+ api_key="your-api-key",
24
+ service_name="payments-api",
25
+ environment="production",
26
+ )
27
+
28
+ # Send telemetry events
29
+ client.success("payment_processed", metadata={"amount": 150, "currency": "USD"})
30
+ client.error("payment_failed", metadata={"reason": "insufficient_funds"})
31
+ client.warning("retry_attempt", metadata={"attempt": 2})
32
+ client.info("user_login", tags={"region": "us-east"})
33
+ client.timeout("external_api_call")
34
+ client.canceled("bulk_import")
35
+ ```
36
+
37
+ ## Error Capture
38
+
39
+ ```python
40
+ try:
41
+ process_payment(order)
42
+ except Exception as exc:
43
+ client.capture_exception(exc, endpoint="/checkout")
44
+ # exc is NOT re-raised by the SDK
45
+ ```
46
+
47
+ Manual error reporting:
48
+
49
+ ```python
50
+ client.send_error(
51
+ "PaymentGatewayError",
52
+ "Gateway returned 503",
53
+ endpoint="/api/payments",
54
+ stacktrace=traceback.format_exc(),
55
+ )
56
+ ```
57
+
58
+ ## Performance Tracking
59
+
60
+ Direct call:
61
+
62
+ ```python
63
+ client.send_performance(
64
+ "db_query",
65
+ target_type="database",
66
+ target_name="users",
67
+ duration_ms=45,
68
+ )
69
+ ```
70
+
71
+ Decorator (automatically measures duration):
72
+
73
+ ```python
74
+ @client.track("transfer", target_type="function", include_exceptions=True)
75
+ def process_transfer(amount: float) -> dict:
76
+ ...
77
+ ```
78
+
79
+ Context manager:
80
+
81
+ ```python
82
+ with client.timing("bank_sync", target_type="job", target_name="nightly_sync"):
83
+ sync_with_bank()
84
+ ```
85
+
86
+ ## FastAPI Integration
87
+
88
+ ```python
89
+ from fastapi import FastAPI
90
+ from heimdall import HeimdallClient
91
+ from heimdall.integrations.fastapi import setup_heimdall
92
+
93
+ app = FastAPI()
94
+ client = HeimdallClient(api_key="your-api-key", service_name="my-api")
95
+
96
+ setup_heimdall(app, client)
97
+ ```
98
+
99
+ Every HTTP request automatically gets a `request_duration` performance metric using the route path template (e.g. `GET /users/{user_id}`).
100
+
101
+ ## Configuration
102
+
103
+ | Parameter | Type | Default | Description |
104
+ |-----------|------|---------|-------------|
105
+ | `api_key` | `str` | required | Heimdall API key |
106
+ | `base_url` | `str` | `https://heimdall-ob.com/api/v1` | Override for local development |
107
+ | `enabled` | `bool` | `True` | Set `False` to disable all network calls |
108
+ | `raise_on_error` | `bool` | `False` | Raise `HeimdallTransportError` on failures |
109
+ | `timeout` | `float` | `5.0` | HTTP request timeout in seconds |
110
+ | `service_name` | `str` | `None` | Injected into every event's tags |
111
+ | `environment` | `str` | `None` | Injected into every event's tags |
112
+ | `default_metadata` | `dict` | `None` | Merged into every event's metadata |
113
+ | `default_tags` | `dict` | `None` | Merged into every event's tags |
114
+
115
+ ## Fail-Safe by Default
116
+
117
+ All send methods return `True` on success and `False` on any failure — the SDK never raises into your application unless `raise_on_error=True`.
118
+
119
+ ```python
120
+ result = client.success("order_placed")
121
+ if not result:
122
+ logger.warning("Heimdall telemetry unavailable")
123
+ ```
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ git clone <repo>
129
+ cd heimdall-sdk
130
+ pip install -e ".[dev]"
131
+ pytest
132
+ ```
133
+
134
+ For more examples see [`specs/001-heimdall-python-sdk/quickstart.md`](specs/001-heimdall-python-sdk/quickstart.md).
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "heimdall-sdk"
7
+ version = "0.1.0"
8
+ description = "Python SDK for Heimdall observability platform"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ dependencies = [
13
+ "httpx>=0.24",
14
+ ]
15
+
16
+ [project.optional-dependencies]
17
+ fastapi = [
18
+ "fastapi>=0.95",
19
+ ]
20
+ dev = [
21
+ "pytest>=7.0",
22
+ "respx>=0.20",
23
+ "ruff>=0.4",
24
+ "mypy>=1.0",
25
+ "fastapi>=0.95",
26
+ "httpx>=0.24",
27
+ ]
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["src"]
31
+ include = ["heimdall*"]
32
+
33
+ [tool.pytest.ini_options]
34
+ testpaths = ["tests"]
35
+ pythonpath = ["src"]
36
+
37
+ [tool.mypy]
38
+ strict = true
39
+ warn_no_return = true
40
+ warn_unreachable = true
41
+
42
+ [tool.ruff]
43
+ target-version = "py310"
44
+ line-length = 88
45
+
46
+ [tool.ruff.lint]
47
+ select = ["F", "E", "W", "I", "UP", "B", "C4", "SIM"]
48
+ ignore = ["E501"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """Heimdall Python SDK."""
2
+
3
+ from heimdall._exceptions import (
4
+ HeimdallConfigError,
5
+ HeimdallError,
6
+ HeimdallTransportError,
7
+ HeimdallValidationError,
8
+ )
9
+ from heimdall.client import (
10
+ HeimdallClient,
11
+ _DEFAULT_BASE_URL as DEFAULT_BASE_URL,
12
+ _ENV_API_KEY as ENV_API_KEY,
13
+ _ENV_BASE_URL as ENV_BASE_URL,
14
+ _ENV_ENABLED as ENV_ENABLED,
15
+ )
16
+
17
+ __all__ = [
18
+ "HeimdallClient",
19
+ "HeimdallError",
20
+ "HeimdallConfigError",
21
+ "HeimdallTransportError",
22
+ "HeimdallValidationError",
23
+ "DEFAULT_BASE_URL",
24
+ "ENV_API_KEY",
25
+ "ENV_BASE_URL",
26
+ "ENV_ENABLED",
27
+ ]
28
+
29
+ __version__ = "0.1.0"
@@ -0,0 +1,39 @@
1
+ """Internal enumerations for event status and level.
2
+
3
+ These enums are NEVER part of the public API. Developers interact with
4
+ them implicitly through the named methods on HeimdallClient.
5
+ """
6
+
7
+ from enum import Enum
8
+
9
+
10
+ class EventStatus(str, Enum):
11
+ """Allowed values for the event/error status field."""
12
+
13
+ SUCCESS = "success"
14
+ ERROR = "error"
15
+ WARNING = "warning"
16
+ INFO = "info"
17
+ TIMEOUT = "timeout"
18
+ CANCELED = "canceled"
19
+
20
+
21
+ class EventLevel(str, Enum):
22
+ """Allowed values for the event/error/performance level field."""
23
+
24
+ DEBUG = "debug"
25
+ INFO = "info"
26
+ WARNING = "warning"
27
+ ERROR = "error"
28
+ CRITICAL = "critical"
29
+
30
+
31
+ # Default level for each status method — determined by method called, not developer.
32
+ STATUS_LEVEL_MAP: dict[EventStatus, EventLevel] = {
33
+ EventStatus.SUCCESS: EventLevel.INFO,
34
+ EventStatus.ERROR: EventLevel.ERROR,
35
+ EventStatus.WARNING: EventLevel.WARNING,
36
+ EventStatus.INFO: EventLevel.INFO,
37
+ EventStatus.TIMEOUT: EventLevel.WARNING,
38
+ EventStatus.CANCELED: EventLevel.INFO,
39
+ }
@@ -0,0 +1,21 @@
1
+ """SDK-specific exception hierarchy."""
2
+
3
+
4
+ class HeimdallError(Exception):
5
+ """Base exception for all Heimdall SDK errors."""
6
+
7
+
8
+ class HeimdallConfigError(HeimdallError):
9
+ """Raised for invalid SDK configuration (e.g., empty api_key)."""
10
+
11
+
12
+ class HeimdallTransportError(HeimdallError):
13
+ """Raised for HTTP transport failures when raise_on_error=True."""
14
+
15
+
16
+ class HeimdallValidationError(HeimdallError):
17
+ """Raised for obviously invalid method arguments (empty names, negative duration, etc.).
18
+
19
+ Always raised regardless of raise_on_error setting because these are
20
+ programmer errors, not runtime conditions.
21
+ """
@@ -0,0 +1,27 @@
1
+ """Exception formatter — extracts type, message, and stacktrace from Python exceptions."""
2
+
3
+ import traceback
4
+ from typing import Any
5
+
6
+
7
+ class ExceptionFormatter:
8
+ """Formats Python exception objects into error payload dicts.
9
+
10
+ Never makes HTTP calls or calls the SDK client.
11
+ Only responsibility: extract structured data from exceptions.
12
+ """
13
+
14
+ @staticmethod
15
+ def format(exc: BaseException) -> dict[str, Any]:
16
+ """Extract error_type, message, and stacktrace from a Python exception.
17
+
18
+ Works correctly when called inside or outside an except block.
19
+ Handles chained exceptions gracefully.
20
+ """
21
+ return {
22
+ "error_type": exc.__class__.__name__,
23
+ "message": str(exc),
24
+ "stacktrace": "".join(
25
+ traceback.format_exception(type(exc), exc, exc.__traceback__)
26
+ ),
27
+ }