Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/5285.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-exporter-otlp`: handle encoding exceptions in OTLP exporters.
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,17 @@ def _export(
return self._result.FAILURE # type: ignore [reportReturnType]

with self._metrics.export_operation(self._count_data(data)) as result:
try:
request = self._translate_data(data)
except Exception as error:
logger.exception(
"Failed to encode %s batch: %s",
self._exporting,
error,
)
result.error = error
return self._result.FAILURE # type: ignore [reportReturnType]

# FIXME remove this check if the export type for traces
# gets updated to a class that represents the proto
# TracesData and use the code below instead.
Expand All @@ -464,7 +475,7 @@ def _export(
if self._client is None:
return self._result.FAILURE
self._client.Export(
request=self._translate_data(data),
request=request,
metadata=self._headers,
timeout=deadline_sec - time(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,22 @@ def test_shutdown_interrupts_export_retry_backoff(self):
"Shutdown in progress, aborting retry.",
)

def test_encoding_error_returns_failure(self):
exporter = OTLPSpanExporterForTesting(
insecure=True, meter_provider=self.meter_provider
)
encoding_error = ValueError("encoding failed")
with patch.object(
exporter, "_translate_data", side_effect=encoding_error
):
with self.assertLogs(
"opentelemetry.exporter.otlp.proto.grpc.exporter",
level="ERROR",
) as log:
result = exporter.export([self.span])
self.assertEqual(result, SpanExportResult.FAILURE)
self.assertIn("Failed to encode traces batch", log.records[0].message)

def test_export_over_closed_grpc_channel(self):
# pylint: disable=protected-access

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,16 @@ def export(
return LogRecordExportResult.FAILURE

with self._metrics.export_operation(len(batch)) as result:
serialized_data = encode_logs(batch).SerializeToString()
try:
serialized_data = encode_logs(batch).SerializeToString()
except Exception as error:
_logger.exception(
"Failed to encode logs batch: %s",
error,
)
result.error = error
return LogRecordExportResult.FAILURE

deadline_sec = time() + self._timeout
for retry_num in range(_MAX_RETRYS):
# multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,17 @@ def export(
for metric in scope_metrics.metrics:
num_items += len(metric.data.data_points)

export_request = encode_metrics(metrics_data)
try:
export_request = encode_metrics(metrics_data)
except Exception as error:
with self._metrics.export_operation(num_items) as result:
_logger.exception(
"Failed to encode metrics batch: %s",
error,
)
result.error = error
return MetricExportResult.FAILURE

deadline_sec = time() + self._timeout

# If no batch size configured, export as single batch with retries as configured
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,18 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
return SpanExportResult.FAILURE

with self._metrics.export_operation(len(spans)) as result:
serialized_data = encode_spans(spans).SerializePartialToString()
try:
serialized_data = encode_spans(
spans
).SerializePartialToString()
except Exception as error:
_logger.exception(
"Failed to encode span batch: %s",
error,
)
result.error = error
return SpanExportResult.FAILURE

deadline_sec = time() + self._timeout
for retry_num in range(_MAX_RETRYS):
# multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ def setUp(self):
),
}

@patch(
"opentelemetry.exporter.otlp.proto.http.metric_exporter.encode_metrics",
side_effect=ValueError("encoding failed"),
)
def test_encoding_error_returns_failure(self, _mock_encode):
exporter = OTLPMetricExporter(meter_provider=self.meter_provider)
with self.assertLogs(
"opentelemetry.exporter.otlp.proto.http.metric_exporter",
level="ERROR",
) as log:
result = exporter.export(self.metrics["sum_int"])
self.assertEqual(result, MetricExportResult.FAILURE)
self.assertIn("Failed to encode metrics batch", log.records[0].message)

def test_constructor_default(self):
exporter = OTLPMetricExporter()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,20 @@ def export_side_effect(*args, **kwargs):
exporter = OTLPLogExporter(timeout=0.4)
exporter.export(self._get_sdk_log_data())

@patch(
"opentelemetry.exporter.otlp.proto.http._log_exporter.encode_logs",
side_effect=ValueError("encoding failed"),
)
def test_encoding_error_returns_failure(self, _mock_encode):
exporter = OTLPLogExporter(meter_provider=self.meter_provider)
with self.assertLogs(
"opentelemetry.exporter.otlp.proto.http._log_exporter",
level="ERROR",
) as log:
result = exporter.export(self._get_sdk_log_data())
self.assertEqual(result, LogRecordExportResult.FAILURE)
self.assertIn("Failed to encode logs batch", log.records[0].message)

@patch.object(Session, "post")
def test_shutdown_interrupts_retry_backoff(self, mock_post):
exporter = OTLPLogExporter(timeout=1.5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,20 @@ def export_side_effect(*args, **kwargs):
exporter = OTLPSpanExporter(timeout=0.4)
exporter.export([BASIC_SPAN])

@patch(
"opentelemetry.exporter.otlp.proto.http.trace_exporter.encode_spans",
side_effect=ValueError("encoding failed"),
)
def test_encoding_error_returns_failure(self, _mock_encode):
exporter = OTLPSpanExporter(meter_provider=self.meter_provider)
with self.assertLogs(
"opentelemetry.exporter.otlp.proto.http.trace_exporter",
level="ERROR",
) as log:
result = exporter.export([BASIC_SPAN])
self.assertEqual(result, SpanExportResult.FAILURE)
self.assertIn("Failed to encode span batch", log.records[0].message)

@patch.object(Session, "post")
def test_shutdown_interrupts_retry_backoff(self, mock_post):
exporter = OTLPSpanExporter(timeout=1.5)
Expand Down
Loading