Skip to content

Commit

Permalink
Add option to record exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
vitor-pinto-maersk committed Oct 20, 2022
1 parent 70930c9 commit d3f5488
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// <copyright file="HangfireInstrumentationOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

namespace OpenTelemetry.Trace;

/// <summary>
/// Options for hangfire jobs instrumentation.
/// </summary>
public class HangfireInstrumentationOptions
{
/// <summary>
/// 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.
/// </remarks>
public bool RecordException { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ namespace OpenTelemetry.Instrumentation.Hangfire.Implementation;
using global::Hangfire.Common;
using global::Hangfire.Server;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;

internal class HangfireInstrumentationJobFilterAttribute : JobFilterAttribute, IServerFilter, IClientFilter
{
private readonly HangfireInstrumentationOptions options;

public HangfireInstrumentationJobFilterAttribute(HangfireInstrumentationOptions options)
{
this.options = options;
}

public void OnPerforming(PerformingContext performingContext)
{
// Short-circuit if nobody is listening
Expand Down Expand Up @@ -75,6 +83,11 @@ public void OnPerformed(PerformedContext performedContext)
activity.SetStatus(ActivityStatusCode.Error, performedContext.Exception.Message);
}

if (performedContext.Exception != null && this.options.RecordException)
{
activity.RecordException(performedContext.Exception);
}

activity.Dispose();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace OpenTelemetry.Trace;

using System;
using OpenTelemetry.Instrumentation.Hangfire.Implementation;
using OpenTelemetry.Internal;

Expand All @@ -33,7 +34,25 @@ public static TracerProviderBuilder AddHangfireInstrumentation(this TracerProvid
{
Guard.ThrowIfNull(builder);

Hangfire.GlobalJobFilters.Filters.Add(new HangfireInstrumentationJobFilterAttribute());
return builder.AddHangfireInstrumentation(null);
}

/// <summary>
/// Adds Hangfire instrumentation to the tracer provider.
/// </summary>
/// <param name="builder"><see cref="TracerProviderBuilder"/> being configured.</param>
/// <param name="configureHangfireInstrumentationOptions">Callback action for configuring <see cref="HangfireInstrumentationOptions"/>.</param>
/// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns>
public static TracerProviderBuilder AddHangfireInstrumentation(
this TracerProviderBuilder builder,
Action<HangfireInstrumentationOptions> configureHangfireInstrumentationOptions)
{
Guard.ThrowIfNull(builder);

var options = new HangfireInstrumentationOptions();
configureHangfireInstrumentationOptions?.Invoke(options);

Hangfire.GlobalJobFilters.Filters.Add(new HangfireInstrumentationJobFilterAttribute(options));

return builder.AddSource(HangfireInstrumentation.ActivitySourceName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@ public async Task Should_Create_Activity_With_Status_Error_When_Job_Failed()
Assert.Equal(ActivityKind.Internal, activity.Kind);
Assert.Equal(ActivityStatusCode.Error, activity.Status);
Assert.NotNull(activity.StatusDescription);
Assert.Empty(activity.Events);
}

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

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

// Assert
Assert.Single(exportedItems, i => i.GetTagItem("job.id") as string == jobId);
var activity = exportedItems.Single(i => i.GetTagItem("job.id") as string == jobId);
Assert.Contains("JOB TestJob.ThrowException", activity.DisplayName);
Assert.Equal(ActivityKind.Internal, activity.Kind);
Assert.Equal(ActivityStatusCode.Error, activity.Status);
Assert.NotNull(activity.StatusDescription);
Assert.Single(activity.Events, evt => evt.Name == "exception");
}

private async Task WaitJobProcessedAsync(string jobId, int timeToWaitInSeconds)
Expand Down

0 comments on commit d3f5488

Please sign in to comment.