Skip to content

Logs and Debugging

This page documents the structured-logging contract Fleans emits via [LoggerMessage] source generators. For health checks, metrics, and tracing, see Observability.

Fleans uses [LoggerMessage] source-generated logging exclusively — no ILogger.LogInformation(...) extension-method calls. Every state mutation in WorkflowInstance and ActivityInstance emits a structured log entry via a private partial void declared on a partial class. This means:

  • EventIds are stable — filter by EventId, not by message text.
  • Field names are typed and consistent — log queries on WorkflowInstanceId always hit the same field.
  • No format-string drift — the source generator validates the format string against the parameters at compile time.

If you author a custom-task plugin, follow the same pattern (see the adding a BPMN activity convention for the boilerplate).

RangeComponent
1000–1199WorkflowInstance
1070–1079WorkflowInstance — pending events and event sub-processes
1080–1089WorkflowInstance — complex gateway
1090–1099WorkflowInstance — escalation
1100–1109WorkflowInstance — transaction sub-process
1110–1119WorkflowInstance — compensation
1066UserTaskClaim rejection (Warning)
2000–2099ActivityInstance
3000–3099WorkflowInstanceState
3030–3032WorkflowInstance — escalation warnings
4000–4099Event handlers
5000–5099WorkflowEventsPublisher
9000–9099BpmnConverter
10000–10099TimerCallbackGrain

For the full per-EventId deep dive and reserved bands, see docs/plans/2026-02-08-structured-workflow-logging.md.

Every grain call is wrapped in a BeginScope by WorkflowLoggingScopeFilter (an Orleans IIncomingGrainCallFilter). The scope carries these structured fields, propagated automatically across grain boundaries via RequestContext:

FieldTypeSet by
WorkflowIdstringWorkflowInstance
ProcessDefinitionIdstringWorkflowInstance
WorkflowInstanceIdstringWorkflowInstance
ActivityIdstringWorkflowInstance / ActivityInstance
ActivityInstanceIdstringWorkflowInstance / ActivityInstance
VariablesIdstringActivityInstance

In a structured-log backend (Seq, Loki, Datadog), query against these field names directly — they are emitted as JSON properties, not embedded in the message text.

In appsettings.json, you can set per-category log levels using the standard Microsoft.Extensions.Logging configuration. For example, to suppress verbose workflow logging while keeping escalation warnings visible:

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Fleans.Application.Grains.WorkflowInstance": "Warning",
"Microsoft.Orleans": "Warning"
}
}
}

This uses the standard LogLevel configuration — no additional packages or custom filter providers are needed.