Flow Studio
Structured Logs
INodeLogger is a scoped structured logger pre-seeded with nodeId, executionId, and tenantId. Executors call it directly from ctx.Observability.Logger. All log entries are sent to Loki and streamed to the Observer Panel via SignalR.
INodeLogger Interface
public interface INodeLogger
{
void LogTrace (string message, params object[] args);
void LogDebug (string message, params object[] args);
void LogInformation(string message, params object[] args);
void LogWarning (string message, params object[] args);
void LogError (string message, params object[] args);
void LogError (Exception ex, string message, params object[] args);
}
Pre-seeded Structured Fields
Every log entry emitted via INodeLogger automatically includes these structured fields — executors do not need to add them manually:
// Fields added automatically to every INodeLogger entry:
{
"nodeId" : "node-approval-1",
"executionId": "exec-abc-123",
"tenantId" : "tenant-001",
"processId" : "proc-xyz",
"nodeType" : "ApprovalNode",
"traceId" : "abc123def456..." // OTel TraceId — links log to trace
}
Log Levels and When to Use Each
| Level | Use For | Default In Production? |
|---|---|---|
| Trace | Very detailed diagnostics — loop iterations, intermediate values | Off |
| Debug | Developer diagnostics — parameter values, branch decisions | Off |
| Information | Normal operation milestones — task submitted, result received | On |
| Warning | Unexpected but recoverable — fallback triggered, retry scheduled | On |
| Error | Operation failed — exception details, failure reason | On |
Auto-emitted Lifecycle Logs
BaseNodeExecutor automatically emits these lifecycle log entries — executors do not need to log these themselves:
// Auto-emitted by BaseNodeExecutor:
LogInformation("Node execution started");
LogInformation("Node execution completed in {DurationMs}ms", durationMs);
LogError(ex, "Node execution failed after {RetryCount} retries", retryCount);
Do not create a new ILogger inside an executor. Always use
ctx.Observability.Logger. A manually created logger will not have the pre-seeded structured fields and will not route to the SignalR sink — entries will appear in the backend log only, not in the Observer Panel.