pure-C99 · OTLP/HTTP · BSD-3
OpenTelemetry,without the C++ runtime
otlp-c emits traces, metrics, and logs over OTLP/HTTP from any C application — kernel modules, firmware, language VMs, preloaded tracers. One static archive. Zero non-libc dependencies. API frozen since 1.0.
One library, three signals
#include <otlp-c/otlp.h>
otlp_exporter_t *exp = otlp_exporter_create(&(otlp_exporter_opts_t){
.service_name = "my-service",
});
otlp_tracer_t *tr = otlp_tracer_create("scope", "lib", "1.0");
otlp_span_t *s = otlp_tracer_start_span(tr, "GET /users");
otlp_span_set_status(s, OTLP_STATUS_CODE_OK, NULL);
otlp_span_mark_end(s);
otlp_exporter_emit_move(exp, s); /* queue it, batch + POST on tick */The library never spawns a thread — you drive I/O withotlp_exporter_tick()from a thread you control.
Built for the places C++ can't go
All three signals
Traces, metrics, logs — including exemplars with trace correlation and schema_url.
Zero non-libc deps
Hand-rolled protobuf encoder + HTTP/1.1 client. No protobuf-c, no libcurl, no OpenSSL.
No library threads
Caller-driven tick(). Never calls pthread_create, never takes a mutex. Lock-free MPSC.
Hardened by construction
CWE-93 header-injection and CWE-190 overflow checks at every boundary — mutation-tested.
Wire-verified
Every schema table descriptor-audited; every payload byte-compared against the reference serialization.
89 ns/span emit
~11M spans/s through the emit pipeline (Release, null transport). Linear in attributes.
From zero to first span in minutes
CMake presets, three CI-pinned consumption paths, and a live otelcol integration suite — or your money back (it's BSD, it's free).
Read the docs