Skip to content

OpenTelemetry ​

Agent SDK sends traces, metrics, and logs to an OTLP collector when you set the OTEL_* variables. Record your own with @opentelemetry/api.

bash
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.com
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20$OTEL_TOKEN"
export OTEL_SERVICE_NAME=incident-agent
agent-sdk serve --dir . --dev
  • The default protocol is http/protobuf. http/json also works, and grpc logs a warning and falls back to http/protobuf.
  • Metrics use delta temporality. Grafana Cloud and Prometheus reject delta, so set OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative when you export to either one.
  • If you don't set an endpoint, Agent SDK doesn't export anything.
  • You can create a meter, tracer, or logger at the top of bot/agent.ts, and it will use Agent SDK's providers.

Variables ​

VariableEffect
OTEL_EXPORTER_OTLP_ENDPOINTBase URL. Appends /v1/traces, /v1/metrics, /v1/logs.
OTEL_EXPORTER_OTLP_TRACES_ENDPOINTTraces URL, used as written. Overrides the base URL.
OTEL_EXPORTER_OTLP_METRICS_ENDPOINTMetrics URL, used as written. Overrides the base URL.
OTEL_EXPORTER_OTLP_LOGS_ENDPOINTLogs URL, used as written. Overrides the base URL.
OTEL_EXPORTER_OTLP_HEADERSSent on every export. URL-encode values. Reread on each export.
OTEL_EXPORTER_OTLP_PROTOCOLhttp/protobuf (default) or http/json.
OTEL_SERVICE_NAMEservice.name. Defaults to the agent name, then unknown_service:node.
OTEL_RESOURCE_ATTRIBUTESservice.namespace, service.version, deployment.environment.name.
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCEdelta (default), cumulative, or lowmemory.
OTEL_LOGS_EXPORTERotlp exports logs. Unset, none, and console do not.
OTEL_SDK_DISABLEDtrue turns export off.
OTEL_TRACES_EXPORTERnone turns traces off.
OTEL_METRICS_EXPORTERnone turns metrics off.
OTEL_LOG_USER_PROMPTS1 adds prompt text to log records.
OTEL_LOG_TOOL_CONTENT1 adds tool payloads to log records.

Hosted agents ​

ts
export default defineAgent({
  hosting: {
    egressDomains: ["otlp-gateway-prod-us-east-0.grafana.net"],
    secretNames: [
      "OTEL_EXPORTER_OTLP_ENDPOINT",
      "OTEL_EXPORTER_OTLP_HEADERS",
      "OTEL_SERVICE_NAME",
    ],
  },
});

service.name is fixed by the first export, so redeploy to change it.

Record and tag ​

ts
import { metrics, trace } from "@opentelemetry/api";
import { logs } from "@opentelemetry/api-logs";
import { currentConversationId } from "@grok/agent-sdk/otel";

const tracer = trace.getTracer("incident-agent");
const lookups = metrics.getMeter("incident-agent").createCounter("incident.lookups");
const logger = logs.getLogger("incident-agent");

lookups.add(1, { "cursor.conversation.id": currentConversationId() });

Spans started during a turn get cursor.conversation.id, and a metric gets it only when you set it.

To tag a whole turn, call ctx.telemetry from a tool. Spans in the turn get the trace keys, Agent SDK's log records get the log keys, and cursor.token.usage gets the metric keys. Outside a tool, use telemetry from @grok/agent-sdk/telemetry.

ts
async execute({ key }, ctx) {
  ctx.telemetry.setTraceAttributes({ "work.item": key });
  ctx.telemetry.setMetricAttributes({ "work.item": key });
  ctx.telemetry.setLogAttributes({ "work.item": key });
  return await jira.getIssue(key);
}

Grafana renames a . in a metric key to _.

promql
sum by (work_item) (cursor_token_usage_total)
  • Each distinct metric value is a new series, so never use a per-turn ID, a secret, an email, or a raw user ID.
  • A session holds up to 32 keys of each kind, with keys up to 128 characters and values up to 256.

Limits ​

  • Agent SDK doesn't support custom span processors, samplers, views, your own OpenTelemetry SDK, or library auto-instrumentation. It reads OTEL_TRACES_SAMPLER and OTEL_METRIC_EXPORT_INTERVAL once at startup, so restart the process after you change either one.
  • Import Agent SDK before any other OpenTelemetry setup. Otherwise the turn's tags may not reach your spans.

Migrating from serve({ otel }) ​

Delete serve({ otel }), bot/otel.ts, bot/instrumentation.ts, and any defineOtel call. Set these variables instead.

Old optionSet this instead
serviceNameOTEL_SERVICE_NAME
serviceVersionservice.version in OTEL_RESOURCE_ATTRIBUTES
resourceAttributesOTEL_RESOURCE_ATTRIBUTES, comma-separated, URL-encoded values
exporters[].urlOTEL_EXPORTER_OTLP_ENDPOINT (appends /v1/traces, /v1/metrics, /v1/logs)
exporters[].url with signalsOTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, used as written
exporters[].headersOTEL_EXPORTER_OTLP_HEADERS, comma-separated, URL-encoded values
exporters[].protocolOTEL_EXPORTER_OTLP_PROTOCOL, or OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, OTEL_EXPORTER_OTLP_LOGS_PROTOCOL
exporters[].signalsThe per-signal URL. OTEL_TRACES_EXPORTER=none, OTEL_METRICS_EXPORTER=none, or OTEL_LOGS_EXPORTER=none stops that signal.
traces: falseOTEL_TRACES_EXPORTER=none
traces: trueLeave OTEL_TRACES_EXPORTER unset. Traces start when an endpoint is set.
traces.sampleRatioOTEL_TRACES_SAMPLER=parentbased_traceidratio and OTEL_TRACES_SAMPLER_ARG, before process start
metrics: falseOTEL_METRICS_EXPORTER=none
metrics: trueLeave OTEL_METRICS_EXPORTER unset. Metrics start when a metrics URL is set.
metrics.exportIntervalMillisOTEL_METRIC_EXPORT_INTERVAL, milliseconds, before process start
logs: falseUnset OTEL_LOGS_EXPORTER, or set none
logs: trueOTEL_LOGS_EXPORTER=otlp
logs.userPromptsOTEL_LOGS_EXPORTER=otlp and OTEL_LOG_USER_PROMPTS=1
logs.toolContentOTEL_LOGS_EXPORTER=otlp and OTEL_LOG_TOOL_CONTENT=1
otel: falseNo endpoint, or OTEL_SDK_DISABLED=true

Set the hosted values after deploy:

bash
agent-sdk secret set my-agent OTEL_SERVICE_NAME OTEL_EXPORTER_OTLP_ENDPOINT OTEL_EXPORTER_OTLP_HEADERS