haiway 0.17.0__py3-none-any.whl → 0.18.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.
Files changed (47) hide show
  1. haiway/__init__.py +18 -16
  2. haiway/context/__init__.py +19 -13
  3. haiway/context/access.py +92 -88
  4. haiway/context/disposables.py +2 -2
  5. haiway/context/identifier.py +4 -5
  6. haiway/context/observability.py +452 -0
  7. haiway/context/state.py +2 -2
  8. haiway/context/tasks.py +1 -3
  9. haiway/context/types.py +2 -2
  10. haiway/helpers/__init__.py +7 -6
  11. haiway/helpers/asynchrony.py +2 -2
  12. haiway/helpers/caching.py +2 -2
  13. haiway/helpers/observability.py +219 -0
  14. haiway/helpers/retries.py +1 -3
  15. haiway/helpers/throttling.py +1 -3
  16. haiway/helpers/timeouted.py +1 -3
  17. haiway/helpers/tracing.py +25 -17
  18. haiway/opentelemetry/__init__.py +3 -0
  19. haiway/opentelemetry/observability.py +420 -0
  20. haiway/state/__init__.py +2 -2
  21. haiway/state/attributes.py +2 -2
  22. haiway/state/path.py +1 -3
  23. haiway/state/requirement.py +1 -3
  24. haiway/state/structure.py +161 -30
  25. haiway/state/validation.py +2 -2
  26. haiway/types/__init__.py +2 -2
  27. haiway/types/default.py +2 -2
  28. haiway/types/frozen.py +1 -3
  29. haiway/types/missing.py +2 -2
  30. haiway/utils/__init__.py +2 -2
  31. haiway/utils/always.py +2 -2
  32. haiway/utils/collections.py +2 -2
  33. haiway/utils/env.py +2 -2
  34. haiway/utils/freezing.py +1 -3
  35. haiway/utils/logs.py +1 -3
  36. haiway/utils/mimic.py +1 -3
  37. haiway/utils/noop.py +2 -2
  38. haiway/utils/queue.py +1 -3
  39. haiway/utils/stream.py +1 -3
  40. {haiway-0.17.0.dist-info → haiway-0.18.0.dist-info}/METADATA +9 -5
  41. haiway-0.18.0.dist-info/RECORD +44 -0
  42. haiway/context/logging.py +0 -242
  43. haiway/context/metrics.py +0 -176
  44. haiway/helpers/metrics.py +0 -465
  45. haiway-0.17.0.dist-info/RECORD +0 -43
  46. {haiway-0.17.0.dist-info → haiway-0.18.0.dist-info}/WHEEL +0 -0
  47. {haiway-0.17.0.dist-info → haiway-0.18.0.dist-info}/licenses/LICENSE +0 -0
