justanalytics-python 0.1.0__py3-none-any.whl
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.
- justanalytics/__init__.py +429 -0
- justanalytics/client.py +665 -0
- justanalytics/context.py +143 -0
- justanalytics/integrations/__init__.py +11 -0
- justanalytics/integrations/django.py +157 -0
- justanalytics/integrations/fastapi.py +197 -0
- justanalytics/integrations/flask.py +203 -0
- justanalytics/integrations/logging.py +175 -0
- justanalytics/integrations/requests.py +149 -0
- justanalytics/integrations/urllib3.py +146 -0
- justanalytics/span.py +281 -0
- justanalytics/trace_context.py +124 -0
- justanalytics/transport.py +430 -0
- justanalytics/types.py +214 -0
- justanalytics_python-0.1.0.dist-info/METADATA +173 -0
- justanalytics_python-0.1.0.dist-info/RECORD +17 -0
- justanalytics_python-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JustAnalytics Python SDK — end-to-end observability for Python applications.
|
|
3
|
+
|
|
4
|
+
Provides traces, error capture, structured logging, and metrics ingestion
|
|
5
|
+
to the JustAnalytics platform (Datadog + Sentry + Google Analytics in One).
|
|
6
|
+
|
|
7
|
+
Quick Start::
|
|
8
|
+
|
|
9
|
+
import justanalytics
|
|
10
|
+
|
|
11
|
+
justanalytics.init(
|
|
12
|
+
site_id="site_abc123",
|
|
13
|
+
api_key="ja_sk_your_api_key_here",
|
|
14
|
+
service_name="api-server",
|
|
15
|
+
environment="production",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Create traced spans
|
|
19
|
+
with justanalytics.start_span("process-order") as span:
|
|
20
|
+
span.set_attribute("order.id", "12345")
|
|
21
|
+
result = process_order()
|
|
22
|
+
|
|
23
|
+
# Capture exceptions
|
|
24
|
+
try:
|
|
25
|
+
risky_operation()
|
|
26
|
+
except Exception as e:
|
|
27
|
+
justanalytics.capture_exception(e, tags={"module": "payments"})
|
|
28
|
+
|
|
29
|
+
# Capture messages
|
|
30
|
+
justanalytics.capture_message("Deployment complete", level="info")
|
|
31
|
+
|
|
32
|
+
# Set user context
|
|
33
|
+
justanalytics.set_user(id="user-123", email="alice@example.com")
|
|
34
|
+
|
|
35
|
+
# Set tags
|
|
36
|
+
justanalytics.set_tag("feature", "checkout")
|
|
37
|
+
|
|
38
|
+
# Flush before shutdown
|
|
39
|
+
justanalytics.flush()
|
|
40
|
+
justanalytics.close()
|
|
41
|
+
|
|
42
|
+
Wire Protocol (all SDKs share the same endpoints)::
|
|
43
|
+
|
|
44
|
+
POST /api/ingest/spans — trace spans (batched array)
|
|
45
|
+
POST /api/ingest/errors — error events (individual)
|
|
46
|
+
POST /api/ingest/logs — log entries (batched array)
|
|
47
|
+
POST /api/ingest/metrics — infrastructure metrics (batched array)
|
|
48
|
+
|
|
49
|
+
Headers:
|
|
50
|
+
Authorization: Bearer ja_sk_...
|
|
51
|
+
Content-Type: application/json
|
|
52
|
+
X-Site-ID: <siteId>
|
|
53
|
+
User-Agent: justanalytics-python/0.1.0
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
from __future__ import annotations
|
|
57
|
+
|
|
58
|
+
__version__ = "0.1.0"
|
|
59
|
+
|
|
60
|
+
from contextlib import contextmanager
|
|
61
|
+
from typing import Any, Callable, Dict, Generator, List, Optional, TypeVar, Union
|
|
62
|
+
|
|
63
|
+
from .client import JustAnalyticsClient
|
|
64
|
+
from .context import get_active_span, get_tags, get_trace_id, get_user
|
|
65
|
+
from .span import Span
|
|
66
|
+
from .trace_context import TraceparentData, parse_traceparent, serialize_traceparent
|
|
67
|
+
from .types import (
|
|
68
|
+
ErrorLevel,
|
|
69
|
+
ErrorPayload,
|
|
70
|
+
LogLevel,
|
|
71
|
+
LogPayload,
|
|
72
|
+
MetricPayload,
|
|
73
|
+
SpanEvent,
|
|
74
|
+
SpanKind,
|
|
75
|
+
SpanPayload,
|
|
76
|
+
SpanStatus,
|
|
77
|
+
UserContext,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
81
|
+
|
|
82
|
+
# --- Singleton Client ---
|
|
83
|
+
|
|
84
|
+
_client = JustAnalyticsClient()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# --- Public API Functions ---
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def init(
|
|
91
|
+
site_id: str,
|
|
92
|
+
api_key: str,
|
|
93
|
+
service_name: str,
|
|
94
|
+
environment: Optional[str] = None,
|
|
95
|
+
release: Optional[str] = None,
|
|
96
|
+
server_url: Optional[str] = None,
|
|
97
|
+
debug: bool = False,
|
|
98
|
+
flush_interval_s: float = 2.0,
|
|
99
|
+
max_batch_size: int = 100,
|
|
100
|
+
enabled: bool = True,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Initialize the JustAnalytics SDK.
|
|
104
|
+
|
|
105
|
+
Must be called before any other SDK method. Calling ``init()`` more
|
|
106
|
+
than once logs a warning and is ignored.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
site_id: Site ID from the JustAnalytics dashboard.
|
|
110
|
+
api_key: API key (format: ``ja_sk_...``).
|
|
111
|
+
service_name: Service name (e.g., "api-server", "worker").
|
|
112
|
+
environment: Deployment environment (e.g., "production", "staging").
|
|
113
|
+
release: Release/version string (e.g., "1.2.3", git SHA).
|
|
114
|
+
server_url: Base URL of JustAnalytics server.
|
|
115
|
+
debug: Enable debug logging.
|
|
116
|
+
flush_interval_s: Flush interval in seconds (default: 2.0).
|
|
117
|
+
max_batch_size: Max items per batch before immediate flush (default: 100).
|
|
118
|
+
enabled: Enable/disable the SDK (default: True).
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
ValueError: If required fields are missing.
|
|
122
|
+
|
|
123
|
+
Example::
|
|
124
|
+
|
|
125
|
+
justanalytics.init(
|
|
126
|
+
site_id="site_abc123",
|
|
127
|
+
api_key="ja_sk_...",
|
|
128
|
+
service_name="api-server",
|
|
129
|
+
environment="production",
|
|
130
|
+
)
|
|
131
|
+
"""
|
|
132
|
+
_client.init(
|
|
133
|
+
site_id=site_id,
|
|
134
|
+
api_key=api_key,
|
|
135
|
+
service_name=service_name,
|
|
136
|
+
environment=environment,
|
|
137
|
+
release=release,
|
|
138
|
+
server_url=server_url,
|
|
139
|
+
debug=debug,
|
|
140
|
+
flush_interval_s=flush_interval_s,
|
|
141
|
+
max_batch_size=max_batch_size,
|
|
142
|
+
enabled=enabled,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def is_initialized() -> bool:
|
|
147
|
+
"""Whether ``init()`` has been called successfully."""
|
|
148
|
+
return _client.is_initialized
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@contextmanager
|
|
152
|
+
def start_span(
|
|
153
|
+
name: str,
|
|
154
|
+
op: Optional[str] = None,
|
|
155
|
+
kind: SpanKind = SpanKind.INTERNAL,
|
|
156
|
+
attributes: Optional[Dict[str, Any]] = None,
|
|
157
|
+
) -> Generator[Span, None, None]:
|
|
158
|
+
"""
|
|
159
|
+
Create a span as a context manager.
|
|
160
|
+
|
|
161
|
+
The span is automatically ended when the ``with`` block exits.
|
|
162
|
+
If an exception occurs, the span status is set to ``error``.
|
|
163
|
+
|
|
164
|
+
Nested ``start_span()`` calls automatically form parent-child
|
|
165
|
+
relationships via ``contextvars``.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
name: Operation name for the span.
|
|
169
|
+
op: Optional operation type.
|
|
170
|
+
kind: Span kind (default: INTERNAL).
|
|
171
|
+
attributes: Initial attributes.
|
|
172
|
+
|
|
173
|
+
Yields:
|
|
174
|
+
The created Span instance.
|
|
175
|
+
|
|
176
|
+
Example::
|
|
177
|
+
|
|
178
|
+
with justanalytics.start_span("fetch-user") as span:
|
|
179
|
+
span.set_attribute("user.id", user_id)
|
|
180
|
+
user = db.get_user(user_id)
|
|
181
|
+
"""
|
|
182
|
+
with _client.start_span(name, op=op, kind=kind, attributes=attributes) as span:
|
|
183
|
+
yield span
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def span(
|
|
187
|
+
name: Optional[str] = None,
|
|
188
|
+
op: Optional[str] = None,
|
|
189
|
+
kind: SpanKind = SpanKind.INTERNAL,
|
|
190
|
+
attributes: Optional[Dict[str, Any]] = None,
|
|
191
|
+
) -> Callable[[F], F]:
|
|
192
|
+
"""
|
|
193
|
+
Decorator that wraps a function in a span.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
name: Span name (defaults to the function's qualified name).
|
|
197
|
+
op: Optional operation type.
|
|
198
|
+
kind: Span kind.
|
|
199
|
+
attributes: Initial span attributes.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
A decorator.
|
|
203
|
+
|
|
204
|
+
Example::
|
|
205
|
+
|
|
206
|
+
@justanalytics.span(op="db.query")
|
|
207
|
+
def get_user(user_id: str):
|
|
208
|
+
return db.query(...)
|
|
209
|
+
"""
|
|
210
|
+
return _client.span_decorator(name=name, op=op, kind=kind, attributes=attributes)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def capture_exception(
|
|
214
|
+
error: BaseException,
|
|
215
|
+
tags: Optional[Dict[str, str]] = None,
|
|
216
|
+
extra: Optional[Dict[str, Any]] = None,
|
|
217
|
+
user: Optional[UserContext] = None,
|
|
218
|
+
level: Union[ErrorLevel, str] = ErrorLevel.ERROR,
|
|
219
|
+
fingerprint: Optional[List[str]] = None,
|
|
220
|
+
) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Capture an exception and send it to JustAnalytics.
|
|
223
|
+
|
|
224
|
+
Automatically attaches the current trace_id, span_id, user context,
|
|
225
|
+
and tags from the contextvars context.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
error: The exception to capture.
|
|
229
|
+
tags: Additional tags (merged with context tags).
|
|
230
|
+
extra: Arbitrary extra data.
|
|
231
|
+
user: User context (overrides context user).
|
|
232
|
+
level: Severity level (default: ERROR).
|
|
233
|
+
fingerprint: Custom fingerprint for grouping.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
A unique event_id, or empty string if SDK is disabled.
|
|
237
|
+
|
|
238
|
+
Example::
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
risky_operation()
|
|
242
|
+
except Exception as e:
|
|
243
|
+
justanalytics.capture_exception(e, tags={"module": "payments"})
|
|
244
|
+
"""
|
|
245
|
+
if isinstance(level, str):
|
|
246
|
+
level = ErrorLevel(level)
|
|
247
|
+
return _client.capture_exception(
|
|
248
|
+
error=error,
|
|
249
|
+
tags=tags,
|
|
250
|
+
extra=extra,
|
|
251
|
+
user=user,
|
|
252
|
+
level=level,
|
|
253
|
+
fingerprint=fingerprint,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def capture_message(
|
|
258
|
+
message: str,
|
|
259
|
+
level: Union[ErrorLevel, str] = ErrorLevel.INFO,
|
|
260
|
+
tags: Optional[Dict[str, str]] = None,
|
|
261
|
+
extra: Optional[Dict[str, Any]] = None,
|
|
262
|
+
) -> str:
|
|
263
|
+
"""
|
|
264
|
+
Capture a message and send it to JustAnalytics.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
message: The message string.
|
|
268
|
+
level: Severity level (default: INFO).
|
|
269
|
+
tags: Additional tags.
|
|
270
|
+
extra: Arbitrary extra data.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
A unique event_id, or empty string if SDK is disabled.
|
|
274
|
+
|
|
275
|
+
Example::
|
|
276
|
+
|
|
277
|
+
justanalytics.capture_message(
|
|
278
|
+
"User exceeded rate limit",
|
|
279
|
+
level="warning",
|
|
280
|
+
tags={"userId": user.id},
|
|
281
|
+
)
|
|
282
|
+
"""
|
|
283
|
+
if isinstance(level, str):
|
|
284
|
+
level = ErrorLevel(level)
|
|
285
|
+
return _client.capture_message(message=message, level=level, tags=tags, extra=extra)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def set_user(
|
|
289
|
+
id: Optional[str] = None,
|
|
290
|
+
email: Optional[str] = None,
|
|
291
|
+
username: Optional[str] = None,
|
|
292
|
+
) -> None:
|
|
293
|
+
"""
|
|
294
|
+
Set user context for the current execution scope.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
id: User ID.
|
|
298
|
+
email: User email.
|
|
299
|
+
username: Username.
|
|
300
|
+
|
|
301
|
+
Example::
|
|
302
|
+
|
|
303
|
+
justanalytics.set_user(id="user-123", email="alice@example.com")
|
|
304
|
+
"""
|
|
305
|
+
_client.set_user(id=id, email=email, username=username)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def set_tag(key: str, value: str) -> None:
|
|
309
|
+
"""
|
|
310
|
+
Set a tag for the current execution scope.
|
|
311
|
+
|
|
312
|
+
Tags are attached as attributes on all spans created within
|
|
313
|
+
the current contextvars scope.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
key: Tag key.
|
|
317
|
+
value: Tag value.
|
|
318
|
+
|
|
319
|
+
Example::
|
|
320
|
+
|
|
321
|
+
justanalytics.set_tag("feature", "checkout")
|
|
322
|
+
"""
|
|
323
|
+
_client.set_tag(key, value)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def log(
|
|
327
|
+
level: Union[LogLevel, str],
|
|
328
|
+
message: str,
|
|
329
|
+
attributes: Optional[Dict[str, Any]] = None,
|
|
330
|
+
) -> None:
|
|
331
|
+
"""
|
|
332
|
+
Send a structured log entry to JustAnalytics.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
level: Log severity level.
|
|
336
|
+
message: Log message.
|
|
337
|
+
attributes: Optional key-value metadata.
|
|
338
|
+
|
|
339
|
+
Example::
|
|
340
|
+
|
|
341
|
+
justanalytics.log("info", "User logged in", {"userId": "u123"})
|
|
342
|
+
"""
|
|
343
|
+
if isinstance(level, str):
|
|
344
|
+
level = LogLevel(level)
|
|
345
|
+
_client.log(level=level, message=message, attributes=attributes)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def record_metric(
|
|
349
|
+
metric_name: str,
|
|
350
|
+
value: float,
|
|
351
|
+
tags: Optional[Dict[str, Any]] = None,
|
|
352
|
+
) -> None:
|
|
353
|
+
"""
|
|
354
|
+
Record a custom infrastructure metric.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
metric_name: Metric name using dot notation.
|
|
358
|
+
value: Numeric value.
|
|
359
|
+
tags: Optional additional tags.
|
|
360
|
+
|
|
361
|
+
Example::
|
|
362
|
+
|
|
363
|
+
justanalytics.record_metric("custom.queue_size", 42, {"queue": "emails"})
|
|
364
|
+
"""
|
|
365
|
+
_client.record_metric(metric_name=metric_name, value=value, tags=tags)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def flush() -> None:
|
|
369
|
+
"""
|
|
370
|
+
Manually flush all pending data to the server.
|
|
371
|
+
|
|
372
|
+
Useful before a serverless function completes or in tests.
|
|
373
|
+
"""
|
|
374
|
+
_client.flush()
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def close() -> None:
|
|
378
|
+
"""
|
|
379
|
+
Shut down the SDK: flush remaining data, stop timers.
|
|
380
|
+
|
|
381
|
+
After calling ``close()``, the SDK cannot be used again
|
|
382
|
+
until ``init()`` is called.
|
|
383
|
+
"""
|
|
384
|
+
_client.close()
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
# --- Exported Names ---
|
|
388
|
+
|
|
389
|
+
__all__ = [
|
|
390
|
+
"__version__",
|
|
391
|
+
# Lifecycle
|
|
392
|
+
"init",
|
|
393
|
+
"is_initialized",
|
|
394
|
+
"flush",
|
|
395
|
+
"close",
|
|
396
|
+
# Spans
|
|
397
|
+
"start_span",
|
|
398
|
+
"span",
|
|
399
|
+
"get_active_span",
|
|
400
|
+
"get_trace_id",
|
|
401
|
+
# Errors
|
|
402
|
+
"capture_exception",
|
|
403
|
+
"capture_message",
|
|
404
|
+
# Context
|
|
405
|
+
"set_user",
|
|
406
|
+
"set_tag",
|
|
407
|
+
"get_user",
|
|
408
|
+
"get_tags",
|
|
409
|
+
# Logging
|
|
410
|
+
"log",
|
|
411
|
+
# Metrics
|
|
412
|
+
"record_metric",
|
|
413
|
+
# W3C Trace Context
|
|
414
|
+
"parse_traceparent",
|
|
415
|
+
"serialize_traceparent",
|
|
416
|
+
# Types
|
|
417
|
+
"Span",
|
|
418
|
+
"SpanKind",
|
|
419
|
+
"SpanStatus",
|
|
420
|
+
"SpanEvent",
|
|
421
|
+
"SpanPayload",
|
|
422
|
+
"ErrorLevel",
|
|
423
|
+
"ErrorPayload",
|
|
424
|
+
"LogLevel",
|
|
425
|
+
"LogPayload",
|
|
426
|
+
"MetricPayload",
|
|
427
|
+
"UserContext",
|
|
428
|
+
"TraceparentData",
|
|
429
|
+
]
|