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_tokensandcache_creation_tokensrather than thegen_ai.usage.*names. Anectico does not read unnamespaced attributes, because doing so would capture unrelated fields from every other application sending data. Add thegen_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
autogenrather 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.namerecords no tool name. The call is still recorded as an MCP step with its protocol version. Setgen_ai.tool.nameas well to get the tool named. - Strands prompt-cache counters do not appear. Strands spells them
gen_ai.usage.cache_read_input_tokensandcache_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.