grpcio-observability 1.71.0__cp311-cp311-musllinux_1_1_aarch64.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.
@@ -0,0 +1,17 @@
1
+ # Copyright 2023 gRPC authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from grpc_observability._open_telemetry_plugin import OpenTelemetryPlugin
16
+
17
+ __all__ = ("OpenTelemetryPlugin",)
@@ -0,0 +1,91 @@
1
+ # Copyright 2023 gRPC authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from opencensus.stats import measure
16
+
17
+ # These measure definitions should be kept in sync across opencensus implementations.
18
+ # https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java.
19
+
20
+ # Unit constants
21
+ UNIT_BYTES = "By"
22
+ UNIT_MILLISECONDS = "ms"
23
+ UNIT_COUNT = "1"
24
+
25
+ # Client
26
+ CLIENT_STARTED_RPCS_MEASURE = measure.MeasureInt(
27
+ "grpc.io/client/started_rpcs",
28
+ "The total number of client RPCs ever opened, including those that have not been completed.",
29
+ UNIT_COUNT,
30
+ )
31
+
32
+ CLIENT_COMPLETED_RPCS_MEASURE = measure.MeasureInt(
33
+ "grpc.io/client/completed_rpcs",
34
+ "The total number of completed client RPCs",
35
+ UNIT_COUNT,
36
+ )
37
+
38
+ CLIENT_ROUNDTRIP_LATENCY_MEASURE = measure.MeasureFloat(
39
+ "grpc.io/client/roundtrip_latency",
40
+ "Time between first byte of request sent to last byte of response received, or terminal error",
41
+ UNIT_MILLISECONDS,
42
+ )
43
+
44
+ CLIENT_API_LATENCY_MEASURE = measure.MeasureInt(
45
+ "grpc.io/client/api_latency",
46
+ "End-to-end time taken to complete an RPC",
47
+ UNIT_MILLISECONDS,
48
+ )
49
+
50
+ CLIENT_SEND_BYTES_PER_RPC_MEASURE = measure.MeasureFloat(
51
+ "grpc.io/client/sent_bytes_per_rpc",
52
+ "Total bytes sent across all request messages per RPC",
53
+ UNIT_BYTES,
54
+ )
55
+
56
+ CLIENT_RECEIVED_BYTES_PER_RPC_MEASURE = measure.MeasureFloat(
57
+ "grpc.io/client/received_bytes_per_rpc",
58
+ "Total bytes received across all response messages per RPC",
59
+ UNIT_BYTES,
60
+ )
61
+
62
+ # Server
63
+ SERVER_STARTED_RPCS_MEASURE = measure.MeasureInt(
64
+ "grpc.io/server/started_rpcs",
65
+ "Total bytes sent across all request messages per RPC",
66
+ UNIT_COUNT,
67
+ )
68
+
69
+ SERVER_COMPLETED_RPCS_MEASURE = measure.MeasureInt(
70
+ "grpc.io/server/completed_rpcs",
71
+ "The total number of completed server RPCs",
72
+ UNIT_COUNT,
73
+ )
74
+
75
+ SERVER_SENT_BYTES_PER_RPC_MEASURE = measure.MeasureFloat(
76
+ "grpc.io/server/sent_bytes_per_rpc",
77
+ "Total bytes sent across all messages per RPC",
78
+ UNIT_BYTES,
79
+ )
80
+
81
+ SERVER_RECEIVED_BYTES_PER_RPC_MEASURE = measure.MeasureFloat(
82
+ "grpc.io/server/received_bytes_per_rpc",
83
+ "Total bytes received across all messages per RPC",
84
+ UNIT_BYTES,
85
+ )
86
+
87
+ SERVER_SERVER_LATENCY_MEASURE = measure.MeasureFloat(
88
+ "grpc.io/server/server_latency",
89
+ "Time between first byte of request received to last byte of response sent, or terminal error",
90
+ UNIT_MILLISECONDS,
91
+ )
@@ -0,0 +1,119 @@
1
+ # Copyright 2023 gRPC authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from __future__ import annotations
15
+
16
+ import abc
17
+ from dataclasses import dataclass
18
+ from dataclasses import field
19
+ import enum
20
+ from typing import AnyStr, Dict, List, Mapping, Set, Tuple
21
+
22
+
23
+ class Exporter(metaclass=abc.ABCMeta):
24
+ """Abstract base class for census data exporters."""
25
+
26
+ @abc.abstractmethod
27
+ def export_stats_data(self, stats_data: List[TracingData]) -> None:
28
+ """Exports a list of TracingData objects to the exporter's destination.
29
+
30
+ Args:
31
+ stats_data: A list of TracingData objects to export.
32
+ """
33
+ raise NotImplementedError()
34
+
35
+ @abc.abstractmethod
36
+ def export_tracing_data(self, tracing_data: List[StatsData]) -> None:
37
+ """Exports a list of StatsData objects to the exporter's destination.
38
+
39
+ Args:
40
+ tracing_data: A list of StatsData objects to export.
41
+ """
42
+ raise NotImplementedError()
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class StatsData:
47
+ """A data class representing stats data.
48
+
49
+ Attributes:
50
+ name: An element of grpc_observability._cyobservability.MetricsName, e.g.
51
+ MetricsName.CLIENT_STARTED_RPCS.
52
+ measure_double: A bool indicate whether the metric is a floating-point
53
+ value.
54
+ value_int: The actual metric value if measure_double is False.
55
+ value_float: The actual metric value if measure_double is True.
56
+ include_exchange_labels: Whether this data should include exchanged labels.
57
+ labels: A dictionary that maps label tags associated with this metric to
58
+ corresponding label value.
59
+ identifiers: A set of strings identifying which stats plugins this StatsData
60
+ belongs to.
61
+ registered_method: Whether the method in this data is a registered method
62
+ in stubs.
63
+ """
64
+
65
+ name: "grpc_observability._cyobservability.MetricsName"
66
+ measure_double: bool
67
+ value_int: int = 0
68
+ value_float: float = 0.0
69
+ include_exchange_labels: bool = False
70
+ labels: Dict[str, AnyStr] = field(default_factory=dict)
71
+ identifiers: Set[str] = field(default_factory=set)
72
+ registered_method: bool = False
73
+
74
+
75
+ @dataclass(frozen=True)
76
+ class TracingData:
77
+ """A data class representing tracing data.
78
+
79
+ Attributes:
80
+ name: The name for tracing data, also the name for the Span.
81
+ start_time: The start time for the span in RFC3339 UTC "Zulu" format, e.g.
82
+ 2014-10-02T15:01:23Z
83
+ end_time: The end time for the span in RFC3339 UTC "Zulu" format, e.g.
84
+ 2014-10-02T15:01:23Z
85
+ trace_id: The identifier for the trace associated with this span as a
86
+ 32-character hexadecimal encoded string,
87
+ e.g. 26ed0036f2eff2b7317bccce3e28d01f
88
+ span_id: The identifier for the span as a 16-character hexadecimal encoded
89
+ string. e.g. 113ec879e62583bc
90
+ parent_span_id: An option identifier for the span's parent id.
91
+ status: An element of grpc.StatusCode in string format representing the
92
+ final status for the trace data.
93
+ should_sample: A bool indicates whether the span is sampled.
94
+ child_span_count: The number of child span associated with this span.
95
+ span_labels: A dictionary that maps labels tags associated with this
96
+ span to corresponding label value.
97
+ span_annotations: A dictionary that maps annotation timeStamp with
98
+ description. The timeStamp have a format which can be converted
99
+ to Python datetime.datetime, e.g. 2023-05-29 17:07:09.895
100
+ """
101
+
102
+ name: str
103
+ start_time: str
104
+ end_time: str
105
+ trace_id: str
106
+ span_id: str
107
+ parent_span_id: str
108
+ status: str
109
+ should_sample: bool
110
+ child_span_count: int
111
+ span_labels: Mapping[str, AnyStr] = field(default_factory=dict)
112
+ span_annotations: List[Tuple[str, str]] = field(default_factory=list)
113
+
114
+
115
+ @enum.unique
116
+ class OptionalLabelType(enum.Enum):
117
+ """What kinds of optional labels to add to metrics."""
118
+
119
+ XDS_SERVICE_LABELS = "kXdsServiceLabels"
@@ -0,0 +1,129 @@
1
+ # Copyright 2023 gRPC authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Helper to read observability config."""
15
+
16
+ from dataclasses import dataclass
17
+ from dataclasses import field
18
+ import json
19
+ import os
20
+ from typing import Mapping, Optional
21
+
22
+ GRPC_GCP_OBSERVABILITY_CONFIG_FILE_ENV = "GRPC_GCP_OBSERVABILITY_CONFIG_FILE"
23
+ GRPC_GCP_OBSERVABILITY_CONFIG_ENV = "GRPC_GCP_OBSERVABILITY_CONFIG"
24
+
25
+
26
+ @dataclass
27
+ class GcpObservabilityConfig:
28
+ project_id: str = ""
29
+ stats_enabled: bool = False
30
+ tracing_enabled: bool = False
31
+ labels: Optional[Mapping[str, str]] = field(default_factory=dict)
32
+ sampling_rate: Optional[float] = 0.0
33
+
34
+ def load_from_string_content(self, config_contents: str) -> None:
35
+ """Loads the configuration from a string.
36
+
37
+ Args:
38
+ config_contents: The configuration string.
39
+
40
+ Raises:
41
+ ValueError: If the configuration is invalid.
42
+ """
43
+ try:
44
+ config_json = json.loads(config_contents)
45
+ except json.decoder.JSONDecodeError:
46
+ raise ValueError("Failed to load Json configuration.")
47
+
48
+ if config_json and not isinstance(config_json, dict):
49
+ raise ValueError("Found invalid configuration.")
50
+
51
+ self.project_id = config_json.get("project_id", "")
52
+ self.labels = config_json.get("labels", {})
53
+ self.stats_enabled = "cloud_monitoring" in config_json.keys()
54
+ self.tracing_enabled = "cloud_trace" in config_json.keys()
55
+ tracing_config = config_json.get("cloud_trace", {})
56
+ self.sampling_rate = tracing_config.get("sampling_rate", 0.0)
57
+
58
+
59
+ def read_config() -> GcpObservabilityConfig:
60
+ """Reads the GCP observability config from the environment variables.
61
+
62
+ Returns:
63
+ The GCP observability config.
64
+
65
+ Raises:
66
+ ValueError: If the configuration is invalid.
67
+ """
68
+ config_contents = _get_gcp_observability_config_contents()
69
+ config = GcpObservabilityConfig()
70
+ config.load_from_string_content(config_contents)
71
+
72
+ if not config.project_id:
73
+ # Get project ID from GCP environment variables since project ID was not
74
+ # set it in the GCP observability config.
75
+ config.project_id = _get_gcp_project_id_from_env_var()
76
+ if not config.project_id:
77
+ # Could not find project ID from GCP environment variables either.
78
+ raise ValueError("GCP Project ID not found.")
79
+ return config
80
+
81
+
82
+ def _get_gcp_project_id_from_env_var() -> Optional[str]:
83
+ """Gets the project ID from the GCP environment variables.
84
+
85
+ Returns:
86
+ The project ID, or an empty string if the project ID could not be found.
87
+ """
88
+
89
+ project_id = ""
90
+ project_id = os.getenv("GCP_PROJECT")
91
+ if project_id:
92
+ return project_id
93
+
94
+ project_id = os.getenv("GCLOUD_PROJECT")
95
+ if project_id:
96
+ return project_id
97
+
98
+ project_id = os.getenv("GOOGLE_CLOUD_PROJECT")
99
+ if project_id:
100
+ return project_id
101
+
102
+ return project_id
103
+
104
+
105
+ def _get_gcp_observability_config_contents() -> str:
106
+ """Get the contents of the observability config from environment variable or file.
107
+
108
+ Returns:
109
+ The content from environment variable.
110
+
111
+ Raises:
112
+ ValueError: If no configuration content was found.
113
+ """
114
+
115
+ contents_str = ""
116
+ # First try get config from GRPC_GCP_OBSERVABILITY_CONFIG_FILE_ENV.
117
+ config_path = os.getenv(GRPC_GCP_OBSERVABILITY_CONFIG_FILE_ENV)
118
+ if config_path:
119
+ with open(config_path, "r") as f:
120
+ contents_str = f.read()
121
+
122
+ # Next, try GRPC_GCP_OBSERVABILITY_CONFIG_ENV env var.
123
+ if not contents_str:
124
+ contents_str = os.getenv(GRPC_GCP_OBSERVABILITY_CONFIG_ENV)
125
+
126
+ if not contents_str:
127
+ raise ValueError("Configuration content not found.")
128
+
129
+ return contents_str
@@ -0,0 +1,301 @@
1
+ # Copyright 2023 gRPC authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from datetime import datetime
16
+ import os
17
+ from typing import List, Mapping, Optional, Tuple
18
+
19
+ from google.rpc import code_pb2
20
+ from grpc_observability import _observability # pytype: disable=pyi-error
21
+ from grpc_observability import _observability_config
22
+ from grpc_observability import _views
23
+ from opencensus.common.transports import async_
24
+ from opencensus.ext.stackdriver import stats_exporter
25
+ from opencensus.ext.stackdriver import trace_exporter
26
+ from opencensus.stats import stats as stats_module
27
+ from opencensus.stats.stats_recorder import StatsRecorder
28
+ from opencensus.stats.view_manager import ViewManager
29
+ from opencensus.tags.tag_key import TagKey
30
+ from opencensus.tags.tag_map import TagMap
31
+ from opencensus.tags.tag_value import TagValue
32
+ from opencensus.trace import execution_context
33
+ from opencensus.trace import samplers
34
+ from opencensus.trace import span
35
+ from opencensus.trace import span_context as span_context_module
36
+ from opencensus.trace import span_data as span_data_module
37
+ from opencensus.trace import status
38
+ from opencensus.trace import time_event
39
+ from opencensus.trace import trace_options
40
+ from opencensus.trace import tracer
41
+
42
+ # 60s is the default time for open census to call export.
43
+ CENSUS_UPLOAD_INTERVAL_SECS = int(
44
+ os.environ.get("GRPC_PYTHON_CENSUS_EXPORT_UPLOAD_INTERVAL_SECS", 20)
45
+ )
46
+
47
+
48
+ class StackDriverAsyncTransport(async_.AsyncTransport):
49
+ """Wrapper class used to pass wait_period.
50
+
51
+ This is required because current StackDriver Tracing Exporter doesn't allow
52
+ us pass wait_period to AsyncTransport directly.
53
+
54
+ Args:
55
+ exporter: An opencensus.trace.base_exporter.Exporter object.
56
+ """
57
+
58
+ def __init__(self, exporter):
59
+ super().__init__(exporter, wait_period=CENSUS_UPLOAD_INTERVAL_SECS)
60
+
61
+
62
+ class OpenCensusExporter(_observability.Exporter):
63
+ config: _observability_config.GcpObservabilityConfig
64
+ default_labels: Optional[Mapping[str, str]]
65
+ project_id: str
66
+ tracer: Optional[tracer.Tracer]
67
+ stats_recorder: Optional[StatsRecorder]
68
+ view_manager: Optional[ViewManager]
69
+
70
+ def __init__(self, config: _observability_config.GcpObservabilityConfig):
71
+ self.config = config.get()
72
+ self.default_labels = self.config.labels
73
+ self.project_id = self.config.project_id
74
+ self.tracer = None
75
+ self.stats_recorder = None
76
+ self.view_manager = None
77
+ self._setup_open_census_stackdriver_exporter()
78
+
79
+ def _setup_open_census_stackdriver_exporter(self) -> None:
80
+ if self.config.stats_enabled:
81
+ stats = stats_module.stats
82
+ self.stats_recorder = stats.stats_recorder
83
+ self.view_manager = stats.view_manager
84
+ # If testing locally please add resource="global" to Options, otherwise
85
+ # StackDriver might override project_id based on detected resource.
86
+ options = stats_exporter.Options(project_id=self.project_id)
87
+ metrics_exporter = stats_exporter.new_stats_exporter(
88
+ options, interval=CENSUS_UPLOAD_INTERVAL_SECS
89
+ )
90
+ self.view_manager.register_exporter(metrics_exporter)
91
+ self._register_open_census_views()
92
+
93
+ if self.config.tracing_enabled:
94
+ current_tracer = execution_context.get_opencensus_tracer()
95
+ trace_id = current_tracer.span_context.trace_id
96
+ span_id = current_tracer.span_context.span_id
97
+ if not span_id:
98
+ span_id = span_context_module.generate_span_id()
99
+ span_context = span_context_module.SpanContext(
100
+ trace_id=trace_id, span_id=span_id
101
+ )
102
+ # Create and Saves Tracer and Sampler to ContextVar
103
+ sampler = samplers.ProbabilitySampler(
104
+ rate=self.config.sampling_rate
105
+ )
106
+ self.trace_exporter = trace_exporter.StackdriverExporter(
107
+ project_id=self.project_id,
108
+ transport=StackDriverAsyncTransport,
109
+ )
110
+ self.tracer = tracer.Tracer(
111
+ sampler=sampler,
112
+ span_context=span_context,
113
+ exporter=self.trace_exporter,
114
+ )
115
+
116
+ def export_stats_data(
117
+ self, stats_data: List[_observability.StatsData]
118
+ ) -> None:
119
+ if not self.config.stats_enabled:
120
+ return
121
+ for data in stats_data:
122
+ measure = _views.METRICS_NAME_TO_MEASURE.get(data.name, None)
123
+ if not measure:
124
+ continue
125
+ # Create a measurement map for each metric, otherwise metrics will
126
+ # be overridden instead of accumulate.
127
+ measurement_map = self.stats_recorder.new_measurement_map()
128
+ # Add data label to default labels.
129
+ labels = data.labels
130
+ labels.update(self.default_labels)
131
+ tag_map = TagMap()
132
+ for key, value in labels.items():
133
+ tag_map.insert(TagKey(key), TagValue(value))
134
+
135
+ if data.measure_double:
136
+ measurement_map.measure_float_put(measure, data.value_float)
137
+ else:
138
+ measurement_map.measure_int_put(measure, data.value_int)
139
+ measurement_map.record(tag_map)
140
+
141
+ def export_tracing_data(
142
+ self, tracing_data: List[_observability.TracingData]
143
+ ) -> None:
144
+ if not self.config.tracing_enabled:
145
+ return
146
+ for span_data in tracing_data:
147
+ # Only traced data will be exported, thus TraceOptions=1.
148
+ span_context = span_context_module.SpanContext(
149
+ trace_id=span_data.trace_id,
150
+ span_id=span_data.span_id,
151
+ trace_options=trace_options.TraceOptions(1),
152
+ )
153
+ span_datas = _get_span_data(
154
+ span_data, span_context, self.default_labels
155
+ )
156
+ self.trace_exporter.export(span_datas)
157
+
158
+ def _register_open_census_views(self) -> None:
159
+ # Client
160
+ self.view_manager.register_view(
161
+ _views.client_started_rpcs(self.default_labels)
162
+ )
163
+ self.view_manager.register_view(
164
+ _views.client_completed_rpcs(self.default_labels)
165
+ )
166
+ self.view_manager.register_view(
167
+ _views.client_roundtrip_latency(self.default_labels)
168
+ )
169
+ self.view_manager.register_view(
170
+ _views.client_api_latency(self.default_labels)
171
+ )
172
+ self.view_manager.register_view(
173
+ _views.client_sent_compressed_message_bytes_per_rpc(
174
+ self.default_labels
175
+ )
176
+ )
177
+ self.view_manager.register_view(
178
+ _views.client_received_compressed_message_bytes_per_rpc(
179
+ self.default_labels
180
+ )
181
+ )
182
+
183
+ # Server
184
+ self.view_manager.register_view(
185
+ _views.server_started_rpcs(self.default_labels)
186
+ )
187
+ self.view_manager.register_view(
188
+ _views.server_completed_rpcs(self.default_labels)
189
+ )
190
+ self.view_manager.register_view(
191
+ _views.server_sent_compressed_message_bytes_per_rpc(
192
+ self.default_labels
193
+ )
194
+ )
195
+ self.view_manager.register_view(
196
+ _views.server_received_compressed_message_bytes_per_rpc(
197
+ self.default_labels
198
+ )
199
+ )
200
+ self.view_manager.register_view(
201
+ _views.server_server_latency(self.default_labels)
202
+ )
203
+
204
+
205
+ def _get_span_annotations(
206
+ span_annotations: List[Tuple[str, str]]
207
+ ) -> List[time_event.Annotation]:
208
+ annotations = []
209
+
210
+ for time_stamp, description in span_annotations:
211
+ time = datetime.fromisoformat(time_stamp)
212
+ annotations.append(time_event.Annotation(time, description))
213
+
214
+ return annotations
215
+
216
+
217
+ # pylint: disable=too-many-return-statements
218
+ # pylint: disable=too-many-branches
219
+ def _status_to_span_status(span_status: str) -> Optional[status.Status]:
220
+ if status == "OK":
221
+ return status.Status(code_pb2.OK, message=span_status)
222
+ elif status == "CANCELLED":
223
+ return status.Status(code_pb2.CANCELLED, message=span_status)
224
+ elif status == "UNKNOWN":
225
+ return status.Status(code_pb2.UNKNOWN, message=span_status)
226
+ elif status == "INVALID_ARGUMENT":
227
+ return status.Status(code_pb2.INVALID_ARGUMENT, message=span_status)
228
+ elif status == "DEADLINE_EXCEEDED":
229
+ return status.Status(code_pb2.DEADLINE_EXCEEDED, message=span_status)
230
+ elif status == "NOT_FOUND":
231
+ return status.Status(code_pb2.NOT_FOUND, message=span_status)
232
+ elif status == "ALREADY_EXISTS":
233
+ return status.Status(code_pb2.ALREADY_EXISTS, message=span_status)
234
+ elif status == "PERMISSION_DENIED":
235
+ return status.Status(code_pb2.PERMISSION_DENIED, message=span_status)
236
+ elif status == "UNAUTHENTICATED":
237
+ return status.Status(code_pb2.UNAUTHENTICATED, message=span_status)
238
+ elif status == "RESOURCE_EXHAUSTED":
239
+ return status.Status(code_pb2.RESOURCE_EXHAUSTED, message=span_status)
240
+ elif status == "FAILED_PRECONDITION":
241
+ return status.Status(code_pb2.FAILED_PRECONDITION, message=span_status)
242
+ elif status == "ABORTED":
243
+ return status.Status(code_pb2.ABORTED, message=span_status)
244
+ elif status == "OUT_OF_RANGE":
245
+ return status.Status(code_pb2.OUT_OF_RANGE, message=span_status)
246
+ elif status == "UNIMPLEMENTED":
247
+ return status.Status(code_pb2.UNIMPLEMENTED, message=span_status)
248
+ elif status == "INTERNAL":
249
+ return status.Status(code_pb2.INTERNAL, message=span_status)
250
+ elif status == "UNAVAILABLE":
251
+ return status.Status(code_pb2.UNAVAILABLE, message=span_status)
252
+ elif status == "DATA_LOSS":
253
+ return status.Status(code_pb2.DATA_LOSS, message=span_status)
254
+ else:
255
+ return None
256
+
257
+
258
+ def _get_span_data(
259
+ span_data: _observability.TracingData,
260
+ span_context: span_context_module.SpanContext,
261
+ labels: Mapping[str, str],
262
+ ) -> List[span_data_module.SpanData]:
263
+ """Extracts a list of SpanData tuples from a span.
264
+
265
+ Args:
266
+ span_data: _observability.TracingData to convert.
267
+ span_context: The context related to the span_data.
268
+ labels: Labels to be added to SpanData.
269
+
270
+ Returns:
271
+ A list of opencensus.trace.span_data.SpanData.
272
+ """
273
+ span_attributes = span_data.span_labels
274
+ span_attributes.update(labels)
275
+ span_status = _status_to_span_status(span_data.status)
276
+ span_annotations = _get_span_annotations(span_data.span_annotations)
277
+ span_datas = [
278
+ span_data_module.SpanData(
279
+ name=span_data.name,
280
+ context=span_context,
281
+ span_id=span_data.span_id,
282
+ parent_span_id=span_data.parent_span_id
283
+ if span_data.parent_span_id
284
+ else None,
285
+ attributes=span_attributes,
286
+ start_time=span_data.start_time,
287
+ end_time=span_data.end_time,
288
+ child_span_count=span_data.child_span_count,
289
+ stack_trace=None,
290
+ annotations=span_annotations,
291
+ message_events=None,
292
+ links=None,
293
+ status=span_status,
294
+ same_process_as_parent_span=True
295
+ if span_data.parent_span_id
296
+ else None,
297
+ span_kind=span.SpanKind.UNSPECIFIED,
298
+ )
299
+ ]
300
+
301
+ return span_datas