Backend tracing
There is no Syncline backend SDK, and there should not be one. Syncline is a plain OTLP sink: if your services already emit OpenTelemetry, the integration is two environment variables.
Point your exporter at Syncline
OTEL_EXPORTER_OTLP_ENDPOINT=https://syncline.example.com/v1/ingest
OTEL_EXPORTER_OTLP_HEADERS=x-syncline-key=sk_live_...The secret key, not the public one. Public keys are browser credentials and are refused here.
Both spellings work. An exporter appends /v1/traces to OTEL_EXPORTER_OTLP_ENDPOINT but uses OTEL_EXPORTER_OTLP_TRACES_ENDPOINT verbatim, so Syncline answers at both paths. Getting that wrong otherwise produces a 404 swallowed inside a batch exporter, which surfaces only as traces never arriving.
Allow the header
Access-Control-Allow-Headers: traceparentYour own API has to accept this from the browser. It is the most common reason an integration appears broken.Nothing else changes
Auto-instrumentation reads the incoming traceparent, makes the server span a child of the browser’s span, and propagates the trace id to everything downstream — including database spans, which is what fills the database lane.
// Node, for example. Nothing Syncline-specific.
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
new NodeSDK({
instrumentations: [getNodeAutoInstrumentations()],
}).start();Keeping your existing vendor
Being an OTLP sink means Syncline does not have to replace anything. Fan out from a collector and send the same spans to both.
# otel-collector-config.yaml
exporters:
otlphttp/syncline:
endpoint: https://syncline.example.com/v1/ingest
headers:
x-syncline-key: sk_live_...
otlphttp/vendor:
endpoint: https://your-vendor.example.com
service:
pipelines:
traces:
exporters: [otlphttp/syncline, otlphttp/vendor]Formats
- OTLP/HTTP with JSON is supported. Protobuf is not yet.
- Trace and span ids may be hex or base64. OTLP/JSON specifies hex, unlike proto3, and not every producer complies — so both are accepted.
service.namecomes from the resource. A span without one is stored asunknownrather than dropped.- A malformed span is dropped and counted, never failing its batch. One bad span must not cost the other 499.
Checking it works
curl -s localhost:4000/v1/health
# {"status":"ok","checks":{"database":...,"redis":...}}Then open a recording. If the network lane has a bar but the backend lane is empty, the spans are arriving under a different trace id — or the traceparent never reached your server, which is the CORS header again.