Skip to content

Observability Guide

Workflows are much easier to trust when you can see how they behave in production.

In Orchestrix, observability is mainly built around:

  • structured execution results
  • lifecycle hooks
  • optional built-in logging
  • plugin-based extensions

Start with the result object

Every execution returns a FlowResult, and every step produces a StepResult.

That already gives you a lot of useful information:

  • final flow status
  • total duration
  • which step failed
  • how many attempts were made
  • per-step durations

This is the first observability layer of the library: execution is visible by design.

Built-in logging

Orchestrix can enable a built-in logger through flow configuration:

ts
const flow = create("observable-flow", {
  logging: true
});

You can also configure logging options when needed through the flow config.

Hooks for operational telemetry

Hooks are the main way to connect Orchestrix to your logging, metrics, and monitoring stack.

ts
const flow = create("my-flow", {
  hooks: {
    onFlowStart: ({ flowName }) => {
      logger.info({ flowName }, "flow started");
    },
    onStepComplete: ({ flowName, stepName, result }) => {
      logger.info({ flowName, stepName, result }, "step completed");
    },
    onFlowFail: ({ flowName, result }) => {
      logger.error({ flowName, result }, "flow failed");
    }
  }
});

Metrics example

Hooks are a good fit for emitting counters and histograms:

ts
const flow = create("metrics-flow", {
  hooks: {
    onFlowComplete: ({ flowName, result }) => {
      flowCounter.inc({ flow_name: flowName, status: result.status });
      flowDuration.observe({ flow_name: flowName }, result.durationMs / 1000);
    },
    onStepFail: ({ flowName, stepName }) => {
      stepFailureCounter.inc({ flow_name: flowName, step_name: stepName });
    }
  }
});

Tracing pattern

You can also use hooks to integrate with tracing systems by storing spans or correlation values in context.

ts
const flow = create("traced-flow", {
  hooks: {
    onFlowStart: ({ flowName, context }) => {
      const span = tracer.startSpan(flowName);
      context.set("root-span", span);
    },
    onStepStart: ({ stepName, context }) => {
      const rootSpan = context.get("root-span");
      const stepSpan = tracer.startSpan(stepName, { parent: rootSpan });
      context.set(`span:${stepName}`, stepSpan);
    },
    onStepComplete: ({ stepName, context }) => {
      const stepSpan = context.get<{ end: () => void }>(`span:${stepName}`);
      stepSpan?.end();
    }
  }
});

Correlation

One of the most useful observability practices is to include a correlation ID in the flow input or context and propagate it through logs and service calls.

That makes it much easier to trace one business operation across multiple systems.

Best Practices

  • Use structured logs.
  • Emit metrics from hooks, not from step internals when possible.
  • Log or store the final FlowResult for important workflows.
  • Add correlation identifiers early in execution.
  • Treat compensation events as important operational signals worth monitoring.

Released under the MIT License.