Skip to content

Commit 63d1efa

Browse files
committed
chore(diagnostics): update netcore example with activity correlation
Update NetCoreSample with an OpenTelemetry based listener to enable the Hangfire activity source. Add initial activity creation along with logging of TraceId to show job correlation in log output. Add background job examples, including error examples.
1 parent 7d926f7 commit 63d1efa

File tree

4 files changed

+254
-70
lines changed

4 files changed

+254
-70
lines changed

NuGet.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
<trustedSigners>
1414
<repository name="nuget.org" serviceIndex="https://api.nuget.org/v3/index.json">
15-
<owners>Microsoft;aspnet;dotnetframework;HangfireIO;xunit;jamesnk;kzu;castleproject;psake;ILRepack;davidebbo;StackExchange;Dapper;brady.holt;dwhelan;raboof;damianh;</owners>
15+
<owners>Microsoft;aspnet;dotnetframework;HangfireIO;xunit;jamesnk;kzu;castleproject;psake;ILRepack;davidebbo;StackExchange;Dapper;brady.holt;dwhelan;raboof;damianh;OpenTelemetry;</owners>
1616
<certificate fingerprint="5a2901d6ada3d18260b9c6dfe2133c95d74b9eef6ae0e5dc334c8454d1477df4" hashAlgorithm="SHA256" allowUntrustedRoot="false" />
1717
<certificate fingerprint="0e5f38f57dc1bcc806d8494f4f90fbcedd988b46760709cbeec6f4219aa6157d" hashAlgorithm="SHA256" allowUntrustedRoot="false" />
1818
<certificate fingerprint="1f4b311d9acc115c8dc8018b5a49e00fce6da8e2855f9f014ca6f34570bc482d" hashAlgorithm="SHA256" allowUntrustedRoot="false" />

samples/NetCoreSample/NetCoreSample.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
<ItemGroup>
1717
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
1818
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
19+
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
20+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
21+
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
1922
</ItemGroup>
2023

2124
</Project>

samples/NetCoreSample/Program.cs

+125-11
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,43 @@
11
using System;
22
using System.Data;
3+
using System.Diagnostics;
34
using System.Threading;
45
using System.Threading.Tasks;
56
using Hangfire;
67
using Hangfire.Annotations;
78
using Hangfire.Client;
89
using Hangfire.Common;
10+
using Hangfire.Diagnostics;
11+
using Hangfire.InMemory;
912
using Hangfire.Server;
1013
using Hangfire.SqlServer;
1114
using Hangfire.States;
15+
using Microsoft.AspNetCore.Mvc.Infrastructure;
16+
using Microsoft.Extensions.Configuration;
1217
using Microsoft.Extensions.DependencyInjection;
1318
using Microsoft.Extensions.DependencyInjection.Extensions;
1419
using Microsoft.Extensions.Hosting;
1520
using Microsoft.Extensions.Logging;
21+
using OpenTelemetry.Trace;
1622

