diff --git a/opentelemetry-otlp/CHANGELOG.md b/opentelemetry-otlp/CHANGELOG.md index d82ef0612c..ae8434abaa 100644 --- a/opentelemetry-otlp/CHANGELOG.md +++ b/opentelemetry-otlp/CHANGELOG.md @@ -16,8 +16,9 @@ now use `.with_resource(RESOURCE::default())` to configure Resource when using These methods would also no longer set the global tracer provider. It would now be the responsibility of users to set it by calling `global::set_tracer_provider(tracer_provider.clone());`. Refer to the [basic-otlp](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry-otlp/examples/basic-otlp/src/main.rs) and [basic-otlp-http](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs) examples on how to initialize OTLP Trace Exporter. - **Breaking** Correct the misspelling of "webkpi" to "webpki" in features [#1842](https://github.com/open-telemetry/opentelemetry-rust/pull/1842) - Bump MSRV to 1.70 [#1840](https://github.com/open-telemetry/opentelemetry-rust/pull/1840) +- Fixing the OTLP HTTP/JSON exporter. [#1882](https://github.com/open-telemetry/opentelemetry-rust/pull/1882) - The exporter was broken in the + previous release. - **Breaking** [1869](https://github.com/open-telemetry/opentelemetry-rust/pull/1869) The OTLP logs exporter now populates the [InstrumentationScope::name](https://github.com/open-telemetry/opentelemetry-proto/blob/b3060d2104df364136d75a35779e6bd48bac449a/opentelemetry/proto/common/v1/common.proto#L73) field with the name of the target/component emitting the logs, instead of the appender's name as done previously. - ## v0.16.0 diff --git a/opentelemetry-otlp/tests/integration_test/src/trace_asserter.rs b/opentelemetry-otlp/tests/integration_test/src/trace_asserter.rs index 47af933a83..00c7c2300d 100644 --- a/opentelemetry-otlp/tests/integration_test/src/trace_asserter.rs +++ b/opentelemetry-otlp/tests/integration_test/src/trace_asserter.rs @@ -216,6 +216,6 @@ fn span_eq(left: &Span, right: &Span) -> bool { pub fn read_spans_from_json(file: File) -> Vec { let reader = std::io::BufReader::new(file); - let trace_data: TracesData = serde_json::from_reader(reader).unwrap(); + let trace_data: TracesData = serde_json::from_reader(reader).expect("Failed to read json file"); trace_data.resource_spans } diff --git a/opentelemetry-otlp/tests/integration_test/tests/traces.rs b/opentelemetry-otlp/tests/integration_test/tests/traces.rs index 6fac7749ff..a6a3980e01 100644 --- a/opentelemetry-otlp/tests/integration_test/tests/traces.rs +++ b/opentelemetry-otlp/tests/integration_test/tests/traces.rs @@ -8,9 +8,11 @@ use opentelemetry::{ trace::{TraceContextExt, Tracer}, Key, KeyValue, }; +use opentelemetry_proto::tonic::trace::v1::TracesData; use opentelemetry_sdk::{runtime, trace as sdktrace, Resource}; use std::error::Error; use std::fs::File; +use std::io::Write; use std::os::unix::fs::MetadataExt; fn init_tracer_provider() -> Result { @@ -83,3 +85,28 @@ pub fn test_assert_span_eq() { TraceAsserter::new(spans.clone(), spans).assert(); } + +#[test] +pub fn test_serde() { + let spans = read_spans_from_json( + File::open("./expected/traces.json").expect("Failed to read traces.json"), + ); + let json = serde_json::to_string_pretty(&TracesData { + resource_spans: spans, + }) + .expect("Failed to serialize spans to json"); + + // Write to file. + let mut file = File::create("./expected/serialized_traces.json").unwrap(); + file.write_all(json.as_bytes()).unwrap(); + + let left = read_spans_from_json( + File::open("./expected/traces.json").expect("Failed to read traces.json"), + ); + let right = read_spans_from_json( + File::open("./expected/serialized_traces.json") + .expect("Failed to read serialized_traces.json"), + ); + + TraceAsserter::new(left, right).assert(); +} diff --git a/opentelemetry-proto/src/proto.rs b/opentelemetry-proto/src/proto.rs index fd7ddd7652..8bc372b889 100644 --- a/opentelemetry-proto/src/proto.rs +++ b/opentelemetry-proto/src/proto.rs @@ -46,14 +46,26 @@ pub(crate) mod serializers { // AnyValue <-> KeyValue conversion pub fn serialize_to_value(value: &Option, serializer: S) -> Result - where - S: Serializer, + where + S: Serializer, { - // Serialize any_value::Value using its own implementation - // If value is None, it will be serialized as such match value { Some(any_value) => match &any_value.value { - Some(Value::IntValue(i)) => serialize_i64_to_string(i, serializer), + Some(Value::IntValue(i)) => { + // Attempt to create a struct to wrap the intValue + let mut state = match serializer.serialize_struct("Value", 1) { + Ok(s) => s, + Err(e) => return Err(e), // Handle the error or return it + }; + + // Attempt to serialize the intValue field + if let Err(e) = state.serialize_field("intValue", &i.to_string()) { + return Err(e); // Handle the error or return it + } + + // Finalize the struct serialization + state.end() + }, Some(value) => value.serialize(serializer), None => serializer.serialize_none(), },