BackgroundJobs.Diagnostics.Hangfire 1.0.0

dotnet add package BackgroundJobs.Diagnostics.Hangfire --version 1.0.0
                    
NuGet\Install-Package BackgroundJobs.Diagnostics.Hangfire -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BackgroundJobs.Diagnostics.Hangfire" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BackgroundJobs.Diagnostics.Hangfire" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="BackgroundJobs.Diagnostics.Hangfire" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BackgroundJobs.Diagnostics.Hangfire --version 1.0.0
                    
#r "nuget: BackgroundJobs.Diagnostics.Hangfire, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BackgroundJobs.Diagnostics.Hangfire@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BackgroundJobs.Diagnostics.Hangfire&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=BackgroundJobs.Diagnostics.Hangfire&version=1.0.0
                    
Install as a Cake Tool

BackgroundJobs.Diagnostics

Application Insights for background jobs in .NET.

Deep observability, diagnostics, and a built-in dashboard for your background jobs — execution history, retry & exception intelligence, slow-job detection, health scores, resource profiling, argument redaction, correlation tracking and OpenTelemetry — with durable storage and minimal setup.

NuGet Downloads CI License: MIT .NET

builder.Services.AddJobDiagnostics();   // capture pipeline + store
app.UseJobDiagnosticsDashboard();        // dashboard at /job-diagnostics

That's the whole setup. Restart, run a job, and open /job-diagnostics.

Status — early preview (0.1.0-preview). The core pipeline, durable storage (SQLite / SQL Server / PostgreSQL, or any EF Core provider), the Hangfire and BackgroundService adapters, OpenTelemetry spans and the dashboard are implemented. Alerting, replay, dead-job rules and the Quartz / ABP / broker adapters are on the roadmap. APIs may change before 1.0.


Contents


Why

When a background job fails at 3am, most teams have a log line and not much else. BackgroundJobs.Diagnostics answers the questions you actually have — automatically:

Question Answer
Which jobs failed, and why? Failed view + exception intelligence (category, failing method, probable cause)
How many retries? Per-attempt retry history
What arguments were used? Captured, redacted argument snapshot
How long did it take — and is it degrading? Duration vs. an online baseline, with slow-job flagging
Which request triggered it? Correlation / trace propagation
Is this job healthy? A 0–100 health score per job
Where can I see it all? A built-in dashboard at /job-diagnostics

Design priorities: never slow down or break a job, redact sensitive data by default, and install in two lines.

Install

# Everything you need to get started (core + BackgroundService adapter + dashboard)
dotnet add package BackgroundJobs.Diagnostics

# Durable storage (pick one)
dotnet add package BackgroundJobs.Diagnostics.Storage.Sqlite
dotnet add package BackgroundJobs.Diagnostics.Storage.SqlServer
dotnet add package BackgroundJobs.Diagnostics.Storage.PostgreSql

# Hangfire integration
dotnet add package BackgroundJobs.Diagnostics.Hangfire

Preview packages are published as prerelease — enable "include prerelease" in your IDE, or append --prerelease to dotnet add package.

Packages

Package Purpose
BackgroundJobs.Diagnostics Meta-package: Core + BackgroundServices + Dashboard
BackgroundJobs.Diagnostics.Core Framework-agnostic capture pipeline, domain model, in-memory store, redaction, baselines, anomaly + health scoring
BackgroundJobs.Diagnostics.BackgroundServices InstrumentedBackgroundService base + TrackAsync(...) for manual / loop jobs
BackgroundJobs.Diagnostics.Hangfire Hangfire server-filter adapter + worker-health probe
BackgroundJobs.Diagnostics.Dashboard Self-contained web dashboard (no build step, no CDN)
BackgroundJobs.Diagnostics.Storage.EntityFrameworkCore Durable EF Core store (provider-agnostic base)
BackgroundJobs.Diagnostics.Storage.Sqlite SQLite storage provider
BackgroundJobs.Diagnostics.Storage.SqlServer SQL Server storage provider
BackgroundJobs.Diagnostics.Storage.PostgreSql PostgreSQL storage provider (Npgsql)

