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
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Instrumentation.Hangfire/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Fix 'FailedToInjectActivityContext' when no ActivityContext exists.
([#2990](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2990))

## 1.12.0-beta.1

Released 2025-Jun-23
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ public void OnCreating(CreatingContext creatingContext)
return;
}

ActivityContext contextToInject = default;
if (Activity.Current != null)
var activity = Activity.Current;
if (activity == null)
{
contextToInject = Activity.Current.Context;
return;
}

var activityContextData = new Dictionary<string, string>();
Propagators.DefaultTextMapPropagator.Inject(new PropagationContext(contextToInject, Baggage.Current), activityContextData, InjectActivityProperties);
Propagators.DefaultTextMapPropagator.Inject(new PropagationContext(activity.Context, Baggage.Current), activityContextData, InjectActivityProperties);
creatingContext.SetJobParameter(HangfireInstrumentationConstants.ActivityContextKey, activityContextData);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

using System.Diagnostics;
using System.Diagnostics.Tracing;
using Hangfire;
using OpenTelemetry.Trace;
using Xunit;
Expand All @@ -26,6 +27,7 @@ public async Task Should_Create_Activity()
using var tel = Sdk.CreateTracerProviderBuilder()
.AddHangfireInstrumentation()
.AddInMemoryExporter(exportedItems)
.SetSampler<AlwaysOnSampler>()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, this test started to fail intermittently when I added a new test. I guess it's related to the static configuration in the HangfireFixture. Adding AlwaysOnSampler seems to fix/workaround it.

.Build();

// Act
Expand Down Expand Up @@ -170,6 +172,27 @@ public async Task Should_Respect_Filter_Option(string filter, bool shouldRecord)
Assert.Equal(shouldRecord, activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
}

[Fact]
public async Task Should_Not_Inject_Invalid_Context()
{
// Arrange
var exportedItems = new List<Activity>();
using var tel = Sdk.CreateTracerProviderBuilder()
.AddHangfireInstrumentation()
.AddInMemoryExporter(exportedItems)
.SetSampler<AlwaysOffSampler>()
.Build();

using var listener = new OpenTelemetryEventListener();

// Act
var jobId = BackgroundJob.Enqueue<TestJob>(x => x.Execute());
await this.WaitJobProcessedAsync(jobId, 5);

// Assert
Assert.All(listener.Messages, args => Assert.NotEqual("FailedToInjectActivityContext", args.EventName));
}

private async Task WaitJobProcessedAsync(string jobId, int timeToWaitInSeconds)
{
var timeout = TimeSpan.FromSeconds(timeToWaitInSeconds);
Expand Down Expand Up @@ -197,4 +220,59 @@ bool Completed()
return !history.All(h => states.Contains(h.StateName));
}
}

private class OpenTelemetryEventListener : EventListener
{
private const string EventSourceName = "OpenTelemetry-Api";

private readonly Queue<EventWrittenEventArgs> events = new();
private readonly AutoResetEvent eventWritten = new(false);
private EventSource? apiEventSource;

public IEnumerable<EventWrittenEventArgs> Messages
{
get
{
if (this.events.Count == 0)
{
this.eventWritten.WaitOne(TimeSpan.FromSeconds(3));
}

while (this.events.Count != 0)
{
yield return this.events.Dequeue();
}
}
}

public override void Dispose()
{
if (this.apiEventSource != null)
{
this.DisableEvents(this.apiEventSource);
}

base.Dispose();
}

protected override void OnEventSourceCreated(EventSource eventSource)
{
if (eventSource.Name == EventSourceName)
{
this.apiEventSource = eventSource;
this.EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All);
}

base.OnEventSourceCreated(eventSource);
}

protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.EventSource.Name == EventSourceName)
{
this.events.Enqueue(eventData);
this.eventWritten.Set();
}
}
}
}
Loading