|
11 | 11 | # define OPENTELEMETRY_ATTRIBUTE_TIMESTAMP_PREVIEW |
12 | 12 |
|
13 | 13 | # include "opentelemetry/exporters/etw/etw_logger_exporter.h" |
| 14 | +# include "opentelemetry/exporters/etw/etw_tracer_exporter.h" |
14 | 15 | # include "opentelemetry/sdk/trace/simple_processor.h" |
15 | 16 |
|
16 | 17 | using namespace OPENTELEMETRY_NAMESPACE; |
@@ -195,4 +196,112 @@ TEST(ETWLogger, LoggerCheckWithTimestampAttributes) |
195 | 196 | logger->Debug("This is a debug log body", opentelemetry::common::MakeAttributes(attribs))); |
196 | 197 | } |
197 | 198 |
|
| 199 | +/** |
| 200 | + * @brief Test that LogRecord created within an active span context |
| 201 | + * inherits TraceId, SpanId, and TraceFlags from the current span. |
| 202 | + * |
| 203 | + * This test verifies the fix for issue #3830 where traceId and spanId |
| 204 | + * were incorrectly reported as all zeros. |
| 205 | + */ |
| 206 | +TEST(ETWLogger, LoggerInheritsTraceContextFromActiveSpan) |
| 207 | +{ |
| 208 | + std::string providerName = kGlobalProviderName; |
| 209 | + |
| 210 | + // Create tracer and logger providers |
| 211 | + exporter::etw::TracerProvider tp; |
| 212 | + exporter::etw::LoggerProvider lp; |
| 213 | + |
| 214 | + auto tracer = tp.GetTracer(providerName); |
| 215 | + auto logger = lp.GetLogger(providerName, "test"); |
| 216 | + |
| 217 | + // Create a span and set it as the active span |
| 218 | + auto span = tracer->StartSpan("TestSpan"); |
| 219 | + auto scope = tracer->WithActiveSpan(span); |
| 220 | + |
| 221 | + // Get the span's trace context for verification |
| 222 | + auto span_context = span->GetContext(); |
| 223 | + |
| 224 | + // Create a log record while the span is active |
| 225 | + auto log_record = logger->CreateLogRecord(); |
| 226 | + ASSERT_NE(log_record, nullptr); |
| 227 | + |
| 228 | + // Cast to ETW LogRecord to access trace context |
| 229 | + auto *etw_record = static_cast<exporter::etw::LogRecord *>(log_record.get()); |
| 230 | + |
| 231 | + // Verify TraceId matches the active span's TraceId |
| 232 | + EXPECT_EQ(etw_record->GetTraceId(), span_context.trace_id()); |
| 233 | + |
| 234 | + // Verify SpanId matches the active span's SpanId |
| 235 | + EXPECT_EQ(etw_record->GetSpanId(), span_context.span_id()); |
| 236 | + |
| 237 | + // Verify TraceFlags match |
| 238 | + EXPECT_EQ(etw_record->GetTraceFlags(), span_context.trace_flags()); |
| 239 | + |
| 240 | + // Emit the log (should not throw) |
| 241 | + EXPECT_NO_THROW(logger->EmitLogRecord(std::move(log_record))); |
| 242 | + |
| 243 | + span->End(); |
| 244 | +} |
| 245 | + |
| 246 | +/** |
| 247 | + * @brief Test that LogRecord created without an active span context |
| 248 | + * has default (invalid/zero) TraceId and SpanId. |
| 249 | + */ |
| 250 | +TEST(ETWLogger, LoggerWithoutActiveSpanHasDefaultTraceContext) |
| 251 | +{ |
| 252 | + std::string providerName = kGlobalProviderName; |
| 253 | + exporter::etw::LoggerProvider lp; |
| 254 | + |
| 255 | + auto logger = lp.GetLogger(providerName, "test"); |
| 256 | + |
| 257 | + // Create a log record without any active span |
| 258 | + auto log_record = logger->CreateLogRecord(); |
| 259 | + ASSERT_NE(log_record, nullptr); |
| 260 | + |
| 261 | + auto *etw_record = static_cast<exporter::etw::LogRecord *>(log_record.get()); |
| 262 | + |
| 263 | + // TraceId and SpanId should be invalid (all zeros) when no span is active |
| 264 | + EXPECT_FALSE(etw_record->GetTraceId().IsValid()); |
| 265 | + EXPECT_FALSE(etw_record->GetSpanId().IsValid()); |
| 266 | + |
| 267 | + EXPECT_NO_THROW(logger->EmitLogRecord(std::move(log_record))); |
| 268 | +} |
| 269 | + |
| 270 | +/** |
| 271 | + * @brief Test that LogRecord timestamp defaults to a valid time (not epoch). |
| 272 | + * |
| 273 | + * This test verifies the fix for issue #3830 where timestamp was |
| 274 | + * incorrectly reported as 1970-01-01 (epoch). |
| 275 | + */ |
| 276 | +TEST(ETWLogger, LoggerTimestampIsNotEpoch) |
| 277 | +{ |
| 278 | + std::string providerName = kGlobalProviderName; |
| 279 | + exporter::etw::LoggerProvider lp; |
| 280 | + |
| 281 | + auto logger = lp.GetLogger(providerName, "test"); |
| 282 | + |
| 283 | + // Capture time before creating log record |
| 284 | + auto before = std::chrono::system_clock::now(); |
| 285 | + |
| 286 | + auto log_record = logger->CreateLogRecord(); |
| 287 | + ASSERT_NE(log_record, nullptr); |
| 288 | + |
| 289 | + // Capture time after creating log record |
| 290 | + auto after = std::chrono::system_clock::now(); |
| 291 | + |
| 292 | + auto *etw_record = static_cast<exporter::etw::LogRecord *>(log_record.get()); |
| 293 | + |
| 294 | + // Get the observed timestamp (which should be initialized to now()) |
| 295 | + std::chrono::system_clock::time_point observed_ts = etw_record->GetObservedTimestamp(); |
| 296 | + |
| 297 | + // Verify observed timestamp is not epoch (1970-01-01) |
| 298 | + EXPECT_GT(observed_ts.time_since_epoch().count(), 0); |
| 299 | + |
| 300 | + // Verify observed timestamp is within the expected range |
| 301 | + EXPECT_GE(observed_ts, before); |
| 302 | + EXPECT_LE(observed_ts, after); |
| 303 | + |
| 304 | + EXPECT_NO_THROW(logger->EmitLogRecord(std::move(log_record))); |
| 305 | +} |
| 306 | + |
198 | 307 | #endif // _WIN32 |
0 commit comments