diff --git a/src/OpenTelemetry.Instrumentation.Hangfire/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Hangfire/CHANGELOG.md index 6a2d279a67..582b2b0919 100644 --- a/src/OpenTelemetry.Instrumentation.Hangfire/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Hangfire/CHANGELOG.md @@ -5,6 +5,10 @@ * Assemblies are now digitally signed using cosign. ([#4637](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4637)) +* Fixed `NullReferenceException` in the metrics filter when a job's definition + cannot be resolved. + ([#4731](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4731)) + ## 1.16.0-beta.1 Released 2026-Jun-24 diff --git a/src/OpenTelemetry.Instrumentation.Hangfire/Implementation/JobNameFormatter.cs b/src/OpenTelemetry.Instrumentation.Hangfire/Implementation/JobNameFormatter.cs index 565072e923..6805c6fb80 100644 --- a/src/OpenTelemetry.Instrumentation.Hangfire/Implementation/JobNameFormatter.cs +++ b/src/OpenTelemetry.Instrumentation.Hangfire/Implementation/JobNameFormatter.cs @@ -9,8 +9,13 @@ namespace OpenTelemetry.Instrumentation.Hangfire.Implementation; internal static class JobNameFormatter { - internal static string FormatJobName(this Job job) + internal static string FormatJobName(this Job? job) { + if (job is null) + { + return "UNKNOWN"; + } + var sb = new StringBuilder() .Append(job.Type.ToGenericTypeString()) .Append('.') diff --git a/test/OpenTelemetry.Instrumentation.Hangfire.Tests/JobNameFormatterTests.cs b/test/OpenTelemetry.Instrumentation.Hangfire.Tests/JobNameFormatterTests.cs new file mode 100644 index 0000000000..d7eec07215 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.Hangfire.Tests/JobNameFormatterTests.cs @@ -0,0 +1,33 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Hangfire; +using Hangfire.Common; +using OpenTelemetry.Instrumentation.Hangfire.Implementation; + +namespace OpenTelemetry.Instrumentation.Hangfire.Tests; + +public class JobNameFormatterTests +{ + [Fact] + public void FormatJobName_Returns_TypeAndMethod_When_Job_Is_Resolved() + { + var job = Job.FromExpression(x => x.Execute()); + var backgroundJob = new BackgroundJob("job-id", job, DateTime.UtcNow); + + var name = backgroundJob.FormatJobName(); + + Assert.Equal("TestJob.Execute", name); + } + + [Fact] + public void FormatJobName_Returns_Unknown_When_Job_Is_Unresolvable() + { + // null! mimics Hangfire's runtime state for a job whose definition failed to deserialize. + var backgroundJob = new BackgroundJob("job-id", job: null!, DateTime.UtcNow); + + var name = backgroundJob.FormatJobName(); + + Assert.Equal("UNKNOWN", name); + } +}