Skip to content

Commit

Permalink
Add Hangfire instrumentation (open-telemetry#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
fred2u authored Apr 12, 2022
1 parent f45edd7 commit ccc0f42
Show file tree
Hide file tree
Showing 12 changed files with 472 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .github/component_owners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ components:
- ejsmith
src/OpenTelemetry.Instrumentation.GrpcCore/:
- pcwiese
src/OpenTelemetry.Instrumentation.Hangfire/:
- fred2u
src/OpenTelemetry.Instrumentation.MassTransit/:
- alexvaluyskiy
src/OpenTelemetry.Instrumentation.MySqlData/:
Expand All @@ -39,6 +41,8 @@ components:
- ejsmith
test/OpenTelemetry.Instrumentation.GrpcCore.Tests/:
- pcwiese
test/OpenTelemetry.Instrumentation.Hangfire.Tests/:
- fred2u
test/OpenTelemetry.Instrumentation.MassTransit.Tests/:
- alexvaluyskiy
test/OpenTelemetry.Instrumentation.MySqlData.Tests/:
Expand Down
14 changes: 14 additions & 0 deletions opentelemetry-dotnet-contrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Tests", "test\OpenTelemetry.Extensions.Tests\OpenTelemetry.Extensions.Tests.csproj", "{2117F4E3-6612-4E4D-A757-27271EEB7783}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.Hangfire", "src\OpenTelemetry.Instrumentation.Hangfire\OpenTelemetry.Instrumentation.Hangfire.csproj", "{BE5FFBBB-D73F-4071-92F4-F1694881604F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.Hangfire.Tests", "test\OpenTelemetry.Instrumentation.Hangfire.Tests\OpenTelemetry.Instrumentation.Hangfire.Tests.csproj", "{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -335,6 +339,14 @@ Global
{2117F4E3-6612-4E4D-A757-27271EEB7783}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2117F4E3-6612-4E4D-A757-27271EEB7783}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2117F4E3-6612-4E4D-A757-27271EEB7783}.Release|Any CPU.Build.0 = Release|Any CPU
{BE5FFBBB-D73F-4071-92F4-F1694881604F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE5FFBBB-D73F-4071-92F4-F1694881604F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE5FFBBB-D73F-4071-92F4-F1694881604F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE5FFBBB-D73F-4071-92F4-F1694881604F}.Release|Any CPU.Build.0 = Release|Any CPU
{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -386,6 +398,8 @@ Global
{6AE92AAD-CF08-4E60-98EF-A7F762DAAB4D} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{42B3FB71-BB42-46E3-9CEC-56620CB76BD9} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{2117F4E3-6612-4E4D-A757-27271EEB7783} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{BE5FFBBB-D73F-4071-92F4-F1694881604F} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// <copyright file="HangfireInstrumentation.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.Instrumentation.Hangfire.Implementation
{
using System;
using System.Diagnostics;
using System.Reflection;

internal static class HangfireInstrumentation
{
/// <summary>
/// The assembly name.
/// </summary>
internal static readonly AssemblyName AssemblyName = typeof(HangfireInstrumentation).Assembly.GetName();

/// <summary>
/// The activity source name.
/// </summary>
internal static readonly string ActivitySourceName = AssemblyName.Name;

/// <summary>
/// The version.
/// </summary>
internal static readonly Version Version = AssemblyName.Version;

/// <summary>
/// The activity source.
/// </summary>
internal static readonly ActivitySource ActivitySource = new(ActivitySourceName, Version.ToString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// <copyright file="HangfireInstrumentationConstants.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.Instrumentation.Hangfire.Implementation
{
internal static class HangfireInstrumentationConstants
{
public const string JobIdTag = "job.id";
public const string JobCreatedAtTag = "job.createdat";

public const string ActivityName = "JOB";
public const string ActivityKey = "opentelemetry_activity_key";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// <copyright file="HangfireInstrumentationJobFilterAttribute.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.Instrumentation.Hangfire.Implementation
{
using System.Diagnostics;
using global::Hangfire.Common;
using global::Hangfire.Server;

internal class HangfireInstrumentationJobFilterAttribute : JobFilterAttribute, IServerFilter
{
public void OnPerforming(PerformingContext performingContext)
{
// Short-circuit if nobody is listening
if (!HangfireInstrumentation.ActivitySource.HasListeners())
{
return;
}

var activity = HangfireInstrumentation.ActivitySource
.StartActivity(HangfireInstrumentationConstants.ActivityName, ActivityKind.Internal, parentContext: default);

if (activity != null)
{
activity.DisplayName = $"JOB {performingContext.BackgroundJob.Job.Type.Name}.{performingContext.BackgroundJob.Job.Method.Name}";

if (activity.IsAllDataRequested)
{
activity.SetTag(HangfireInstrumentationConstants.JobIdTag, performingContext.BackgroundJob.Id);
activity.SetTag(HangfireInstrumentationConstants.JobCreatedAtTag, performingContext.BackgroundJob.CreatedAt.ToString("O"));
}

performingContext.Items.Add(HangfireInstrumentationConstants.ActivityKey, activity);
}
}

public void OnPerformed(PerformedContext performedContext)
{
// Short-circuit if nobody is listening
if (!HangfireInstrumentation.ActivitySource.HasListeners() || !performedContext.Items.ContainsKey(HangfireInstrumentationConstants.ActivityKey))
{
return;
}

if (performedContext.Items[HangfireInstrumentationConstants.ActivityKey] is Activity activity)
{
if (performedContext.Exception != null)
{
activity.SetStatus(ActivityStatusCode.Error, performedContext.Exception.Message);
}

activity.Dispose();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>OpenTelemetry Hangfire Instrumentation</Description>
<PackageTags>$(PackageTags);Hangfire</PackageTags>
<MinVerTagPrefix>Instrumentation.Hangfire-</MinVerTagPrefix>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Hangfire.Core" Version="[1.7.0,1.8.0)" />
<PackageReference Include="OpenTelemetry.Api" Version="1.2.0-rc4" />
</ItemGroup>
</Project>
62 changes: 62 additions & 0 deletions src/OpenTelemetry.Instrumentation.Hangfire/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Hangfire Instrumentation for OpenTelemetry .NET

[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.Hangfire.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Hangfire)
[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.Hangfire.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Hangfire)

This is an
[Instrumentation Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library),
which instruments
[Hangfire](https://www.nuget.org/packages/Hangfire/)
and collects telemetry about BackgroundJob.

## Steps to enable OpenTelemetry.Instrumentation.Hangfire

### Step 1: Install and configure Hangfire

[Getting Started](https://docs.hangfire.io/en/latest/getting-started/index.html)

### Step 2: Install Hangfire instrumentation Package

Add a reference to the
[`OpenTelemetry.Instrumentation.Hangfire`](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Hangfire)
package. Also, add any other instrumentations & exporters you will need.

```shell
dotnet add package OpenTelemetry.Instrumentation.Hangfire --prerelease
```

### Step 3: Enable Hangfire Instrumentation at application startup

Hangfire instrumentation must be enabled at application startup.

The following example demonstrates adding Hangfire instrumentation to a
console application. This example also sets up the OpenTelemetry Console
exporter, which requires adding the package
[`OpenTelemetry.Exporter.Console`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Console/README.md)
to the application.

```csharp
using OpenTelemetry.Trace;

public class Program
{
public static void Main(string[] args)
{
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddHangfireInstrumentation()
.AddConsoleExporter()
.Build();
}
}
```

For an ASP.NET Core application, adding instrumentation is typically done in
the `ConfigureServices` of your `Startup` class. Refer to [example](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/examples/AspNetCore/Program.cs).

For an ASP.NET application, adding instrumentation is typically done in the
`Global.asax.cs`. Refer to [example](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/examples/AspNet/Global.asax.cs).

## References

* [OpenTelemetry Project](https://opentelemetry.io/)
* [Hangfire Project](https://www.hangfire.io/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// <copyright file="TracerProviderBuilderExtensions.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
{
using System;
using OpenTelemetry.Instrumentation.Hangfire.Implementation;

/// <summary>
/// Extension methods to simplify registering of Hangfire job instrumentation.
/// </summary>
public static class TracerProviderBuilderExtensions
{
/// <summary>
/// Adds Hangfire instrumentation to the tracer provider.
/// </summary>
/// <param name="builder"><see cref="TracerProviderBuilder"/> being configured.</param>
/// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns>
public static TracerProviderBuilder AddHangfireInstrumentation(this TracerProviderBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

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

return builder.AddSource(HangfireInstrumentation.ActivitySourceName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// <copyright file="HangfireFixture.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>

using System;
using Hangfire;
using Hangfire.MemoryStorage;
using Hangfire.Storage;

namespace OpenTelemetry.Instrumentation.Hangfire.Tests
{
public class HangfireFixture : IDisposable
{
public HangfireFixture()
{
GlobalConfiguration.Configuration
.UseMemoryStorage();
this.Server = new BackgroundJobServer();
this.MonitoringApi = JobStorage.Current.GetMonitoringApi();
}

public BackgroundJobServer Server { get; }

public IMonitoringApi MonitoringApi { get; }

public void Dispose()
{
this.Server.Dispose();
}
}
}
Loading

0 comments on commit ccc0f42

Please sign in to comment.