Enabling OpenTelemetry for an Effect Agent

SkillMonitoring & ops

Lets your agent send tracing, logging, and metric data from Effect-based apps to an observability backend.

Use Enabling OpenTelemetry for an Effect Agent in Claude, ChatGPT or Ahel Desktop

Free. Sign in, add Enabling OpenTelemetry for an Effect Agent and connect your AI. About a minute.

Also: Claude Code · Cursor · Codex

Then ask your AI: use the Enabling OpenTelemetry for an Effect Agent skill

Details

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Enabling OpenTelemetry for an Effect AgentStart free
About this skill

Enabling the built-in OpenTelemetry (OTLP) exporter for Effect-based Golem agents. Use when exporting traces, logs, and metrics or adding Effect spans and structured log annotations.

What this skill tells your AI

The instructions your AI receives, as published by golemcloud/golem in golem-skills/skills/effect/golem-enable-otlp-effect/SKILL.md and read by ahel’s review.

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.0"
        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_countCounterPending invocations
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 database transactions
golem_transaction_rolled_backCounterRolled-back database 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.

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 presets when collector configuration differs by environment:

components:
  my-app:service:
    plugins:
      - name: golem-otlp-exporter
        version: "1.5.0"
        parameters:
          endpoint: "http://localhost:4318"
          signals: "traces,logs,metrics"
    presets:
      production:
        pluginsMergeMode: replace
        plugins:
          - name: golem-otlp-exporter
            version: "1.5.0"
            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/.

Signals

GitHub stars
2k
Forks
210
Last commit
Oct 2026
Advanced
Item type
skill
Key
golem-enable-otlp-effect
Source
github.com/golemcloud/golem