Enabling OpenTelemetry for an Effect Agent
SkillMonitoring & opsLets 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.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
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
| Parameter | Required | Description |
|---|---|---|
endpoint | Yes | OTLP collector base URL, such as http://localhost:4318 |
signals | No | Comma-separated traces, logs, and/or metrics; defaults to traces |
headers | No | Comma-separated key=value HTTP headers, such as x-api-key=secret |
service-name-mode | No | agent-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:
| Metric | Type | Description |
|---|---|---|
golem_invocation_count | Counter | Agent method invocations |
golem_invocation_duration_ns | Counter | Invocation duration |
golem_invocation_fuel_consumed | Counter | Fuel consumed by invocations |
golem_invocation_pending_count | Counter | Pending invocations |
golem_host_call_count | Counter | Internal host calls |
golem_log_count | Counter | Emitted log entries |
golem_memory_initial_bytes | Gauge | Initially allocated memory |
golem_memory_total_bytes | Gauge | Total allocated memory |
golem_memory_growth_bytes | Counter | Memory growth since start |
golem_component_size_bytes | Gauge | Component size |
golem_error_count | Counter | Recorded errors |
golem_interruption_count | Counter | Interrupt requests |
golem_exit_count | Counter | Process exit signals |
golem_restart_count | Counter | Fresh state creations |
golem_resources_created | Counter | Internal resources created |
golem_resources_dropped | Counter | Internal resources dropped |
golem_resources_active | Gauge | Active internal resources |
golem_update_success_count | Counter | Successful updates |
golem_update_failure_count | Counter | Failed updates |
golem_transaction_committed | Counter | Committed database transactions |
golem_transaction_rolled_back | Counter | Rolled-back database transactions |
golem_snapshot_size_bytes | Counter | Snapshot size |
golem_oplog_processor_lag | Gauge | Oplog processor delivery lag |
Metrics include service.name, golem.agent.id, golem.component.id, and
golem.component.version attributes.
Export Semantics
- Durable
Start,End, andCancelledmetadata 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-typeservice-name mode,service.nameexcludes constructor parameters and phantom instance IDs;golem.agent.idremains 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-golemhas no OTLP or plugin installation helper. - Use
Effect.withSpanandEffect.annotateCurrentSpan; there is no publicTracing.startSpan(...)wrapper. - Use
Effect.logInfo,Effect.logDebug, andEffect.annotateLogs; do not inventLogging.info(...)or another SDK logger. - Do not manually finish an Effect span. Its scoped lifetime is managed by
Effect.withSpan. - Do not provide
Logging.layerorTracing.layerin 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
effectand@golemcloud/effect-golemversions aligned, and do not edit files undergolem-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
Related picks
Skill · mattpocock
The pick for TypeScripttypescript-pro
Skill · jeffallan
The pick for TypeScriptnodejs-backend-patterns
Skill · wshobson
The pick for Noderun-node-tests
Skill · hiroro-work
The pick for Nodeinternal-comms
Skill · anthropics
More in Monitoring & opsagent-eval
Skill · affaan-m
More in Monitoring & ops