Skip to content

Commit c955356

Browse files
lalitbutpilla
andauthored
fix(geneva uploader): Replace unwrap/expect with graceful error handling (#485)
Co-authored-by: Utkarsh Umesan Pillai <[email protected]>
1 parent b49f00e commit c955356

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

opentelemetry-exporter-geneva/geneva-uploader/src/config_service/client.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use reqwest::{
88
use serde::Deserialize;
99
use std::time::Duration;
1010
use thiserror::Error;
11-
use tracing::{debug, info};
11+
use tracing::{debug, error, info};
1212
use uuid::Uuid;
1313

1414
use chrono::{DateTime, Utc};
@@ -402,7 +402,21 @@ impl GenevaConfigClient {
402402
fn build_static_headers(agent_identity: &str, agent_version: &str) -> HeaderMap {
403403
let mut headers = HeaderMap::new();
404404
let user_agent = format!("{agent_identity}-{agent_version}");
405-
headers.insert(USER_AGENT, HeaderValue::from_str(&user_agent).unwrap());
405+
// Gracefully fallback to default header if formatting fails (though hardcoded values should always be valid)
406+
headers.insert(
407+
USER_AGENT,
408+
HeaderValue::from_str(&user_agent).unwrap_or_else(|e| {
409+
error!(
410+
name: "config_client.build_static_headers.error",
411+
target: "geneva-uploader",
412+
error = %e,
413+
agent_identity = agent_identity,
414+
agent_version = agent_version,
415+
"User-Agent header creation failed, using 'GenevaUploader-0.1'"
416+
);
417+
HeaderValue::from_static("GenevaUploader-0.1")
418+
}),
419+
);
406420
headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
407421
headers
408422
}

opentelemetry-exporter-geneva/geneva-uploader/src/payload_encoder/otlp_encoder.rs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use opentelemetry_proto::tonic::logs::v1::LogRecord;
1010
use opentelemetry_proto::tonic::trace::v1::Span;
1111
use std::borrow::Cow;
1212
use std::sync::Arc;
13-
use tracing::debug;
13+
use tracing::{debug, error};
1414

1515
const FIELD_ENV_NAME: &str = "env_name";
1616
const FIELD_ENV_VER: &str = "env_ver";
@@ -82,7 +82,8 @@ impl OtlpEncoder {
8282
acc.push(';');
8383
}
8484
let md5_hash = md5::compute(s.id.to_le_bytes());
85-
write!(&mut acc, "{md5_hash:x}").unwrap();
85+
// Writing to String never fails in practice, ignore error
86+
let _ = write!(&mut acc, "{md5_hash:x}");
8687
acc
8788
},
8889
)
@@ -273,7 +274,8 @@ impl OtlpEncoder {
273274
acc.push(';');
274275
}
275276
let md5_hash = md5::compute(s.id.to_le_bytes());
276-
write!(&mut acc, "{md5_hash:x}").unwrap();
277+
// Writing to String never fails in practice, ignore error
278+
let _ = write!(&mut acc, "{md5_hash:x}");
277279
acc
278280
},
279281
)
@@ -568,12 +570,14 @@ impl OtlpEncoder {
568570
}
569571
FIELD_TRACE_ID => {
570572
let hex_bytes = Self::encode_id_to_hex::<32>(&span.trace_id);
571-
let hex_str = std::str::from_utf8(&hex_bytes).unwrap();
573+
let hex_str = std::str::from_utf8(&hex_bytes)
574+
.expect("hex encoding always produces valid UTF-8");
572575
BondWriter::write_string(&mut buffer, hex_str);
573576
}
574577
FIELD_SPAN_ID => {
575578
let hex_bytes = Self::encode_id_to_hex::<16>(&span.span_id);
576-
let hex_str = std::str::from_utf8(&hex_bytes).unwrap();
579+
let hex_str = std::str::from_utf8(&hex_bytes)
580+
.expect("hex encoding always produces valid UTF-8");
577581
BondWriter::write_string(&mut buffer, hex_str);
578582
}
579583
FIELD_TRACE_FLAGS => {
@@ -587,7 +591,8 @@ impl OtlpEncoder {
587591
}
588592
FIELD_PARENT_ID => {
589593
let hex_bytes = Self::encode_id_to_hex::<16>(&span.parent_span_id);
590-
let hex_str = std::str::from_utf8(&hex_bytes).unwrap();
594+
let hex_str = std::str::from_utf8(&hex_bytes)
595+
.expect("hex encoding always produces valid UTF-8");
591596
BondWriter::write_string(&mut buffer, hex_str);
592597
}
593598
FIELD_LINKS => {
@@ -636,12 +641,14 @@ impl OtlpEncoder {
636641
}
637642
FIELD_TRACE_ID => {
638643
let hex_bytes = Self::encode_id_to_hex::<32>(&log.trace_id);
639-
let hex_str = std::str::from_utf8(&hex_bytes).unwrap();
644+
let hex_str = std::str::from_utf8(&hex_bytes)
645+
.expect("hex encoding always produces valid UTF-8");
640646
BondWriter::write_string(&mut buffer, hex_str);
641647
}
642648
FIELD_SPAN_ID => {
643649
let hex_bytes = Self::encode_id_to_hex::<16>(&log.span_id);
644-
let hex_str = std::str::from_utf8(&hex_bytes).unwrap();
650+
let hex_str = std::str::from_utf8(&hex_bytes)
651+
.expect("hex encoding always produces valid UTF-8");
645652
BondWriter::write_string(&mut buffer, hex_str);
646653
}
647654
FIELD_TRACE_FLAGS => {
@@ -679,7 +686,23 @@ impl OtlpEncoder {
679686

680687
fn encode_id_to_hex<const N: usize>(id: &[u8]) -> [u8; N] {
681688
let mut hex_bytes = [0u8; N];
682-
hex::encode_to_slice(id, &mut hex_bytes).unwrap();
689+
// If encoding fails (buffer size mismatch), log error and return zeros
690+
if let Err(e) = hex::encode_to_slice(id, &mut hex_bytes) {
691+
let id_type = match N {
692+
32 => "trace ID",
693+
16 => "span ID",
694+
_ => "input",
695+
};
696+
error!(
697+
name: "encoder.encode_id_to_hex.error",
698+
target: "geneva-uploader",
699+
error = %e,
700+
id_len = id.len(),
701+
buffer_size = N,
702+
"Hex encoding failed, using zeros - indicates an invalid {}",
703+
id_type
704+
);
705+
}
683706
hex_bytes
684707
}
685708

@@ -722,14 +745,25 @@ impl OtlpEncoder {
722745
json
723746
}
724747

725-
/// Format timestamp from nanoseconds
748+
/// Format timestamp from nanoseconds to RFC3339 string
726749
fn format_timestamp(nanos: u64) -> String {
727750
let secs = (nanos / 1_000_000_000) as i64;
728751
let nsec = (nanos % 1_000_000_000) as u32;
729-
Utc.timestamp_opt(secs, nsec)
730-
.single()
731-
.unwrap_or_else(|| Utc.timestamp_opt(0, 0).single().unwrap())
732-
.to_rfc3339()
752+
753+
match Utc.timestamp_opt(secs, nsec).single() {
754+
Some(dt) => dt.to_rfc3339(),
755+
None => {
756+
error!(
757+
name: "encoder.format_timestamp.invalid",
758+
target: "geneva-uploader",
759+
nanos = nanos,
760+
secs = secs,
761+
nsec = nsec,
762+
"Timestamp out of range, using epoch"
763+
);
764+
"1970-01-01T00:00:00+00:00".to_string()
765+
}
766+
}
733767
}
734768

735769
/// Write attribute value based on its type

0 commit comments

Comments
 (0)