From 3e1b6218fc55895b51f6559d81f4e4884740aeff Mon Sep 17 00:00:00 2001 From: Julian Bright Date: Wed, 21 Jan 2026 17:44:15 -0800 Subject: [PATCH 1/2] feat: Capture EventName for logs --- src/exporters/clickhouse/schema.rs | 2 + src/exporters/clickhouse/transform_logs.rs | 116 +++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/exporters/clickhouse/schema.rs b/src/exporters/clickhouse/schema.rs index 174e1196..daa14508 100644 --- a/src/exporters/clickhouse/schema.rs +++ b/src/exporters/clickhouse/schema.rs @@ -104,6 +104,7 @@ pub struct LogRecordRow<'a> { pub(crate) scope_version: &'a str, pub(crate) scope_attributes: &'a MapOrJson<'a>, pub(crate) log_attributes: MapOrJson<'a>, + pub(crate) event_name: &'a str, } pub fn get_log_row_col_keys() -> String { @@ -123,6 +124,7 @@ pub fn get_log_row_col_keys() -> String { "ScopeVersion", "ScopeAttributes", "LogAttributes", + "EventName", ]; fields.join(",") diff --git a/src/exporters/clickhouse/transform_logs.rs b/src/exporters/clickhouse/transform_logs.rs index d4d5b84e..9635094d 100644 --- a/src/exporters/clickhouse/transform_logs.rs +++ b/src/exporters/clickhouse/transform_logs.rs @@ -77,6 +77,7 @@ impl TransformPayload for Transformer { scope_version: &scope_version, scope_attributes: &scope_attrs, log_attributes: self.transform_attrs_kv(&log_attrs), + event_name: &log.event_name, }; match payload_builder.add_row(&row) { @@ -101,3 +102,118 @@ impl TransformPayload for Transformer { (result, None) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::exporters::clickhouse::transformer::Transformer; + use opentelemetry_proto::tonic::common::v1::{AnyValue, InstrumentationScope, KeyValue}; + use opentelemetry_proto::tonic::common::v1::any_value::Value as AnyValueValue; + use opentelemetry_proto::tonic::logs::v1::{LogRecord, ScopeLogs}; + use opentelemetry_proto::tonic::resource::v1::Resource; + + fn create_test_log_record(event_name: &str, body: &str) -> LogRecord { + LogRecord { + time_unix_nano: 1234567890, + observed_time_unix_nano: 1234567890, + severity_number: 9, // INFO + severity_text: "INFO".to_string(), + body: Some(AnyValue { + value: Some(AnyValueValue::StringValue(body.to_string())), + }), + attributes: vec![ + KeyValue { + key: "test.attr".to_string(), + value: Some(AnyValue { + value: Some(AnyValueValue::StringValue("test_value".to_string())), + }), + }, + ], + dropped_attributes_count: 0, + flags: 1, + trace_id: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + span_id: vec![1, 2, 3, 4, 5, 6, 7, 8], + event_name: event_name.to_string(), + } + } + + fn create_test_resource_logs(log_records: Vec) -> ResourceLogs { + ResourceLogs { + resource: Some(Resource { + attributes: vec![KeyValue { + key: "service.name".to_string(), + value: Some(AnyValue { + value: Some(AnyValueValue::StringValue("test-service".to_string())), + }), + }], + dropped_attributes_count: 0, + entity_refs: vec![], + }), + scope_logs: vec![ScopeLogs { + scope: Some(InstrumentationScope { + name: "test-scope".to_string(), + version: "1.0.0".to_string(), + attributes: vec![], + dropped_attributes_count: 0, + }), + log_records, + schema_url: "".to_string(), + }], + schema_url: "".to_string(), + } + } + + #[test] + fn test_event_name_is_preserved() { + let transformer = Transformer::new(crate::exporters::clickhouse::Compression::None, true); + + let log_record = create_test_log_record("introspection.feedback", "thumbs_up"); + let resource_logs = create_test_resource_logs(vec![log_record]); + + let message = Message { + payload: vec![resource_logs], + metadata: None, + request_context: None, + }; + + let (result, _) = transformer.transform(vec![message]); + assert!(result.is_ok(), "Transform should succeed"); + + // The transform succeeded, which means the LogRecordRow was created with event_name + // The actual serialization includes the event_name field + } + + #[test] + fn test_empty_event_name() { + let transformer = Transformer::new(crate::exporters::clickhouse::Compression::None, true); + + let log_record = create_test_log_record("", "test body"); + let resource_logs = create_test_resource_logs(vec![log_record]); + + let message = Message { + payload: vec![resource_logs], + metadata: None, + request_context: None, + }; + + let (result, _) = transformer.transform(vec![message]); + assert!(result.is_ok(), "Transform should succeed with empty event_name"); + } + + #[test] + fn test_log_body_string_value() { + let transformer = Transformer::new(crate::exporters::clickhouse::Compression::None, true); + + let log_record = create_test_log_record("test.event", "hello world"); + let resource_logs = create_test_resource_logs(vec![log_record]); + + let message = Message { + payload: vec![resource_logs], + metadata: None, + request_context: None, + }; + + let (result, _) = transformer.transform(vec![message]); + assert!(result.is_ok(), "Transform should succeed"); + } +} From 47df5bd436226a8d973a17c3d1cb961631ef3461 Mon Sep 17 00:00:00 2001 From: Julian Bright Date: Wed, 21 Jan 2026 20:43:00 -0800 Subject: [PATCH 2/2] Fix format --- src/exporters/clickhouse/transform_logs.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/exporters/clickhouse/transform_logs.rs b/src/exporters/clickhouse/transform_logs.rs index 9635094d..d8371309 100644 --- a/src/exporters/clickhouse/transform_logs.rs +++ b/src/exporters/clickhouse/transform_logs.rs @@ -107,8 +107,8 @@ impl TransformPayload for Transformer { mod tests { use super::*; use crate::exporters::clickhouse::transformer::Transformer; - use opentelemetry_proto::tonic::common::v1::{AnyValue, InstrumentationScope, KeyValue}; use opentelemetry_proto::tonic::common::v1::any_value::Value as AnyValueValue; + use opentelemetry_proto::tonic::common::v1::{AnyValue, InstrumentationScope, KeyValue}; use opentelemetry_proto::tonic::logs::v1::{LogRecord, ScopeLogs}; use opentelemetry_proto::tonic::resource::v1::Resource; @@ -121,14 +121,12 @@ mod tests { body: Some(AnyValue { value: Some(AnyValueValue::StringValue(body.to_string())), }), - attributes: vec![ - KeyValue { - key: "test.attr".to_string(), - value: Some(AnyValue { - value: Some(AnyValueValue::StringValue("test_value".to_string())), - }), - }, - ], + attributes: vec![KeyValue { + key: "test.attr".to_string(), + value: Some(AnyValue { + value: Some(AnyValueValue::StringValue("test_value".to_string())), + }), + }], dropped_attributes_count: 0, flags: 1, trace_id: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], @@ -197,7 +195,10 @@ mod tests { }; let (result, _) = transformer.transform(vec![message]); - assert!(result.is_ok(), "Transform should succeed with empty event_name"); + assert!( + result.is_ok(), + "Transform should succeed with empty event_name" + ); } #[test]