blaxel 0.2.28__py3-none-any.whl → 0.2.30__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.
- blaxel/__init__.py +2 -2
- blaxel/core/common/autoload.py +57 -5
- blaxel/telemetry/__init__.py +7 -3
- blaxel/telemetry/exporters.py +28 -13
- blaxel/telemetry/manager.py +50 -18
- {blaxel-0.2.28.dist-info → blaxel-0.2.30.dist-info}/METADATA +1 -1
- {blaxel-0.2.28.dist-info → blaxel-0.2.30.dist-info}/RECORD +9 -9
- {blaxel-0.2.28.dist-info → blaxel-0.2.30.dist-info}/WHEEL +0 -0
- {blaxel-0.2.28.dist-info → blaxel-0.2.30.dist-info}/licenses/LICENSE +0 -0
blaxel/__init__.py
CHANGED
|
@@ -4,8 +4,8 @@ from .core.common.autoload import autoload
|
|
|
4
4
|
from .core.common.env import env
|
|
5
5
|
from .core.common.settings import settings
|
|
6
6
|
|
|
7
|
-
__version__ = "0.2.
|
|
8
|
-
__commit__ = "
|
|
7
|
+
__version__ = "0.2.30"
|
|
8
|
+
__commit__ = "65a88e27efd0b99c49c23a5cc339865d3bafca2e"
|
|
9
9
|
__sentry_dsn__ = "https://9711de13cd02b285ca4378c01de8dc30@o4508714045276160.ingest.us.sentry.io/4510461121462272"
|
|
10
10
|
__all__ = ["autoload", "settings", "env"]
|
|
11
11
|
|
blaxel/core/common/autoload.py
CHANGED
|
@@ -2,6 +2,7 @@ import atexit
|
|
|
2
2
|
import logging
|
|
3
3
|
import sys
|
|
4
4
|
import threading
|
|
5
|
+
from asyncio import CancelledError
|
|
5
6
|
|
|
6
7
|
from sentry_sdk import Client, Hub
|
|
7
8
|
|
|
@@ -19,21 +20,72 @@ logger = logging.getLogger(__name__)
|
|
|
19
20
|
_sentry_hub: Hub | None = None
|
|
20
21
|
_captured_exceptions: set = set() # Track already captured exceptions to avoid duplicates
|
|
21
22
|
|
|
23
|
+
# Exceptions that are part of normal control flow and should not be captured
|
|
24
|
+
_IGNORED_EXCEPTIONS = (
|
|
25
|
+
StopIteration, # Iterator exhaustion
|
|
26
|
+
StopAsyncIteration, # Async iterator exhaustion
|
|
27
|
+
GeneratorExit, # Generator cleanup
|
|
28
|
+
KeyboardInterrupt, # User interrupt (Ctrl+C)
|
|
29
|
+
SystemExit, # Program exit
|
|
30
|
+
CancelledError, # Async task cancellation
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Optional dependencies that may not be installed - import errors for these are expected
|
|
34
|
+
_OPTIONAL_DEPENDENCIES = (
|
|
35
|
+
'opentelemetry',
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_exception_key(exc_type, exc_value, frame) -> str:
|
|
40
|
+
"""Generate a unique key for an exception based on type, message, and origin."""
|
|
41
|
+
# Use type name + message + original file/line where exception was raised
|
|
42
|
+
# This ensures the same logical exception is only captured once
|
|
43
|
+
exc_name = exc_type.__name__ if exc_type else "Unknown"
|
|
44
|
+
exc_msg = str(exc_value) if exc_value else ""
|
|
45
|
+
# Get the original traceback location (where exception was first raised)
|
|
46
|
+
tb = getattr(exc_value, '__traceback__', None)
|
|
47
|
+
if tb:
|
|
48
|
+
# Walk to the deepest frame (origin of exception)
|
|
49
|
+
while tb.tb_next:
|
|
50
|
+
tb = tb.tb_next
|
|
51
|
+
origin = f"{tb.tb_frame.f_code.co_filename}:{tb.tb_lineno}"
|
|
52
|
+
else:
|
|
53
|
+
origin = f"{frame.f_code.co_filename}:{frame.f_lineno}"
|
|
54
|
+
return f"{exc_name}:{exc_msg}:{origin}"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _is_optional_dependency_error(exc_type, exc_value) -> bool:
|
|
58
|
+
"""Check if the exception is an import error for an optional dependency."""
|
|
59
|
+
# ModuleNotFoundError is a subclass of ImportError, so checking ImportError covers both
|
|
60
|
+
if exc_type and issubclass(exc_type, ImportError):
|
|
61
|
+
msg = str(exc_value).lower()
|
|
62
|
+
return any(dep in msg for dep in _OPTIONAL_DEPENDENCIES)
|
|
63
|
+
return False
|
|
64
|
+
|
|
22
65
|
|
|
23
66
|
def _trace_blaxel_exceptions(frame, event, arg):
|
|
24
67
|
"""Trace function that captures exceptions from blaxel SDK code."""
|
|
25
68
|
if event == 'exception':
|
|
26
69
|
exc_type, exc_value, exc_tb = arg
|
|
70
|
+
|
|
71
|
+
# Skip control flow exceptions (not actual errors)
|
|
72
|
+
if exc_type and issubclass(exc_type, _IGNORED_EXCEPTIONS):
|
|
73
|
+
return _trace_blaxel_exceptions
|
|
74
|
+
|
|
75
|
+
# Skip import errors for optional dependencies (expected when not installed)
|
|
76
|
+
if _is_optional_dependency_error(exc_type, exc_value):
|
|
77
|
+
return _trace_blaxel_exceptions
|
|
78
|
+
|
|
27
79
|
filename = frame.f_code.co_filename
|
|
28
80
|
|
|
29
81
|
# Only capture if it's from blaxel in site-packages
|
|
30
82
|
if 'site-packages/blaxel' in filename:
|
|
31
|
-
# Avoid capturing the same exception multiple times
|
|
32
|
-
|
|
33
|
-
if
|
|
34
|
-
_captured_exceptions.add(
|
|
83
|
+
# Avoid capturing the same exception multiple times using a content-based key
|
|
84
|
+
exc_key = _get_exception_key(exc_type, exc_value, frame)
|
|
85
|
+
if exc_key not in _captured_exceptions:
|
|
86
|
+
_captured_exceptions.add(exc_key)
|
|
35
87
|
capture_exception(exc_value)
|
|
36
|
-
# Clean up old exception
|
|
88
|
+
# Clean up old exception keys to prevent memory leak
|
|
37
89
|
if len(_captured_exceptions) > 1000:
|
|
38
90
|
_captured_exceptions.clear()
|
|
39
91
|
|
blaxel/telemetry/__init__.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"""Blaxel telemetry module."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
from .
|
|
5
|
-
from .
|
|
3
|
+
try:
|
|
4
|
+
from .exporters import * # noqa: F403, F401
|
|
5
|
+
from .instrumentation import * # noqa: F403, F401
|
|
6
|
+
from .log import * # noqa: F403, F401
|
|
7
|
+
except ImportError:
|
|
8
|
+
pass
|
|
9
|
+
|
|
6
10
|
from .manager import * # noqa: F403, F401
|
blaxel/telemetry/exporters.py
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
|
|
4
|
-
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
5
|
-
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
6
|
-
from opentelemetry.sdk._logs import LogData
|
|
7
|
-
from opentelemetry.sdk.metrics.export import MetricExportResult, MetricsData
|
|
1
|
+
from __future__ import annotations
|
|
8
2
|
|
|
3
|
+
from typing import Callable, Dict, Sequence
|
|
9
4
|
|
|
10
|
-
|
|
5
|
+
try:
|
|
6
|
+
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
|
|
7
|
+
from opentelemetry.exporter.otlp.proto.http.metric_exporter import (
|
|
8
|
+
OTLPMetricExporter,
|
|
9
|
+
)
|
|
10
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
11
|
+
from opentelemetry.sdk._logs import LogData
|
|
12
|
+
from opentelemetry.sdk.metrics.export import MetricExportResult, MetricsData
|
|
13
|
+
|
|
14
|
+
_OPENTELEMETRY_AVAILABLE = True
|
|
15
|
+
except ImportError:
|
|
16
|
+
_OPENTELEMETRY_AVAILABLE = False
|
|
17
|
+
OTLPLogExporter = object
|
|
18
|
+
OTLPMetricExporter = object
|
|
19
|
+
OTLPSpanExporter = object
|
|
20
|
+
LogData = None
|
|
21
|
+
MetricExportResult = None
|
|
22
|
+
MetricsData = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DynamicHeadersSpanExporter(OTLPSpanExporter): # type: ignore[misc]
|
|
11
26
|
"""Span exporter with dynamic headers."""
|
|
12
27
|
|
|
13
28
|
def __init__(self, get_headers: Callable[[], Dict[str, str]]):
|
|
@@ -18,7 +33,7 @@ class DynamicHeadersSpanExporter(OTLPSpanExporter):
|
|
|
18
33
|
self._session.headers.update(self._get_headers())
|
|
19
34
|
return super().export(spans)
|
|
20
35
|
|
|
21
|
-
class DynamicHeadersMetricExporter(OTLPMetricExporter):
|
|
36
|
+
class DynamicHeadersMetricExporter(OTLPMetricExporter): # type: ignore[misc]
|
|
22
37
|
"""Metric exporter with dynamic headers."""
|
|
23
38
|
|
|
24
39
|
def __init__(self, get_headers: Callable[[], Dict[str, str]]):
|
|
@@ -27,19 +42,19 @@ class DynamicHeadersMetricExporter(OTLPMetricExporter):
|
|
|
27
42
|
|
|
28
43
|
def export(
|
|
29
44
|
self,
|
|
30
|
-
metrics_data: MetricsData,
|
|
45
|
+
metrics_data: MetricsData, # type: ignore[reportUnknownReturnType]
|
|
31
46
|
timeout_millis: float = 10_000,
|
|
32
47
|
**kwargs,
|
|
33
|
-
) -> MetricExportResult:
|
|
48
|
+
) -> MetricExportResult: # type: ignore[reportUnknownReturnType]
|
|
34
49
|
self._session.headers.update(self._get_headers())
|
|
35
50
|
return super().export(metrics_data, timeout_millis, **kwargs)
|
|
36
51
|
|
|
37
|
-
class DynamicHeadersLogExporter(OTLPLogExporter):
|
|
52
|
+
class DynamicHeadersLogExporter(OTLPLogExporter): # type: ignore[misc]
|
|
38
53
|
"""Log exporter with dynamic headers."""
|
|
39
54
|
def __init__(self, get_headers: Callable[[], Dict[str, str]]):
|
|
40
55
|
self._get_headers = get_headers
|
|
41
56
|
super().__init__()
|
|
42
57
|
|
|
43
|
-
def export(self, batch: Sequence[LogData]):
|
|
58
|
+
def export(self, batch: Sequence[LogData]): # type: ignore[reportUnknownReturnType]
|
|
44
59
|
self._session.headers.update(self._get_headers())
|
|
45
60
|
return super().export(batch)
|
blaxel/telemetry/manager.py
CHANGED
|
@@ -3,6 +3,8 @@ This module provides utilities for setting up and managing OpenTelemetry instrum
|
|
|
3
3
|
It includes classes and functions for configuring tracers, meters, loggers, and integrating with FastAPI applications.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import importlib
|
|
7
9
|
import logging
|
|
8
10
|
import os
|
|
@@ -10,27 +12,52 @@ import signal
|
|
|
10
12
|
import time
|
|
11
13
|
from typing import Any, Dict, List, Type
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
from opentelemetry
|
|
15
|
-
from opentelemetry.
|
|
16
|
-
from opentelemetry.
|
|
17
|
-
from opentelemetry.sdk.
|
|
18
|
-
from opentelemetry.sdk.metrics
|
|
19
|
-
from opentelemetry.sdk.
|
|
20
|
-
from opentelemetry.sdk.
|
|
21
|
-
from opentelemetry.sdk.trace
|
|
22
|
-
from opentelemetry.trace import
|
|
15
|
+
try:
|
|
16
|
+
from opentelemetry import metrics, trace
|
|
17
|
+
from opentelemetry._logs import set_logger_provider
|
|
18
|
+
from opentelemetry.metrics import NoOpMeterProvider
|
|
19
|
+
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
|
|
20
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
21
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
22
|
+
from opentelemetry.sdk.resources import Resource
|
|
23
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
24
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
25
|
+
from opentelemetry.trace import NoOpTracerProvider
|
|
26
|
+
|
|
27
|
+
_OPENTELEMETRY_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
_OPENTELEMETRY_AVAILABLE = False
|
|
30
|
+
metrics = None
|
|
31
|
+
trace = None
|
|
32
|
+
set_logger_provider = None
|
|
33
|
+
NoOpMeterProvider = None
|
|
34
|
+
LoggerProvider = None
|
|
35
|
+
LoggingHandler = None
|
|
36
|
+
MeterProvider = None
|
|
37
|
+
PeriodicExportingMetricReader = None
|
|
38
|
+
Resource = None
|
|
39
|
+
TracerProvider = None
|
|
40
|
+
BatchSpanProcessor = None
|
|
41
|
+
NoOpTracerProvider = None
|
|
23
42
|
|
|
24
43
|
from blaxel.core.common import Settings
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
from .
|
|
33
|
-
from .
|
|
45
|
+
if _OPENTELEMETRY_AVAILABLE:
|
|
46
|
+
from .exporters import (
|
|
47
|
+
DynamicHeadersLogExporter,
|
|
48
|
+
DynamicHeadersMetricExporter,
|
|
49
|
+
DynamicHeadersSpanExporter,
|
|
50
|
+
)
|
|
51
|
+
from .instrumentation.map import MAPPINGS
|
|
52
|
+
from .log.log import AsyncLogRecordProcessor
|
|
53
|
+
from .span import DefaultAttributesSpanProcessor
|
|
54
|
+
else:
|
|
55
|
+
DynamicHeadersLogExporter = None
|
|
56
|
+
DynamicHeadersMetricExporter = None
|
|
57
|
+
DynamicHeadersSpanExporter = None
|
|
58
|
+
MAPPINGS = {}
|
|
59
|
+
AsyncLogRecordProcessor = None
|
|
60
|
+
DefaultAttributesSpanProcessor = None
|
|
34
61
|
|
|
35
62
|
logger = logging.getLogger(__name__)
|
|
36
63
|
|
|
@@ -46,6 +73,8 @@ class TelemetryManager:
|
|
|
46
73
|
|
|
47
74
|
@property
|
|
48
75
|
def enabled(self) -> bool:
|
|
76
|
+
if not _OPENTELEMETRY_AVAILABLE:
|
|
77
|
+
return False
|
|
49
78
|
return (self.settings and self.settings.enable_opentelemetry) or False
|
|
50
79
|
|
|
51
80
|
@property
|
|
@@ -127,6 +156,9 @@ class TelemetryManager:
|
|
|
127
156
|
def initialize(self, settings: Settings):
|
|
128
157
|
"""Initialize the telemetry system."""
|
|
129
158
|
self.settings = settings
|
|
159
|
+
if not _OPENTELEMETRY_AVAILABLE:
|
|
160
|
+
logger.debug("OpenTelemetry not available, telemetry disabled")
|
|
161
|
+
return
|
|
130
162
|
if not self.enabled or self.initialized:
|
|
131
163
|
return
|
|
132
164
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
blaxel/__init__.py,sha256=
|
|
1
|
+
blaxel/__init__.py,sha256=cYJGcG2cbn4ieiOOx5G6CgS82USYtqTTSk8I9dSIxrY,412
|
|
2
2
|
blaxel/core/__init__.py,sha256=CKMC7TaCYdOdnwqcJCN9VjBbNg366coZUGTxI1mgFQQ,1710
|
|
3
3
|
blaxel/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
blaxel/core/agents/__init__.py,sha256=nZfYRnBlpYLD2rnMpVgl3VabQm4w6NoDbGU3qHtpGhw,4376
|
|
@@ -333,7 +333,7 @@ blaxel/core/client/models/workspace_labels.py,sha256=WbnUY6eCTkUNdY7hhhSF-KQCl8f
|
|
|
333
333
|
blaxel/core/client/models/workspace_runtime.py,sha256=dxEpmwCFPOCRKHRKhY-iW7j6TbtL5qUsbjzSn00uTXU,1665
|
|
334
334
|
blaxel/core/client/models/workspace_user.py,sha256=70CcifQWYbeWG7TDui4pblTzUe5sVK0AS19vNCzKE8g,3423
|
|
335
335
|
blaxel/core/common/__init__.py,sha256=K5uIZe98NIjYkjMPdKvP5RuwzIfv1Obm6g3aWh2qjZ0,599
|
|
336
|
-
blaxel/core/common/autoload.py,sha256=
|
|
336
|
+
blaxel/core/common/autoload.py,sha256=MBOyVG3neNwE2zC0G8u-Ijyk3hB0E2nclYDZqhUs5FM,6404
|
|
337
337
|
blaxel/core/common/env.py,sha256=-nns-h3p0x6MGyGdUNAOEfn4Hi_J_pQfJjRKHz43A8E,1231
|
|
338
338
|
blaxel/core/common/internal.py,sha256=NDTFh9Duj84os8GkMXjGzn-UVS9zBDyLAcfPxIpoQGA,3218
|
|
339
339
|
blaxel/core/common/logger.py,sha256=Jt0MCJgYDPq36rl7UyKRDJH76a-AwYdfggNeNYJt6N0,4779
|
|
@@ -475,9 +475,9 @@ blaxel/pydantic/model.py,sha256=LQui3m7SjpOunlvm7EjxQOfDof8gfUUnig_EkztEsj0,6039
|
|
|
475
475
|
blaxel/pydantic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
476
476
|
blaxel/pydantic/tools.py,sha256=94eYhgPsE7PwL8F2zYh5nWUh4GDgAg_GP0RgsMb2UZE,1969
|
|
477
477
|
blaxel/pydantic/custom/gemini.py,sha256=2oZFb-7-qaaCY1ajoCkiHSavZqV4uzCLwqp1192u7VM,1051
|
|
478
|
-
blaxel/telemetry/__init__.py,sha256=
|
|
479
|
-
blaxel/telemetry/exporters.py,sha256=
|
|
480
|
-
blaxel/telemetry/manager.py,sha256=
|
|
478
|
+
blaxel/telemetry/__init__.py,sha256=g5wNqaXAVdRmUe0cX7qKHN1cftF2MaHo_QCS4D-u8CQ,257
|
|
479
|
+
blaxel/telemetry/exporters.py,sha256=2eHcHlCXJ4yQcKfWnRcnRhkBim8_yUNqgFIVUDPHkv0,2194
|
|
480
|
+
blaxel/telemetry/manager.py,sha256=r7fbx1ElUO8J4aNJ8SFlT0VxmQI2-an1HrmvannkO4Q,10339
|
|
481
481
|
blaxel/telemetry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
482
482
|
blaxel/telemetry/span.py,sha256=_mLYE_8vSEenUGmnC-OqPExJqBpOx6iT3BnkmkmQxN4,3644
|
|
483
483
|
blaxel/telemetry/instrumentation/blaxel_core.py,sha256=j0NwvC92DgMIXd6bMwC_Wor5tOPNPizNDNQDU4wsYAM,4930
|
|
@@ -488,7 +488,7 @@ blaxel/telemetry/instrumentation/map.py,sha256=PCzZJj39yiYVYJrxLBNP-NW-tjjYyTijw
|
|
|
488
488
|
blaxel/telemetry/instrumentation/utils.py,sha256=KInMYZH-mu9_wvetmf0EmgrfN3Sw8IWk2Y95v2u90_U,1901
|
|
489
489
|
blaxel/telemetry/log/log.py,sha256=k9Gcs_wCDrZFFGCkcgMBROk2vTJIy7eIVNiP_pI2EPY,2430
|
|
490
490
|
blaxel/telemetry/log/logger.py,sha256=NPAS3g82ryROjvc_DEZaTIfrcehoLEZoP-JkLxADxc0,4113
|
|
491
|
-
blaxel-0.2.
|
|
492
|
-
blaxel-0.2.
|
|
493
|
-
blaxel-0.2.
|
|
494
|
-
blaxel-0.2.
|
|
491
|
+
blaxel-0.2.30.dist-info/METADATA,sha256=PqakkZQjcfocQtRxicP0Pamw7AwerRtiId4IkhlxmXw,10102
|
|
492
|
+
blaxel-0.2.30.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
493
|
+
blaxel-0.2.30.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
|
|
494
|
+
blaxel-0.2.30.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|