Skip to content

Commit 99990dc

Browse files
committed
Allow passing task id when dispaching bgtasks
add a sample improve logs
1 parent 9fa754d commit 99990dc

17 files changed

+185
-45
lines changed

Directory.Packages.props

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
<PackageVersion Include="OpenTelemetry" Version="$(OtelVersion)" />
1717
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="$(OtelVersion)" />
1818
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="$(OtelVersion)" />
19-
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="$(OtelVersion)" />
19+
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
20+
<PackageVersion Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
21+
<PackageVersion Include="OpenTelemetry.Exporter.Zipkin" Version="1.9.0" />
22+
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
23+
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
2024
<PackageVersion Include="System.Collections.Immutable" Version="$(MExVersion)" />
2125
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
2226
<PackageVersion Include="System.Threading.Channels" Version="8.0.0" />

DotNetToolkit.sln

+11-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
2020
global.json = global.json
2121
EndProjectSection
2222
EndProject
23-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DCA.Extensions.BackgroundTask.MediatR", "src\DCA.Extensions.BackgroundTask.MediatR\DCA.Extensions.BackgroundTask.MediatR.csproj", "{57D9573C-487B-4B98-85C1-29F00A8DACC9}"
23+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DCA.Extensions.BackgroundTask.MediatR", "src\DCA.Extensions.BackgroundTask.MediatR\DCA.Extensions.BackgroundTask.MediatR.csproj", "{57D9573C-487B-4B98-85C1-29F00A8DACC9}"
2424
EndProject
25-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DCA.Extensions.BackgroundTask.MediatR.Test", "test\DCA.Extensions.BackgroundTask.MediatR.Test\DCA.Extensions.BackgroundTask.MediatR.Test.csproj", "{4AE336AA-2BB5-4073-A26F-20E946071D96}"
25+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DCA.Extensions.BackgroundTask.MediatR.Test", "test\DCA.Extensions.BackgroundTask.MediatR.Test\DCA.Extensions.BackgroundTask.MediatR.Test.csproj", "{4AE336AA-2BB5-4073-A26F-20E946071D96}"
26+
EndProject
27+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{32B7D449-4244-46CD-BB71-A3C8DCF692B5}"
28+
EndProject
29+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "samples\Sample\Sample.csproj", "{0ADD59DD-13C7-4AD5-94BB-4DA73498DA71}"
2630
EndProject
2731
Global
2832
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -50,6 +54,10 @@ Global
5054
{4AE336AA-2BB5-4073-A26F-20E946071D96}.Debug|Any CPU.Build.0 = Debug|Any CPU
5155
{4AE336AA-2BB5-4073-A26F-20E946071D96}.Release|Any CPU.ActiveCfg = Release|Any CPU
5256
{4AE336AA-2BB5-4073-A26F-20E946071D96}.Release|Any CPU.Build.0 = Release|Any CPU
57+
{0ADD59DD-13C7-4AD5-94BB-4DA73498DA71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58+
{0ADD59DD-13C7-4AD5-94BB-4DA73498DA71}.Debug|Any CPU.Build.0 = Debug|Any CPU
59+
{0ADD59DD-13C7-4AD5-94BB-4DA73498DA71}.Release|Any CPU.ActiveCfg = Release|Any CPU
60+
{0ADD59DD-13C7-4AD5-94BB-4DA73498DA71}.Release|Any CPU.Build.0 = Release|Any CPU
5361
EndGlobalSection
5462
GlobalSection(SolutionProperties) = preSolution
5563
HideSolutionNode = FALSE
@@ -60,6 +68,7 @@ Global
6068
{9D38CDEB-6995-4568-A93B-2F5325386BFC} = {DCD02B36-60E6-440C-96EF-05A6B590497C}
6169
{57D9573C-487B-4B98-85C1-29F00A8DACC9} = {90B56126-AFE5-436B-9F6E-6F5980D681E2}
6270
{4AE336AA-2BB5-4073-A26F-20E946071D96} = {DCD02B36-60E6-440C-96EF-05A6B590497C}
71+
{0ADD59DD-13C7-4AD5-94BB-4DA73498DA71} = {32B7D449-4244-46CD-BB71-A3C8DCF692B5}
6372
EndGlobalSection
6473
GlobalSection(ExtensibilityGlobals) = postSolution
6574
SolutionGuid = {55D950D2-91EF-4D4D-8D5E-C5A41E46B1BF}

readme.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ dotnet add package DCA.Extensions.BackgroundTask --prerelease
1313
```csharp
1414
// Register background task components
1515
services.AddBackgroundTask();
16-
// Register background dependency
16+
// Register dependency of background tasks
1717
services.AddScoped<ServiceA>();
1818

1919
// dispatch a task to run in background
2020
var dispatcher = serviceProvider.GetRequiredService<IBackgroundTaskDispatcher>();
2121
await dispatcher.DispatchAsync<ServiceA>(async dep =>
2222
{
2323
await dep.DoWork();
24-
});
24+
});
2525

2626
```
2727

samples/Sample/Program.cs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using DCA.Extensions.BackgroundTask;
2+
using OpenTelemetry.Metrics;
3+
using OpenTelemetry.Trace;
4+
5+
6+
var builder = WebApplication.CreateBuilder(args);
7+
builder.Services
8+
.AddOpenTelemetry()
9+
.WithTracing(b => b
10+
.AddSource("DCA.DotNetToolkit")
11+
.AddAspNetCoreInstrumentation()
12+
.AddZipkinExporter())
13+
.WithMetrics(b => b
14+
.AddMeter("DCA.DotNetToolkit")
15+
.AddPrometheusExporter());
16+
17+
18+
builder.Services.AddBackgroundTask();
19+
builder.Services.AddScoped<ServiceA>();
20+
21+
var app = builder.Build();
22+
app.UseOpenTelemetryPrometheusScrapingEndpoint();
23+
24+
app.MapPost("/fast-api", async (IBackgroundTaskDispatcher background) =>
25+
{
26+
for (int i = 0; i < 100; i++)
27+
{
28+
await background.DispatchAsync<ServiceA>(async serviceA => await serviceA.DoWork());
29+
}
30+
return Results.Ok();
31+
});
32+
33+
app.Run();
34+
35+
public class ServiceA
36+
{
37+
public async Task DoWork()
38+
{
39+
await Task.Delay(10 * 1000);
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"applicationUrl": "http://localhost:5000",
7+
"environmentVariables": {
8+
"ASPNETCORE_ENVIRONMENT": "Development",
9+
"OTEL_TRACES_SAMPLER": "always_on",
10+
"OTEL_SERVICE_NAME": "sample"
11+
}
12+
}
13+
}
14+
}

