Skip to content

Commit

Permalink
Add Filter to HangfireInstrumentationOptions (#1440)
Browse files Browse the repository at this point in the history
  • Loading branch information
gao-artur authored Dec 14, 2023
1 parent 97bd0ae commit 06b8bb7
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
OpenTelemetry.Trace.HangfireInstrumentationOptions
OpenTelemetry.Trace.HangfireInstrumentationOptions.DisplayNameFunc.get -> System.Func<Hangfire.BackgroundJob!, string!>!
OpenTelemetry.Trace.HangfireInstrumentationOptions.DisplayNameFunc.set -> void
OpenTelemetry.Trace.HangfireInstrumentationOptions.Filter.get -> System.Func<Hangfire.BackgroundJob!, bool>?
OpenTelemetry.Trace.HangfireInstrumentationOptions.Filter.set -> void
OpenTelemetry.Trace.HangfireInstrumentationOptions.HangfireInstrumentationOptions() -> void
OpenTelemetry.Trace.HangfireInstrumentationOptions.RecordException.get -> bool
OpenTelemetry.Trace.HangfireInstrumentationOptions.RecordException.set -> void
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Instrumentation.Hangfire/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
options management
([#1442](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1442))

* Add Filter to HangfireInstrumentationOptions.
([#1440](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1440))

## 1.5.0-beta.1

Released 2023-Jun-23
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class HangfireInstrumentationOptions
/// Gets or sets a value indicating whether the exception will be recorded as ActivityEvent or not.
/// </summary>
/// <remarks>
/// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md.
/// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-spans.md.
/// </remarks>
public bool RecordException { get; set; }

Expand All @@ -27,4 +27,23 @@ public class HangfireInstrumentationOptions
/// Defaults to <c>{backgroundJob.Job.Type.Name}.{backgroundJob.Job.Method.Name}</c>.
/// </remarks>
public Func<BackgroundJob, string> DisplayNameFunc { get; set; } = HangfireInstrumentation.DefaultDisplayNameFunc;

/// <summary>
/// Gets or sets a filter function that determines whether or not to
/// collect telemetry about the the <see cref="BackgroundJob"/> being executed.
/// </summary>
/// <remarks>
/// <b>Notes:</b>
/// <list type="bullet">
/// <item>The first parameter passed to the filter function is <see cref="BackgroundJob"/> being executed.</item>
/// <item>The return value for the filter:
/// <list type="number">
/// <item>If filter returns <see langword="true" />, the command is
/// collected.</item>
/// <item>If filter returns <see langword="false" /> or throws an
/// exception, the command is <b>NOT</b> collected.</item>
/// </list></item>
/// </list>
/// </remarks>
public Func<BackgroundJob, bool>? Filter { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ public void OnPerforming(PerformingContext performingContext)

if (activity.IsAllDataRequested)
{
try
{
if (this.options.Filter?.Invoke(performingContext.BackgroundJob) == false)
{
activity.IsAllDataRequested = false;
activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
return;
}
}
catch (Exception)
{
activity.IsAllDataRequested = false;
activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
return;
}

activity.SetTag(HangfireInstrumentationConstants.JobIdTag, performingContext.BackgroundJob.Id);
activity.SetTag(HangfireInstrumentationConstants.JobCreatedAtTag, performingContext.BackgroundJob.CreatedAt.ToString("O"));
}
Expand Down
77 changes: 76 additions & 1 deletion src/OpenTelemetry.Instrumentation.Hangfire/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public class Program
{
public static void Main(string[] args)
{
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
using var tracerProvider = Sdk
.CreateTracerProviderBuilder()
.AddHangfireInstrumentation()
.AddConsoleExporter()
.Build();
Expand All @@ -58,6 +59,22 @@ For an ASP.NET application, adding instrumentation is typically done in the

## Advanced configuration

This instrumentation can be configured to change the default behavior by using
`HangfireInstrumentationOptions`.

```csharp
using var tracerProvider = Sdk
.CreateTracerProviderBuilder()
.AddHangfireInstrumentation(options =>
{
options.DisplayNameFunc = job => $"JOB {job.Id}";
options.Filter = job => job.Id == "Filter this job";
options.RecordException = true;
})
.AddConsoleExporter()
.Build();
```

When used with
[`OpenTelemetry.Extensions.Hosting`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Extensions.Hosting/README.md),
all configurations to `HangfireInstrumentationOptions`
Expand All @@ -79,6 +96,64 @@ services.AddOpenTelemetry()
.AddConsoleExporter());
```

### RecordException

Configures a value indicating whether the exception will be recorded as
ActivityEvent or not. See
[Semantic Conventions for Exceptions on Spans](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-spans.md)

```csharp
using var tracerProvider = Sdk
.CreateTracerProviderBuilder()
.AddHangfireInstrumentation(options =>
{
options.RecordException = true;
})
.AddConsoleExporter()
.Build();
```

### DisplayNameFunc

This option allows changing activity display name.

```C#
using var tracerProvider = Sdk
.CreateTracerProviderBuilder()
.AddHangfireInstrumentation(options =>
{
options.DisplayNameFunc = job => $"JOB {job.Id}";
})
.AddConsoleExporter()
.Build();
```

If not configured the default is

```C#
$"JOB {BackgroundJob.Job.Type.Name}.{BackgroundJob.Job.Method.Name}"
```

### Filter

This option can be used to filter out activities based on the `BackgroundJob`
being executed. The `Filter` function should return `true` if the telemetry is
to be collected, and `false` if it should not.

The following code snippet shows how to use `Filter` to filter out traces for
job with a specified job id.

```csharp
using var tracerProvider = Sdk
.CreateTracerProviderBuilder()
.AddHangfireInstrumentation(options =>
{
options.Filter = job => job.Id == "Filter this job";
})
.AddConsoleExporter()
.Build();
```

## References

* [OpenTelemetry Project](https://opentelemetry.io/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,43 @@ public async Task Should_Create_Activity_With_Custom_DisplayName()
Assert.Equal(ActivityKind.Internal, activity.Kind);
}

[Theory]
[InlineData("null", true)]
[InlineData("true", true)]
[InlineData("false", false)]
[InlineData("throw", false)]
public async Task Should_Respect_Filter_Option(string filter, bool shouldRecord)
{
// Arrange
Action<HangfireInstrumentationOptions> configure = filter switch
{
"null" => options => options.Filter = null,
"true" => options => options.Filter = _ => true,
"false" => options => options.Filter = _ => false,
"throw" => options => options.Filter = _ => throw new Exception("Filter throws exception"),
_ => throw new ArgumentOutOfRangeException(nameof(filter), filter, "Unexpected value"),
};

var processedItems = new List<Activity>();
var activityProcessor = new ProcessorMock<Activity>(onStart: processedItems.Add);

using var tel = Sdk.CreateTracerProviderBuilder()
.AddHangfireInstrumentation(configure)
.AddProcessor(activityProcessor)
.Build();

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

// Assert
Assert.Single(processedItems);
var activity = processedItems.First();

Assert.Equal(shouldRecord, activity.IsAllDataRequested);
Assert.Equal(shouldRecord, activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
}

private async Task WaitJobProcessedAsync(string jobId, int timeToWaitInSeconds)
{
var timeout = DateTime.Now.AddSeconds(timeToWaitInSeconds);
Expand Down
28 changes: 28 additions & 0 deletions test/OpenTelemetry.Instrumentation.Hangfire.Tests/ProcessorMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System;

namespace OpenTelemetry.Instrumentation.Hangfire.Tests;

public class ProcessorMock<T> : BaseProcessor<T>
{
private readonly Action<T>? onStart;
private readonly Action<T>? onEnd;

public ProcessorMock(Action<T>? onStart = null, Action<T>? onEnd = null)
{
this.onStart = onStart;
this.onEnd = onEnd;
}

public override void OnStart(T data)
{
this.onStart?.Invoke(data);
}

public override void OnEnd(T data)
{
this.onEnd?.Invoke(data);
}
}

0 comments on commit 06b8bb7

Please sign in to comment.