haiway 0.19.5__py3-none-any.whl → 0.20.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.
- haiway/__init__.py +2 -0
- haiway/context/__init__.py +2 -0
- haiway/context/access.py +88 -8
- haiway/context/disposables.py +63 -0
- haiway/context/identifier.py +81 -27
- haiway/context/observability.py +303 -7
- haiway/context/state.py +126 -0
- haiway/context/tasks.py +66 -0
- haiway/context/types.py +16 -0
- haiway/helpers/asynchrony.py +61 -12
- haiway/helpers/caching.py +31 -0
- haiway/helpers/observability.py +94 -11
- haiway/helpers/retries.py +59 -18
- haiway/helpers/throttling.py +42 -15
- haiway/helpers/timeouted.py +25 -10
- haiway/helpers/tracing.py +31 -0
- haiway/opentelemetry/observability.py +346 -29
- haiway/state/attributes.py +104 -0
- haiway/state/path.py +427 -12
- haiway/state/requirement.py +196 -0
- haiway/state/structure.py +359 -1
- haiway/state/validation.py +293 -0
- haiway/types/default.py +56 -0
- haiway/types/frozen.py +18 -0
- haiway/types/missing.py +89 -0
- haiway/utils/collections.py +36 -28
- haiway/utils/env.py +145 -13
- haiway/utils/formatting.py +27 -0
- haiway/utils/freezing.py +21 -1
- haiway/utils/noop.py +34 -2
- haiway/utils/queue.py +68 -1
- haiway/utils/stream.py +83 -0
- {haiway-0.19.5.dist-info → haiway-0.20.0.dist-info}/METADATA +1 -1
- haiway-0.20.0.dist-info/RECORD +46 -0
- haiway-0.19.5.dist-info/RECORD +0 -46
- {haiway-0.19.5.dist-info → haiway-0.20.0.dist-info}/WHEEL +0 -0
- {haiway-0.19.5.dist-info → haiway-0.20.0.dist-info}/licenses/LICENSE +0 -0
haiway/helpers/timeouted.py
CHANGED
@@ -14,23 +14,38 @@ def timeout[**Args, Result](
|
|
14
14
|
[Callable[Args, Coroutine[Any, Any, Result]]],
|
15
15
|
Callable[Args, Coroutine[Any, Any, Result]],
|
16
16
|
]:
|
17
|
-
"""
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
function
|
22
|
-
|
17
|
+
"""
|
18
|
+
Add a timeout to an asynchronous function.
|
19
|
+
|
20
|
+
This decorator enforces a maximum execution time for the decorated function.
|
21
|
+
If the function does not complete within the specified timeout period, it
|
22
|
+
will be cancelled and a TimeoutError will be raised.
|
23
23
|
|
24
24
|
Parameters
|
25
25
|
----------
|
26
26
|
timeout: float
|
27
|
-
|
27
|
+
Maximum execution time in seconds allowed for the function
|
28
28
|
|
29
29
|
Returns
|
30
30
|
-------
|
31
|
-
Callable[[Callable[
|
32
|
-
function
|
33
|
-
|
31
|
+
Callable[[Callable[Args, Coroutine[Any, Any, Result]]], Callable[Args, Coroutine[Any, Any, Result]]]
|
32
|
+
A decorator that can be applied to an async function to add timeout behavior
|
33
|
+
|
34
|
+
Notes
|
35
|
+
-----
|
36
|
+
- Works only with asynchronous functions.
|
37
|
+
- The wrapped function will be properly cancelled when the timeout occurs.
|
38
|
+
- Not thread-safe, should only be used within a single event loop.
|
39
|
+
- The original function should handle cancellation properly to ensure
|
40
|
+
resources are released when timeout occurs.
|
41
|
+
|
42
|
+
Examples
|
43
|
+
--------
|
44
|
+
>>> @timeout(5.0)
|
45
|
+
... async def fetch_data(url):
|
46
|
+
... # Will raise TimeoutError if it takes more than 5 seconds
|
47
|
+
... return await http_client.get(url)
|
48
|
+
""" # noqa: E501
|
34
49
|
|
35
50
|
def _wrap(
|
36
51
|
function: Callable[Args, Coroutine[Any, Any, Result]],
|
haiway/helpers/tracing.py
CHANGED
@@ -33,6 +33,37 @@ def traced[**Args, Result](
|
|
33
33
|
level: ObservabilityLevel = ObservabilityLevel.DEBUG,
|
34
34
|
label: str | None = None,
|
35
35
|
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]] | Callable[Args, Result]:
|
36
|
+
"""
|
37
|
+
Decorator that adds tracing to functions, recording inputs, outputs, and exceptions.
|
38
|
+
|
39
|
+
Automatically records function arguments, return values, and any exceptions
|
40
|
+
within the current observability context. The recorded data can be used for
|
41
|
+
debugging, performance analysis, and understanding program execution flow.
|
42
|
+
|
43
|
+
In non-debug builds (when __debug__ is False), this decorator has no effect
|
44
|
+
and returns the original function to avoid performance impact in production.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
function: Callable[Args, Result] | None
|
49
|
+
The function to be traced
|
50
|
+
level: ObservabilityLevel
|
51
|
+
The observability level at which to record trace information (default: DEBUG)
|
52
|
+
label: str | None
|
53
|
+
Custom label for the trace; defaults to the function name if not provided
|
54
|
+
|
55
|
+
Returns
|
56
|
+
-------
|
57
|
+
Callable
|
58
|
+
A decorated function that performs the same operation as the original
|
59
|
+
but with added tracing
|
60
|
+
|
61
|
+
Notes
|
62
|
+
-----
|
63
|
+
Works with both synchronous and asynchronous functions. For asynchronous
|
64
|
+
functions, properly awaits the result before recording it.
|
65
|
+
"""
|
66
|
+
|
36
67
|
def wrap(
|
37
68
|
wrapped: Callable[Args, Result],
|
38
69
|
) -> Callable[Args, Result]:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
from collections.abc import Mapping
|
3
3
|
from typing import Any, ClassVar, Self, cast, final
|
4
|
+
from uuid import UUID
|
4
5
|
|
5
6
|
from opentelemetry import metrics, trace
|
6
7
|
from opentelemetry._logs import get_logger, set_logger_provider
|
@@ -37,6 +38,14 @@ __all__ = ("OpenTelemetry",)
|
|
37
38
|
|
38
39
|
|
39
40
|
class ScopeStore:
|
41
|
+
"""
|
42
|
+
Internal class for storing and managing OpenTelemetry scope data.
|
43
|
+
|
44
|
+
This class tracks scope state including its span, meter, logger, and context.
|
45
|
+
It manages the lifecycle of OpenTelemetry resources for a specific scope,
|
46
|
+
including recording logs, metrics, events, and maintaining the context hierarchy.
|
47
|
+
"""
|
48
|
+
|
40
49
|
__slots__ = (
|
41
50
|
"_completed",
|
42
51
|
"_counters",
|
@@ -59,6 +68,22 @@ class ScopeStore:
|
|
59
68
|
meter: Meter,
|
60
69
|
logger: Logger,
|
61
70
|
) -> None:
|
71
|
+
"""
|
72
|
+
Initialize a new scope store with OpenTelemetry resources.
|
73
|
+
|
74
|
+
Parameters
|
75
|
+
----------
|
76
|
+
identifier : ScopeIdentifier
|
77
|
+
The identifier for this scope
|
78
|
+
context : Context
|
79
|
+
The OpenTelemetry context for this scope
|
80
|
+
span : Span
|
81
|
+
The OpenTelemetry span for this scope
|
82
|
+
meter : Meter
|
83
|
+
The OpenTelemetry meter for recording metrics
|
84
|
+
logger : Logger
|
85
|
+
The OpenTelemetry logger for recording logs
|
86
|
+
"""
|
62
87
|
self.identifier: ScopeIdentifier = identifier
|
63
88
|
self.nested: list[ScopeStore] = []
|
64
89
|
self._counters: dict[str, Counter] = {}
|
@@ -75,17 +100,59 @@ class ScopeStore:
|
|
75
100
|
|
76
101
|
@property
|
77
102
|
def exited(self) -> bool:
|
103
|
+
"""
|
104
|
+
Check if this scope has been marked as exited.
|
105
|
+
|
106
|
+
Returns
|
107
|
+
-------
|
108
|
+
bool
|
109
|
+
True if the scope has been exited, False otherwise
|
110
|
+
"""
|
78
111
|
return self._exited
|
79
112
|
|
80
113
|
def exit(self) -> None:
|
114
|
+
"""
|
115
|
+
Mark this scope as exited.
|
116
|
+
|
117
|
+
Raises
|
118
|
+
------
|
119
|
+
AssertionError
|
120
|
+
If the scope has already been exited
|
121
|
+
"""
|
81
122
|
assert not self._exited # nosec: B101
|
82
123
|
self._exited = True
|
83
124
|
|
84
125
|
@property
|
85
126
|
def completed(self) -> bool:
|
127
|
+
"""
|
128
|
+
Check if this scope and all its nested scopes are completed.
|
129
|
+
|
130
|
+
A scope is considered completed when it has been marked as completed
|
131
|
+
and all of its nested scopes are also completed.
|
132
|
+
|
133
|
+
Returns
|
134
|
+
-------
|
135
|
+
bool
|
136
|
+
True if the scope and all nested scopes are completed
|
137
|
+
"""
|
86
138
|
return self._completed and all(nested.completed for nested in self.nested)
|
87
139
|
|
88
140
|
def try_complete(self) -> bool:
|
141
|
+
"""
|
142
|
+
Try to complete this scope if all conditions are met.
|
143
|
+
|
144
|
+
A scope can be completed if:
|
145
|
+
- It has been exited
|
146
|
+
- It has not already been completed
|
147
|
+
- All nested scopes are completed
|
148
|
+
|
149
|
+
When completed, the span is ended and the context token is detached.
|
150
|
+
|
151
|
+
Returns
|
152
|
+
-------
|
153
|
+
bool
|
154
|
+
True if the scope was successfully completed, False otherwise
|
155
|
+
"""
|
89
156
|
if not self._exited:
|
90
157
|
return False # not elegible for completion yet
|
91
158
|
|
@@ -107,6 +174,19 @@ class ScopeStore:
|
|
107
174
|
/,
|
108
175
|
level: ObservabilityLevel,
|
109
176
|
) -> None:
|
177
|
+
"""
|
178
|
+
Record a log message with the specified level.
|
179
|
+
|
180
|
+
Creates a LogRecord with the current span context and scope identifiers,
|
181
|
+
and emits it through the OpenTelemetry logger.
|
182
|
+
|
183
|
+
Parameters
|
184
|
+
----------
|
185
|
+
message : str
|
186
|
+
The log message to record
|
187
|
+
level : ObservabilityLevel
|
188
|
+
The severity level of the log
|
189
|
+
"""
|
110
190
|
span_context: SpanContext = self.span.get_span_context()
|
111
191
|
self.logger.emit(
|
112
192
|
LogRecord(
|
@@ -116,11 +196,6 @@ class ScopeStore:
|
|
116
196
|
body=message,
|
117
197
|
severity_text=level.name,
|
118
198
|
severity_number=SEVERITY_MAPPING[level],
|
119
|
-
attributes={
|
120
|
-
"context.trace_id": self.identifier.trace_id,
|
121
|
-
"context.scope_id": self.identifier.scope_id,
|
122
|
-
"context.parent_id": self.identifier.parent_id,
|
123
|
-
},
|
124
199
|
)
|
125
200
|
)
|
126
201
|
|
@@ -129,6 +204,14 @@ class ScopeStore:
|
|
129
204
|
exception: BaseException,
|
130
205
|
/,
|
131
206
|
) -> None:
|
207
|
+
"""
|
208
|
+
Record an exception in the current span.
|
209
|
+
|
210
|
+
Parameters
|
211
|
+
----------
|
212
|
+
exception : BaseException
|
213
|
+
The exception to record
|
214
|
+
"""
|
132
215
|
self.span.record_exception(exception)
|
133
216
|
|
134
217
|
def record_event(
|
@@ -138,6 +221,16 @@ class ScopeStore:
|
|
138
221
|
*,
|
139
222
|
attributes: Mapping[str, ObservabilityAttribute],
|
140
223
|
) -> None:
|
224
|
+
"""
|
225
|
+
Record an event in the current span.
|
226
|
+
|
227
|
+
Parameters
|
228
|
+
----------
|
229
|
+
event : str
|
230
|
+
The name of the event to record
|
231
|
+
attributes : Mapping[str, ObservabilityAttribute]
|
232
|
+
Attributes to attach to the event
|
233
|
+
"""
|
141
234
|
self.span.add_event(
|
142
235
|
event,
|
143
236
|
attributes={
|
@@ -156,6 +249,23 @@ class ScopeStore:
|
|
156
249
|
unit: str | None,
|
157
250
|
attributes: Mapping[str, ObservabilityAttribute],
|
158
251
|
) -> None:
|
252
|
+
"""
|
253
|
+
Record a metric with the given name, value, and attributes.
|
254
|
+
|
255
|
+
Creates a counter if one does not already exist for the metric name,
|
256
|
+
and adds the value to it with the provided attributes.
|
257
|
+
|
258
|
+
Parameters
|
259
|
+
----------
|
260
|
+
name : str
|
261
|
+
The name of the metric to record
|
262
|
+
value : float | int
|
263
|
+
The value to add to the metric
|
264
|
+
unit : str | None
|
265
|
+
The unit of the metric (if any)
|
266
|
+
attributes : Mapping[str, ObservabilityAttribute]
|
267
|
+
Attributes to attach to the metric
|
268
|
+
"""
|
159
269
|
if name not in self._counters:
|
160
270
|
self._counters[name] = self.meter.create_counter(
|
161
271
|
name=name,
|
@@ -165,16 +275,9 @@ class ScopeStore:
|
|
165
275
|
self._counters[name].add(
|
166
276
|
value,
|
167
277
|
attributes={
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
"context.parent_id": self.identifier.parent_id,
|
172
|
-
},
|
173
|
-
**{
|
174
|
-
key: cast(Any, value)
|
175
|
-
for key, value in attributes.items()
|
176
|
-
if value is not None and value is not MISSING
|
177
|
-
},
|
278
|
+
key: cast(Any, value)
|
279
|
+
for key, value in attributes.items()
|
280
|
+
if value is not None and value is not MISSING
|
178
281
|
},
|
179
282
|
)
|
180
283
|
|
@@ -183,6 +286,16 @@ class ScopeStore:
|
|
183
286
|
attributes: Mapping[str, ObservabilityAttribute],
|
184
287
|
/,
|
185
288
|
) -> None:
|
289
|
+
"""
|
290
|
+
Record attributes in the current span.
|
291
|
+
|
292
|
+
Sets each attribute on the span, skipping None and MISSING values.
|
293
|
+
|
294
|
+
Parameters
|
295
|
+
----------
|
296
|
+
attributes : Mapping[str, ObservabilityAttribute]
|
297
|
+
Attributes to set on the span
|
298
|
+
"""
|
186
299
|
for name, value in attributes.items():
|
187
300
|
if value is None or value is MISSING:
|
188
301
|
continue
|
@@ -195,6 +308,17 @@ class ScopeStore:
|
|
195
308
|
|
196
309
|
@final
|
197
310
|
class OpenTelemetry:
|
311
|
+
"""
|
312
|
+
Integration with OpenTelemetry for distributed tracing, metrics, and logging.
|
313
|
+
|
314
|
+
This class provides a bridge between Haiway's observability abstractions and
|
315
|
+
the OpenTelemetry SDK, enabling distributed tracing, metrics collection, and
|
316
|
+
structured logging with minimal configuration.
|
317
|
+
|
318
|
+
The class must be configured once at application startup using the configure()
|
319
|
+
class method before it can be used.
|
320
|
+
"""
|
321
|
+
|
198
322
|
service: ClassVar[str]
|
199
323
|
environment: ClassVar[str]
|
200
324
|
|
@@ -210,6 +334,36 @@ class OpenTelemetry:
|
|
210
334
|
export_interval_millis: int = 5000,
|
211
335
|
attributes: Mapping[str, Any] | None = None,
|
212
336
|
) -> type[Self]:
|
337
|
+
"""
|
338
|
+
Configure the OpenTelemetry integration.
|
339
|
+
|
340
|
+
This method must be called once at application startup to configure the
|
341
|
+
OpenTelemetry SDK with the appropriate service information, exporters,
|
342
|
+
and resource attributes.
|
343
|
+
|
344
|
+
Parameters
|
345
|
+
----------
|
346
|
+
service : str
|
347
|
+
The name of the service
|
348
|
+
version : str
|
349
|
+
The version of the service
|
350
|
+
environment : str
|
351
|
+
The deployment environment (e.g., "production", "staging")
|
352
|
+
otlp_endpoint : str | None, optional
|
353
|
+
The OTLP endpoint URL to export telemetry data to. If None, console
|
354
|
+
exporters will be used instead.
|
355
|
+
insecure : bool, default=True
|
356
|
+
Whether to use insecure connections to the OTLP endpoint
|
357
|
+
export_interval_millis : int, default=5000
|
358
|
+
How often to export metrics, in milliseconds
|
359
|
+
attributes : Mapping[str, Any] | None, optional
|
360
|
+
Additional resource attributes to include with all telemetry
|
361
|
+
|
362
|
+
Returns
|
363
|
+
-------
|
364
|
+
type[Self]
|
365
|
+
The OpenTelemetry class, for method chaining
|
366
|
+
"""
|
213
367
|
cls.service = service
|
214
368
|
cls.environment = environment
|
215
369
|
# Create shared resource for both metrics and traces
|
@@ -284,12 +438,64 @@ class OpenTelemetry:
|
|
284
438
|
cls,
|
285
439
|
level: ObservabilityLevel = ObservabilityLevel.INFO,
|
286
440
|
) -> Observability:
|
441
|
+
"""
|
442
|
+
Create an Observability implementation using OpenTelemetry.
|
443
|
+
|
444
|
+
This method creates an Observability implementation that bridges Haiway's
|
445
|
+
observability abstractions to OpenTelemetry, allowing transparent usage
|
446
|
+
of OpenTelemetry for distributed tracing, metrics, and logging.
|
447
|
+
|
448
|
+
Parameters
|
449
|
+
----------
|
450
|
+
level : ObservabilityLevel, default=ObservabilityLevel.INFO
|
451
|
+
The minimum observability level to record
|
452
|
+
|
453
|
+
Returns
|
454
|
+
-------
|
455
|
+
Observability
|
456
|
+
An Observability implementation that uses OpenTelemetry
|
457
|
+
|
458
|
+
Notes
|
459
|
+
-----
|
460
|
+
The OpenTelemetry class must be configured using configure() before
|
461
|
+
calling this method.
|
462
|
+
"""
|
287
463
|
tracer: Tracer = trace.get_tracer(cls.service)
|
288
464
|
meter: Meter | None = None
|
289
465
|
root_scope: ScopeIdentifier | None = None
|
290
|
-
scopes: dict[
|
466
|
+
scopes: dict[UUID, ScopeStore] = {}
|
291
467
|
observed_level: ObservabilityLevel = level
|
292
468
|
|
469
|
+
def trace_identifying(
|
470
|
+
scope: ScopeIdentifier,
|
471
|
+
/,
|
472
|
+
) -> UUID:
|
473
|
+
"""
|
474
|
+
Get the unique trace identifier for a scope.
|
475
|
+
|
476
|
+
This function retrieves the OpenTelemetry trace ID for the specified scope
|
477
|
+
and converts it to a UUID for compatibility with Haiway's observability system.
|
478
|
+
|
479
|
+
Parameters
|
480
|
+
----------
|
481
|
+
scope: ScopeIdentifier
|
482
|
+
The scope identifier to get the trace ID for
|
483
|
+
|
484
|
+
Returns
|
485
|
+
-------
|
486
|
+
UUID
|
487
|
+
A UUID representation of the OpenTelemetry trace ID
|
488
|
+
|
489
|
+
Raises
|
490
|
+
------
|
491
|
+
AssertionError
|
492
|
+
If called outside an initialized scope context
|
493
|
+
"""
|
494
|
+
assert root_scope is not None # nosec: B101
|
495
|
+
assert scope.scope_id in scopes # nosec: B101
|
496
|
+
|
497
|
+
return UUID(int=scopes[scope.scope_id].span.get_span_context().trace_id)
|
498
|
+
|
293
499
|
def log_recording(
|
294
500
|
scope: ScopeIdentifier,
|
295
501
|
/,
|
@@ -298,6 +504,25 @@ class OpenTelemetry:
|
|
298
504
|
*args: Any,
|
299
505
|
exception: BaseException | None,
|
300
506
|
) -> None:
|
507
|
+
"""
|
508
|
+
Record a log message using OpenTelemetry logging.
|
509
|
+
|
510
|
+
Creates a log record with the appropriate severity level and attributes
|
511
|
+
based on the current scope context.
|
512
|
+
|
513
|
+
Parameters
|
514
|
+
----------
|
515
|
+
scope: ScopeIdentifier
|
516
|
+
The scope identifier the log is associated with
|
517
|
+
level: ObservabilityLevel
|
518
|
+
The severity level for this log message
|
519
|
+
message: str
|
520
|
+
The log message text, may contain format placeholders
|
521
|
+
*args: Any
|
522
|
+
Format arguments for the message
|
523
|
+
exception: BaseException | None
|
524
|
+
Optional exception to associate with the log
|
525
|
+
"""
|
301
526
|
assert root_scope is not None # nosec: B101
|
302
527
|
assert scope.scope_id in scopes # nosec: B101
|
303
528
|
|
@@ -319,6 +544,23 @@ class OpenTelemetry:
|
|
319
544
|
event: str,
|
320
545
|
attributes: Mapping[str, ObservabilityAttribute],
|
321
546
|
) -> None:
|
547
|
+
"""
|
548
|
+
Record an event using OpenTelemetry spans.
|
549
|
+
|
550
|
+
Creates a span event with the specified name and attributes in the
|
551
|
+
current active span for the scope.
|
552
|
+
|
553
|
+
Parameters
|
554
|
+
----------
|
555
|
+
scope: ScopeIdentifier
|
556
|
+
The scope identifier the event is associated with
|
557
|
+
level: ObservabilityLevel
|
558
|
+
The severity level for this event
|
559
|
+
event: str
|
560
|
+
The name of the event
|
561
|
+
attributes: Mapping[str, ObservabilityAttribute]
|
562
|
+
Key-value attributes associated with the event
|
563
|
+
"""
|
322
564
|
assert root_scope is not None # nosec: B101
|
323
565
|
assert scope.scope_id in scopes # nosec: B101
|
324
566
|
|
@@ -340,6 +582,27 @@ class OpenTelemetry:
|
|
340
582
|
unit: str | None,
|
341
583
|
attributes: Mapping[str, ObservabilityAttribute],
|
342
584
|
) -> None:
|
585
|
+
"""
|
586
|
+
Record a metric using OpenTelemetry metrics.
|
587
|
+
|
588
|
+
Records a numeric measurement using the appropriate OpenTelemetry
|
589
|
+
instrument type based on the metric name and value type.
|
590
|
+
|
591
|
+
Parameters
|
592
|
+
----------
|
593
|
+
scope: ScopeIdentifier
|
594
|
+
The scope identifier the metric is associated with
|
595
|
+
level: ObservabilityLevel
|
596
|
+
The severity level for this metric
|
597
|
+
metric: str
|
598
|
+
The name of the metric
|
599
|
+
value: float | int
|
600
|
+
The numeric value of the metric
|
601
|
+
unit: str | None
|
602
|
+
Optional unit for the metric (e.g., "ms", "bytes")
|
603
|
+
attributes: Mapping[str, ObservabilityAttribute]
|
604
|
+
Key-value attributes associated with the metric
|
605
|
+
"""
|
343
606
|
assert root_scope is not None # nosec: B101
|
344
607
|
assert scope.scope_id in scopes # nosec: B101
|
345
608
|
|
@@ -359,6 +622,21 @@ class OpenTelemetry:
|
|
359
622
|
level: ObservabilityLevel,
|
360
623
|
attributes: Mapping[str, ObservabilityAttribute],
|
361
624
|
) -> None:
|
625
|
+
"""
|
626
|
+
Record standalone attributes using OpenTelemetry span attributes.
|
627
|
+
|
628
|
+
Records key-value attributes by adding them to the current active span
|
629
|
+
for the scope.
|
630
|
+
|
631
|
+
Parameters
|
632
|
+
----------
|
633
|
+
scope: ScopeIdentifier
|
634
|
+
The scope identifier the attributes are associated with
|
635
|
+
level: ObservabilityLevel
|
636
|
+
The severity level for these attributes
|
637
|
+
attributes: Mapping[str, ObservabilityAttribute]
|
638
|
+
Key-value attributes to record
|
639
|
+
"""
|
362
640
|
if level < observed_level:
|
363
641
|
return
|
364
642
|
|
@@ -371,6 +649,27 @@ class OpenTelemetry:
|
|
371
649
|
scope: ScopeIdentifier,
|
372
650
|
/,
|
373
651
|
) -> None:
|
652
|
+
"""
|
653
|
+
Handle scope entry by creating a new OpenTelemetry span.
|
654
|
+
|
655
|
+
This method is called when a new scope is entered. It creates a new
|
656
|
+
OpenTelemetry span for the scope and sets up the appropriate parent-child
|
657
|
+
relationships with existing spans.
|
658
|
+
|
659
|
+
Parameters
|
660
|
+
----------
|
661
|
+
scope: ScopeIdentifier
|
662
|
+
The identifier for the scope being entered
|
663
|
+
|
664
|
+
Returns
|
665
|
+
-------
|
666
|
+
None
|
667
|
+
|
668
|
+
Notes
|
669
|
+
-----
|
670
|
+
This method initializes the scopes dictionary entry for the new scope
|
671
|
+
and creates meter instruments if this is the first scope entry.
|
672
|
+
"""
|
374
673
|
assert scope.scope_id not in scopes # nosec: B101
|
375
674
|
|
376
675
|
nonlocal root_scope
|
@@ -378,19 +677,18 @@ class OpenTelemetry:
|
|
378
677
|
|
379
678
|
scope_store: ScopeStore
|
380
679
|
if root_scope is None:
|
381
|
-
meter = metrics.get_meter(scope.
|
382
|
-
context: Context =
|
680
|
+
meter = metrics.get_meter(scope.label)
|
681
|
+
context: Context = Context(
|
682
|
+
**get_current(),
|
683
|
+
# trace_id=scope.trace_id,
|
684
|
+
# span_id=scope.scope_id,
|
685
|
+
)
|
383
686
|
scope_store = ScopeStore(
|
384
687
|
scope,
|
385
688
|
context=context,
|
386
689
|
span=tracer.start_span(
|
387
690
|
name=scope.label,
|
388
691
|
context=context,
|
389
|
-
attributes={
|
390
|
-
"context.trace_id": scope.trace_id,
|
391
|
-
"context.scope_id": scope.scope_id,
|
392
|
-
"context.parent_id": scope.parent_id,
|
393
|
-
},
|
394
692
|
),
|
395
693
|
meter=meter,
|
396
694
|
logger=get_logger(scope.label),
|
@@ -405,11 +703,6 @@ class OpenTelemetry:
|
|
405
703
|
span=tracer.start_span(
|
406
704
|
name=scope.label,
|
407
705
|
context=scopes[scope.parent_id].context,
|
408
|
-
attributes={
|
409
|
-
"context.trace_id": scope.trace_id,
|
410
|
-
"context.scope_id": scope.scope_id,
|
411
|
-
"context.parent_id": scope.parent_id,
|
412
|
-
},
|
413
706
|
),
|
414
707
|
meter=meter,
|
415
708
|
logger=get_logger(scope.label),
|
@@ -424,6 +717,28 @@ class OpenTelemetry:
|
|
424
717
|
*,
|
425
718
|
exception: BaseException | None,
|
426
719
|
) -> None:
|
720
|
+
"""
|
721
|
+
Handle scope exit by completing the OpenTelemetry span.
|
722
|
+
|
723
|
+
This method is called when a scope is exited. It marks the scope as exited,
|
724
|
+
attempts to complete it, and ends the associated OpenTelemetry span.
|
725
|
+
|
726
|
+
Parameters
|
727
|
+
----------
|
728
|
+
scope: ScopeIdentifier
|
729
|
+
The identifier for the scope being exited
|
730
|
+
exception: BaseException | None
|
731
|
+
Optional exception that caused the scope to exit
|
732
|
+
|
733
|
+
Returns
|
734
|
+
-------
|
735
|
+
None
|
736
|
+
|
737
|
+
Notes
|
738
|
+
-----
|
739
|
+
This method ensures proper cleanup of spans, including recording any
|
740
|
+
exception that occurred during the scope's execution.
|
741
|
+
"""
|
427
742
|
nonlocal root_scope
|
428
743
|
nonlocal scopes
|
429
744
|
nonlocal meter
|
@@ -442,7 +757,7 @@ class OpenTelemetry:
|
|
442
757
|
|
443
758
|
# try complete parent scopes
|
444
759
|
if scope != root_scope:
|
445
|
-
parent_id:
|
760
|
+
parent_id: UUID = scope.parent_id
|
446
761
|
while scopes[parent_id].try_complete():
|
447
762
|
if scopes[parent_id].identifier == root_scope:
|
448
763
|
break
|
@@ -457,6 +772,7 @@ class OpenTelemetry:
|
|
457
772
|
scopes = {}
|
458
773
|
|
459
774
|
return Observability(
|
775
|
+
trace_identifying=trace_identifying,
|
460
776
|
log_recording=log_recording,
|
461
777
|
event_recording=event_recording,
|
462
778
|
metric_recording=metric_recording,
|
@@ -472,3 +788,4 @@ SEVERITY_MAPPING = {
|
|
472
788
|
ObservabilityLevel.WARNING: SeverityNumber.WARN,
|
473
789
|
ObservabilityLevel.ERROR: SeverityNumber.ERROR,
|
474
790
|
}
|
791
|
+
"""Mapping from Haiway ObservabilityLevel to OpenTelemetry SeverityNumber."""
|