haiway/__init__.py CHANGED
@@ -1,13 +1,16 @@
1
1
  from haiway.context import (
2
2
  Disposable,
3
3
  Disposables,
4
- MetricsContext,
5
- MetricsHandler,
6
- MetricsRecording,
7
- MetricsScopeEntering,
8
- MetricsScopeExiting,
9
4
  MissingContext,
10
5
  MissingState,
6
+ Observability,
7
+ ObservabilityContext,
8
+ ObservabilityEventRecording,
9
+ ObservabilityLevel,
10
+ ObservabilityLogRecording,
11
+ ObservabilityMetricRecording,
12
+ ObservabilityScopeEntering,
13
+ ObservabilityScopeExiting,
11
14
  ScopeContext,
12
15
  ScopeIdentifier,
13
16
  StateContext,
@@ -15,8 +18,6 @@ from haiway.context import (
15
18
  )
16
19
  from haiway.helpers import (
17
20
  ArgumentsTrace,
18
- MetricsHolder,
19
- MetricsLogger,
20
21
  ResultTrace,
21
22
  asynchronous,
22
23
  cache,
@@ -60,7 +61,7 @@ from haiway.utils import (
60
61
  without_missing,
61
62
  )
62
63
 
63
- __all__ = [
64
+ __all__ = (
64
65
  "MISSING",
65
66
  "ArgumentsTrace",
66
67
  "AsyncQueue",
@@ -71,16 +72,17 @@ __all__ = [
71
72
  "DefaultValue",
72
73
  "Disposable",
73
74
  "Disposables",
74
- "MetricsContext",
75
- "MetricsHandler",
76
- "MetricsHolder",
77
- "MetricsLogger",
78
- "MetricsRecording",
79
- "MetricsScopeEntering",
80
- "MetricsScopeExiting",
81
75
  "Missing",
82
76
  "MissingContext",
83
77
  "MissingState",
78
+ "Observability",
79
+ "ObservabilityContext",
80
+ "ObservabilityEventRecording",
81
+ "ObservabilityLevel",
82
+ "ObservabilityLogRecording",
83
+ "ObservabilityMetricRecording",
84
+ "ObservabilityScopeEntering",
85
+ "ObservabilityScopeExiting",
84
86
  "ResultTrace",
85
87
  "ScopeContext",
86
88
  "ScopeIdentifier",
@@ -116,4 +118,4 @@ __all__ = [
116
118
  "when_missing",
117
119
  "without_missing",
118
120
  "wrap_async",
119
- ]
121
+ )
@@ -1,28 +1,34 @@
1
1
  from haiway.context.access import ScopeContext, ctx
2
2
  from haiway.context.disposables import Disposable, Disposables
3
3
  from haiway.context.identifier import ScopeIdentifier
4
- from haiway.context.metrics import (
5
- MetricsContext,
6
- MetricsHandler,
7
- MetricsRecording,
8
- MetricsScopeEntering,
9
- MetricsScopeExiting,
4
+ from haiway.context.observability import (
5
+ Observability,
6
+ ObservabilityContext,
7
+ ObservabilityEventRecording,
8
+ ObservabilityLevel,
9
+ ObservabilityLogRecording,
10
+ ObservabilityMetricRecording,
11
+ ObservabilityScopeEntering,
12
+ ObservabilityScopeExiting,
10
13
  )
11
14
  from haiway.context.state import StateContext
12
15
  from haiway.context.types import MissingContext, MissingState
13
16
 
14
- __all__ = [
17
+ __all__ = (
15
18
  "Disposable",
16
19
  "Disposables",
17
- "MetricsContext",
18
- "MetricsHandler",
19
- "MetricsRecording",
20
- "MetricsScopeEntering",
21
- "MetricsScopeExiting",
22
20
  "MissingContext",
23
21
  "MissingState",
22
+ "Observability",
23
+ "ObservabilityContext",
24
+ "ObservabilityEventRecording",
25
+ "ObservabilityLevel",
26
+ "ObservabilityLogRecording",
27
+ "ObservabilityMetricRecording",
28
+ "ObservabilityScopeEntering",
29
+ "ObservabilityScopeExiting",
24
30
  "ScopeContext",
25
31
  "ScopeIdentifier",
26
32
  "StateContext",
27
33
  "ctx",
28
- ]
34
+ )
haiway/context/access.py CHANGED
@@ -18,17 +18,14 @@ 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.logging import LoggerContext
22
- from haiway.context.metrics import MetricsContext, MetricsHandler
21
+ from haiway.context.observability import Observability, ObservabilityContext, ObservabilityLevel
23
22
  from haiway.context.state import ScopeState, StateContext
24
23
  from haiway.context.tasks import TaskGroupContext
25
24
  from haiway.state import State
26
25
  from haiway.utils import mimic_function
27
26
  from haiway.utils.stream import AsyncStream
28
27
 
29
- __all__ = [
30
- "ctx",
31
- ]
28
+ __all__ = ("ctx",)
32
29
 
33
30
 
34
31
  @final
@@ -36,8 +33,7 @@ class ScopeContext:
36
33
  __slots__ = (
37
34
  "_disposables",
38
35
  "_identifier",
39
- "_logger_context",
40
- "_metrics_context",
36
+ "_observability_context",
41
37
  "_state_context",
42
38
  "_task_group_context",
43
39
  )
@@ -45,11 +41,10 @@ class ScopeContext:
45
41
  def __init__(
46
42
  self,
47
43
  label: str,
48
- logger: Logger | None,
49
44
  task_group: TaskGroup | None,
50
45
  state: tuple[State, ...],
51
46
  disposables: Disposables | None,
52
- metrics: MetricsHandler | None,
47
+ observability: Observability | Logger | None,
53
48
  ) -> None:
54
49
  self._identifier: ScopeIdentifier
55
50
  object.__setattr__(
@@ -57,23 +52,6 @@ class ScopeContext:
57
52
  "_identifier",
58
53
  ScopeIdentifier.scope(label),
59
54
  )
60
- self._logger_context: LoggerContext
61
- object.__setattr__(
62
- self,
63
- "_logger_context",
64
- LoggerContext(
65
- self._identifier,
66
- logger=logger,
67
- ),
68
- )
69
- self._task_group_context: TaskGroupContext | None
70
- object.__setattr__(
71
- self,
72
- "_task_group_context",
73
- TaskGroupContext(task_group=task_group)
74
- if task_group is not None or self._identifier.is_root
75
- else None,
76
- )
77
55
  # prepare state context to capture current state
78
56
  self._state_context: StateContext
79
57
  object.__setattr__(
@@ -87,16 +65,24 @@ class ScopeContext:
87
65
  "_disposables",
88
66
  disposables,
89
67
  )
90
- self._metrics_context: MetricsContext
68
+ self._observability_context: ObservabilityContext
91
69
  object.__setattr__(
92
70
  self,
93
- "_metrics_context",
94
- # pre-building metrics context to ensure nested context registering
95
- MetricsContext.scope(
71
+ "_observability_context",
72
+ # pre-building observability context to ensure nested context registering
73
+ ObservabilityContext.scope(
96
74
  self._identifier,
97
- metrics=metrics,
75
+ observability=observability,
98
76
  ),
99
77
  )
78
+ self._task_group_context: TaskGroupContext | None
79
+ object.__setattr__(
80
+ self,
81
+ "_task_group_context",
82
+ TaskGroupContext(task_group=task_group)
83
+ if task_group is not None or self._identifier.is_root
84
+ else None,
85
+ )
100
86
 
101
87
  def __setattr__(
102
88
  self,
@@ -123,9 +109,8 @@ class ScopeContext:
123
109
  ), "Can't enter synchronous context with task group"
124
110
  assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
125
111
  self._identifier.__enter__()
126
- self._logger_context.__enter__()
112
+ self._observability_context.__enter__()
127
113
  self._state_context.__enter__()
128
- self._metrics_context.__enter__()
129
114
 
130
115
  return self._identifier.trace_id
131
116
 
@@ -135,24 +120,16 @@ class ScopeContext:
135
120
  exc_val: BaseException | None,
136
121
  exc_tb: TracebackType | None,
137
122
  ) -> None:
138
- self._metrics_context.__exit__(
139
- exc_type=exc_type,
140
- exc_val=exc_val,
141
- exc_tb=exc_tb,
142
- )
143
-
144
123
  self._state_context.__exit__(
145
124
  exc_type=exc_type,
146
125
  exc_val=exc_val,
147
126
  exc_tb=exc_tb,
148
127
  )
149
-
150
- self._logger_context.__exit__(
128
+ self._observability_context.__exit__(
151
129
  exc_type=exc_type,
152
130
  exc_val=exc_val,
153
131
  exc_tb=exc_tb,
154
132
  )
155
-
156
133
  self._identifier.__exit__(
157
134
  exc_type=exc_type,
158
135
  exc_val=exc_val,
@@ -161,7 +138,7 @@ class ScopeContext:
161
138
 
162
139
  async def __aenter__(self) -> str:
163
140
  self._identifier.__enter__()
164
- self._logger_context.__enter__()
141
+ self._observability_context.__enter__()
165
142
 
166
143
  if task_group := self._task_group_context:
167
144
  await task_group.__aenter__()
@@ -183,7 +160,6 @@ class ScopeContext:
183
160
  )
184
161
 
185
162
  self._state_context.__enter__()
186
- self._metrics_context.__enter__()
187
163
 
188
164
  return self._identifier.trace_id
189
165
 
@@ -207,19 +183,13 @@ class ScopeContext:
207
183
  exc_tb=exc_tb,
208
184
  )
209
185
 
210
- self._metrics_context.__exit__(
211
- exc_type=exc_type,
212
- exc_val=exc_val,
213
- exc_tb=exc_tb,
214
- )
215
-
216
186
  self._state_context.__exit__(
217
187
  exc_type=exc_type,
218
188
  exc_val=exc_val,
219
189
  exc_tb=exc_tb,
220
190
  )
221
191
 
222
- self._logger_context.__exit__(
192
+ self._observability_context.__exit__(
223
193
  exc_type=exc_type,
224
194
  exc_val=exc_val,
225
195
  exc_tb=exc_tb,
@@ -288,9 +258,8 @@ class ctx:
288
258
  /,
289
259
  *state: State,
290
260
  disposables: Disposables | Iterable[Disposable] | None = None,
291
- logger: Logger | None = None,
292
261
  task_group: TaskGroup | None = None,
293
- metrics: MetricsHandler | None = None,
262
+ observability: Observability | Logger | None = None,
294
263
  ) -> ScopeContext:
295
264
  """
296
265
  Prepare scope context with given parameters. When called within an existing context\
@@ -310,18 +279,14 @@ class ctx:
310
279
  be added to the scope state. Using asynchronous context is required if any disposables\
311
280
  were provided.
312
281
 
313
- logger: Logger | None
314
- logger used within the scope context, when not provided current logger will be used\
315
- if any, otherwise the logger with the scope name will be requested.
316
-
317
282
  task_group: TaskGroup | None
318
283
  task group used for spawning and joining tasks within the context. Root scope will
319
284
  always have task group created even when not set.
320
285
 
321
- metrics_store: MetricsStore | None = None
322
- metrics storage solution responsible for recording and storing metrics.\
323
- Metrics recroding will be ignored if storage is not provided.
324
- Assigning metrics_store within existing context will result in an error.
286
+ observability: Observability | Logger | None = None
287
+ observability solution responsible for recording and storing metrics, logs and events.\
288
+ Assigning observability within existing context will result in an error.
289
+ When not provided, logger with the scope name will be requested and used.
325
290
 
326
291
  Returns
327
292
  -------
@@ -343,11 +308,10 @@ class ctx:
343
308
 
344
309
  return ScopeContext(
345
310
  label=label,
346
- logger=logger,
347
311
  task_group=task_group,
348
312
  state=state,
349
313
  disposables=resolved_disposables,
350
- metrics=metrics,
314
+ observability=observability,
351
315
  )
352
316
 
353
317
  @staticmethod
@@ -492,27 +456,6 @@ class ctx:
492
456
  default=default,
493
457
  )
494
458
 
495
- @staticmethod
496
- def record(
497
- metric: State,
498
- /,
499
- ) -> None:
500
- """
501
- Record metric within current scope context.
502
-
503
- Parameters
504
- ----------
505
- metric: State
506
- value of metric to be recorded. When a metric implements __add__ it will be added to\
507
- current value if any, otherwise subsequent calls may replace existing value.
508
-
509
- Returns
510
- -------
511
- None
512
- """
513
-
514
- MetricsContext.record(metric)
515
-
516
459
  @staticmethod
517
460
  def log_error(
518
461
  message: str,
@@ -540,7 +483,8 @@ class ctx:
540
483
  None
541
484
  """
542
485
 
543
- LoggerContext.log_error(
486
+ ObservabilityContext.record_log(
487
+ ObservabilityLevel.ERROR,
544
488
  message,
545
489
  *args,
546
490
  exception=exception,
@@ -573,7 +517,8 @@ class ctx:
573
517
  None
574
518
  """
575
519
 
576
- LoggerContext.log_warning(
520
+ ObservabilityContext.record_log(
521
+ ObservabilityLevel.WARNING,
577
522
  message,
578
523
  *args,
579
524
  exception=exception,
@@ -602,9 +547,11 @@ class ctx:
602
547
  None
603
548
  """
604
549
 
605
- LoggerContext.log_info(
550
+ ObservabilityContext.record_log(
551
+ ObservabilityLevel.INFO,
606
552
  message,
607
553
  *args,
554
+ exception=None,
608
555
  )
609
556
 
610
557
  @staticmethod
@@ -634,8 +581,65 @@ class ctx:
634
581
  None
635
582
  """
636
583
 
637
- LoggerContext.log_debug(
584
+ ObservabilityContext.record_log(
585
+ ObservabilityLevel.DEBUG,
638
586
  message,
639
587
  *args,
640
588
  exception=exception,
641
589
  )
590
+
591
+ @staticmethod
592
+ def event(
593
+ event: State,
594
+ /,
595
+ *,
596
+ level: ObservabilityLevel = ObservabilityLevel.INFO,
597
+ ) -> None:
598
+ """
599
+ Record event within current scope context.
600
+
601
+ Parameters
602
+ ----------
603
+ event: State
604
+ contents of event to be recorded.
605
+
606
+ Returns
607
+ -------
608
+ None
609
+ """
610
+
611
+ ObservabilityContext.record_event(
612
+ event,
613
+ level=level,
614
+ )
615
+
616
+ @staticmethod
617
+ def metric(
618
+ metric: str,
619
+ /,
620
+ *,
621
+ value: float | int,
622
+ unit: str | None = None,
623
+ ) -> None:
624
+ """
625
+ Record metric within current scope context.
626
+
627
+ Parameters
628
+ ----------
629
+ metric: State
630
+ name of metric to be recorded.
631
+ value: float | int
632
+ value of metric to be recorded.
633
+ unit: str | None = None
634
+ unit of metric to be recorded.
635
+
636
+ Returns
637
+ -------
638
+ None
639
+ """
640
+
641
+ ObservabilityContext.record_metric(
642
+ metric,
643
+ value=value,
644
+ unit=unit,
645
+ )
@@ -7,10 +7,10 @@ from typing import Any, final
7
7
 
8
8
  from haiway.state import State
9
9
 
10
- __all__ = [
10
+ __all__ = (
11
11
  "Disposable",
12
12
  "Disposables",
13
- ]
13
+ )
14
14
 
15
15
  type Disposable = AbstractAsyncContextManager[Iterable[State] | State | None]
16
16
 
@@ -3,9 +3,7 @@ from types import TracebackType
3
3
  from typing import Any, Self, final
4
4
  from uuid import uuid4
5
5
 
6
- __all__ = [
7
- "ScopeIdentifier",
8
- ]
6
+ __all__ = ("ScopeIdentifier",)
9
7
 
10
8
 
11
9
  @final
@@ -33,10 +31,11 @@ class ScopeIdentifier:
33
31
  except LookupError:
34
32
  # create root scope when missing
35
33
  trace_id: str = uuid4().hex
34
+ scope_id: str = uuid4().hex
36
35
  return cls(
37
36
  label=label,
38
- scope_id=uuid4().hex,
39
- parent_id=trace_id, # trace_id is parent_id for root
37
+ scope_id=scope_id,
38
+ parent_id=scope_id, # own id is parent_id for root
40
39
  trace_id=trace_id,
41
40
  )
42
41