Skip to content

Commit 2c6c7b1

Browse files
authored
Merge branch 'main' into main
2 parents 5dd724a + 5307dd0 commit 2c6c7b1

File tree

7 files changed

+51
-23
lines changed

7 files changed

+51
-23
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212

1313
## Unreleased
1414

15+
- `opentelemetry-api`: Convert objects of any type other than AnyValue in attributes to string to be exportable
16+
([#4808](https://github.com/open-telemetry/opentelemetry-python/pull/4808))
1517
- docs: Added sqlcommenter example
1618
([#4734](https://github.com/open-telemetry/opentelemetry-python/pull/4734))
1719
- build: bump ruff to 0.14.1
@@ -27,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2729
([#4676](https://github.com/open-telemetry/opentelemetry-python/pull/4676))
2830
- [BREAKING] Rename several classes from Log to LogRecord
2931
([#4647](https://github.com/open-telemetry/opentelemetry-python/pull/4647))
32+
- Fix type checking for built in metric exporters
33+
([#4820](https://github.com/open-telemetry/opentelemetry-python/pull/4820))
3034

3135
**Migration Guide:**
3236

@@ -1721,7 +1725,7 @@ can cause a deadlock to occur over `logging._lock` in some cases ([#4636](https:
17211725
- Add reset for the global configuration object, for testing purposes
17221726
([#636](https://github.com/open-telemetry/opentelemetry-python/pull/636))
17231727
- Add support for programmatic instrumentation
1724-
([#579](https://github.com/open-telemetry/opentelemetry-python/pull/569))
1728+
([#579](https://github.com/open-telemetry/opentelemetry-python/pull/579))
17251729

17261730
### Changed
17271731

opentelemetry-api/src/opentelemetry/attributes/__init__.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def _clean_attribute(
118118
return None
119119

120120

121-
def _clean_extended_attribute_value(
121+
def _clean_extended_attribute_value( # pylint: disable=too-many-branches
122122
value: types.AnyValue, max_len: Optional[int]
123123
) -> types.AnyValue:
124124
# for primitive types just return the value and eventually shorten the string length
@@ -180,11 +180,19 @@ def _clean_extended_attribute_value(
180180
# Freeze mutable sequences defensively
181181
return tuple(cleaned_seq)
182182

183-
raise TypeError(
184-
f"Invalid type {type(value).__name__} for attribute value. "
185-
f"Expected one of {[valid_type.__name__ for valid_type in _VALID_ANY_VALUE_TYPES]} or a "
186-
"sequence of those types",
187-
)
183+
# Some applications such as Django add values to log records whose types fall outside the
184+
# primitive types and `_VALID_ANY_VALUE_TYPES`, i.e., they are not of type `AnyValue`.
185+
# Rather than attempt to whitelist every possible instrumentation, we stringify those values here
186+
# so they can still be represented as attributes, falling back to the original TypeError only if
187+
# converting to string raises.
188+
try:
189+
return str(value)
190+
except Exception:
191+
raise TypeError(
192+
f"Invalid type {type(value).__name__} for attribute value. "
193+
f"Expected one of {[valid_type.__name__ for valid_type in _VALID_ANY_VALUE_TYPES]} or a "
194+
"sequence of those types",
195+
)
188196

189197

190198
def _clean_extended_attribute(

opentelemetry-api/tests/attributes/test_attributes.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,22 @@ def test_extended_attributes(self):
301301
bdict["key"] = "value"
302302

303303
clean_extended_attribute_mock.assert_called_once()
304+
305+
def test_wsgi_request_conversion_to_string(self):
306+
"""Test that WSGI request objects are converted to strings when _clean_extended_attribute is called."""
307+
308+
class DummyWSGIRequest:
309+
def __str__(self):
310+
return "<DummyWSGIRequest method=GET path=/example/>"
311+
312+
wsgi_request = DummyWSGIRequest()
313+
314+
cleaned_value = _clean_extended_attribute(
315+
"request", wsgi_request, None
316+
)
317+
318+
# Verify we get a string back from the cleaner
319+
self.assertIsInstance(cleaned_value, str)
320+
self.assertEqual(
321+
"<DummyWSGIRequest method=GET path=/example/>", cleaned_value
322+
)

opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def __init__(
145145
self,
146146
out: IO = stdout,
147147
formatter: Callable[
148-
["opentelemetry.sdk.metrics.export.MetricsData"], str
148+
[MetricsData], str
149149
] = lambda metrics_data: metrics_data.to_json() + linesep,
150150
preferred_temporality: dict[type, AggregationTemporality]
151151
| None = None,
@@ -362,7 +362,7 @@ def _set_collect_callback(
362362
@abstractmethod
363363
def _receive_metrics(
364364
self,
365-
metrics_data: "opentelemetry.sdk.metrics.export.MetricsData",
365+
metrics_data: MetricsData,
366366
timeout_millis: float = 10_000,
367367
**kwargs,
368368
) -> None:
@@ -406,13 +406,11 @@ def __init__(
406406
preferred_aggregation=preferred_aggregation,
407407
)
408408
self._lock = RLock()
409-
self._metrics_data: "opentelemetry.sdk.metrics.export.MetricsData" = (
410-
None
411-
)
409+
self._metrics_data: MetricsData = None
412410

413411
def get_metrics_data(
414412
self,
415-
) -> Optional["opentelemetry.sdk.metrics.export.MetricsData"]:
413+
) -> Optional[MetricsData]:
416414
"""Reads and returns current metrics from the SDK"""
417415
with self._lock:
418416
self.collect()
@@ -422,7 +420,7 @@ def get_metrics_data(
422420

423421
def _receive_metrics(
424422
self,
425-
metrics_data: "opentelemetry.sdk.metrics.export.MetricsData",
423+
metrics_data: MetricsData,
426424
timeout_millis: float = 10_000,
427425
**kwargs,
428426
) -> None:

opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
# limitations under the License.
1414

1515

16-
from opentelemetry.sdk.metrics._internal.export import (
16+
from opentelemetry.sdk.metrics._internal.aggregation import (
1717
AggregationTemporality,
18+
)
19+
from opentelemetry.sdk.metrics._internal.export import (
1820
ConsoleMetricExporter,
1921
InMemoryMetricReader,
2022
MetricExporter,

opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class SpanExporter:
6363

6464
def export(
6565
self, spans: typing.Sequence[ReadableSpan]
66-
) -> "SpanExportResult":
66+
) -> "SpanExportResult": # pyright: ignore[reportReturnType]
6767
"""Exports a batch of telemetry data.
6868
6969
Args:
@@ -79,7 +79,7 @@ def shutdown(self) -> None:
7979
Called when the SDK is shut down.
8080
"""
8181

82-
def force_flush(self, timeout_millis: int = 30000) -> bool:
82+
def force_flush(self, timeout_millis: int = 30000) -> bool: # pyright: ignore[reportReturnType]
8383
"""Hint to ensure that the export of any spans the exporter has received
8484
prior to the call to ForceFlush SHOULD be completed as soon as possible, preferably
8585
before returning from this method.
@@ -102,7 +102,7 @@ def on_start(
102102
pass
103103

104104
def on_end(self, span: ReadableSpan) -> None:
105-
if not span.context.trace_flags.sampled:
105+
if not (span.context and span.context.trace_flags.sampled):
106106
return
107107
token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
108108
try:
@@ -188,7 +188,7 @@ def on_start(
188188
pass
189189

190190
def on_end(self, span: ReadableSpan) -> None:
191-
if not span.context.trace_flags.sampled:
191+
if not (span.context and span.context.trace_flags.sampled):
192192
return
193193
self._batch_processor.emit(span)
194194

pyproject.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,8 @@ include = [
111111
exclude = [
112112
"opentelemetry-sdk/tests",
113113
"opentelemetry-sdk/src/opentelemetry/sdk/_events",
114-
"opentelemetry-sdk/src/opentelemetry/sdk/error_handler",
115-
"opentelemetry-sdk/src/opentelemetry/sdk/metrics",
116-
"opentelemetry-sdk/src/opentelemetry/sdk/trace/export",
114+
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/",
117115
"opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py",
118-
"opentelemetry-sdk/src/opentelemetry/sdk/util",
119116
"opentelemetry-sdk/benchmarks",
120117
"exporter/opentelemetry-exporter-otlp-proto-grpc/tests",
121118
]

0 commit comments

Comments
 (0)