Skip to content

Dashboard and monitoring

Secure the embedded dashboard and use its HTTP and programmatic monitoring APIs.

5 min read
dotnet add package Immediate.Jobs.Dashboard --prerelease

Register the dashboard’s generated API handlers before building the application, then map the embedded UI and JSON/SSE API:

using Immediate.Jobs.Dashboard;

var traceExplorer = new Uri("https://traces.example/");
var logExplorer = new Uri("https://logs.example/");

builder.Services.AddImmediateJobsDashboard(options =>
{
	_ = options.RequireAuthorization("operations");
	_ = options.AddTelemetryLink(
		"View execution trace",
		JobTelemetryLinkKind.Trace,
		context => context.Execution?.ExecutionTraceId is { } traceId
			? new(traceExplorer, $"trace/{traceId}")
			: null
	);
	_ = options.AddTelemetryLink(
		"View execution logs",
		JobTelemetryLinkKind.Logs,
		context => context.Execution is { } execution
			? new(logExplorer,
				$"search?jobId={Uri.EscapeDataString(context.Job.Id)}&attempt={execution.Attempt}")
			: null
	);
	_ = options.AddTelemetryLink(
		"View all retry logs",
		JobTelemetryLinkKind.Logs,
		context => context.Execution is null
			? new(logExplorer, $"search?jobId={Uri.EscapeDataString(context.Job.Id)}")
			: null
	);
});

var app = builder.Build();
app.MapImmediateJobsDashboard("/jobs");

Without RequireAuthorization, every dashboard endpoint is development-only by default and returns 403 in other environments. For a trusted custom development environment, explicitly disable this restriction when mapping the dashboard:

app.MapImmediateJobsDashboard("/jobs", options =>
	_ = options.AllowInAnyEnvironment()
);

Treat the dashboard as an administrative surface: it exposes payloads, errors, identifiers and mutations. Prefer RequireAuthorization whenever the dashboard is available outside a trusted development environment. A named policy applies to UI assets and APIs together and remains authoritative if AllowInAnyEnvironment is also configured.

The UI shows queue/state totals, including skipped work, recent history, jobs and details, recurring schedules, scheduler servers, batches and workflow graphs. Graph views appear only for graph-capable storage.

Dashboard UI

Inspect jobs

The Jobs view lists durable invocations and their current state. Select a job to open its dedicated detail route with payload data and a newest-first, collapsible timeline of retained executions. Each execution records its attempt, state, worker, acquisition/start/completion times, trace/span identifiers and full failure text. Failed jobs offer Retry; scheduled first attempts and delayed retries offer Run now, which moves the existing invocation to Pending without changing its attempt or failure history. Every non-terminal job offers a confirmation-backed Cancel action.

Follow batch workflows

The Batches view visualizes the jobs in a batch and the continuations between them. Progress and workflow nodes distinguish skipped conditional branches from explicitly cancelled work. Select a node to inspect that job without losing the surrounding workflow context. Executing batches offer a confirmation-backed Cancel action from their workflow details.

The Immediate.Jobs dashboard batch workflow graph in light mode, showing succeeded jobs and their continuations
A batch workflow graph with continuation relationships and job details available in place.

AddTelemetryLink adds application-defined destinations to job and execution details. The dashboard evaluates its synchronous URL factory through the job-level or exact-execution telemetry endpoint and supplies a JobTelemetryLinkContext.

ArgumentPurpose
labelUser-facing description of the destination.
kindTrace or Logs; controls how the dashboard identifies the link.
createUrlBuilds the destination from context.Job and optional context.Execution; return null to hide it.

For a job-level request, context.Execution is null and the execution fields on context.Job describe the latest attempt. For an exact-execution request, context.Execution contains the selected retained record; context.Job.Attempt, ExecutionTraceId, ExecutionSpanId and ExecutionStartedAt also represent that same attempt. Use Execution for per-attempt trace/log links, and use Job.Id when a destination should search across every retry.

Factories may return HTTP(S) or dashboard-relative URLs. Other absolute URI schemes are rejected when the endpoint evaluates the link. Return null before an execution trace exists or whenever a destination does not apply to the current record.

HTTP endpoints

All paths below are relative to the mapped prefix.

Method and pathPurpose
GET /api/overviewMonitoring snapshot and storage capabilities.
GET /api/jobsFilter by state, queue, search; skip; take 1–200 (default 50).
GET /api/jobs/{jobId}Latest durable record.
GET /api/jobs/{jobId}/executionsRetained attempts newest first; skip; take 1–200 (default 50).
GET /api/jobs/{jobId}/telemetry-linksConfigured job-level trace/log destinations.
GET /api/jobs/{jobId}/executions/{executionNumber}/telemetry-linksConfigured destinations for one retained execution.
POST /api/jobs/{jobId}/cancelCancel non-terminal work.
POST /api/jobs/{jobId}/retryRetry failed work or run scheduled work now.
GET /api/recurringRecurring schedules.
POST /api/recurring/{name}/triggerMaterialize an immediate invocation.
POST /api/recurring/{name}/pause / resumeChange schedule state.
GET /api/serversWorker heartbeat snapshots.
GET /api/batchesFilter by state, skip, take 1–500 (default 100).
GET /api/batches/{id}Aggregate status.
GET /api/batches/{id}/membersFiltered/paged member status.
GET /api/batches/{id}/graphDependency graph.
POST /api/batches/{id}/cancelCascade-cancel unsettled members.
DELETE /api/batches/{id}Delete a terminal graph.
GET /api/eventsSSE state snapshots at UpdateInterval.
GET /api/batches/{id}/streamSSE status and graph events on change.

Successful cancel, retry, pause, resume and batch mutations return 204; recurring trigger returns 202. A request returns 400 Validation Problem Details for invalid route or paging values, 404 when its job, execution, batch or recurring schedule does not exist (or the provider lacks the required capability), and 409 when a mutation targets a resource whose lifecycle state does not allow the operation.

SSE sends retry: 3000, disables proxy buffering and ends when the request is aborted. It is a poll-backed live view, not a durable event log; clients must refresh after reconnecting.

Programmatic monitoring

Inject scoped IJobMonitor and call GetJobAsync. With a graph provider, inject IJobBatchMonitor and call GetStatusAsync, QueryMembersAsync, or GetGraphAsync. These are read-only contracts suitable for application status endpoints. Custom operational views can query newest-first attempts through IJobStorage.QueryJobExecutionsAsync; the execution history remains with its owning job or batch until that aggregate is deleted or purged.

Monitoring snapshots include only scheduler servers whose last heartbeat is at most two minutes old. SQL providers prune stale server rows on later heartbeats, while Redis expires their hashes.