1+ use std:: sync:: Arc ;
12use core:: fmt;
23use opentelemetry:: otel_debug;
34use opentelemetry_proto:: tonic:: collector:: logs:: v1:: {
@@ -12,6 +13,8 @@ use opentelemetry_proto::transform::logs::tonic::group_logs_by_resource_and_scop
1213use super :: BoxInterceptor ;
1314use tokio:: sync:: Mutex ;
1415
16+ use crate :: retry:: { retry_with_exponential_backoff, RetryPolicy } ;
17+
1518pub ( crate ) struct TonicLogsClient {
1619 inner : Option < ClientInner > ,
1720 #[ allow( dead_code) ]
@@ -57,33 +60,50 @@ impl TonicLogsClient {
5760
5861impl LogExporter for TonicLogsClient {
5962 async fn export ( & self , batch : LogBatch < ' _ > ) -> OTelSdkResult {
60- let ( mut client, metadata, extensions) = match & self . inner {
61- Some ( inner) => {
62- let ( m, e, _) = inner
63- . interceptor
64- . lock ( )
65- . await // tokio::sync::Mutex doesn't return a poisoned error, so we can safely use the interceptor here
66- . call ( Request :: new ( ( ) ) )
67- . map_err ( |e| OTelSdkError :: InternalFailure ( format ! ( "error: {:?}" , e) ) ) ?
68- . into_parts ( ) ;
69- ( inner. client . clone ( ) , m, e)
70- }
71- None => return Err ( OTelSdkError :: AlreadyShutdown ) ,
63+ let policy = RetryPolicy {
64+ max_retries : 3 ,
65+ initial_delay_ms : 100 ,
66+ max_delay_ms : 1600 ,
67+ jitter_ms : 100 ,
7268 } ;
7369
74- let resource_logs = group_logs_by_resource_and_scope ( batch, & self . resource ) ;
70+ let batch = Arc :: new ( batch) ; // Wrap batch in Arc<Mutex<LogBatch>>
71+
72+ retry_with_exponential_backoff ( policy, "TonicLogsClient.Export" , {
73+ let batch = Arc :: clone ( & batch) ;
74+ move || {
75+ let batch = Arc :: clone ( & batch) ; // Clone the Arc inside the closure
76+ Box :: pin ( async move {
77+ let ( mut client, metadata, extensions) = match & self . inner {
78+ Some ( inner) => {
79+ let ( m, e, _) = inner
80+ . interceptor
81+ . lock ( )
82+ . await // tokio::sync::Mutex doesn't return a poisoned error, so we can safely use the interceptor here
83+ . call ( Request :: new ( ( ) ) )
84+ . map_err ( |e| OTelSdkError :: InternalFailure ( format ! ( "error: {:?}" , e) ) ) ?
85+ . into_parts ( ) ;
86+ ( inner. client . clone ( ) , m, e)
87+ }
88+ None => return Err ( OTelSdkError :: AlreadyShutdown ) ,
89+ } ;
7590
76- otel_debug ! ( name : "TonicsLogsClient.CallingExport" ) ;
91+ let resource_logs = group_logs_by_resource_and_scope ( & * batch , & self . resource ) ;
7792
78- client
79- . export ( Request :: from_parts (
80- metadata,
81- extensions,
82- ExportLogsServiceRequest { resource_logs } ,
83- ) )
84- . await
85- . map_err ( |e| OTelSdkError :: InternalFailure ( format ! ( "export error: {:?}" , e) ) ) ?;
86- Ok ( ( ) )
93+ otel_debug ! ( name: "TonicsLogsClient.CallingExport" ) ;
94+
95+ client
96+ . export ( Request :: from_parts (
97+ metadata,
98+ extensions,
99+ ExportLogsServiceRequest { resource_logs } ,
100+ ) )
101+ . await
102+ . map ( |_| ( ) ) // Map the successful result to Ok(())
103+ . map_err ( |e| OTelSdkError :: InternalFailure ( format ! ( "export error: {:?}" , e) ) )
104+ } )
105+ }
106+ } ) . await
87107 }
88108
89109 fn shutdown ( & mut self ) -> OTelSdkResult {
0 commit comments