Quick start

using BackgroundJobs.Diagnostics.Core.DependencyInjection;
using BackgroundJobs.Diagnostics.Core.Options;
using BackgroundJobs.Diagnostics.BackgroundServices;
using BackgroundJobs.Diagnostics.Dashboard;
using BackgroundJobs.Diagnostics.Storage.Sqlite;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddJobDiagnostics(options =>
    {
        options.ServiceName = "billing-api";
        options.Capture.Arguments = ArgumentCaptureMode.Sampled;   // 10% sampled by default
        options.Security.RedactProperties("password", "ssn");      // on top of safe defaults
    })
    .UseSqlite("Data Source=jobdiagnostics.db")                    // durable storage
    .AddBackgroundServices();

var app = builder.Build();

app.UseJobDiagnosticsDashboard();   // → /job-diagnostics
app.Run();

Instrumenting jobs

A recurring BackgroundService

Derive from InstrumentedBackgroundService and every tick is captured automatically (duration, exceptions, profiling, health). Throwing records a failure without killing the loop.

public sealed class CreateInvoiceJob(IJobDiagnostics diag) : InstrumentedBackgroundService(diag)
{
    protected override TimeSpan Interval => TimeSpan.FromMinutes(1);

    protected override async Task RunAsync(IJobExecutionScope scope, CancellationToken ct)
    {
        scope.AddTimelineEvent("Loading customer", TimelineEventKind.Custom);
        await DoWorkAsync(ct);
    }
}

// Program.cs
builder.Services.AddHostedService<CreateInvoiceJob>();

Any unit of work

Wrap an arbitrary operation with TrackAsync — useful inside an existing IHostedService, a message consumer, or a one-off task.

await diagnostics.TrackAsync("ReconcileLedger", async scope =>
{
    scope.AddDependency(new DependencyCall { System = "SqlServer", Type = DependencyType.Sql });
    await ledger.ReconcileAsync();
});

Hangfire

builder.Services.AddJobDiagnostics().AddHangfire();

builder.Services.AddHangfire((sp, config) => config
    .UseSqlServerStorage(connectionString)
    .UseJobDiagnostics(sp));   // attaches the diagnostics server filter

Hangfire job lifecycle, retry counts, arguments, correlation and server (worker) health are captured automatically.

Dashboard

app.UseJobDiagnosticsDashboard() mounts a self-contained UI at /job-diagnostics — no build step, no CDN, no external assets:

  • Overview — throughput, success rate, p95 duration, failures and worker health
  • Running / All executions / Failed / Slow / Retried — searchable, paginated tables
  • Incidents and Workers
  • Execution detail — arguments (redacted), exception intelligence, timeline, dependencies, resource profile
  • Light / dark theme and selectable time range
app.UseJobDiagnosticsDashboard(options =>
{
    options.BasePath = "/admin/jobs";
    options.AuthorizationPolicy = "JobDiagnosticsAdmin";   // require a policy
    options.ReadOnly = true;
});

Security: in Production the dashboard fails closed — it is inaccessible unless you set an AuthorizationPolicy. In Development it is open for convenience.

Storage

The default store is in-memory (great for development; data is lost on restart). For durable storage, add a provider package and select it on the builder:

builder.Services.AddJobDiagnostics()
    .UseSqlite("Data Source=jobdiagnostics.db")
    // .UseSqlServer("Server=...;Database=Diagnostics;Trusted_Connection=True;TrustServerCertificate=True")
    // .UsePostgreSql("Host=localhost;Database=diagnostics;Username=postgres;Password=...")
    .AddBackgroundServices();

Any other EF Core provider (MySQL, Oracle, …) works through the generic hook:

builder.Services.AddJobDiagnostics()
    .UseEntityFrameworkCore(db => db.UseOracle(connectionString))
    .AddBackgroundServices();

