haiway 0.18.0__py3-none-any.whl → 0.18.1__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 +6 -2
- haiway/context/__init__.py +4 -0
- haiway/context/access.py +37 -5
- haiway/context/observability.py +75 -1
- haiway/helpers/__init__.py +3 -6
- haiway/helpers/observability.py +27 -2
- haiway/helpers/tracing.py +13 -35
- haiway/opentelemetry/observability.py +33 -1
- {haiway-0.18.0.dist-info → haiway-0.18.1.dist-info}/METADATA +1 -1
- {haiway-0.18.0.dist-info → haiway-0.18.1.dist-info}/RECORD +12 -12
- {haiway-0.18.0.dist-info → haiway-0.18.1.dist-info}/WHEEL +0 -0
- {haiway-0.18.0.dist-info → haiway-0.18.1.dist-info}/licenses/LICENSE +0 -0
haiway/__init__.py
CHANGED
@@ -4,6 +4,8 @@ from haiway.context import (
|
|
4
4
|
MissingContext,
|
5
5
|
MissingState,
|
6
6
|
Observability,
|
7
|
+
ObservabilityAttribute,
|
8
|
+
ObservabilityAttributesRecording,
|
7
9
|
ObservabilityContext,
|
8
10
|
ObservabilityEventRecording,
|
9
11
|
ObservabilityLevel,
|
@@ -17,7 +19,7 @@ from haiway.context import (
|
|
17
19
|
ctx,
|
18
20
|
)
|
19
21
|
from haiway.helpers import (
|
20
|
-
|
22
|
+
LoggerObservability,
|
21
23
|
ResultTrace,
|
22
24
|
asynchronous,
|
23
25
|
cache,
|
@@ -63,7 +65,6 @@ from haiway.utils import (
|
|
63
65
|
|
64
66
|
__all__ = (
|
65
67
|
"MISSING",
|
66
|
-
"ArgumentsTrace",
|
67
68
|
"AsyncQueue",
|
68
69
|
"AsyncStream",
|
69
70
|
"AttributePath",
|
@@ -72,10 +73,13 @@ __all__ = (
|
|
72
73
|
"DefaultValue",
|
73
74
|
"Disposable",
|
74
75
|
"Disposables",
|
76
|
+
"LoggerObservability",
|
75
77
|
"Missing",
|
76
78
|
"MissingContext",
|
77
79
|
"MissingState",
|
78
80
|
"Observability",
|
81
|
+
"ObservabilityAttribute",
|
82
|
+
"ObservabilityAttributesRecording",
|
79
83
|
"ObservabilityContext",
|
80
84
|
"ObservabilityEventRecording",
|
81
85
|
"ObservabilityLevel",
|
haiway/context/__init__.py
CHANGED
@@ -3,6 +3,8 @@ from haiway.context.disposables import Disposable, Disposables
|
|
3
3
|
from haiway.context.identifier import ScopeIdentifier
|
4
4
|
from haiway.context.observability import (
|
5
5
|
Observability,
|
6
|
+
ObservabilityAttribute,
|
7
|
+
ObservabilityAttributesRecording,
|
6
8
|
ObservabilityContext,
|
7
9
|
ObservabilityEventRecording,
|
8
10
|
ObservabilityLevel,
|
@@ -20,6 +22,8 @@ __all__ = (
|
|
20
22
|
"MissingContext",
|
21
23
|
"MissingState",
|
22
24
|
"Observability",
|
25
|
+
"ObservabilityAttribute",
|
26
|
+
"ObservabilityAttributesRecording",
|
23
27
|
"ObservabilityContext",
|
24
28
|
"ObservabilityEventRecording",
|
25
29
|
"ObservabilityLevel",
|
haiway/context/access.py
CHANGED
@@ -18,7 +18,12 @@ from typing import Any, final, overload
|
|
18
18
|
|
19
19
|
from haiway.context.disposables import Disposable, Disposables
|
20
20
|
from haiway.context.identifier import ScopeIdentifier
|
21
|
-
from haiway.context.observability import
|
21
|
+
from haiway.context.observability import (
|
22
|
+
Observability,
|
23
|
+
ObservabilityAttribute,
|
24
|
+
ObservabilityContext,
|
25
|
+
ObservabilityLevel,
|
26
|
+
)
|
22
27
|
from haiway.context.state import ScopeState, StateContext
|
23
28
|
from haiway.context.tasks import TaskGroupContext
|
24
29
|
from haiway.state import State
|
@@ -462,6 +467,7 @@ class ctx:
|
|
462
467
|
/,
|
463
468
|
*args: Any,
|
464
469
|
exception: BaseException | None = None,
|
470
|
+
**extra: Any,
|
465
471
|
) -> None:
|
466
472
|
"""
|
467
473
|
Log using ERROR level within current scope context. When there is no current scope\
|
@@ -488,6 +494,7 @@ class ctx:
|
|
488
494
|
message,
|
489
495
|
*args,
|
490
496
|
exception=exception,
|
497
|
+
**extra,
|
491
498
|
)
|
492
499
|
|
493
500
|
@staticmethod
|
@@ -496,6 +503,7 @@ class ctx:
|
|
496
503
|
/,
|
497
504
|
*args: Any,
|
498
505
|
exception: Exception | None = None,
|
506
|
+
**extra: Any,
|
499
507
|
) -> None:
|
500
508
|
"""
|
501
509
|
Log using WARNING level within current scope context. When there is no current scope\
|
@@ -522,6 +530,7 @@ class ctx:
|
|
522
530
|
message,
|
523
531
|
*args,
|
524
532
|
exception=exception,
|
533
|
+
**extra,
|
525
534
|
)
|
526
535
|
|
527
536
|
@staticmethod
|
@@ -529,6 +538,7 @@ class ctx:
|
|
529
538
|
message: str,
|
530
539
|
/,
|
531
540
|
*args: Any,
|
541
|
+
**extra: Any,
|
532
542
|
) -> None:
|
533
543
|
"""
|
534
544
|
Log using INFO level within current scope context. When there is no current scope\
|
@@ -552,6 +562,7 @@ class ctx:
|
|
552
562
|
message,
|
553
563
|
*args,
|
554
564
|
exception=None,
|
565
|
+
**extra,
|
555
566
|
)
|
556
567
|
|
557
568
|
@staticmethod
|
@@ -560,6 +571,7 @@ class ctx:
|
|
560
571
|
/,
|
561
572
|
*args: Any,
|
562
573
|
exception: Exception | None = None,
|
574
|
+
**extra: Any,
|
563
575
|
) -> None:
|
564
576
|
"""
|
565
577
|
Log using DEBUG level within current scope context. When there is no current scope\
|
@@ -582,10 +594,7 @@ class ctx:
|
|
582
594
|
"""
|
583
595
|
|
584
596
|
ObservabilityContext.record_log(
|
585
|
-
ObservabilityLevel.DEBUG,
|
586
|
-
message,
|
587
|
-
*args,
|
588
|
-
exception=exception,
|
597
|
+
ObservabilityLevel.DEBUG, message, *args, exception=exception, **extra
|
589
598
|
)
|
590
599
|
|
591
600
|
@staticmethod
|
@@ -594,6 +603,7 @@ class ctx:
|
|
594
603
|
/,
|
595
604
|
*,
|
596
605
|
level: ObservabilityLevel = ObservabilityLevel.INFO,
|
606
|
+
**extra: Any,
|
597
607
|
) -> None:
|
598
608
|
"""
|
599
609
|
Record event within current scope context.
|
@@ -611,6 +621,7 @@ class ctx:
|
|
611
621
|
ObservabilityContext.record_event(
|
612
622
|
event,
|
613
623
|
level=level,
|
624
|
+
**extra,
|
614
625
|
)
|
615
626
|
|
616
627
|
@staticmethod
|
@@ -620,6 +631,7 @@ class ctx:
|
|
620
631
|
*,
|
621
632
|
value: float | int,
|
622
633
|
unit: str | None = None,
|
634
|
+
**extra: Any,
|
623
635
|
) -> None:
|
624
636
|
"""
|
625
637
|
Record metric within current scope context.
|
@@ -642,4 +654,24 @@ class ctx:
|
|
642
654
|
metric,
|
643
655
|
value=value,
|
644
656
|
unit=unit,
|
657
|
+
**extra,
|
658
|
+
)
|
659
|
+
|
660
|
+
@staticmethod
|
661
|
+
def attributes(**attributes: ObservabilityAttribute) -> None:
|
662
|
+
"""
|
663
|
+
Record attributes within current scope context.
|
664
|
+
|
665
|
+
Parameters
|
666
|
+
----------
|
667
|
+
**attributes: ObservabilityAttribute,
|
668
|
+
attributes to be recorded within current context.
|
669
|
+
|
670
|
+
Returns
|
671
|
+
-------
|
672
|
+
None
|
673
|
+
"""
|
674
|
+
|
675
|
+
ObservabilityContext.record_attributes(
|
676
|
+
**attributes,
|
645
677
|
)
|
haiway/context/observability.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from collections.abc import Sequence
|
1
2
|
from contextvars import ContextVar, Token
|
2
3
|
from enum import IntEnum
|
3
4
|
from logging import DEBUG as DEBUG_LOGGING
|
@@ -19,6 +20,8 @@ __all__ = (
|
|
19
20
|
"INFO",
|
20
21
|
"WARNING",
|
21
22
|
"Observability",
|
23
|
+
"ObservabilityAttribute",
|
24
|
+
"ObservabilityAttributesRecording",
|
22
25
|
"ObservabilityContext",
|
23
26
|
"ObservabilityEventRecording",
|
24
27
|
"ObservabilityLevel",
|
@@ -42,6 +45,10 @@ WARNING: Final[int] = ObservabilityLevel.WARNING
|
|
42
45
|
INFO: Final[int] = ObservabilityLevel.INFO
|
43
46
|
DEBUG: Final[int] = ObservabilityLevel.DEBUG
|
44
47
|
|
48
|
+
type ObservabilityAttribute = (
|
49
|
+
Sequence[str] | Sequence[float] | Sequence[int] | Sequence[bool] | str | float | int | bool
|
50
|
+
)
|
51
|
+
|
45
52
|
|
46
53
|
@runtime_checkable
|
47
54
|
class ObservabilityLogRecording(Protocol):
|
@@ -53,6 +60,7 @@ class ObservabilityLogRecording(Protocol):
|
|
53
60
|
message: str,
|
54
61
|
*args: Any,
|
55
62
|
exception: BaseException | None,
|
63
|
+
**extra: Any,
|
56
64
|
) -> None: ...
|
57
65
|
|
58
66
|
|
@@ -65,6 +73,7 @@ class ObservabilityEventRecording(Protocol):
|
|
65
73
|
*,
|
66
74
|
level: ObservabilityLevel,
|
67
75
|
event: State,
|
76
|
+
**extra: Any,
|
68
77
|
) -> None: ...
|
69
78
|
|
70
79
|
|
@@ -78,6 +87,17 @@ class ObservabilityMetricRecording(Protocol):
|
|
78
87
|
metric: str,
|
79
88
|
value: float | int,
|
80
89
|
unit: str | None,
|
90
|
+
**extra: Any,
|
91
|
+
) -> None: ...
|
92
|
+
|
93
|
+
|
94
|
+
@runtime_checkable
|
95
|
+
class ObservabilityAttributesRecording(Protocol):
|
96
|
+
def __call__(
|
97
|
+
self,
|
98
|
+
scope: ScopeIdentifier,
|
99
|
+
/,
|
100
|
+
**attributes: ObservabilityAttribute,
|
81
101
|
) -> None: ...
|
82
102
|
|
83
103
|
|
@@ -103,6 +123,7 @@ class ObservabilityScopeExiting(Protocol):
|
|
103
123
|
|
104
124
|
class Observability: # avoiding State inheritance to prevent propagation as scope state
|
105
125
|
__slots__ = (
|
126
|
+
"attributes_recording",
|
106
127
|
"event_recording",
|
107
128
|
"log_recording",
|
108
129
|
"metric_recording",
|
@@ -115,6 +136,7 @@ class Observability: # avoiding State inheritance to prevent propagation as sco
|
|
115
136
|
log_recording: ObservabilityLogRecording,
|
116
137
|
metric_recording: ObservabilityMetricRecording,
|
117
138
|
event_recording: ObservabilityEventRecording,
|
139
|
+
attributes_recording: ObservabilityAttributesRecording,
|
118
140
|
scope_entering: ObservabilityScopeEntering,
|
119
141
|
scope_exiting: ObservabilityScopeExiting,
|
120
142
|
) -> None:
|
@@ -136,6 +158,13 @@ class Observability: # avoiding State inheritance to prevent propagation as sco
|
|
136
158
|
"event_recording",
|
137
159
|
event_recording,
|
138
160
|
)
|
161
|
+
self.attributes_recording: ObservabilityAttributesRecording
|
162
|
+
object.__setattr__(
|
163
|
+
self,
|
164
|
+
"attributes_recording",
|
165
|
+
attributes_recording,
|
166
|
+
)
|
167
|
+
|
139
168
|
self.scope_entering: ObservabilityScopeEntering
|
140
169
|
object.__setattr__(
|
141
170
|
self,
|
@@ -180,6 +209,7 @@ def _logger_observability(
|
|
180
209
|
message: str,
|
181
210
|
*args: Any,
|
182
211
|
exception: BaseException | None,
|
212
|
+
**extra: Any,
|
183
213
|
) -> None:
|
184
214
|
logger.log(
|
185
215
|
level,
|
@@ -194,6 +224,7 @@ def _logger_observability(
|
|
194
224
|
*,
|
195
225
|
level: ObservabilityLevel,
|
196
226
|
event: State,
|
227
|
+
**extra: Any,
|
197
228
|
) -> None:
|
198
229
|
logger.log(
|
199
230
|
level,
|
@@ -207,10 +238,25 @@ def _logger_observability(
|
|
207
238
|
metric: str,
|
208
239
|
value: float | int,
|
209
240
|
unit: str | None,
|
241
|
+
**extra: Any,
|
242
|
+
) -> None:
|
243
|
+
logger.log(
|
244
|
+
INFO,
|
245
|
+
f"{scope.unique_name} Recorded metric: {metric}={value}{unit or ''}",
|
246
|
+
)
|
247
|
+
|
248
|
+
def attributes_recording(
|
249
|
+
scope: ScopeIdentifier,
|
250
|
+
/,
|
251
|
+
**attributes: ObservabilityAttribute,
|
210
252
|
) -> None:
|
253
|
+
if not attributes:
|
254
|
+
return
|
255
|
+
|
211
256
|
logger.log(
|
212
257
|
INFO,
|
213
|
-
f"{scope.unique_name} Recorded
|
258
|
+
f"{scope.unique_name} Recorded attributes:"
|
259
|
+
f"\n{'\n'.join([f'{k}: {v}' for k, v in attributes.items()])}",
|
214
260
|
)
|
215
261
|
|
216
262
|
def scope_entering[Metric: State](
|
@@ -238,6 +284,7 @@ def _logger_observability(
|
|
238
284
|
log_recording=log_recording,
|
239
285
|
event_recording=event_recording,
|
240
286
|
metric_recording=metric_recording,
|
287
|
+
attributes_recording=attributes_recording,
|
241
288
|
scope_entering=scope_entering,
|
242
289
|
scope_exiting=scope_exiting,
|
243
290
|
)
|
@@ -302,6 +349,7 @@ class ObservabilityContext:
|
|
302
349
|
/,
|
303
350
|
*args: Any,
|
304
351
|
exception: BaseException | None,
|
352
|
+
**extra: Any,
|
305
353
|
) -> None:
|
306
354
|
try: # catch exceptions - we don't wan't to blow up on observability
|
307
355
|
context: Self = cls._context.get()
|
@@ -313,6 +361,7 @@ class ObservabilityContext:
|
|
313
361
|
message,
|
314
362
|
*args,
|
315
363
|
exception=exception,
|
364
|
+
**extra,
|
316
365
|
)
|
317
366
|
|
318
367
|
except LookupError:
|
@@ -330,6 +379,7 @@ class ObservabilityContext:
|
|
330
379
|
/,
|
331
380
|
*,
|
332
381
|
level: ObservabilityLevel,
|
382
|
+
**extra: Any,
|
333
383
|
) -> None:
|
334
384
|
try: # catch exceptions - we don't wan't to blow up on observability
|
335
385
|
context: Self = cls._context.get()
|
@@ -339,6 +389,7 @@ class ObservabilityContext:
|
|
339
389
|
context._scope,
|
340
390
|
level=level,
|
341
391
|
event=event,
|
392
|
+
**extra,
|
342
393
|
)
|
343
394
|
|
344
395
|
except Exception as exc:
|
@@ -356,6 +407,7 @@ class ObservabilityContext:
|
|
356
407
|
*,
|
357
408
|
value: float | int,
|
358
409
|
unit: str | None,
|
410
|
+
**extra: Any,
|
359
411
|
) -> None:
|
360
412
|
try: # catch exceptions - we don't wan't to blow up on observability
|
361
413
|
context: Self = cls._context.get()
|
@@ -366,6 +418,7 @@ class ObservabilityContext:
|
|
366
418
|
metric=metric,
|
367
419
|
value=value,
|
368
420
|
unit=unit,
|
421
|
+
**extra,
|
369
422
|
)
|
370
423
|
|
371
424
|
except Exception as exc:
|
@@ -375,6 +428,27 @@ class ObservabilityContext:
|
|
375
428
|
exception=exc,
|
376
429
|
)
|
377
430
|
|
431
|
+
@classmethod
|
432
|
+
def record_attributes(
|
433
|
+
cls,
|
434
|
+
**attributes: ObservabilityAttribute,
|
435
|
+
) -> None:
|
436
|
+
try: # catch exceptions - we don't wan't to blow up on observability
|
437
|
+
context: Self = cls._context.get()
|
438
|
+
|
439
|
+
if context.observability is not None:
|
440
|
+
context.observability.attributes_recording(
|
441
|
+
context._scope,
|
442
|
+
**attributes,
|
443
|
+
)
|
444
|
+
|
445
|
+
except Exception as exc:
|
446
|
+
cls.record_log(
|
447
|
+
ERROR,
|
448
|
+
f"Failed to record attributes: {attributes}",
|
449
|
+
exception=exc,
|
450
|
+
)
|
451
|
+
|
378
452
|
__slots__ = (
|
379
453
|
"_scope",
|
380
454
|
"_token",
|
haiway/helpers/__init__.py
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
from haiway.helpers.asynchrony import asynchronous, wrap_async
|
2
2
|
from haiway.helpers.caching import CacheMakeKey, CacheRead, CacheWrite, cache
|
3
|
+
from haiway.helpers.observability import LoggerObservability
|
3
4
|
from haiway.helpers.retries import retry
|
4
5
|
from haiway.helpers.throttling import throttle
|
5
6
|
from haiway.helpers.timeouted import timeout
|
6
|
-
from haiway.helpers.tracing import
|
7
|
-
ArgumentsTrace,
|
8
|
-
ResultTrace,
|
9
|
-
traced,
|
10
|
-
)
|
7
|
+
from haiway.helpers.tracing import ResultTrace, traced
|
11
8
|
|
12
9
|
__all__ = (
|
13
|
-
"ArgumentsTrace",
|
14
10
|
"CacheMakeKey",
|
15
11
|
"CacheRead",
|
16
12
|
"CacheWrite",
|
13
|
+
"LoggerObservability",
|
17
14
|
"ResultTrace",
|
18
15
|
"asynchronous",
|
19
16
|
"cache",
|
haiway/helpers/observability.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from collections.abc import Sequence
|
1
2
|
from logging import Logger
|
2
3
|
from time import monotonic
|
3
4
|
from typing import Any
|
@@ -60,7 +61,7 @@ class ScopeStore:
|
|
60
61
|
return True # successfully completed
|
61
62
|
|
62
63
|
|
63
|
-
def LoggerObservability( # noqa: C901
|
64
|
+
def LoggerObservability( # noqa: C901, PLR0915
|
64
65
|
logger: Logger,
|
65
66
|
/,
|
66
67
|
*,
|
@@ -76,6 +77,7 @@ def LoggerObservability( # noqa: C901
|
|
76
77
|
message: str,
|
77
78
|
*args: Any,
|
78
79
|
exception: BaseException | None,
|
80
|
+
**extra: Any,
|
79
81
|
) -> None:
|
80
82
|
assert root_scope is not None # nosec: B101
|
81
83
|
assert scope.scope_id in scopes # nosec: B101
|
@@ -93,6 +95,7 @@ def LoggerObservability( # noqa: C901
|
|
93
95
|
*,
|
94
96
|
level: ObservabilityLevel,
|
95
97
|
event: State,
|
98
|
+
**extra: Any,
|
96
99
|
) -> None:
|
97
100
|
assert root_scope is not None # nosec: B101
|
98
101
|
assert scope.scope_id in scopes # nosec: B101
|
@@ -113,11 +116,12 @@ def LoggerObservability( # noqa: C901
|
|
113
116
|
metric: str,
|
114
117
|
value: float | int,
|
115
118
|
unit: str | None,
|
119
|
+
**extra: Any,
|
116
120
|
) -> None:
|
117
121
|
assert root_scope is not None # nosec: B101
|
118
122
|
assert scope.scope_id in scopes # nosec: B101
|
119
123
|
|
120
|
-
metric_str: str = f"Metric
|
124
|
+
metric_str: str = f"Metric: {metric}={value}{unit or ''}"
|
121
125
|
if summarize_context: # store only for summary
|
122
126
|
scopes[scope.scope_id].store.append(metric_str)
|
123
127
|
|
@@ -126,6 +130,26 @@ def LoggerObservability( # noqa: C901
|
|
126
130
|
f"{scope.unique_name} {metric_str}",
|
127
131
|
)
|
128
132
|
|
133
|
+
def attributes_recording(
|
134
|
+
scope: ScopeIdentifier,
|
135
|
+
/,
|
136
|
+
**attributes: Sequence[str | float | int] | str | float | int,
|
137
|
+
) -> None:
|
138
|
+
if not attributes:
|
139
|
+
return
|
140
|
+
|
141
|
+
attributes_str: str = (
|
142
|
+
f"{scope.unique_name} Attributes:"
|
143
|
+
f"\n{'\n'.join([f'{k}: {v}' for k, v in attributes.items()])}"
|
144
|
+
)
|
145
|
+
if summarize_context: # store only for summary
|
146
|
+
scopes[scope.scope_id].store.append(attributes_str)
|
147
|
+
|
148
|
+
logger.log(
|
149
|
+
ObservabilityLevel.INFO,
|
150
|
+
attributes_str,
|
151
|
+
)
|
152
|
+
|
129
153
|
def scope_entering[Metric: State](
|
130
154
|
scope: ScopeIdentifier,
|
131
155
|
/,
|
@@ -196,6 +220,7 @@ def LoggerObservability( # noqa: C901
|
|
196
220
|
log_recording=log_recording,
|
197
221
|
event_recording=event_recording,
|
198
222
|
metric_recording=metric_recording,
|
223
|
+
attributes_recording=attributes_recording,
|
199
224
|
scope_entering=scope_entering,
|
200
225
|
scope_exiting=scope_exiting,
|
201
226
|
)
|
haiway/helpers/tracing.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from asyncio import iscoroutinefunction
|
2
|
-
from collections.abc import Callable, Coroutine
|
2
|
+
from collections.abc import Callable, Coroutine
|
3
3
|
from typing import Any, Self, cast, overload
|
4
4
|
|
5
5
|
from haiway.context import ctx
|
@@ -8,43 +8,11 @@ from haiway.types import MISSING, Missing
|
|
8
8
|
from haiway.utils import mimic_function
|
9
9
|
|
10
10
|
__all__ = (
|
11
|
-
"ArgumentsTrace",
|
12
11
|
"ResultTrace",
|
13
12
|
"traced",
|
14
13
|
)
|
15
14
|
|
16
15
|
|
17
|
-
class ArgumentsTrace(State):
|
18
|
-
if __debug__:
|
19
|
-
|
20
|
-
@classmethod
|
21
|
-
def of(
|
22
|
-
cls,
|
23
|
-
*args: Any,
|
24
|
-
**kwargs: Any,
|
25
|
-
) -> Self:
|
26
|
-
return cls(
|
27
|
-
args=[f"{arg}" for arg in args] if args else MISSING,
|
28
|
-
kwargs=[f"{key}:{arg}" for key, arg in kwargs.items()] if kwargs else MISSING,
|
29
|
-
)
|
30
|
-
|
31
|
-
else: # remove tracing for non debug runs to prevent accidental secret leaks
|
32
|
-
|
33
|
-
@classmethod
|
34
|
-
def of(
|
35
|
-
cls,
|
36
|
-
*args: Any,
|
37
|
-
**kwargs: Any,
|
38
|
-
) -> Self:
|
39
|
-
return cls(
|
40
|
-
args=MISSING,
|
41
|
-
kwargs=MISSING,
|
42
|
-
)
|
43
|
-
|
44
|
-
args: Sequence[str] | Missing
|
45
|
-
kwargs: Sequence[str] | Missing
|
46
|
-
|
47
|
-
|
48
16
|
class ResultTrace(State):
|
49
17
|
if __debug__:
|
50
18
|
|
@@ -128,7 +96,12 @@ def _traced_sync[**Args, Result](
|
|
128
96
|
**kwargs: Args.kwargs,
|
129
97
|
) -> Result:
|
130
98
|
with ctx.scope(label):
|
131
|
-
|
99
|
+
for idx, arg in enumerate(args):
|
100
|
+
ctx.attributes(**{f"[{idx}]": f"{arg}"})
|
101
|
+
|
102
|
+
for key, arg in kwargs.items():
|
103
|
+
ctx.attributes(**{key: f"{arg}"})
|
104
|
+
|
132
105
|
try:
|
133
106
|
result: Result = function(*args, **kwargs)
|
134
107
|
ctx.event(ResultTrace.of(result))
|
@@ -154,7 +127,12 @@ def _traced_async[**Args, Result](
|
|
154
127
|
**kwargs: Args.kwargs,
|
155
128
|
) -> Result:
|
156
129
|
with ctx.scope(label):
|
157
|
-
|
130
|
+
for idx, arg in enumerate(args):
|
131
|
+
ctx.attributes(**{f"[{idx}]": f"{arg}"})
|
132
|
+
|
133
|
+
for key, arg in kwargs.items():
|
134
|
+
ctx.attributes(**{key: f"{arg}"})
|
135
|
+
|
158
136
|
try:
|
159
137
|
result: Result = await function(*args, **kwargs)
|
160
138
|
ctx.event(ResultTrace.of(result))
|
@@ -2,7 +2,6 @@ import os
|
|
2
2
|
from collections.abc import Mapping
|
3
3
|
from typing import Any, Self, final
|
4
4
|
|
5
|
-
###
|
6
5
|
from opentelemetry import metrics, trace
|
7
6
|
from opentelemetry._logs import get_logger, set_logger_provider
|
8
7
|
from opentelemetry._logs._internal import Logger
|
@@ -32,6 +31,9 @@ from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExport
|
|
32
31
|
from opentelemetry.trace import Span, StatusCode, Tracer
|
33
32
|
|
34
33
|
from haiway.context import Observability, ObservabilityLevel, ScopeIdentifier
|
34
|
+
|
35
|
+
###
|
36
|
+
from haiway.context.observability import ObservabilityAttribute
|
35
37
|
from haiway.state import State
|
36
38
|
|
37
39
|
__all__ = ("OpenTelemetry",)
|
@@ -159,6 +161,18 @@ class ScopeStore:
|
|
159
161
|
attributes=attributes,
|
160
162
|
)
|
161
163
|
|
164
|
+
def record_attribute(
|
165
|
+
self,
|
166
|
+
name: str,
|
167
|
+
/,
|
168
|
+
*,
|
169
|
+
value: ObservabilityAttribute,
|
170
|
+
) -> None:
|
171
|
+
self.span.set_attribute(
|
172
|
+
name,
|
173
|
+
value=value,
|
174
|
+
)
|
175
|
+
|
162
176
|
|
163
177
|
@final
|
164
178
|
class OpenTelemetry:
|
@@ -259,6 +273,7 @@ class OpenTelemetry:
|
|
259
273
|
message: str,
|
260
274
|
*args: Any,
|
261
275
|
exception: BaseException | None,
|
276
|
+
**extra: Any,
|
262
277
|
) -> None:
|
263
278
|
assert root_scope is not None # nosec: B101
|
264
279
|
assert scope.scope_id in scopes # nosec: B101
|
@@ -279,6 +294,7 @@ class OpenTelemetry:
|
|
279
294
|
*,
|
280
295
|
level: ObservabilityLevel,
|
281
296
|
event: State,
|
297
|
+
**extra: Any,
|
282
298
|
) -> None:
|
283
299
|
assert root_scope is not None # nosec: B101
|
284
300
|
assert scope.scope_id in scopes # nosec: B101
|
@@ -295,6 +311,7 @@ class OpenTelemetry:
|
|
295
311
|
metric: str,
|
296
312
|
value: float | int,
|
297
313
|
unit: str | None,
|
314
|
+
**extra: Any,
|
298
315
|
) -> None:
|
299
316
|
assert root_scope is not None # nosec: B101
|
300
317
|
assert scope.scope_id in scopes # nosec: B101
|
@@ -308,6 +325,20 @@ class OpenTelemetry:
|
|
308
325
|
unit=unit,
|
309
326
|
)
|
310
327
|
|
328
|
+
def attributes_recording(
|
329
|
+
scope: ScopeIdentifier,
|
330
|
+
/,
|
331
|
+
**attributes: ObservabilityAttribute,
|
332
|
+
) -> None:
|
333
|
+
if not attributes:
|
334
|
+
return
|
335
|
+
|
336
|
+
for attribute, value in attributes.items():
|
337
|
+
scopes[scope.scope_id].record_attribute(
|
338
|
+
attribute,
|
339
|
+
value=value,
|
340
|
+
)
|
341
|
+
|
311
342
|
def scope_entering[Metric: State](
|
312
343
|
scope: ScopeIdentifier,
|
313
344
|
/,
|
@@ -407,6 +438,7 @@ class OpenTelemetry:
|
|
407
438
|
log_recording=log_recording,
|
408
439
|
event_recording=event_recording,
|
409
440
|
metric_recording=metric_recording,
|
441
|
+
attributes_recording=attributes_recording,
|
410
442
|
scope_entering=scope_entering,
|
411
443
|
scope_exiting=scope_exiting,
|
412
444
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.18.
|
3
|
+
Version: 0.18.1
|
4
4
|
Summary: Framework for dependency injection and state management within structured concurrency model.
|
5
5
|
Project-URL: Homepage, https://miquido.com
|
6
6
|
Project-URL: Repository, https://github.com/miquido/haiway.git
|
@@ -1,23 +1,23 @@
|
|
1
|
-
haiway/__init__.py,sha256=
|
1
|
+
haiway/__init__.py,sha256=GAukz1qQxO0mhvxSXdabC0JYiWw4nGKIGnmuImtDWdQ,2389
|
2
2
|
haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
haiway/context/__init__.py,sha256=
|
4
|
-
haiway/context/access.py,sha256=
|
3
|
+
haiway/context/__init__.py,sha256=eoxqxUmFtkWLhc_gH6tqax9m90tSDie-jiP1BHruTdk,1102
|
4
|
+
haiway/context/access.py,sha256=49yzFt9yJU6p0pDE3jV_ZIvmgSk6_czzOOT2gijSJk8,18648
|
5
5
|
haiway/context/disposables.py,sha256=xE8RZYsYgXiOZY_TjWR7UiPG6dirna6y2LBZvMwTkIs,2588
|
6
6
|
haiway/context/identifier.py,sha256=i6nO-tozps7iDnpS5Se7CRch7hh6z2akjZthutxHte8,3943
|
7
|
-
haiway/context/observability.py,sha256=
|
7
|
+
haiway/context/observability.py,sha256=Z89y2MYTMBwfsEprm9fGP-s_ZqrA9NLtLZo4Fj2JDGM,13738
|
8
8
|
haiway/context/state.py,sha256=0oq7ctNO0urJd7rVzwwNtgpguoXuI6Tp1exfCsxrS2M,5981
|
9
9
|
haiway/context/tasks.py,sha256=QOxFdjmMp4IYff0ihHElKLCQrcVksSJmxqTlOKfoH4o,2907
|
10
10
|
haiway/context/types.py,sha256=WulPvpqUbI1vYyny-s2NItldDnk3zh1O-n_hGibFZRY,142
|
11
|
-
haiway/helpers/__init__.py,sha256=
|
11
|
+
haiway/helpers/__init__.py,sha256=awAEVFv9_talefS6ymMbofDXigB-Klsxkj2uth8la-g,615
|
12
12
|
haiway/helpers/asynchrony.py,sha256=k_A0yCWUKSFfzYZ8WvqK4wqTMljv6ykMivmERrDLHIU,6266
|
13
13
|
haiway/helpers/caching.py,sha256=4WX2Md5AOncduYB_RLLENI2s9C2zD5kNJgKZjMzPIGY,13257
|
14
|
-
haiway/helpers/observability.py,sha256
|
14
|
+
haiway/helpers/observability.py,sha256=-UXr7dHWTq-rmGe5HAR-tx2gzCbzAIoO42mwTISnMDE,6990
|
15
15
|
haiway/helpers/retries.py,sha256=unssUKBDOENvquh6R4Ud65TuSKl4mTHgZ5N_b7mAYa4,7533
|
16
16
|
haiway/helpers/throttling.py,sha256=U6HJvSzffw47730VeiXxXSW4VVxpDx48k0oIAOpL-O4,4115
|
17
17
|
haiway/helpers/timeouted.py,sha256=_M8diuD_GN49pl5KQA5fMKn4iUHsUuhkDSatAwWXiK8,3331
|
18
|
-
haiway/helpers/tracing.py,sha256=
|
18
|
+
haiway/helpers/tracing.py,sha256=0KIpr-phVHikfoFrbgzeQi581tjp65fZnMqX0h0MRDo,3653
|
19
19
|
haiway/opentelemetry/__init__.py,sha256=TV-1C14mDAtcHhFZ29ActFQdrGH6x5KuGV9w-JlKYJg,91
|
20
|
-
haiway/opentelemetry/observability.py,sha256=
|
20
|
+
haiway/opentelemetry/observability.py,sha256=oOjIVZldk14SfffWQh5FO1lGMW3Dce1PMLbiDZ9lV5s,14264
|
21
21
|
haiway/state/__init__.py,sha256=AaMqlMhO4zKS_XNevy3A7BHh5PxmguA-Sk_FnaNDY1Q,355
|
22
22
|
haiway/state/attributes.py,sha256=p6jUBzg62bOl0zAYTCa7NIllsaNY2Kt68IooQ9tb-y8,23311
|
23
23
|
haiway/state/path.py,sha256=-IpbUpF2QHWg3hEITkWYHJ6ZPoRVixu-SOSuWk-bbBY,21318
|
@@ -38,7 +38,7 @@ haiway/utils/mimic.py,sha256=L5AS4WEL2aPMZAQZlvLvRzHl0cipI7ivky60_eL4iwY,1822
|
|
38
38
|
haiway/utils/noop.py,sha256=f54PSLHGEwCQNYXQHkPAW5NDE-tk5yjzkNL1pZj0TJQ,344
|
39
39
|
haiway/utils/queue.py,sha256=YTvCn3wgSwLJiLqolMx44sa3304Xkv3tJG77gvfWnZs,4114
|
40
40
|
haiway/utils/stream.py,sha256=Mjhy2S-ZDR1g_NsgS_nuBA8AgVbhrGXKvG3wjJ5mCJQ,2826
|
41
|
-
haiway-0.18.
|
42
|
-
haiway-0.18.
|
43
|
-
haiway-0.18.
|
44
|
-
haiway-0.18.
|
41
|
+
haiway-0.18.1.dist-info/METADATA,sha256=dIvIkA3j0iGbQuF5VGH2vhdgPMTWYQS0LXG7GwDKw7E,4527
|
42
|
+
haiway-0.18.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
43
|
+
haiway-0.18.1.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
|
44
|
+
haiway-0.18.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|