Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/OpenTelemetry.Exporter.Geneva/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
* Updated OpenTelemetry core component version(s) to `1.17.0`.
([#4773](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4773))

* Updated ETW manifest and payload in `EtwDataTransport`
with synthetic payload so that the runtime-generated .NET
ETW manifest matches the actual payload.
([#4729](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4729)

## 1.16.0

Released 2026-Jul-09
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public EtwDataTransport(string providerName)

public void Send(byte[] data, int size)
{
this.eventSource.SendEvent((int)EtwEventSource.EtwEventId.TraceEvent, data, size);
this.eventSource.SendEvent(data, size);
}

public bool IsEnabled()
Expand Down Expand Up @@ -54,25 +54,44 @@ public enum EtwEventId

#pragma warning disable CA1822 // Mark members as static

/// <summary>
/// Dummy _data_ field is present so that when <see cref="SendEvent"/> is called, the event has at least one
/// parameter with <see cref="EtwEventId.TraceEvent"/> and single data object, it matches the runtime metadata
/// in ETW manifest.
/// </summary>
/// <param name="data">
/// Dummy placeholder for <see cref="EventSource"/> reflection-based ETW manifest generation.
Comment thread
martincostello marked this conversation as resolved.
/// In the ETW manifest for .NET, <c>byte[]</c> payload is always prepended with a synthetic <c>length</c>
/// field.
/// </param>
[Event((int)EtwEventId.TraceEvent, Version = 1, Level = EventLevel.Informational)]
public void InformationalEvent()
public void InformationalEvent(byte[] data)
{
}

#pragma warning restore CA1822 // Mark members as static

/// <summary>
/// Writes given raw data to ETW buffer.
/// Two fields are written to conform to .NET notation: length and data.
/// A separate length field isn't explicitly needed, since raw ETW payload already comes with buffer length
/// data, but the convention for .NET ETW libraries requires that field.
/// </summary>
/// <param name="data">Buffer with data to be sent.</param>
/// <param name="size">How many bytes of that buffer to send.</param>
[NonEvent]
#if NET
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "WriteEventCore is safe when eventData object is a primitive type, which it is in this case.")]
#endif
public unsafe void SendEvent(int eventId, byte[] data, int size)
public unsafe void SendEvent(byte[] data, int size)
{
var dataDesc = stackalloc EventData[1];
var dataDesc = stackalloc EventData[2];
fixed (byte* ptr = data)
{
dataDesc[0].DataPointer = (IntPtr)ptr;
dataDesc[0].Size = size;
this.WriteEventCore(eventId, 1, dataDesc);
dataDesc[0].DataPointer = (IntPtr)(&size);
Comment thread
martincostello marked this conversation as resolved.
dataDesc[0].Size = 4;
dataDesc[1].DataPointer = (IntPtr)ptr;
dataDesc[1].Size = size;
this.WriteEventCore((int)EtwEventId.TraceEvent, 2, dataDesc);
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions test/OpenTelemetry.Exporter.Geneva.Tests/EtwCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace OpenTelemetry.Exporter.Geneva.Tests;

/// <summary>
/// Collection definition for ETW tests.
/// <para/>
/// Since ETW is a system-global (not even process-global) resource, these tests shouldn't be run in parallel.
/// </summary>
[CollectionDefinition(Name, DisableParallelization = true)]
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
public sealed class EtwCollection
#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
{
public const string Name = "ETW collection";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System.Diagnostics.Tracing;
using OpenTelemetry.Exporter.Geneva.Transports;

namespace OpenTelemetry.Exporter.Geneva.Tests.Internal.Transports;

[Collection(EtwCollection.Name)]
public class EtwDataTransportTests
{
[Fact]
public void TestEtwMessageRoundtrip()
{
var randomProviderName = "x" + Guid.NewGuid().ToString("N");
using var listener = new TestEventSourceListener(randomProviderName);
using var transport = new EtwDataTransport(randomProviderName);
byte[] payload = [1, 2, 3, 4];

transport.Send(payload, payload.Length);

Assert.Single(listener.Events);
var theEvent = listener.Events[0];
Assert.NotNull(theEvent.Payload);
Assert.Single(theEvent.Payload);

var rawPayload = theEvent.Payload[0] as byte[];
Assert.NotNull(rawPayload);
Assert.Equal(payload, rawPayload);
}

/// <summary>
/// Test event source listener.
/// </summary>
private sealed class TestEventSourceListener : EventListener
{
/// <summary>
/// Event source name to automatically attach to.
/// </summary>
private readonly string eventSourceName;

/// <summary>
/// Initializes a new instance of the <see cref="TestEventSourceListener"/> class.
/// </summary>
/// <param name="eventSourceName">The name of the event source to attach to.</param>
public TestEventSourceListener(string eventSourceName)
{
this.eventSourceName = eventSourceName;
}

/// <summary>
/// Gets events emitted by ETW.
/// </summary>
public List<EventWrittenEventArgs> Events { get; } = new();

/// <inheritdoc />
protected override void OnEventSourceCreated(EventSource eventSource)
{
base.OnEventSourceCreated(eventSource);
if (string.Equals(eventSource.Name, this.eventSourceName, StringComparison.Ordinal))
{
this.EnableEvents(eventSource, EventLevel.LogAlways);
}
}

/// <inheritdoc/>
protected override void OnEventWritten(EventWrittenEventArgs eventData) => this.Events.Add(eventData);
}
}