CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/API & SDK
API & SDK/2026-05-29Advanced

Rewiring Claude API Observability with OpenTelemetry GenAI Conventions — A Design Memo for Model Migrations and Cost Audits

An implementation memo for rewiring production observability around Claude API to match the OpenTelemetry GenAI semantic conventions — span attributes, metrics, cost tracking, and model-migration replay — written from running this in indie services for six months.

Claude API115OpenTelemetry2Observability5Production23Cost MonitoringGenAI

Premium Article

Twelve years of running AdMob-funded wallpaper apps since 2014, alone, taught me that observability isn't a "nice to have" — it's the difference between sleeping and getting paged at 2am. When I bolted Claude API onto production services six months ago, the first wall I hit was standardising the telemetry. The Anthropic SDK returns a usage object, but how to land it on the Prometheus / Datadog / Grafana dashboards I already run, and how to keep span attributes consistent between Cloudflare Workers and Node runtimes, is left for you to figure out.

In 2024, OpenTelemetry's Semantic Conventions for GenAI entered draft, and the major attributes stabilised through 2025. If you align to them, your dashboard queries mostly survive even if you later mix in AWS Bedrock or Vertex AI. This is my memo on redesigning Claude API spans and metrics against those conventions. I implemented it both in Dolice Labs' four parallel sites (Claude Lab / Gemini Lab / Antigravity Lab / Rork Lab) and in my personal app portfolio, and I'll leave behind both code and actual numbers from that work.

Why I align to the OpenTelemetry GenAI conventions instead of bespoke attributes

For my first three months I instrumented with custom attributes like anthropic.prompt_tokens, anthropic.output_tokens, anthropic.model. It worked, but three real costs surfaced.

First, when I tried to point the same dashboard at the Gemini API from Gemini Lab, Grafana's sum by aggregation broke because the attribute names didn't match. Second, on Datadog APM I wanted to compare per-model latency, but my attribute names were so bespoke that the search UI offered no autocomplete suggestions — I ended up copy-pasting tag names every time. Third, the new wave of OSS LLM gateways (LiteLLM, Helicone, Cloudflare AI Gateway) all started shipping output against the GenAI conventions, and they collided with my custom names.

The OpenTelemetry GenAI conventions are aimed at "generative AI in general", so once you keep gen_ai.system = "anthropic" separate from gen_ai.request.model = "claude-sonnet-4-6", adding another vendor later is a matter of inserting one more gen_ai.system value. For an indie developer juggling three families (Claude, Gemini, OSS) in production, the payoff for that alignment is bigger than it looks on paper.

The minimum span attribute set I attach to Claude API calls

Across all four sites, this is what I now standardise on. I've kept only the GenAI convention attributes that are meaningful for the Anthropic API.

// src/lib/otel-claude.ts
import { trace, SpanStatusCode } from "@opentelemetry/api";
import Anthropic from "@anthropic-ai/sdk";
 
const tracer = trace.getTracer("dolice.claude-api", "1.0.0");
const client = new Anthropic();
 
export async function tracedMessage(params: {
  model: string;
  system?: string;
  messages: Array<{ role: "user" | "assistant"; content: string }>;
  max_tokens: number;
  cache_control?: { type: "ephemeral" };
}) {
  return await tracer.startActiveSpan(
    `chat ${params.model}`,
    {
      attributes: {
        // GenAI semantic conventions
        "gen_ai.system": "anthropic",
        "gen_ai.operation.name": "chat",
        "gen_ai.request.model": params.model,
        "gen_ai.request.max_tokens": params.max_tokens,
        // App-specific — required in production for tenant billing and rate limits
        "dolice.tenant_id": currentTenantId(),
        "dolice.surface": currentSurface(), // "claudelab" | "gemilab" | "app:wallpaper" etc.
      },
    },
    async (span) => {
      try {
        const startedAt = performance.now();
        const res = await client.messages.create({
          model: params.model,
          system: params.system,
          messages: params.messages,
          max_tokens: params.max_tokens,
        });
 
        span.setAttributes({
          "gen_ai.response.id": res.id,
          "gen_ai.response.model": res.model, // The model that actually served
          "gen_ai.response.finish_reasons": JSON.stringify([res.stop_reason]),
          "gen_ai.usage.input_tokens": res.usage.input_tokens,
          "gen_ai.usage.output_tokens": res.usage.output_tokens,
          // Anthropic-specific — cache fields aren't in the convention, so prefix with vendor
          "anthropic.usage.cache_creation_input_tokens":
            res.usage.cache_creation_input_tokens ?? 0,
          "anthropic.usage.cache_read_input_tokens":
            res.usage.cache_read_input_tokens ?? 0,
          "dolice.latency_ms": Math.round(performance.now() - startedAt),
        });
        span.setStatus({ code: SpanStatusCode.OK });
        return res;
      } catch (err) {
        span.recordException(err as Error);
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: (err as Error).message,
        });
        if (err && typeof err === "object" && "status" in err) {
          span.setAttribute("http.response.status_code", (err as any).status);
        }
        throw err;
      } finally {
        span.end();
      }
    },
  );
}

I deliberately leave out two attributes: gen_ai.prompt (the request body) and gen_ai.completion (the response body). The convention marks them as opt-in, and for two reasons I keep them OFF by default in production.

  1. PII / copyright leak risk. My apps in the manifestation / healing niche occasionally receive deeply personal worries as input, and that content should not enter the telemetry pipeline.
  2. Datadog indexing cost. Stuffing hundreds-to-thousands of tokens of body into each span sends log unit pricing skyward. Measured on 3M calls/month, the body-on case was 7.3x the body-off case in storage spend.

Instead, the replay dumps I describe later live in a separate bucket as JSONL with a 30-day TTL.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Concrete placement for GenAI-conformant span attributes on Claude API calls (gen_ai.system / gen_ai.request.model / gen_ai.usage.input_tokens etc.)
PromQL/SQL queries for tracking cost on three axes — input/output tokens, cache hit rate, and $0.003/$0.015 unit pricing — with a monthly reconciliation procedure that stays within ±5% of the Anthropic invoice
A Python implementation that replays a 24-hour shadow of production traffic against claude-opus-4-6 to regression-test a switch from claude-sonnet-4-6
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API & SDK2026-06-20
Putting Cloudflare AI Gateway in Front of Claude Made the Numbers I Needed Disappear — Field Notes on Instrumentation
After putting Cloudflare AI Gateway in front of Claude API, here is where I actually got stung — cost attribution, semantic-cache false hits, fallback quietly lowering quality, and budgets that don't really stop anything — with the code I used to fix each.
API & SDK2026-07-07
When Claude API Suddenly Starts Returning 429 in Production — Field Notes on Measuring Rate-Limit Headroom from Headers and Throttling Before You Run Dry
Scrambling to add retries after a 429 is always a step behind. Claude API writes how much you have left into every response header. These are field notes on measuring that headroom continuously and throttling yourself before it runs out.
API & SDK2026-07-01
When Claude API Document Extraction Is Confidently Wrong — Field Notes on Catching Silent Errors with Invariants
In structured extraction from invoices and contracts, the real danger isn't a crash — it's a value that's silently wrong while the schema validates and confidence reads high. Field notes on invariants, two-pass extraction, and tracking field-level error rates.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →