Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.PlatformService
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptExport traces from Node or a browser
Axial.Telemetry.JavaScript writes spans through the JavaScript OpenTelemetry API. JavaScript uses different span and context-propagation APIs from the .NET adapter. Both packages consume the same ambient
Axial.Telemetry.Context from core and emit the same Axial attribute names.
Install the packages
dotnet add package Axial.Telemetry.JavaScript
npm install @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http
@opentelemetry/sdk-trace-node pulls in @opentelemetry/context-async-hooks and registers the
AsyncLocalStorageContextManager Node applications need. A browser host installs @opentelemetry/sdk-trace-web
and @opentelemetry/context-zone instead, then registers a zone-based context manager. Axial accepts the API
object; it does not import or configure an SDK for you.
A minimal Node bootstrap registers a provider, an exporter, and the default context manager before any Axial workflow runs:
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const provider = new NodeTracerProvider();
provider.addSpanProcessor(new BatchSpanProcessor(new OTLPTraceExporter()));
provider.register(); // registers the provider and AsyncLocalStorageContextManager
The browser equivalent is new WebTracerProvider() from @opentelemetry/sdk-trace-web with
provider.register({ contextManager: new ZoneContextManager() }) from @opentelemetry/context-zone. Axial then
reads the registered API object in Install the tracer.
Install the tracer
After the host has registered its OpenTelemetry provider and context manager, pass the API object and the application's instrumentation scope name to Axial:
open Fable.Core.JsInterop
open Axial.Telemetry.JavaScript
Otel.installNamed (importAll "@opentelemetry/api") "Checkout.Web"Checkout.Web identifies the application code being traced; it is not the service name or a span name. The JavaScript
SDK separately configures the service resource, while calls such as Otel.trace "checkout.submit" provide span names.
Otel.install remains a shortcut using the default Axial scope, and Otel.installWith accepts an explicitly created
tracer.
Node applications normally use an AsyncLocalStorageContextManager. Browser applications commonly use a zone-based
context manager. Without a context manager, a span can lose its active parent after an awaited boundary.
Trace and tag a workflow
The context API works on .NET and JavaScript. This snippet is schematic: checkout, order, userId,
CheckoutAttributes, tenantId, and CheckoutError.describe are application values defined in the checked
example of the .NET telemetry guide. Only the JavaScript Otel.traceWith adapter and the extra
Axial.Telemetry.JavaScript open differ here; the context calls are identical.
open Axial.Telemetry
open Axial.Telemetry.JavaScript
checkout order
|> Context.withEndUserId userId
|> Context.withAttributes [
Context.attribute CheckoutAttributes.tenantId tenantId
]
|> Otel.traceWith CheckoutError.describe "checkout.submit"Otel.traceWith is the JavaScript counterpart of Activity.traceWith. It records the same Flow outcome, typed-error,
defect, interruption, fiber, runtime-annotation, and ambient-context attributes.
Connect browser and server traces
OpenTelemetry's HTTP instrumentation propagates the W3C traceparent header. To see one trace across a browser and a
.NET backend:
- Register fetch or XMLHttpRequest instrumentation in the browser.
- Allow
traceparentthrough CORS for cross-origin requests. - Configure
propagateTraceHeaderCorsUrlsfor the backend origins. - Enable ASP.NET Core instrumentation on the server.
- Export both applications to the same collector or Aspire-compatible OTLP backend.
The browser workflow span then parents the fetch span, and the server request span continues the same trace. Axial does not manually copy correlation identifiers; the OpenTelemetry SDKs propagate trace context.
Observe fibers
Install defect-only observation at the application edge:
application
|> FiberTelemetry.observeUse FiberTelemetry.observeWithSpans when you need one span per forked fiber. Under Fable, unobserved defects are
reported at deterministic runtime detection points. JavaScript garbage collection does not provide the same finalization
signal used by .NET.
Verify the result
Open your trace backend and confirm that:
- the workflow span has the expected parent;
axial.flow.outcomematches the workflow exit;enduser.idand custom context attributes are present;- defects carry
exception.type,exception.message, andexception.stacktrace; - named fiber spans appear when span-per-fiber observation is enabled.
For the signal model, Aspire walkthrough, metrics, and fiber dumps, read
Trace workflows and inspect them in Aspire. Fiber metrics and System.Diagnostics-based dump events are
provided by the .NET adapter; the core FiberRegistry and rendered dumps remain available on supported Fable hosts.