samples/Sample/Sample.csproj

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="OpenTelemetry.Exporter.Console" />
11+
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" />
12+
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" />
13+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
14+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\..\src\DCA.Extensions.BackgroundTask\DCA.Extensions.BackgroundTask.csproj" />
19+
</ItemGroup>
20+
21+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}

samples/Sample/appsettings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}

samples/Sample/test.http

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# For more info on HTTP files go to https://aka.ms/vs/httpfile
2+
3+
POST http://localhost:5000/fast-api
4+
Content-Type: application/json
5+
6+
{
7+
}

src/DCA.Extensions.BackgroundTask.MediatR/MediatorDispatcherExtensions.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@ public static class MediatorDispatcherExtensions
1212
/// <typeparam name="TTask">Task payload type</typeparam>
1313
/// <param name="dispatcher"></param>
1414
/// <param name="task">Task payload</param>
15+
/// <param name="id">Task id</param>
1516
/// <param name="channel">Channel name</param>
1617
/// <param name="startNow">Should this tast start now</param>
1718
/// <returns></returns>
1819
public static ValueTask DispatchAsync<TTask>(
1920
this IBackgroundTaskDispatcher dispatcher,
2021
TTask task,
22+
string? id = null,
2123
string? channel = null,
2224
bool startNow = true) where TTask : IRequest
2325
{
2426
var context = new MediatorTaskContext<TTask>(
2527
dispatcher.ServiceProvider,
2628
task
2729
);
28-
return dispatcher.DispatchAsync(Execute, context, channel, startNow);
30+
return dispatcher.DispatchAsync(Execute, context, id, channel, startNow);
2931

3032
static ValueTask Execute(MediatorTaskContext<TTask> context)
3133
{

src/DCA.Extensions.BackgroundTask/BackgroundTask.cs

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public interface IBackgroundTaskContext;
88

99
public interface IBackgroundTask : IThreadPoolWorkItem
1010
{
11+
string? Id { get; }
1112
bool Started { get; }
1213
void Start();
1314
ValueTask WaitToCompleteAsync();
@@ -60,10 +61,14 @@ internal async ValueTask ExecuteAsync()
6061
{
6162
activity?.SetTag("task.id", id);
6263
}
64+
65+
66+
Logs.BackgroundTaskExecuting(logger, id);
6367
try
6468
{
6569
await taskDelegate(context).ConfigureAwait(false);
6670
activity?.SetStatus(ActivityStatusCode.Ok);
71+
Logs.BackgroundTaskExecuted(logger, id);
6772
}
6873
catch (Exception ex)
6974
{

src/DCA.Extensions.BackgroundTask/BackgroundTaskChannel.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public async ValueTask DispatchAsync(IBackgroundTask task, bool startNow)
9191
};
9292
Metrics.CounterInflightBackgroundTasks.Add(1, tagList);
9393
Metrics.CounterDispatchedTasks.Add(1);
94-
Logs.BackgroundTaskDispatched(_logger, Key);
94+
Logs.BackgroundTaskDispatched(_logger, Key, task.Id);
9595

9696
if (startNow)
9797
{

src/DCA.Extensions.BackgroundTask/BackgroundTaskChannelReader.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ private async Task ReadLoop()
4141
Metrics.CounterProcessedTasks.Add(1, tagList);
4242
if (!task.Started)
4343
{
44-
Logs.TaskStarting(logger, key);
44+
Logs.TaskStarting(logger, key, task.Id);
4545
task.Start();
4646
}
4747
var vt = task.WaitToCompleteAsync();
4848
if (!vt.IsCompletedSuccessfully)
4949
{
50-
Logs.TaskWaitingToComplete(logger, key);
50+
Logs.TaskWaitingToComplete(logger, key, task.Id);
5151
await vt.ConfigureAwait(false);
5252
}
5353
Checkpoint = task;

src/DCA.Extensions.BackgroundTask/BackgroundTaskDispatcher.cs

+5-28
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,25 @@
44

55
namespace DCA.Extensions.BackgroundTask;
66

7-
/// <summary>
8-
/// Background task dispatcher
9-
/// </summary>
10-
public interface IBackgroundTaskDispatcher
11-
{
12-
/// <summary>
13-
/// An <see cref="IServiceProvider"/> instance, used to create a scope for each task execution
14-
/// </summary>
15-
IServiceProvider ServiceProvider { get; }
16-
17-
/// <summary>
18-
/// Dispatch a background task
19-
/// </summary>
20-
/// <typeparam name="TContext">Type of task context</typeparam>
21-
/// <param name="taskDelegate">Task delegate</param>
22-
/// <param name="taskContext">Task context</param>
23-
/// <param name="channel">Channel key</param>
24-
/// <param name="startNow">Should the task start immediately</param>
25-
/// <returns></returns>
26-
ValueTask DispatchAsync<TContext>(
27-
Func<TContext, ValueTask> taskDelegate,
28-
TContext taskContext,
29-
string? channel = null,
30-
bool startNow = true) where TContext : IBackgroundTaskContext;
31-
}
32-
337
public sealed class BackgroundTaskDispatcher(
348
FrozenDictionary<string, BackgroundTaskChannel> channels,
35-
ILogger<BackgroundTaskDispatcher> logger,
9+
//ILogger<BackgroundTaskDispatcher> logger,
10+
ILoggerFactory loggerFactory,
3611
IServiceProvider serviceProvider)
3712
: IBackgroundTaskDispatcher
3813
{
14+
private readonly ILogger logger = loggerFactory.CreateLogger<IBackgroundTask>();
3915
public IServiceProvider ServiceProvider { get; } = serviceProvider;
4016

4117
public ValueTask DispatchAsync<TContext>(
4218
Func<TContext, ValueTask> taskDelegate,
4319
TContext taskContext,
20+
string? id = null,
4421
string? channel = null,
4522
bool startNow = true) where TContext : IBackgroundTaskContext
4623
{
4724
var task = new BackgroundTask<TContext>(
48-
null,
25+
id ?? Guid.NewGuid().ToString(),
4926
taskDelegate,
5027
taskContext,
5128
logger,

src/DCA.Extensions.BackgroundTask/BackgroundTaskDispatcherExtensions.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,22 @@ public static class BackgroundTaskDispatcherExtensions
1515
/// <typeparam name="TDependency">Dependency type</typeparam>
1616
/// <param name="dispatcher"></param>
1717
/// <param name="taskDelegate">Task delegate</param>
18+
/// <param name="id">Task id</param>
1819
/// <param name="channel">Channel name</param>
1920
/// <param name="startNow">Should this tast start now</param>
2021
/// <returns></returns>
2122
public static ValueTask DispatchAsync<TDependency>(
2223
this IBackgroundTaskDispatcher dispatcher,
2324
Func<TDependency, ValueTask> taskDelegate,
25+
string? id = null,
2426
string? channel = null,
2527
bool startNow = true) where TDependency : notnull
2628
{
2729
var context = new DelegateBackgroundTaskContext<TDependency>(
2830
dispatcher.ServiceProvider,
2931
taskDelegate
3032
);
31-
return dispatcher.DispatchAsync(Execute, context, channel, startNow);
33+
return dispatcher.DispatchAsync(Execute, context, id, channel, startNow);
3234

3335
static ValueTask Execute(DelegateBackgroundTaskContext<TDependency> context)
3436
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace DCA.Extensions.BackgroundTask;
2+
3+
/// <summary>
4+
/// Background task dispatcher
5+
/// </summary>
6+
public interface IBackgroundTaskDispatcher
7+
{
8+
/// <summary>
9+
/// An <see cref="IServiceProvider"/> instance, used to create a scope for each task execution
10+
/// </summary>
11+
IServiceProvider ServiceProvider { get; }
12+
13+
/// <summary>
14+
/// Dispatch a background task
15+
/// </summary>
16+
/// <typeparam name="TContext">Type of task context</typeparam>
17+
/// <param name="taskDelegate">Task delegate</param>
18+
/// <param name="taskContext">Task context</param>
19+
/// <param name="id">Task id</param>
20+
/// <param name="channel">Channel key</param>
21+
/// <param name="startNow">Should the task start immediately</param>
22+
/// <returns></returns>
23+
ValueTask DispatchAsync<TContext>(
24+
Func<TContext, ValueTask> taskDelegate,
25+
TContext taskContext,
26+
string? id = null,
27+
string? channel = null,
28+
bool startNow = true) where TContext : IBackgroundTaskContext;
29+
}

0 commit comments

Comments
 (0)