dagster-cloud 1.12.10__py3-none-any.whl → 1.12.12__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.
- dagster_cloud/agent/dagster_cloud_agent.py +43 -86
- dagster_cloud/instance/__init__.py +1 -28
- dagster_cloud/pex/grpc/server/manager.py +3 -3
- dagster_cloud/version.py +1 -1
- dagster_cloud/workspace/docker/__init__.py +2 -1
- dagster_cloud/workspace/ecs/client.py +1 -1
- dagster_cloud/workspace/ecs/launcher.py +8 -4
- dagster_cloud/workspace/kubernetes/launcher.py +5 -3
- dagster_cloud/workspace/kubernetes/utils.py +6 -3
- dagster_cloud/workspace/user_code_launcher/user_code_launcher.py +41 -104
- dagster_cloud/workspace/user_code_launcher/utils.py +14 -0
- {dagster_cloud-1.12.10.dist-info → dagster_cloud-1.12.12.dist-info}/METADATA +16 -13
- {dagster_cloud-1.12.10.dist-info → dagster_cloud-1.12.12.dist-info}/RECORD +15 -37
- {dagster_cloud-1.12.10.dist-info → dagster_cloud-1.12.12.dist-info}/WHEEL +1 -1
- dagster_cloud/agent/instrumentation/__init__.py +0 -0
- dagster_cloud/agent/instrumentation/constants.py +0 -2
- dagster_cloud/agent/instrumentation/run_launch.py +0 -23
- dagster_cloud/agent/instrumentation/schedule.py +0 -34
- dagster_cloud/agent/instrumentation/sensor.py +0 -34
- dagster_cloud/opentelemetry/__init__.py +0 -0
- dagster_cloud/opentelemetry/config/__init__.py +0 -73
- dagster_cloud/opentelemetry/config/exporter.py +0 -81
- dagster_cloud/opentelemetry/config/log_record_processor.py +0 -40
- dagster_cloud/opentelemetry/config/logging_handler.py +0 -14
- dagster_cloud/opentelemetry/config/meter_provider.py +0 -9
- dagster_cloud/opentelemetry/config/metric_reader.py +0 -39
- dagster_cloud/opentelemetry/controller.py +0 -319
- dagster_cloud/opentelemetry/enum.py +0 -58
- dagster_cloud/opentelemetry/factories/__init__.py +0 -1
- dagster_cloud/opentelemetry/factories/logs.py +0 -113
- dagster_cloud/opentelemetry/factories/metrics.py +0 -121
- dagster_cloud/opentelemetry/metrics/__init__.py +0 -0
- dagster_cloud/opentelemetry/metrics/meter.py +0 -140
- dagster_cloud/opentelemetry/observers/__init__.py +0 -0
- dagster_cloud/opentelemetry/observers/dagster_exception_handler.py +0 -40
- dagster_cloud/opentelemetry/observers/execution_observer.py +0 -178
- {dagster_cloud-1.12.10.dist-info → dagster_cloud-1.12.12.dist-info}/top_level.txt +0 -0
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
from collections.abc import Generator, Iterable, Sequence
|
|
2
|
-
from typing import Callable, Optional, Union, cast
|
|
3
|
-
|
|
4
|
-
# (Gauge is only exposed as a private class)
|
|
5
|
-
from opentelemetry.metrics import (
|
|
6
|
-
CallbackOptions,
|
|
7
|
-
Counter,
|
|
8
|
-
Histogram,
|
|
9
|
-
Instrument,
|
|
10
|
-
ObservableCounter,
|
|
11
|
-
ObservableGauge,
|
|
12
|
-
ObservableUpDownCounter,
|
|
13
|
-
Observation,
|
|
14
|
-
UpDownCounter,
|
|
15
|
-
)
|
|
16
|
-
from opentelemetry.metrics._internal import Meter as SDKMeter
|
|
17
|
-
|
|
18
|
-
InstrumentKeyType = tuple[str, str, str]
|
|
19
|
-
ObservableCallback = Callable[[CallbackOptions], Iterable[Observation]]
|
|
20
|
-
ObservableGenerator = Generator[Iterable[Observation], CallbackOptions, None]
|
|
21
|
-
ObserverType = Union[ObservableCallback, ObservableGenerator]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class Meter:
|
|
25
|
-
def __init__(self, meter: SDKMeter):
|
|
26
|
-
self._meter = meter
|
|
27
|
-
self._instruments: dict[InstrumentKeyType, Instrument] = {}
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def name(self) -> str:
|
|
31
|
-
return self._meter.name
|
|
32
|
-
|
|
33
|
-
@property
|
|
34
|
-
def version(self) -> Optional[str]:
|
|
35
|
-
return self._meter.version
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def schema_url(self) -> Optional[str]:
|
|
39
|
-
return self._meter.schema_url
|
|
40
|
-
|
|
41
|
-
def get_counter(self, name: str, description: str = "", unit: str = "") -> Counter:
|
|
42
|
-
key = (name, description, unit)
|
|
43
|
-
instrument = self._instruments.get(key)
|
|
44
|
-
if not instrument:
|
|
45
|
-
instrument = self._meter.create_counter(
|
|
46
|
-
name=name,
|
|
47
|
-
description=description,
|
|
48
|
-
unit=unit,
|
|
49
|
-
)
|
|
50
|
-
self._instruments[key] = instrument
|
|
51
|
-
|
|
52
|
-
return cast("Counter", instrument)
|
|
53
|
-
|
|
54
|
-
def get_up_down_counter(
|
|
55
|
-
self, name: str, description: str = "", unit: str = ""
|
|
56
|
-
) -> UpDownCounter:
|
|
57
|
-
key = (name, description, unit)
|
|
58
|
-
instrument = self._instruments.get(key)
|
|
59
|
-
if not instrument:
|
|
60
|
-
instrument = self._meter.create_up_down_counter(
|
|
61
|
-
name=name,
|
|
62
|
-
description=description,
|
|
63
|
-
unit=unit,
|
|
64
|
-
)
|
|
65
|
-
self._instruments[key] = instrument
|
|
66
|
-
|
|
67
|
-
return cast("UpDownCounter", instrument)
|
|
68
|
-
|
|
69
|
-
def get_histogram(self, name: str, description: str = "", unit: str = "") -> Histogram:
|
|
70
|
-
key = (name, description, unit)
|
|
71
|
-
instrument = self._instruments.get(key)
|
|
72
|
-
if not instrument:
|
|
73
|
-
instrument = self._meter.create_histogram(
|
|
74
|
-
name=name,
|
|
75
|
-
description=description,
|
|
76
|
-
unit=unit,
|
|
77
|
-
)
|
|
78
|
-
self._instruments[key] = instrument
|
|
79
|
-
|
|
80
|
-
return cast("Histogram", instrument)
|
|
81
|
-
|
|
82
|
-
def get_observable_counter(
|
|
83
|
-
self,
|
|
84
|
-
name: str,
|
|
85
|
-
description: str = "",
|
|
86
|
-
unit: str = "",
|
|
87
|
-
observers: Optional[Sequence[ObserverType]] = None,
|
|
88
|
-
) -> ObservableCounter:
|
|
89
|
-
key = (name, description, unit)
|
|
90
|
-
instrument = self._instruments.get(key)
|
|
91
|
-
if not instrument:
|
|
92
|
-
instrument = self._meter.create_observable_counter(
|
|
93
|
-
name=name,
|
|
94
|
-
description=description,
|
|
95
|
-
unit=unit,
|
|
96
|
-
callbacks=observers,
|
|
97
|
-
)
|
|
98
|
-
self._instruments[key] = instrument
|
|
99
|
-
|
|
100
|
-
return cast("ObservableCounter", instrument)
|
|
101
|
-
|
|
102
|
-
def get_observable_up_down_counter(
|
|
103
|
-
self,
|
|
104
|
-
name: str,
|
|
105
|
-
description: str = "",
|
|
106
|
-
unit: str = "",
|
|
107
|
-
observers: Optional[Sequence[ObserverType]] = None,
|
|
108
|
-
) -> ObservableUpDownCounter:
|
|
109
|
-
key = (name, description, unit)
|
|
110
|
-
instrument = self._instruments.get(key)
|
|
111
|
-
if not instrument:
|
|
112
|
-
instrument = self._meter.create_observable_up_down_counter(
|
|
113
|
-
name=name,
|
|
114
|
-
description=description,
|
|
115
|
-
unit=unit,
|
|
116
|
-
callbacks=observers,
|
|
117
|
-
)
|
|
118
|
-
self._instruments[key] = instrument
|
|
119
|
-
|
|
120
|
-
return cast("ObservableUpDownCounter", instrument)
|
|
121
|
-
|
|
122
|
-
def get_observable_gauge(
|
|
123
|
-
self,
|
|
124
|
-
name: str,
|
|
125
|
-
description: str = "",
|
|
126
|
-
unit: str = "",
|
|
127
|
-
observers: Optional[Sequence[ObserverType]] = None,
|
|
128
|
-
) -> ObservableGauge:
|
|
129
|
-
key = (name, description, unit)
|
|
130
|
-
instrument = self._instruments.get(key)
|
|
131
|
-
if not instrument:
|
|
132
|
-
instrument = self._meter.create_observable_gauge(
|
|
133
|
-
name=name,
|
|
134
|
-
description=description,
|
|
135
|
-
unit=unit,
|
|
136
|
-
callbacks=observers,
|
|
137
|
-
)
|
|
138
|
-
self._instruments[key] = instrument
|
|
139
|
-
|
|
140
|
-
return cast("ObservableGauge", instrument)
|
|
File without changes
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from dagster import DagsterError
|
|
2
|
-
from dagster._utils.error import SerializableErrorInfo
|
|
3
|
-
from grpc import Call as GrpcCall
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def extract_dagster_error_attributes(error: DagsterError) -> dict[str, str]:
|
|
7
|
-
"""Extracts attributes from a DagsterError that can be used for logging or metrics."""
|
|
8
|
-
error_attributes = {
|
|
9
|
-
"exception": type(error).__name__,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
# DagsterError subtypes often have a field that contains a SerializableErrorInfo object
|
|
13
|
-
# which identify the cause of the error. This field names vary between error types and is not
|
|
14
|
-
# always present, but if they are they often identify the root cause of the error.
|
|
15
|
-
for field in dir(error):
|
|
16
|
-
if field.startswith("__"):
|
|
17
|
-
# Skip magic methods and fields
|
|
18
|
-
continue
|
|
19
|
-
|
|
20
|
-
attr = getattr(error, field)
|
|
21
|
-
if type(attr) is list and all(isinstance(item, SerializableErrorInfo) for item in attr):
|
|
22
|
-
serializable_error_infos: list[SerializableErrorInfo] = getattr(error, field)
|
|
23
|
-
for serializable_error_info in serializable_error_infos:
|
|
24
|
-
if serializable_error_info.cause and serializable_error_info.cause.cls_name:
|
|
25
|
-
error_attributes["cause"] = serializable_error_info.cause.cls_name
|
|
26
|
-
return error_attributes
|
|
27
|
-
|
|
28
|
-
# If not obtained from SerializableErrorInfo, use __cause__ to determine the error attributes
|
|
29
|
-
if hasattr(error, "__cause__") and error.__cause__:
|
|
30
|
-
error_cause = error.__cause__
|
|
31
|
-
cause_name = type(error_cause).__name__
|
|
32
|
-
if cause_name:
|
|
33
|
-
error_attributes["cause"] = cause_name
|
|
34
|
-
|
|
35
|
-
if isinstance(error_cause, GrpcCall):
|
|
36
|
-
grpc_call: GrpcCall = error_cause
|
|
37
|
-
code = grpc_call.code()
|
|
38
|
-
error_attributes["code"] = code.name
|
|
39
|
-
|
|
40
|
-
return error_attributes
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
from typing import Callable, Optional
|
|
4
|
-
|
|
5
|
-
from dagster import DagsterError
|
|
6
|
-
from dagster._time import get_current_timestamp
|
|
7
|
-
|
|
8
|
-
from dagster_cloud.opentelemetry.controller import OpenTelemetryController
|
|
9
|
-
from dagster_cloud.opentelemetry.observers.dagster_exception_handler import (
|
|
10
|
-
extract_dagster_error_attributes,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
ResultEvaluatorCallback = Callable[..., str]
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ExecutionObserverInstruments:
|
|
17
|
-
"""OpenTelemetry instruments used to record generic code block executions."""
|
|
18
|
-
|
|
19
|
-
def __init__(
|
|
20
|
-
self, opentelemetry: OpenTelemetryController, metric_base_name: str, description: str
|
|
21
|
-
):
|
|
22
|
-
self._meter = opentelemetry.get_meter(
|
|
23
|
-
name=metric_base_name,
|
|
24
|
-
attributes={
|
|
25
|
-
"description": description,
|
|
26
|
-
},
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
self._executions = self._meter.get_counter(
|
|
30
|
-
name=metric_base_name,
|
|
31
|
-
description=f"Number of {description}",
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
self._completions = self._meter.get_counter(
|
|
35
|
-
name=f"{metric_base_name}.completions",
|
|
36
|
-
description=f"Number of {description} completions by status",
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
self._exceptions = self._meter.get_counter(
|
|
40
|
-
name=f"{metric_base_name}.exceptions",
|
|
41
|
-
description=f"Number of exceptions caught in {description}",
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
self._duration = self._meter.get_histogram(
|
|
45
|
-
name=f"{metric_base_name}.duration",
|
|
46
|
-
description=f"Duration of {description}",
|
|
47
|
-
unit="seconds",
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def meter(self):
|
|
52
|
-
"""Get the OpenTelemetry meter. This is useful for adding custom metrics."""
|
|
53
|
-
return self._meter
|
|
54
|
-
|
|
55
|
-
@property
|
|
56
|
-
def executions(self):
|
|
57
|
-
"""Get the OpenTelemetry counter for the number of executions of the observed code block."""
|
|
58
|
-
return self._executions
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
def completions(self):
|
|
62
|
-
"""Get the OpenTelemetry counter for the number of completions of the observed code block."""
|
|
63
|
-
return self._completions
|
|
64
|
-
|
|
65
|
-
@property
|
|
66
|
-
def exceptions(self):
|
|
67
|
-
"""Get the OpenTelemetry counter for the number of exceptions caught in the observed code block."""
|
|
68
|
-
return self._exceptions
|
|
69
|
-
|
|
70
|
-
@property
|
|
71
|
-
def duration(self):
|
|
72
|
-
"""Get the OpenTelemetry gauge for the duration of the observed code block."""
|
|
73
|
-
return self._duration
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class ExecutionResultObserver(ABC):
|
|
77
|
-
@abstractmethod
|
|
78
|
-
def evaluate_result(self, *args, **kwargs):
|
|
79
|
-
raise NotImplementedError("Subclasses must implement this method")
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class _NoopExecutionResultObserver(ExecutionResultObserver):
|
|
83
|
-
"""A no-op observer that does not record any metrics."""
|
|
84
|
-
|
|
85
|
-
def evaluate_result(self, *args, **kwargs):
|
|
86
|
-
pass
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class _DagsterExecutionResultObserver(ExecutionResultObserver):
|
|
90
|
-
def __init__(
|
|
91
|
-
self,
|
|
92
|
-
instruments: ExecutionObserverInstruments,
|
|
93
|
-
attributes: dict[str, str],
|
|
94
|
-
result_evaluator_callback: Optional[ResultEvaluatorCallback] = None,
|
|
95
|
-
):
|
|
96
|
-
self._result_evaluator_callback = result_evaluator_callback
|
|
97
|
-
self._instruments = instruments
|
|
98
|
-
self._attributes = attributes or {}
|
|
99
|
-
self._status = None
|
|
100
|
-
|
|
101
|
-
@property
|
|
102
|
-
def meter(self):
|
|
103
|
-
"""Get the OpenTelemetry meter. This is useful for adding custom metrics."""
|
|
104
|
-
return self._instruments.meter
|
|
105
|
-
|
|
106
|
-
def evaluate_result(self, *args, **kwargs):
|
|
107
|
-
"""Evaluate the result of the observed code block.
|
|
108
|
-
|
|
109
|
-
If a result evaluator callback is present, it will be called with the arguments passed to
|
|
110
|
-
this method, as well as the attributes and instruments passed to the observer.
|
|
111
|
-
|
|
112
|
-
If no result evaluator callback is present, the status will be set to "success".
|
|
113
|
-
"""
|
|
114
|
-
if self._result_evaluator_callback:
|
|
115
|
-
self._status = self._result_evaluator_callback(
|
|
116
|
-
*args, attributes=self._attributes, instruments=self._instruments, **kwargs
|
|
117
|
-
)
|
|
118
|
-
else:
|
|
119
|
-
self._status = "success"
|
|
120
|
-
|
|
121
|
-
def set_status(self, status: str):
|
|
122
|
-
self._status = status
|
|
123
|
-
|
|
124
|
-
def get_status(self) -> Optional[str]:
|
|
125
|
-
return self._status
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
@contextmanager
|
|
129
|
-
def observe_execution(
|
|
130
|
-
opentelemetry: Optional[OpenTelemetryController],
|
|
131
|
-
event_key: str,
|
|
132
|
-
short_description: str,
|
|
133
|
-
result_evaluator_callback: Optional[ResultEvaluatorCallback] = None,
|
|
134
|
-
attributes: Optional[dict[str, str]] = None,
|
|
135
|
-
):
|
|
136
|
-
"""This context manager is used to observe the execution of a code block.
|
|
137
|
-
|
|
138
|
-
It records the number of executions, completions, exceptions, and the duration of the code block.
|
|
139
|
-
An optional evaluator callback can be provided to evaluate the result of the code block,
|
|
140
|
-
setting a completion status and also optionally implementing other side effects such as
|
|
141
|
-
recording additional metrics or logging based on the results.
|
|
142
|
-
"""
|
|
143
|
-
attributes = attributes or {}
|
|
144
|
-
if opentelemetry and opentelemetry.metrics_enabled:
|
|
145
|
-
instruments = ExecutionObserverInstruments(
|
|
146
|
-
opentelemetry=opentelemetry,
|
|
147
|
-
metric_base_name=event_key,
|
|
148
|
-
description=short_description,
|
|
149
|
-
)
|
|
150
|
-
instruments.executions.add(1, attributes)
|
|
151
|
-
|
|
152
|
-
start_time = get_current_timestamp()
|
|
153
|
-
observer = _DagsterExecutionResultObserver(
|
|
154
|
-
instruments=instruments,
|
|
155
|
-
attributes=attributes,
|
|
156
|
-
result_evaluator_callback=result_evaluator_callback,
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
try:
|
|
160
|
-
yield observer
|
|
161
|
-
except DagsterError as e:
|
|
162
|
-
err_attributes = extract_dagster_error_attributes(e)
|
|
163
|
-
observer.set_status("exception")
|
|
164
|
-
instruments.exceptions.add(1, attributes={**attributes, **err_attributes})
|
|
165
|
-
raise
|
|
166
|
-
except Exception as e:
|
|
167
|
-
err_attributes = {"exception": type(e).__name__, **attributes}
|
|
168
|
-
observer.set_status("exception")
|
|
169
|
-
instruments.exceptions.add(1, err_attributes)
|
|
170
|
-
raise
|
|
171
|
-
finally:
|
|
172
|
-
instruments.completions.add(
|
|
173
|
-
1, attributes={**attributes, "status": observer.get_status() or "unknown"}
|
|
174
|
-
)
|
|
175
|
-
instruments.duration.record(get_current_timestamp() - start_time, attributes)
|
|
176
|
-
|
|
177
|
-
else:
|
|
178
|
-
yield _NoopExecutionResultObserver()
|
|
File without changes
|