Skip to content
anecticoDocsDashboard
Browse documentation
Guide

OpenTelemetry

Send existing OTLP traces, logs, and metrics to Anectico without replacing your instrumentation.

On this page

Anectico accepts OpenTelemetry Protocol data. Keep your current instrumentation and change the exporter destination and authentication header.

Endpoint and authentication

Use the Anectico ingestion host:

OTLP HTTP base: https://api.anectico.com
Header:         X-Anectico-API-Key=an_...

Signal paths follow the OTLP HTTP convention:

/v1/traces
/v1/logs
/v1/metrics

Example environment configuration:

export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.anectico.com"
export OTEL_EXPORTER_OTLP_HEADERS="X-Anectico-API-Key=an_..."
export OTEL_SERVICE_NAME="orders-api"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production"

Use an API key containing only ingest:write for collectors and application exporters.

OTLP over gRPC

OTLP/HTTP is the transport to use. The public endpoint does not currently accept OTLP over gRPC. Set OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf — the default for the settings above — and point your exporter at https://api.anectico.com as shown.

Some SDKs and the OpenTelemetry Collector default to gRPC, so an exporter left on its own default will fail to connect rather than report an authentication or validation error. If telemetry is not arriving and your collector logs a connection failure rather than a rejection, check the protocol setting first.

The two transports are otherwise equivalent: the same key, the same ingest:write permission, the same project, and the same data. A credential without that permission is refused before anything is stored, and the refusal is permanent — an exporter will not retry it.

Add Anectico features to an existing Python SDK

If your Python application already owns OpenTelemetry resources, sampling, providers, processors, exporters, instrumentation, and lifecycle, do not start a second telemetry pipeline. Keep those application providers, point their OTLP exporters or Collector at Anectico, and add Anectico's identity-only processors before registering the providers. The snippet is a composition template: the three *_provider and two existing_*_export_processor names stand for objects your application already creates.

import os

from opentelemetry import _logs as otel_logs
from opentelemetry import metrics, trace
import anectico

# These are the providers and export processors from your existing OTel setup.
tracer_provider.add_span_processor(anectico.BaggageIdentitySpanProcessor())
tracer_provider.add_span_processor(existing_span_export_processor)
logger_provider.add_log_record_processor(
    anectico.BaggageIdentityLogRecordProcessor()
)
logger_provider.add_log_record_processor(existing_log_export_processor)

trace.set_tracer_provider(tracer_provider)
metrics.set_meter_provider(meter_provider)
otel_logs.set_logger_provider(logger_provider)

client = anectico.AnecticoClient(
    api_key=os.environ["ANECTICO_API_KEY"],
    service_name="marketplace-api",
    open_telemetry_mode="existing",
)
client.start()

ANECTICO_OTEL_MODE=existing is equivalent. Register the identity processors before application work emits spans or logs, and register the application providers before starting Anectico. The processors only stamp anectico.distinct_id; they do not export, register globals, or flush/shut down anything. Do not add person identity to metric labels.

Existing mode creates no providers, exporters, export processors, metric readers, propagator, requests/httpx instrumentation, or standard-library logging bridge. Anectico's manual spans, captured errors, AI/tool/agent helpers, metrics, and log_event() use the registered global APIs. Without a real application Logger Provider, log_event() is a safe no-op. Ordinary Python logging continues through the application's existing logging integration only.

The application owns sampling and export decisions. Anectico's managed resource, sampler, endpoint, batching, exporter, propagation, and logging-bridge settings do not change the existing pipeline; Anectico signal enable flags still gate helper emission. client.flush() returns False, and client.stop() returns attempted=False, success=None, because neither call may flush or shut down application providers. After the final helper call, stop Anectico and use the application's existing provider lifecycle hook to flush and shut down.

To roll back the Anectico feature layer, remove the client startup and the two identity processors. The original application-owned OTel pipeline stays in place. Restore its prior exporter/Collector destination and authentication separately if the rollback must also stop OTLP delivery to Anectico.

Add Anectico features to an existing Node SDK

If your application already starts NodeSDK or registers its own OpenTelemetry providers, keep that code as the single owner of providers, processors, instrumentation, exporters, flush, and shutdown. Start it first, point its OTLP exporters at Anectico, and initialize Anectico in existing-provider mode. Adapt the exporter and processor placeholders below to the objects already in your setup:

import { context, trace } from '@opentelemetry/api';
import { NodeSDK } from '@opentelemetry/sdk-node';
import {
  BaggageLogRecordProcessor,
  BaggageSpanProcessor,
  contextWithDistinctId,
  initNode,
} from '@anectico/sdk/node';

const otel = new NodeSDK({
  // Keep your existing resource, instrumentations, and Anectico OTLP exporters.
  // Add these identity-only processors before the application SDK starts.
  spanProcessors: [
    new BaggageSpanProcessor(),
    // ...your existing export processors
  ],
  logRecordProcessors: [
    new BaggageLogRecordProcessor(),
    // ...your existing export processors
  ],
});
otel.start();

const anectico = await initNode({
  apiKey: process.env.ANECTICO_API_KEY,
  serviceName: 'orders-api',
  openTelemetryMode: 'existing',
});

const requestContext = contextWithDistinctId(context.active(), 'customer_8842');
await context.with(requestContext, async () => {
  await trace.getTracer('orders-api').startActiveSpan('orders.create', async span => {
    // Existing application work.
    span.end();
  });
});

openTelemetryMode: 'existing' (or ANECTICO_OTEL_MODE=existing) does not create providers, exporters, propagators, or auto-instrumentations and never shuts down application-owned providers. Anectico's manual spans, errors, metrics, and logs use the globals your application registered. A signal needs an existing provider to export: for example, logEvent() requires your application to have registered an OpenTelemetry Logger Provider.