1723
namespace NetCoreSample
1824
{
1925
class Program
2026
{
27+
// To use in-memory store instead of database:
28+
// dotnet run -- --UseInMemory true
29+
30+
// To show trace console exporter output:
31+
// dotnet run -- --TraceConsoleExporter true
32+
33+
public static readonly ActivitySource ActivitySource = new ActivitySource(nameof(NetCoreSample));
34+
2135
static async Task Main(string[] args)
2236
{
23-
var host = new HostBuilder()
24-
.ConfigureLogging(x => x.AddConsole().SetMinimumLevel(LogLevel.Information))
37+
var host = Host.CreateDefaultBuilder(args)
38+
.ConfigureLogging(x => x
39+
.AddSimpleConsole()
40+
.SetMinimumLevel(LogLevel.Information))
2541
.ConfigureServices((hostContext, services) =>
2642
{
2743
services.Configure<HostOptions>(option =>
@@ -49,19 +65,40 @@ static async Task Main(string[] args)
4965
services.TryAddSingleton<IBackgroundJobStateChanger>(x => new CustomBackgroundJobStateChanger(
5066
new BackgroundJobStateChanger(x.GetRequiredService<IJobFilterProvider>())));
5167

52-
services.AddHangfire((provider, configuration) => configuration
68+
var useInMemory = hostContext.Configuration.GetValue<bool>("UseInMemory");
69+
services.AddHangfire((provider, configuration) => {
70+
configuration
5371
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
54-
.UseSimpleAssemblyNameTypeSerializer()
55-
.UseSqlServerStorage(
56-
@"Server=.\;Database=Hangfire.Sample;Trusted_Connection=True;",
57-
provider.GetRequiredService<SqlServerStorageOptions>()));
72+
.UseSimpleAssemblyNameTypeSerializer();
73+
if (useInMemory) {
74+
configuration.UseInMemoryStorage();
75+
}
76+
else
77+
{
78+
configuration.UseSqlServerStorage(
79+
@"Server=.\;Database=Hangfire.Sample;Trusted_Connection=True;",
80+
provider.GetRequiredService<SqlServerStorageOptions>());
81+
}
82+
});
5883

5984
services.AddHostedService<RecurringJobsService>();
85+
services.AddHostedService<BackgroundJobsService>();
6086
services.AddHangfireServer(options =>
6187
{
6288
options.StopTimeout = TimeSpan.FromSeconds(15);
6389
options.ShutdownTimeout = TimeSpan.FromSeconds(30);
6490
});
91+
92+
var traceConsoleExporter = hostContext.Configuration.GetValue<bool>("TraceConsoleExporter");
93+
services.AddOpenTelemetry()
94+
.WithTracing(tracing => {
95+
tracing.AddSource(DiagnosticHeaders.DefaultListenerName);
96+
tracing.AddSource(nameof(NetCoreSample));
97+
if (traceConsoleExporter)
98+
{
99+
tracing.AddConsoleExporter();
100+
}
101+
});
65102
})
66103
.Build();
67104

@@ -123,24 +160,35 @@ internal class RecurringJobsService : BackgroundService
123160
{
124161
private readonly IBackgroundJobClient _backgroundJobs;
125162
private readonly IRecurringJobManager _recurringJobs;
126-
private readonly ILogger<RecurringJobScheduler> _logger;
163+
private readonly ILogger<RecurringJobsService> _logger;
164+
private readonly ILoggerFactory _loggerFactory;
127165

128166
public RecurringJobsService(
129167
[NotNull] IBackgroundJobClient backgroundJobs,
130168
[NotNull] IRecurringJobManager recurringJobs,
131-
[NotNull] ILogger<RecurringJobScheduler> logger)
169+
[NotNull] ILogger<RecurringJobsService> logger,
170+
ILoggerFactory loggerFactory)
132171
{
133172
_backgroundJobs = backgroundJobs ?? throw new ArgumentNullException(nameof(backgroundJobs));
134173
_recurringJobs = recurringJobs ?? throw new ArgumentNullException(nameof(recurringJobs));
135174
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
175+
_loggerFactory = loggerFactory;
136176
}
137177

138178
protected override Task ExecuteAsync(CancellationToken stoppingToken)
139179
{
140180
try
141181
{
142-
_recurringJobs.AddOrUpdate("seconds", () => Console.WriteLine("Hello, seconds!"), "*/15 * * * * *");
143-
_recurringJobs.AddOrUpdate("minutely", () => Console.WriteLine("Hello, world!"), Cron.Minutely);
182+
_logger.LogInformation("Creating recurring jobs");
183+
184+
_recurringJobs.AddOrUpdate("seconds", () => Hello("seconds"), "*/15 * * * * *");
185+
186+
using (var activity = Program.ActivitySource.StartActivity("enqueue"))
187+
{
188+
_logger.LogInformation("Creating job minutely (hello world), trace_id={ActivityTraceId}", activity.TraceId);
189+
_recurringJobs.AddOrUpdate("minutely", () => Hello("world"), Cron.Minutely);
190+
}
191+
144192
_recurringJobs.AddOrUpdate("hourly", () => Console.WriteLine("Hello"), "25 15 * * *");
145193
_recurringJobs.AddOrUpdate("neverfires", () => Console.WriteLine("Can only be triggered"), "0 0 31 2 *");
146194

@@ -161,5 +209,71 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken)
161209

162210
return Task.CompletedTask;
163211
}
212+
213+
public void Hello(string name)
214+
{
215+
Console.WriteLine($"Hello, {name}!");
216+
var logger = _loggerFactory.CreateLogger<RecurringJobsService>();
217+
logger.LogInformation("Hello, {Name}! trace_id={ActivityTraceId}", name, Activity.Current?.TraceId);
218+
}
219+
}
220+
221+
internal class BackgroundJobsService : BackgroundService
222+
{
223+
private readonly IBackgroundJobClient _backgroundJobs;
224+
private readonly ILogger _logger;
225+
private readonly ILoggerFactory _loggerFactory;
226+
227+
public BackgroundJobsService(
228+
[NotNull] IBackgroundJobClient backgroundJobs,
229+
[NotNull] ILogger<BackgroundJobsService> logger,
230+
ILoggerFactory loggerFactory)
231+
{
232+
_backgroundJobs = backgroundJobs ?? throw new ArgumentNullException(nameof(backgroundJobs));
233+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
234+
_loggerFactory = loggerFactory;
235+
}
236+
237+
protected override Task ExecuteAsync(CancellationToken stoppingToken)
238+
{
239+
try
240+
{
241+
_logger.LogInformation("Creating backgriound jobs");
242+
243+
using (var activity = Program.ActivitySource.StartActivity("enqueue"))
244+
{
245+
_logger.LogInformation("Creating job 10, trace_id={ActivityTraceId}", activity.TraceId);
246+
var jobId1 = _backgroundJobs.Enqueue(() => Job(10));
247+
}
248+
using (var activity = Program.ActivitySource.StartActivity("schedule"))
249+
{
250+
_logger.LogInformation("Scheduling job 20, continue with 30, trace_id={ActivityTraceId}", activity.TraceId);
251+
var jobId2 = _backgroundJobs.Schedule(() => Job(20), TimeSpan.FromSeconds(30));
252+
var jobId3 = _backgroundJobs.ContinueJobWith(jobId2, () => Job(30));
253+
}
254+
using (var activity = Program.ActivitySource.StartActivity("error"))
255+
{
256+
_logger.LogInformation("Scheduling error job 40, trace_id={ActivityTraceId}", activity.TraceId);
257+
var jobId4 = _backgroundJobs.Schedule(() => Job(40), TimeSpan.FromSeconds(60));
258+
}
259+
}
260+
catch (Exception e)
261+
{
262+
_logger.LogError(e, "An exception occurred while creating recurring jobs.");
263+
}
264+
265+
return Task.CompletedTask;
266+
}
267+
268+
public void Job(int counter) {
269+
Console.WriteLine("Hello, job {0}!", counter);
270+
var logger = _loggerFactory.CreateLogger<BackgroundJobsService>();
271+
logger.LogInformation("Hello, job {Counter} trace_id={ActivityTraceId}", counter, Activity.Current?.TraceId);
272+
if (counter == 40)
273+
{
274+
throw new InvalidOperationException("Counter 40 is invalid.");
275+
}
276+
}
164277
}
278+
165279
}

0 commit comments

Comments
 (0)