@@ -16,7 +16,8 @@ use tonic::{codegen::CompressionEncoding, service::Interceptor, transport::Chann
1616
1717use super :: BoxInterceptor ;
1818
19- use opentelemetry_sdk:: retry:: { retry_with_exponential_backoff, RetryPolicy } ;
19+ use crate :: retry_classification:: grpc:: classify_tonic_status;
20+ use opentelemetry_sdk:: retry:: { RetryPolicy , retry_with_exponential_backoff_classified} ;
2021use opentelemetry_sdk:: runtime:: Tokio ;
2122
2223pub ( crate ) struct TonicTracesClient {
@@ -73,54 +74,52 @@ impl SpanExporter for TonicTracesClient {
7374
7475 let batch = Arc :: new ( batch) ;
7576
76- retry_with_exponential_backoff ( Tokio , policy, "TonicTracesClient.Export" , {
77- let batch = Arc :: clone ( & batch) ;
78- let inner = & self . inner ;
79- let resource = & self . resource ;
80- move || {
81- let batch = Arc :: clone ( & batch) ;
82- Box :: pin ( async move {
83- let ( mut client, metadata, extensions) = match inner {
84- Some ( inner) => {
85- let ( m, e, _) = inner
86- . interceptor
87- . lock ( )
88- . await // tokio::sync::Mutex doesn't return a poisoned error, so we can safely use the interceptor here
89- . call ( Request :: new ( ( ) ) )
90- . map_err ( |e| OTelSdkError :: InternalFailure ( format ! ( "error: {e:?}" ) ) ) ?
91- . into_parts ( ) ;
92- ( inner. client . clone ( ) , m, e)
93- }
94- None => return Err ( OTelSdkError :: AlreadyShutdown ) ,
95- } ;
96-
97- let resource_spans = group_spans_by_resource_and_scope ( ( * batch) . clone ( ) , resource) ;
98-
99- otel_debug ! ( name: "TonicTracesClient.ExportStarted" ) ;
100-
101- let result = client
102- . export ( Request :: from_parts (
103- metadata,
104- extensions,
105- ExportTraceServiceRequest { resource_spans } ,
106- ) )
107- . await ;
108-
109- match result {
110- Ok ( _) => {
111- otel_debug ! ( name: "TonicTracesClient.ExportSucceeded" ) ;
112- Ok ( ( ) )
113- }
114- Err ( e) => {
115- let error = format ! ( "export error: {e:?}" ) ;
116- otel_debug ! ( name: "TonicTracesClient.ExportFailed" , error = & error) ;
117- Err ( OTelSdkError :: InternalFailure ( error) )
118- }
77+ match retry_with_exponential_backoff_classified (
78+ Tokio ,
79+ policy,
80+ classify_tonic_status,
81+ "TonicTracesClient.Export" ,
82+ || async {
83+ let batch_clone = Arc :: clone ( & batch) ;
84+
85+ // Execute the export operation
86+ let ( mut client, metadata, extensions) = match & self . inner {
87+ Some ( inner) => {
88+ let ( m, e, _) = inner
89+ . interceptor
90+ . lock ( )
91+ . await // tokio::sync::Mutex doesn't return a poisoned error, so we can safely use the interceptor here
92+ . call ( Request :: new ( ( ) ) )
93+ . map_err ( |e| {
94+ // Convert interceptor errors to tonic::Status for retry classification
95+ tonic:: Status :: internal ( format ! ( "interceptor error: {e:?}" ) )
96+ } ) ?
97+ . into_parts ( ) ;
98+ ( inner. client . clone ( ) , m, e)
11999 }
120- } )
100+ None => return Err ( tonic:: Status :: failed_precondition ( "exporter already shutdown" ) ) ,
101+ } ;
102+
103+ let resource_spans =
104+ group_spans_by_resource_and_scope ( ( * batch_clone) . clone ( ) , & self . resource ) ;
105+
106+ otel_debug ! ( name: "TonicTracesClient.ExportStarted" ) ;
107+
108+ client
109+ . export ( Request :: from_parts (
110+ metadata,
111+ extensions,
112+ ExportTraceServiceRequest { resource_spans } ,
113+ ) )
114+ . await
115+ . map ( |_| {
116+ otel_debug ! ( name: "TonicTracesClient.ExportSucceeded" ) ;
117+ } )
121118 }
122- } )
123- . await
119+ ) . await {
120+ Ok ( _) => Ok ( ( ) ) ,
121+ Err ( tonic_status) => Err ( OTelSdkError :: InternalFailure ( format ! ( "export error: {tonic_status:?}" ) ) ) ,
122+ }
124123 }
125124
126125 fn shutdown ( & mut self ) -> OTelSdkResult {
0 commit comments