The schema is created on first run. To manage it yourself (migrations / DBA script), disable it:

.UseSqlite(conn, o => o.AutoCreateSchema = false)

Configuration

All options are set via AddJobDiagnostics(options => …).

Option Default Description
ServiceName "default" Logical service name stamped on records and spans
Capture.Arguments Sampled None · Sampled · Always
Capture.ArgumentSampleRate 0.10 Fraction of executions that keep arguments
Capture.MaxArgumentBytes 16384 Size cap for captured arguments
Capture.Profiling OnSlowOrFailed Off · OnSlowOrFailed · Sampled · Always
Capture.Timeline / Capture.Dependencies true Capture timeline events / dependency calls
Security.RedactProperties(...) safe defaults Add sensitive property names to mask
Security.RedactPattern(regex) JWT, PAN Add value patterns to mask
SlowJob.Sigma 3 Flag if duration > mean + Sigma·σ (with a percentile guard)
SlowJob.WarmupSamples 30 Samples needed before slow detection activates
Retention.KeepExecutions 30 days Executions older than this are pruned
Pipeline.ChannelCapacity 10000 Bounded capture buffer; events drop (never block) when full
EnableOpenTelemetry true Emit job-execution spans

Security & redaction

Background-job arguments often contain PII (emails, tokens, card numbers). Redaction is on by default and runs on a background thread before anything is persisted — sensitive data never reaches the store.

options.Security.RedactProperties("password", "ssn", "authorization", "cardNumber");
options.Security.RedactPattern(@"\b\d{13,19}\b");   // PAN-like numbers

Out of the box it masks common sensitive keys (password, token, secret, apikey, connectionstring, …) and patterns for JWTs and long digit sequences.

OpenTelemetry

Each execution emits an Activity span on the BackgroundJobs.Diagnostics source. Register it with your existing OpenTelemetry pipeline to export job traces to Jaeger, Tempo, Zipkin, Application Insights, or any OTLP backend:

services.AddOpenTelemetry().WithTracing(tracing => tracing
    .AddSource("BackgroundJobs.Diagnostics")
    .AddOtlpExporter());

It works standalone too — no OpenTelemetry pipeline is required.

How it works

 Job thread ──(one non-blocking write)──▶ bounded channel ──▶ background dispatcher ──▶ store
                                                                     │
                                          redact · baseline · score · exception intel
  • Never slows down or breaks a job. The hot path is a single non-blocking write to a bounded channel. Under extreme load, diagnostic events are dropped (and counted) — never jobs. If the store is down, jobs keep running.
  • Online baselines. Mean/variance (Welford) and EWMA rates are maintained per job signature, so slow-job detection and health scoring are O(1) and never scan history.
  • Pluggable. IJobDiagnosticsStore, IExceptionAnalyzer, ISlowJobDetector, IHealthScorer and IWorkerProbe are all swappable.

Roadmap

  • Alerting (Slack / Teams / email / webhook) with dedup & throttling
  • Job replay & snapshots
  • Dead-job rules engine and incident grouping
  • Automatic SQL / HTTP / Redis dependency capture
  • Adapters for Quartz.NET, ABP, and message-broker consumers (RabbitMQ / Kafka / MassTransit)

Building from source

git clone https://github.com/Osama94/BackgroundJobs.Diagnostics.git
cd BackgroundJobs.Diagnostics

dotnet build  -c Release
dotnet test   -c Release
dotnet run --project samples/SampleApi   # → http://localhost:5080/job-diagnostics

Requires the .NET 8 SDK.

Contributing

Issues and pull requests are welcome. Please open an issue to discuss substantial changes before submitting a PR, and make sure dotnet build and dotnet test pass.

License

MIT © Osama Ahmed

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 119 6/10/2026

Early preview. Core capture pipeline, durable storage (SQLite/SQL Server/PostgreSQL + any EF Core provider), Hangfire and BackgroundService adapters, OpenTelemetry spans, and a built-in dashboard. APIs may change before 1.0.