Add BaggageSpanProcessor and BaggageLogRecordProcessor to the application-owned providers before they start when request-scoped contextWithDistinctId() or W3C baggage should become the anectico.distinct_id attribute stored on spans and logs. These processors add identity attributes only; they do not export, register globals, or own provider lifecycle. Metrics should remain service-scoped and must not carry customer identity labels.

Keep lifecycle ownership in the application. On graceful or fatal shutdown, finish active work and flush or shut down NodeSDK before calling anectico.stop() to finish Anectico identity requests and remove its process handlers. anectico.flush() cannot flush providers it does not own. Do not combine the default managed mode with an already-started OpenTelemetry SDK, and do not use the Anectico preload in existing-provider mode. Removing the Anectico initialization later leaves the original OTLP-only pipeline unchanged.

Attach customer identity to request evidence

OTLP data without customer identity still appears in service, trace, log, and metric views. To add traces and logs to a customer timeline, attach the stable ID as:

anectico.distinct_id=user_8842

Use one scalar string, boolean, or number; strings are recommended. Arrays and maps are ignored as identity values because their rendered form is not a stable customer identifier.

For distributed requests, propagate the same value through trusted W3C baggage. Do not accept a customer identity directly from an unauthenticated public request. Do not copy customer identity onto metric labels: identifiers create high-cardinality series, and metrics are normally service-scoped evidence rather than one-customer timeline entries.

Resource fields that matter

Set these consistently:

Field Purpose
service.name Groups telemetry by application or service
service.version Connects failures to a release
deployment.environment Separates production, staging, and development
anectico.distinct_id Connects request evidence to a customer; do not use it as a metric label

Agent and LLM framework compatibility

Anectico reads agent and model telemetry from the conventions below without any Anectico-specific wrapper. Send the spans your framework already produces. Every entry is covered by a compatibility test that replays a span in that framework's own attribute shape and checks the values Anectico computes from it, so a change that stopped reading one of these is a build failure rather than a quiet blank column.

What you run Attributes it writes What Anectico reads from it
Current OpenTelemetry GenAI conventions gen_ai.provider.name, gen_ai.usage.input_tokens, … Provider, requested and served model, operation, all five token counts, agent and workflow identity, conversation, response id, finish reason
Superseded OpenTelemetry GenAI conventions gen_ai.system, gen_ai.usage.prompt_tokens, … The same fields, read from the older key names
OpenInference openinference.span.kind, llm.* Provider, model, token detail including cache and reasoning, agent name, session, tool name and call id, cost hint
OpenAI Agents SDK Either convention, depending on the instrumentation package Provider, model, token detail; agent spans keep their agent name
OpenAI Responses API gen_ai.operation.name=fetch_response Model, response id, finish reason and the response's own terminal status, with no token counts — that operation does not report them
Anthropic Messages API Current conventions Provider, model, both prompt-cache counters, reasoning tokens, conversation, whether the call was streamed
Claude Agent SDK (Claude Code tracing) gen_ai.system plus the SDK's own names Provider, model, response id, stop reason
Google ADK and Gemini gen_ai.system, agent and invocation attributes Provider, model, tokens, agent name, session; the agent and model spans resolve to one agent
LangGraph OpenInference plus a thread id Session identity from the thread id
MCP clients mcp.method.name, mcp.session.id, mcp.protocol.version Tool name, MCP session and protocol version, filed as an MCP step rather than a plain tool call
MCP servers mcp.method.name, mcp.protocol.version The MCP step and its protocol version
Strands Agents Current conventions, framework name as the provider Provider, model, input and output tokens, operation
Amazon Bedrock AgentCore The framework's spans, plus a session id the runtime injects Everything the framework writes, plus session identity and agent name
Microsoft Agent Framework gen_ai.provider.name, agent id and name Provider, operation, agent id and name
AutoGen gen_ai.system, agent id and name Operation, agent id and name; the provider reads as the framework — see below

Where a framework's own choices limit what appears

These are not defects to report; they are places where a framework writes something the conventions do not define, and Anectico declines to guess at it. Each has a one-line fix you can apply in your own instrumentation.

  • Claude Agent SDK token counts do not appear. The SDK writes input_tokens, output_tokens, cache_read_tokens and cache_creation_tokens rather than the gen_ai.usage.* names. Anectico does not read unnamespaced attributes, because doing so would capture unrelated fields from every other application sending data. Add the gen_ai.usage.* attributes alongside them to get token and cost figures for those calls.
  • AutoGen reports the framework as the provider. AutoGen sets the provider attribute to autogen rather than to the model vendor, and its own tracing records no token usage, so its spans carry no cost. Instrument the model client as well to get provider, model and spend.
  • An MCP server that names its tool only as mcp.tool.name records no tool name. The call is still recorded as an MCP step with its protocol version. Set gen_ai.tool.name as well to get the tool named.
  • Strands prompt-cache counters do not appear. Strands spells them gen_ai.usage.cache_read_input_tokens and cache_write_input_tokens, which are not the convention's names. Input and output tokens are unaffected.
  • Google ADK records no agent id. Its agent identity is the agent name plus the service name, which is what Anectico groups on for that framework.
  • AgentCore's own runtime, gateway and memory spans are not agent steps. They carry AWS attributes rather than gen_ai.* ones and appear as ordinary spans. The agent framework running inside the runtime is what produces agent telemetry.

Verify

Send one span, one log, and one recognizable metric, then check Traces, Logs, and Metrics in the same project and environment. Allow for the metric export interval and shut down the Meter Provider cleanly in a short-lived test. If you supplied anectico.distinct_id on the request evidence, also search Customers for that value.