Skip to Content
Next — You're reading the unreleased preview docs. APIs and pages may change before the next release.Switch to the current docs →
How-To GuidesEffectEnabling OpenTelemetry for an Effect Agent

Enabling OpenTelemetry for an Effect Agent

The golem-otlp-exporter is a built-in Golem plugin that exports agent telemetry to an OTLP-compatible collector over OTLP/HTTP. Enable the plugin in golem.yaml; do not add a JavaScript OpenTelemetry exporter to the component.

@golemcloud/effect-golem automatically connects Effect’s logger and tracer to the Golem host. Consequently, normal Effect.log*, Effect.withSpan, and Effect.annotateCurrentSpan calls are captured by the plugin without an application-provided logging or tracing Layer.

Enable the Plugin

Add the plugin to the Effect component that should emit telemetry:

components: my-app:service: plugins: - name: golem-otlp-exporter version: "1.5.8" parameters: endpoint: "http://localhost:4318" signals: "traces,logs,metrics"

Plugin Parameters

ParameterRequiredDescription
endpointYesOTLP collector base URL, such as http://localhost:4318
signalsNoComma-separated traces, logs, and/or metrics; defaults to traces
headersNoComma-separated key=value HTTP headers, such as x-api-key=secret
service-name-modeNoagent-id (default) or agent-type

Deploy the updated application:

golem deploy --yes

New agents created from that component use the configured exporter. Existing plugin activation can also be managed per agent with golem agent activate-plugin and golem agent deactivate-plugin.

Add Effect Spans and Logs

Use Effect v4 APIs rather than mechanically translating the TypeScript SDK’s golem:api/context span handles. Effect.withSpan scopes the host span around the Effect and finishes it on success, failure, or interruption. Initial attributes belong in the withSpan options; add attributes discovered during execution with Effect.annotateCurrentSpan.

import { Effect, Schema } from "effect"; import { defineAgent, method } from "@golemcloud/effect-golem"; export const TracedAgent = defineAgent({ name: "TracedAgent", mode: "durable", id: { instanceName: Schema.String, }, methods: { doTracedWork: method({ input: { taskName: Schema.String }, success: Schema.String, }), }, }).implement({ init: ({ instanceName }) => Effect.succeed(instanceName), methods: (instanceName) => ({ doTracedWork: ({ taskName }) => Effect.gen(function* () { yield* Effect.logInfo(`processing: ${taskName}`).pipe( Effect.annotateLogs({ instanceName, taskName }), ); return "traced"; }).pipe( Effect.withSpan("process-task", { attributes: { task: taskName }, }), ), }), });

Import the implemented agent module from src/main.ts so its top-level registration runs:

import "./traced-agent.js";

When an attribute is only known after the span starts, annotate the current span inside its scoped Effect:

const processTask = Effect.gen(function* () { const queue = "priority"; yield* Effect.annotateCurrentSpan({ queue, retryable: true }); yield* Effect.logInfo("task accepted").pipe(Effect.annotateLogs({ queue })); }).pipe(Effect.withSpan("process-task"));

The SDK converts Effect span attribute values to the string-valued attributes supported by golem:api/context@1.5.0. Effect log annotations and log spans are rendered with the active host trace and span IDs and emitted through wasi:logging; including logs in the plugin’s signals forwards them to the collector.

What Gets Exported

Traces

Golem creates invocation and host-operation spans automatically. The Effect SDK chains Effect.withSpan spans under the invocation’s host span, and Golem propagates trace context for supported inbound HTTP and RPC paths. Failed scoped Effects mark their host spans as errors.

Logs

Use Effect’s logging APIs inside handlers:

const logRequest = Effect.gen(function* () { yield* Effect.logInfo("request received"); yield* Effect.logDebug("cache lookup").pipe( Effect.annotateLogs({ cacheKey: "item-42" }), Effect.withLogSpan("lookup"), ); });

Prefer these over the lower-level Logging.log(...) SDK API so log annotations and Effect log spans are retained.

Metrics

When metrics is included in signals, the exporter sends Golem runtime metrics including:

MetricTypeDescription
golem.invocation.countCounterAgent method invocations
golem.invocation.duration_nsCounterInvocation duration
golem.invocation.fuel_consumedCounterFuel consumed by invocations
golem.invocation.pending_countCounterQueued invocation requests, not current queue depth
golem.host_call.countCounterInternal host calls
golem.log.countCounterEmitted log entries
golem.memory.initial_bytesGaugeInitially allocated memory
golem.memory.total_bytesGaugeTotal allocated memory
golem.memory.growth_bytesCounterMemory growth since start
golem.component.size_bytesGaugeComponent size
golem.error.countCounterRecorded errors
golem.interruption.countCounterInterrupt requests
golem.exit.countCounterProcess exit signals
golem.restart.countCounterFresh state creations
golem.resources.createdCounterInternal resources created
golem.resources.droppedCounterInternal resources dropped
golem.resources.activeGaugeActive internal resources
golem.update.success_countCounterSuccessful updates
golem.update.failure_countCounterFailed updates
golem.transaction.committedCounterCommitted remote transactions
golem.transaction.rolled_backCounterRolled-back remote transactions
golem.snapshot.size_bytesCounterSnapshot size
golem.oplog_processor.lagGaugeOplog processor delivery lag

Metrics include service.name, golem.agent.id, golem.component.id, and golem.component.version attributes.

These are emitted OTLP names. A collector or Prometheus backend can normalize dots to underscores and add suffixes; use the backend’s observed names when writing queries, not as the exporter contract.

Export Semantics

  • Durable Start, End, and Cancelled metadata drives span lifecycle. Long-lived spans retain their origin trace across invocations; failed or retrying attempts do not prematurely close the logical span. Logs use their recorded trace context.
  • Stream summaries use bounded event/outcome metric labels and aggregate item counts; they do not emit a span per item. Active resource and memory values are gauges, and metric labels avoid resource IDs and payload values.
  • In agent-type service-name mode, service.name excludes constructor parameters and phantom instance IDs; golem.agent.id remains the full identity.
  • Collector export is best effort. Accepted source state advances when a send fails, and traces, logs, and metrics are still attempted independently. Exactly-once plugin batch delivery is not exactly-once collector delivery.
  • Fork/revert cannot retract telemetry already accepted by a collector. Source deletion does not provide a terminal signal, so no successful close is invented. Switching plugin instances can lose open-span state; continuity is deferred to GOL-667.

Local Observability Stack

The Golem repository includes an OTLP Collector, Jaeger, Prometheus, Loki, and Grafana setup:

docker compose -f docker-examples/otlp-collector/docker-compose.yml up -d

It exposes OTLP/HTTP on port 4318, Jaeger on port 16686, Prometheus on port 9090, and Grafana on port 3000. Point the plugin’s endpoint at http://localhost:4318.

Per-Environment Configuration

Use an environment preset when collector configuration differs by environment. The reserved app-env:production name applies only to the production environment and does not become the default custom preset for local deployments:

components: my-app:service: plugins: - name: golem-otlp-exporter version: "1.5.8" parameters: endpoint: "http://localhost:4318" signals: "traces,logs,metrics" presets: app-env:production: pluginsMergeMode: replace plugins: - name: golem-otlp-exporter version: "1.5.8" parameters: endpoint: "https://otel.prod.example.com:4318" headers: "x-api-key={{ OTLP_API_KEY }}" signals: "traces,logs,metrics"

Key Constraints

  • Keep OTLP exporter setup in golem.yaml; @golemcloud/effect-golem has no OTLP or plugin installation helper.
  • Use Effect.withSpan and Effect.annotateCurrentSpan; there is no public Tracing.startSpan(...) wrapper.
  • Use Effect.logInfo, Effect.logDebug, and Effect.annotateLogs; do not invent Logging.info(...) or another SDK logger.
  • Do not manually finish an Effect span. Its scoped lifetime is managed by Effect.withSpan.
  • Do not provide Logging.layer or Tracing.layer in an agent. The SDK dispatcher installs both.
  • Do not add @effect/opentelemetry, a Node OpenTelemetry SDK, or a second OTLP exporter inside the QuickJS WebAssembly component.
  • Keep the generated effect and @golemcloud/effect-golem versions aligned, and do not edit files under golem-temp/.