Browse resources

Send data

OpenTelemetry

Send traces from any OpenTelemetry SDK or Collector over OTLP/HTTP, JSON or protobuf.

Anything that speaks OpenTelemetry can send traces to knotel: Node.js, Python, Go and Java services, and the OpenTelemetry Collector. Auto-instrumentation libraries give you database spans for MongoDB, Postgres, Redis and more without code changes.

Endpoint

URLhttps://YOUR-INSTANCE/v1/traces
MethodPOST
ProtocolOTLP/HTTP, JSON or protobuf encoding
Authx-knotel-key: kn_…, or Authorization: Bearer kn_…
BodyUp to 5 MB and 5,000 spans per request, uncompressed

Environment variables

SDKs that read the standard variables need only these:

OTEL_SERVICE_NAME=orders-api
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://YOUR-INSTANCE/v1/traces
OTEL_EXPORTER_OTLP_TRACES_HEADERS=x-knotel-key=kn_YOUR_KEY

Node.js

@opentelemetry/exporter-trace-otlp-http sends JSON, so it works directly. With auto-instrumentations you get HTTP, Express, MongoDB, pg, Redis and other spans automatically.

// instrumentation.ts, loaded before your app (node --import ./instrumentation.js)
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";

new NodeSDK({
  serviceName: "orders-api",
  traceExporter: new OTLPTraceExporter({
    url: "https://YOUR-INSTANCE/v1/traces",
    headers: { "x-knotel-key": process.env.KNOTEL_KEY },
  }),
  instrumentations: [getNodeAutoInstrumentations()],
}).start();

Other languages

Python's and Go's HTTP exporters send protobuf, which ingest accepts directly — no gateway needed. For batching or sampling you can still put an OpenTelemetry Collector in front and have it forward to knotel. The Collector batches, and can sample or drop noisy spans before they cost you anything.

otel-collector.yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:

exporters:
  otlphttp/knotel:
    traces_endpoint: https://YOUR-INSTANCE/v1/traces
    compression: none
    headers:
      x-knotel-key: kn_YOUR_KEY

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/knotel]

Attributes knotel understands

Every attribute is stored and searchable. These are also indexed as columns for fast filtering and power the service map. Older semantic convention names are accepted too.

FieldRead from
HTTP methodhttp.request.method, http.method
HTTP routehttp.route
HTTP statushttp.response.status_code, http.status_code
URLurl.full, http.url, url.path, http.target
Database systemdb.system.name, db.system
Database operationdb.operation.name, db.operation
Database namespacedb.namespace, db.name
Collection / tabledb.collection.name, db.mongodb.collection, db.sql.table
Query textdb.query.text, db.statement
Peerpeer.service, server.address, net.peer.name

Sampling

knotel samples nothing itself: every span it receives is stored. When your SDK samples, tell knotel the rate and counts are scaled back up — a service sampling at 2% otherwise reads as fifty times quieter than it is. Error rates and percentiles survive sampling on their own; counts do not.

OpenTelemetry's consistent probability samplers write their rate into the W3C tracestate header as ot=th:<threshold>, and knotel reads it with no setup at all. That covers the Collector's probabilistic_sampler processor and the SDKs' consistent samplers, where the rate rides on every span of a sampled trace.

Plain TraceIdRatioBased is not one of them: it drops spans and records nothing about it. For that sampler, and for sampling you do yourself, set one of these as a span attribute, or on the resource for a whole service:

AttributeMeaning
sampleRate1 in N kept: 50 means this span stands for fifty. sample_rate and SampleRate work too.
sampling.probabilityThe fraction kept (0.02), i.e. the sampler's own argument. sampler.ratio works too.

A span attribute wins over tracestate, which wins over a rate on the resource: each is more specific about this span than the next. Undeclared and unpropagated means 1: one span counts as one request. Nothing else is inferred, because guessing a rate would invent traffic that never happened. Where counts are scaled, the dashboard says how many spans are behind the estimate.

Responses

StatusMeaning
200Stored. If some spans were invalid, the body has partialSuccess.rejectedSpans.
400The body isn't valid JSON or protobuf.
401Missing or unknown ingest key.
413The body is over 5 MB. Send smaller batches.
415Not OTLP/HTTP. Send JSON or protobuf.
503The spans couldn't be stored. Retry later.

Ids may be hex or base64, kinds and status codes numbers or enum names, and timestamps nanoseconds as numbers or strings. Spans without a valid trace id, span id or start time are rejected. Long strings are trimmed (attributes at 8,000 characters, query text at 4,000). CORS is open, so browsers can post directly. See limits.