From 4ed99ac11c1d7ded30d70f32f11f27c1a686688e Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 16:38:03 -0500 Subject: [PATCH 01/30] feat(sender): align OTLP exporter configuration with OTel spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add OTEL_EXPORTER_OTLP_ENDPOINT as the spec-compliant base URL define, replacing the non-standard OTEL_COLLECTOR_BASE_URL (kept as a legacy fallback for backward compatibility). Add per-signal endpoint overrides (OTEL_EXPORTER_OTLP_{LOGS,TRACES,METRICS}_ENDPOINT) used verbatim per the exporter spec — no path is appended. Add global and per-signal header configuration (OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_{LOGS,TRACES,METRICS}_HEADERS) as comma-separated key=value pairs, parsed and cached on first send. Add automatic HTTPS support: when an endpoint URL starts with https://, a WiFiClientSecure is used. Certificate validation is skipped by default (OTEL_TLS_INSECURE=1) which is the practical default for embedded devices. Priority chain for URL resolution: 1. Per-signal endpoint override (used as-is) 2. OTEL_EXPORTER_OTLP_ENDPOINT (paths appended) 3. OTEL_COLLECTOR_BASE_URL (legacy fallback, paths appended) --- include/OtelSender.h | 53 ++++++++++++- src/OtelSender.cpp | 182 +++++++++++++++++++++++++++++-------------- 2 files changed, 174 insertions(+), 61 deletions(-) diff --git a/include/OtelSender.h b/include/OtelSender.h index 0a30a90..18f328b 100644 --- a/include/OtelSender.h +++ b/include/OtelSender.h @@ -16,12 +16,61 @@ #define OTEL_WORKER_SLEEP_MS 0 #endif -// Base URL of your OTLP/HTTP collector (no trailing slash), e.g. "http://192.168.8.50:4318" -// You can override this via build_flags: -DOTEL_COLLECTOR_BASE_URL="\"http://…:4318\"" +// Base URL for all signals. Signal paths (/v1/traces, /v1/metrics, /v1/logs) +// are appended automatically, matching the OTLP exporter spec: +// https://opentelemetry.io/docs/specs/otel/protocol/exporter/ +// +// Standard name (preferred): +// -DOTEL_EXPORTER_OTLP_ENDPOINT="\"http://192.168.8.50:4318\"" +// Legacy name (still accepted, overridden by the standard name if both are set): +// -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.50:4318\"" +#ifndef OTEL_EXPORTER_OTLP_ENDPOINT +#define OTEL_EXPORTER_OTLP_ENDPOINT "" +#endif #ifndef OTEL_COLLECTOR_BASE_URL #define OTEL_COLLECTOR_BASE_URL "http://192.168.8.50:4318" #endif +// Per-signal endpoint overrides (full URL used as-is, no path appending). +// These override OTEL_EXPORTER_OTLP_ENDPOINT for their respective signal. +// Example (Datadog US1): +// -DOTEL_EXPORTER_OTLP_LOGS_ENDPOINT="\"https://otlp.datadoghq.com/v1/logs\"" +#ifndef OTEL_EXPORTER_OTLP_LOGS_ENDPOINT +#define OTEL_EXPORTER_OTLP_LOGS_ENDPOINT "" +#endif +#ifndef OTEL_EXPORTER_OTLP_TRACES_ENDPOINT +#define OTEL_EXPORTER_OTLP_TRACES_ENDPOINT "" +#endif +#ifndef OTEL_EXPORTER_OTLP_METRICS_ENDPOINT +#define OTEL_EXPORTER_OTLP_METRICS_ENDPOINT "" +#endif + +// HTTP headers added to every outgoing OTLP request (all signals). +// Format: comma-separated "key=value" pairs. +// Example: -DOTEL_EXPORTER_OTLP_HEADERS="\"dd-api-key=abc123,dd-otlp-source=myapp\"" +#ifndef OTEL_EXPORTER_OTLP_HEADERS +#define OTEL_EXPORTER_OTLP_HEADERS "" +#endif + +// Per-signal header overrides, merged on top of OTEL_EXPORTER_OTLP_HEADERS. +// Same "key=value,…" format. +#ifndef OTEL_EXPORTER_OTLP_LOGS_HEADERS +#define OTEL_EXPORTER_OTLP_LOGS_HEADERS "" +#endif +#ifndef OTEL_EXPORTER_OTLP_TRACES_HEADERS +#define OTEL_EXPORTER_OTLP_TRACES_HEADERS "" +#endif +#ifndef OTEL_EXPORTER_OTLP_METRICS_HEADERS +#define OTEL_EXPORTER_OTLP_METRICS_HEADERS "" +#endif + +// TLS: when the endpoint URL starts with "https://", a WiFiClientSecure is used. +// Set OTEL_TLS_INSECURE=0 and supply a CA cert via OTEL_TLS_CA_CERT to enable +// certificate validation. Defaults to skipping validation (common on embedded). +#ifndef OTEL_TLS_INSECURE +#define OTEL_TLS_INSECURE 1 +#endif + // Internal queue capacity for async sender on RP2040. // Keep small to bound RAM; increase if you see drops. #ifndef OTEL_QUEUE_CAPACITY diff --git a/src/OtelSender.cpp b/src/OtelSender.cpp index 1b3180e..7ae040a 100644 --- a/src/OtelSender.cpp +++ b/src/OtelSender.cpp @@ -1,21 +1,24 @@ #include "OtelSender.h" +#include +#include // --- HTTP + WiFi includes (portable) --- #if defined(ESP8266) #include #include + #include #elif defined(ESP32) #include #include + #include #elif defined(ARDUINO_ARCH_RP2040) #include // Earle Philhower core #include // Arduino HTTPClient + #include #else #error "Unsupported platform: need WiFi + HTTPClient" #endif - - #ifdef ARDUINO_ARCH_RP2040 #include "pico/multicore.h" #endif @@ -26,28 +29,119 @@ std::atomic OTelSender::head_{0}; std::atomic OTelSender::tail_{0}; std::atomic OTelSender::drops_{0}; std::atomic OTelSender::worker_started_{false}; -// Begin HTTP on all platforms (ESP8266 requires WiFiClient) -static bool httpBeginCompat(HTTPClient& http, const String& url) { -#if defined(ESP8266) - WiFiClient client; // or WiFiClientSecure if you later do HTTPS - return http.begin(client, url); // new API on ESP8266 -#else - return http.begin(url); // ESP32 / RP2040 -#endif + +// ---------- Header parsing ---------- + +// Append "key=value,…" pairs from raw into out. Existing entries are preserved +// so callers can layer global headers then per-signal headers. +static void appendParsedHeaders_(const char* raw, + std::vector>& out) { + if (!raw || !*raw) return; + String s(raw); + int start = 0; + while (start <= (int)s.length()) { + int comma = s.indexOf(',', start); + if (comma < 0) comma = (int)s.length(); + String token = s.substring(start, comma); + token.trim(); + int eq = token.indexOf('='); + if (eq > 0) { + String k = token.substring(0, eq); + String v = token.substring(eq + 1); + k.trim(); v.trim(); + if (k.length()) out.push_back({k, v}); + } + start = comma + 1; + } } +// Returns the merged header list for the given OTLP path (/v1/logs etc.). +// Built once on first call and cached for the lifetime of the program. +static const std::vector>& headersForPath_(const char* path) { + static bool init = false; + static std::vector> logH, traceH, metricH; + if (!init) { + init = true; + // Global headers first, then per-signal overrides + appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, logH); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, traceH); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, metricH); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_LOGS_HEADERS, logH); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_TRACES_HEADERS, traceH); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_METRICS_HEADERS, metricH); + } + if (path && strcmp(path, "/v1/logs") == 0) return logH; + if (path && strcmp(path, "/v1/traces") == 0) return traceH; + return metricH; +} -// Build "http://host:4318" + "/v1/…" +// ---------- URL resolution ---------- + +// Returns the full URL for the given OTLP signal path. +// +// Priority (highest to lowest): +// 1. Per-signal endpoint (OTEL_EXPORTER_OTLP_{LOGS,TRACES,METRICS}_ENDPOINT) +// → used as-is, no path appended (spec requirement) +// 2. Standard base URL (OTEL_EXPORTER_OTLP_ENDPOINT) +// → signal path appended automatically +// 3. Legacy base URL (OTEL_COLLECTOR_BASE_URL) +// → signal path appended automatically String OTelSender::fullUrl_(const char* path) { - // Avoid double slashes if a user accidentally sets a trailing slash - String base = String(OTEL_COLLECTOR_BASE_URL); + // 1. Per-signal overrides (used verbatim per spec) + if (path) { + if (strcmp(path, "/v1/logs") == 0 && strlen(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) > 0) + return String(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT); + if (strcmp(path, "/v1/traces") == 0 && strlen(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) > 0) + return String(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT); + if (strcmp(path, "/v1/metrics") == 0 && strlen(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) > 0) + return String(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT); + } + + // 2/3. Base URL with signal path appended + String base = strlen(OTEL_EXPORTER_OTLP_ENDPOINT) > 0 + ? String(OTEL_EXPORTER_OTLP_ENDPOINT) + : String(OTEL_COLLECTOR_BASE_URL); if (base.endsWith("/")) base.remove(base.length() - 1); if (path && *path == '/') return base + String(path); return base + "/" + String(path ? path : ""); } +// ---------- HTTP/HTTPS POST ---------- + +// Executes a single POST. Handles plain HTTP and HTTPS transparently based on +// the URL scheme. Custom headers are applied after Content-Type. +static void doPost_(const String& url, const String& payload, const char* path) { + HTTPClient http; + const auto& hdrs = headersForPath_(path); + + // Lambda keeps the send logic in one place regardless of client type. + auto fire = [&](bool ok) { + if (!ok) return; + http.addHeader("Content-Type", "application/json"); + for (const auto& h : hdrs) http.addHeader(h.first, h.second); + (void)http.POST(payload); + http.end(); + }; + + if (url.startsWith("https://")) { + // WiFiClientSecure must remain in scope until after http.end() (fire()). + WiFiClientSecure sc; +#if OTEL_TLS_INSECURE + sc.setInsecure(); +#endif + fire(http.begin(sc, url)); + } else { +#if defined(ESP8266) + WiFiClient wc; + fire(http.begin(wc, url)); +#else + fire(http.begin(url)); +#endif + } +} + // ---------- Queue (SPSC) ---------- -// Single-producer (core0) enqueue; drop oldest on overflow + bool OTelSender::enqueue_(const char* path, String&& payload) { size_t h = head_.load(std::memory_order_relaxed); size_t t = tail_.load(std::memory_order_acquire); @@ -69,32 +163,22 @@ bool OTelSender::enqueue_(const char* path, String&& payload) { bool OTelSender::dequeue_(OTelQueuedItem& out) { size_t t = tail_.load(std::memory_order_relaxed); size_t h = head_.load(std::memory_order_acquire); - if (t == h) return false; // empty + if (t == h) return false; out = std::move(q_[t]); - q_[t].payload = String(); // release memory + q_[t].payload = String(); size_t next = (t + 1) % QCAP; tail_.store(next, std::memory_order_release); return true; } // ---------- Worker ---------- + void OTelSender::pumpOnce_() { -#if OTEL_SEND_ENABLE OTelQueuedItem it; if (!dequeue_(it)) return; - - HTTPClient http; - if (httpBeginCompat(http, fullUrl_(it.path))) { - http.addHeader("Content-Type", "application/json"); - // Fire the POST; the blocking happens on core 1, not in the control path. - (void)http.POST(it.payload); - http.end(); - } -#else - // If globally disabled, just drain the queue without sending. - OTelQueuedItem sink; - (void)dequeue_(sink); +#if OTEL_SEND_ENABLE + doPost_(fullUrl_(it.path), it.payload, it.path); #endif } @@ -103,28 +187,18 @@ void OTelSender::workerLoop_() { for (int i = 0; i < OTEL_WORKER_BURST; ++i) { OTelQueuedItem it; if (!dequeue_(it)) break; - - HTTPClient http; - // Keep-alive where supported; harmless otherwise - #if defined(HTTPCLIENT_1_2_COMPATIBLE) || defined(ESP8266) || defined(ESP32) - http.setReuse(true); - #endif - if (httpBeginCompat(http, fullUrl_(it.path))) { - http.addHeader("Content-Type", "application/json"); - (void)http.POST(it.payload); - http.end(); - } +#if OTEL_SEND_ENABLE + doPost_(fullUrl_(it.path), it.payload, it.path); +#endif } delay(OTEL_WORKER_SLEEP_MS); } } - #ifdef ARDUINO_ARCH_RP2040 void otel_worker_entry() { OTelSender::workerLoop_(); } #endif - void OTelSender::launchWorkerOnce_() { #ifdef ARDUINO_ARCH_RP2040 bool expected = false; @@ -147,30 +221,20 @@ bool OTelSender::queueIsHealthy() { } // ---------- Public send API ---------- + void OTelSender::sendJson(const char* path, JsonDocument& doc) { #if !OTEL_SEND_ENABLE - // Compile-time: completely disable sends (useful for latency tests) (void)path; (void)doc; return; #else - // Serialize on the caller's core (cheap), then: - // - RP2040: enqueue for core-1 worker to POST (non-blocking for control path) - // - others: POST synchronously (unchanged behaviour) String payload; serializeJson(doc, payload); - #ifdef ARDUINO_ARCH_RP2040 - // Ensure worker is launched (safe to call repeatedly) - launchWorkerOnce_(); - enqueue_(path, std::move(payload)); - #else - HTTPClient http; - if (httpBeginCompat(http, fullUrl_(path))) { - http.addHeader("Content-Type", "application/json"); - (void)http.POST(payload); - http.end(); - } - #endif +#ifdef ARDUINO_ARCH_RP2040 + launchWorkerOnce_(); + enqueue_(path, std::move(payload)); +#else + doPost_(fullUrl_(path), payload, path); +#endif #endif } - From 11274838c6b9ff869bab995775450392e64c7c5a Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 16:38:10 -0500 Subject: [PATCH 02/30] fix(metrics): serialize aggregationTemporality as integer enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OTLP JSON spec encodes AggregationTemporality as an integer: AGGREGATION_TEMPORALITY_CUMULATIVE = 1 AGGREGATION_TEMPORALITY_DELTA = 2 Previously the value was serialized as a plain string ("DELTA", "CUMULATIVE") which is not spec-compliant and causes strict endpoints such as Datadog direct OTLP to silently ignore the field. The public API is unchanged — callers still pass "DELTA" or "CUMULATIVE" to sum(). The mapping to the correct integer happens at serialization time. --- src/OtelMetrics.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/OtelMetrics.cpp b/src/OtelMetrics.cpp index 41f8eb4..487eee3 100644 --- a/src/OtelMetrics.cpp +++ b/src/OtelMetrics.cpp @@ -104,9 +104,17 @@ void Metrics::buildAndSendSum(const String& name, double value, metric["unit"] = unit; metric["type"] = "sum"; + // OTLP JSON encodes AggregationTemporality as an integer enum, not a string. + // AGGREGATION_TEMPORALITY_CUMULATIVE = 1, AGGREGATION_TEMPORALITY_DELTA = 2. + // Previously this sent the raw string (e.g. "DELTA"), which is not spec-compliant. + // Spec-compliant collectors (including Datadog direct OTLP) require the integer. + // Collectors that were previously accepting the string may silently ignore or + // default the field — switching to the integer is the correct behaviour. + // The public API (passing "DELTA" / "CUMULATIVE" to sum()) is unchanged. + int temporalityInt = (temporality == "CUMULATIVE") ? 1 : 2; // default to DELTA JsonObject sum = metric["sum"].to(); - sum["isMonotonic"] = isMonotonic; - sum["aggregationTemporality"] = temporality; // "DELTA" or "CUMULATIVE" + sum["isMonotonic"] = isMonotonic; + sum["aggregationTemporality"] = temporalityInt; JsonArray dps = sum["dataPoints"].to(); JsonObject dp = dps.add(); From fe17536146ec8afe39bbeb1fe438d1a8bea930b4 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 16:38:17 -0500 Subject: [PATCH 03/30] feat(examples): fix basic example and add direct_otlp example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit basic/main.cpp: fix API calls that referenced non-existent methods (Logger::begin, 4-arg Tracer::begin, OTelGauge). Replace with the correct current API: Tracer::begin(scope, version), Metrics::begin, and Metrics::gauge(). Remove the unused OTEL_COLLECTOR_HOST define. direct_otlp/main.cpp: new example demonstrating direct vendor OTLP ingestion with the full attribute and tagging API across all signals — typed span attributes (string/int/double/bool), span events, per-call and default metric labels, per-call and default log labels. Includes build flag examples for Datadog and Grafana Cloud. basic/README.md, direct_otlp/README.md: concise READMEs explaining what each example does and the minimum build flags required. --- examples/basic/README.md | 42 ++++++++ examples/basic/main.cpp | 122 +++++++++------------- examples/direct_otlp/README.md | 60 +++++++++++ examples/direct_otlp/main.cpp | 180 +++++++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+), 76 deletions(-) create mode 100644 examples/basic/README.md create mode 100644 examples/direct_otlp/README.md create mode 100644 examples/direct_otlp/main.cpp diff --git a/examples/basic/README.md b/examples/basic/README.md new file mode 100644 index 0000000..7553ad0 --- /dev/null +++ b/examples/basic/README.md @@ -0,0 +1,42 @@ +# basic example + +Minimal working sketch that sends a heartbeat trace, a correlated log, and a +gauge metric on a fixed interval. + +## What it does + +Every 5 seconds the main loop: + +1. Opens a trace span called `heartbeat` +2. Emits an `INFO` log correlated to that span (the `traceId` and `spanId` are + attached automatically) +3. Records a gauge metric (`heartbeat.uptime_seconds`) +4. Closes the span, which triggers the OTLP send + +## Configuration + +Set these via `-D` flags in your `platformio.ini` `build_flags`: + +| Flag | Purpose | +|---|---| +| `WIFI_SSID` / `WIFI_PASS` | Wi-Fi credentials | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | Base URL of your collector, e.g. `http://192.168.1.100:4318` | +| `OTEL_SERVICE_NAME` | Service name reported in all telemetry | + +See the root [README](../../README.md) for the full list of configuration flags. + +## Running + +```ini +; platformio.ini +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = https://github.com/proffalken/otel-embedded-cpp.git#main +build_flags = + -DWIFI_SSID="\"your-ssid\"" + -DWIFI_PASS="\"your-password\"" + -DOTEL_EXPORTER_OTLP_ENDPOINT="\"http://192.168.1.100:4318\"" + -DOTEL_SERVICE_NAME="\"my-device\"" +``` diff --git a/examples/basic/main.cpp b/examples/basic/main.cpp index c16b5ec..845d25e 100644 --- a/examples/basic/main.cpp +++ b/examples/basic/main.cpp @@ -1,8 +1,6 @@ #include -// —————————————————————————————————————————————————————————— -// Platform‐specific networking includes -// —————————————————————————————————————————————————————————— +// ── Platform networking ─────────────────────────────────────────────────────── #if defined(ESP8266) #include #include @@ -10,41 +8,35 @@ #include #include #else - #error "Unsupported platform: must be ESP8266, ESP32 or RP2040" + #error "Unsupported platform: must be ESP8266, ESP32, or RP2040" #endif -// NTP / time #include -// For PRNG seeding #if defined(ARDUINO_ARCH_ESP32) #include #endif -// OTLP library -#include "OtelDefaults.h" #include "OtelSender.h" #include "OtelLogger.h" #include "OtelTracer.h" #include "OtelMetrics.h" -// ———————————————————————————————————————————————— -// Build‐time defaults (override with -D OTEL_* flags) -// ———————————————————————————————————————————————— -#ifndef OTEL_WIFI_SSID -#define OTEL_WIFI_SSID "default" -#endif +// ── Build-time defaults (override via -D flags in platformio.ini) ───────────── -#ifndef OTEL_WIFI_PASS -#define OTEL_WIFI_PASS "default" +#ifndef WIFI_SSID +#define WIFI_SSID "default" #endif -#ifndef OTEL_COLLECTOR_HOST -#define OTEL_COLLECTOR_HOST "http://192.168.1.100:4318" +#ifndef WIFI_PASS +#define WIFI_PASS "default" #endif +// Collector endpoint — set OTEL_EXPORTER_OTLP_ENDPOINT (standard) or the +// legacy OTEL_COLLECTOR_BASE_URL. See OtelSender.h for full priority chain. + #ifndef OTEL_SERVICE_NAME -#define OTEL_SERVICE_NAME "demo_service" +#define OTEL_SERVICE_NAME "demo_service" #endif #ifndef OTEL_SERVICE_INSTANCE @@ -60,91 +52,69 @@ #endif #ifndef OTEL_DEPLOY_ENV -#define OTEL_DEPLOY_ENV "dev" +#define OTEL_DEPLOY_ENV "dev" #endif -// Delay between heartbeats (ms) +// ───────────────────────────────────────────────────────────────────────────── + static constexpr uint32_t HEARTBEAT_INTERVAL = 5000; void setup() { Serial.begin(115200); - // —————————— - // 0) Seed the PRNG for truly fresh IDs each boot - // —————————— -#if defined(ARDUINO_ARCH_ESP32) - randomSeed(esp_random()); -#else - randomSeed(micros()); -#endif - - // 1) Connect to Wi-Fi - Serial.printf("Connecting to %s …\n", OTEL_WIFI_SSID); - WiFi.begin(OTEL_WIFI_SSID, OTEL_WIFI_PASS); + // Connect to Wi-Fi + Serial.printf("Connecting to %s\n", WIFI_SSID); + WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print('.'); } - Serial.println("\nWi-Fi connected!"); + Serial.println("\nWi-Fi connected"); - // 2) Sync NTP (configTime works on Pico W, ESP32 & ESP8266 in Arduino land) - // We're polling until we get something > Jan 1 2020 (1609459200). + // Sync NTP so telemetry timestamps are meaningful configTime(0, 0, "pool.ntp.org", "time.nist.gov"); - Serial.print("Waiting NTP sync"); + Serial.print("Waiting for NTP"); while (time(nullptr) < 1609459200UL) { - Serial.print('.'); delay(500); + Serial.print('.'); } Serial.println(); - // 3) (Optional) print the calendar time - { - time_t now = time(nullptr); - struct tm tm; - gmtime_r(&now, &tm); - Serial.printf("NTP time: %s", asctime(&tm)); - } + // Seed PRNG for fresh trace IDs each boot +#if defined(ARDUINO_ARCH_ESP32) + randomSeed(esp_random()); +#else + randomSeed(micros()); +#endif + + // Initialise tracer and metrics (scopeName, scopeVersion) + OTel::Tracer::begin("otel-embedded", OTEL_SERVICE_VERSION); + OTel::Metrics::begin("otel-embedded", OTEL_SERVICE_VERSION); - // 4) Init your OTLP logger & tracer - OTel::Logger::begin( - OTEL_SERVICE_NAME, - OTEL_SERVICE_NAMESPACE, - OTEL_SERVICE_INSTANCE, - OTEL_SERVICE_INSTANCE, - OTEL_SERVICE_VERSION - ); - - OTel::Tracer::begin( - OTEL_SERVICE_NAME, - OTEL_SERVICE_NAMESPACE, - OTEL_SERVICE_INSTANCE, - OTEL_SERVICE_VERSION - ); - Serial.println("OTLP Logger ready"); + // Tag every metric datapoint with deployment metadata + OTel::Metrics::setDefaultMetricLabel("deploy.environment", OTEL_DEPLOY_ENV); + OTel::Metrics::setDefaultMetricLabel("service.namespace", OTEL_SERVICE_NAMESPACE); + + // On RP2040, start the core-1 async send worker after Wi-Fi is ready +#if defined(ARDUINO_ARCH_RP2040) + OTelSender::beginAsyncWorker(); +#endif + + Serial.println("OTel ready"); } void loop() { - // Print the calendar time - { - time_t now = time(nullptr); - struct tm tm; - gmtime_r(&now, &tm); - Serial.printf("NTP time: %s", asctime(&tm)); - } - - // Start a new trace span called "heartbeat" + // Open a trace span auto span = OTel::Tracer::startSpan("heartbeat"); - // Emit a simple INFO log + // Log correlated to the active span OTel::Logger::logInfo("Heartbeat event"); - // Record a gauge - static OTel::OTelGauge heartbeatGauge("heartbeat.gauge", "1"); - heartbeatGauge.set(1.0f); + // Gauge: current value (no temporality) + OTel::Metrics::gauge("heartbeat.uptime_seconds", + static_cast(millis() / 1000), "s"); - // End the trace span (this actually sends the trace) span.end(); delay(HEARTBEAT_INTERVAL); } - diff --git a/examples/direct_otlp/README.md b/examples/direct_otlp/README.md new file mode 100644 index 0000000..ed7888c --- /dev/null +++ b/examples/direct_otlp/README.md @@ -0,0 +1,60 @@ +# direct_otlp example + +Demonstrates sending traces, metrics, and logs directly to a vendor OTLP/HTTP +endpoint (no intermediate collector required), and shows the full attribute and +tagging API across all three signals. + +## What it does + +Every 10 seconds the main loop: + +1. Opens a trace span with typed attributes (string, int, double, bool) +2. Emits a correlated log with per-call labels merged with setup-time defaults +3. Adds a timestamped span event if the simulated reading crosses a threshold +4. Records a gauge metric and a delta-temporality sum, both with per-call and + default labels +5. Closes the span + +## Attributes and tags + +| Where | API | Notes | +|---|---|---| +| Span | `span.setAttribute(key, value)` | String, int64, double, or bool | +| Span | `span.addEvent(name, attrs)` | Timestamped annotation within span | +| Metric (per-call) | last arg of `gauge()` / `sum()` | `{{"key", "val"}, ...}` | +| Metric (default) | `Metrics::setDefaultMetricLabel(k, v)` | Applied to every datapoint | +| Log (per-call) | last arg of `logInfo()` etc. | `{{"key", "val"}, ...}` | +| Log (default) | `Logger::setDefaultLabel(k, v)` | Applied to every log record | + +## Configuration + +All endpoint and authentication config lives in build flags — no credentials in +source. + +```ini +; platformio.ini +build_flags = + -DWIFI_SSID="\"your-ssid\"" + -DWIFI_PASS="\"your-password\"" + -DOTEL_EXPORTER_OTLP_ENDPOINT="\"https://your-collector\"" + -DOTEL_EXPORTER_OTLP_HEADERS="\"Authorization=Bearer your-token\"" +``` + +### Datadog (US1) + +```ini +build_flags = + -DOTEL_EXPORTER_OTLP_ENDPOINT="\"https://otlp.datadoghq.com\"" + -DOTEL_EXPORTER_OTLP_HEADERS="\"dd-api-key=${sysenv.DD_API_KEY}\"" +``` + +### Grafana Cloud + +```ini +build_flags = + -DOTEL_EXPORTER_OTLP_ENDPOINT="\"https://.grafana.net/otlp\"" + -DOTEL_EXPORTER_OTLP_HEADERS="\"Authorization=Basic \"" +``` + +HTTPS is enabled automatically when the endpoint URL starts with `https://`. +See the root [README](../../README.md) for the full list of configuration flags. diff --git a/examples/direct_otlp/main.cpp b/examples/direct_otlp/main.cpp new file mode 100644 index 0000000..2249e57 --- /dev/null +++ b/examples/direct_otlp/main.cpp @@ -0,0 +1,180 @@ +/* + * direct_otlp — send traces, metrics, and logs directly to any OTLP/HTTP + * vendor endpoint without an intermediate collector. + * + * All endpoint and authentication configuration lives in build flags so no + * credentials are baked into source. The relevant flags are: + * + * OTEL_EXPORTER_OTLP_ENDPOINT Base URL for all signals (paths appended + * automatically). Overrides the legacy + * OTEL_COLLECTOR_BASE_URL if both are set. + * + * OTEL_EXPORTER_OTLP_HEADERS Headers applied to every signal, as a + * comma-separated "key=value" list. + * Values containing commas must be + * percent-encoded (%2C). + * + * OTEL_EXPORTER_OTLP_*_ENDPOINT Per-signal endpoint override (used + * OTEL_EXPORTER_OTLP_*_HEADERS verbatim, no path appended). Useful + * when routing signals to different + * backends, or when a vendor requires + * signal-specific authentication. + * + * HTTPS is enabled automatically when an endpoint URL starts with "https://". + * Certificate validation is skipped by default (OTEL_TLS_INSECURE=1). + * + * ── Datadog (US1) example ──────────────────────────────────────────────────── + * + * build_flags = + * -DOTEL_EXPORTER_OTLP_ENDPOINT="\"https://otlp.datadoghq.com\"" + * -DOTEL_EXPORTER_OTLP_HEADERS="\"dd-api-key=${sysenv.DD_API_KEY}\"" + * + * Or route signals to separate endpoints with per-signal headers: + * + * -DOTEL_EXPORTER_OTLP_LOGS_ENDPOINT="\"https://otlp.datadoghq.com/v1/logs\"" + * -DOTEL_EXPORTER_OTLP_TRACES_ENDPOINT="\"https://otlp.datadoghq.com/v1/traces\"" + * -DOTEL_EXPORTER_OTLP_METRICS_ENDPOINT="\"https://otlp.datadoghq.com/v1/metrics\"" + * -DOTEL_EXPORTER_OTLP_METRICS_HEADERS="\"dd-api-key=${sysenv.DD_API_KEY},dd-otel-metric-config=%7B%22resource_attributes_as_tags%22%3Atrue%7D\"" + * + * Note: Datadog requires delta temporality for Sum metrics. This library + * defaults sum() to DELTA, so no extra configuration is needed. + * + * ── Grafana Cloud example ──────────────────────────────────────────────────── + * + * build_flags = + * -DOTEL_EXPORTER_OTLP_ENDPOINT="\"https://.grafana.net/otlp\"" + * -DOTEL_EXPORTER_OTLP_HEADERS="\"Authorization=Basic \"" + * + * ───────────────────────────────────────────────────────────────────────────── + */ + +#include + +#if defined(ESP8266) + #include +#elif defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_RP2040) + #include +#else + #error "Unsupported platform: must be ESP8266, ESP32, or RP2040" +#endif + +#include + +#include "OtelSender.h" +#include "OtelLogger.h" +#include "OtelTracer.h" +#include "OtelMetrics.h" + +// ── Build-time defaults (override via -D flags in platformio.ini) ───────────── + +#ifndef WIFI_SSID +#define WIFI_SSID "your-ssid" +#endif + +#ifndef WIFI_PASS +#define WIFI_PASS "your-password" +#endif + +// OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS are defined in +// OtelSender.h with empty defaults. Set them via build_flags — see above. + +#ifndef OTEL_DEPLOY_ENV +#define OTEL_DEPLOY_ENV "dev" +#endif + +// ───────────────────────────────────────────────────────────────────────────── + +static constexpr uint32_t SEND_INTERVAL_MS = 10000; + +void setup() { + Serial.begin(115200); + + // Connect to Wi-Fi + Serial.printf("Connecting to %s\n", WIFI_SSID); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print('.'); + } + Serial.println("\nWi-Fi connected"); + + // Sync NTP so timestamps in telemetry are meaningful + configTime(0, 0, "pool.ntp.org", "time.nist.gov"); + Serial.print("Waiting for NTP"); + while (time(nullptr) < 1609459200UL) { + delay(500); + Serial.print('.'); + } + Serial.println(); + + // Initialise tracer and metrics scope + OTel::Tracer::begin("direct-otlp-example", "1.0.0"); + OTel::Metrics::begin("direct-otlp-example", "1.0.0"); + + // Default attributes — merged into every metric datapoint and log record + // automatically, without needing to pass them on each call. + OTel::Metrics::setDefaultMetricLabel("device.id", "esp32-abc123"); + OTel::Metrics::setDefaultMetricLabel("deploy.env", OTEL_DEPLOY_ENV); + OTel::Logger::setDefaultLabel("device.id", "esp32-abc123"); + OTel::Logger::setDefaultLabel("deploy.env", OTEL_DEPLOY_ENV); + + // On RP2040, start the core-1 async send worker after Wi-Fi is ready +#if defined(ARDUINO_ARCH_RP2040) + OTelSender::beginAsyncWorker(); +#endif + + Serial.println("OTel ready — sending to: " OTEL_EXPORTER_OTLP_ENDPOINT); +} + +void loop() { + // ── Trace ────────────────────────────────────────────────────────────────── + { + auto span = OTel::Tracer::startSpan("sensor-read"); + + // Span attributes support multiple types: string, int, double, bool. + // These appear as tags on the span in your backend. + span.setAttribute("sensor.id", "1"); + span.setAttribute("sensor.type", "temperature"); + span.setAttribute("sensor.channel", (int64_t)0); + span.setAttribute("sensor.enabled", true); + + // ── Log (correlated to the active span via traceId/spanId) ─────────────── + // Per-call labels are merged with the defaults set in setup(). + OTel::Logger::logInfo("Reading sensor", {{"sensor.id", "1"}}); + + // Simulate a sensor read + float temperature = 20.0f + (random(0, 100) / 10.0f); + bool overThreshold = temperature > 28.0f; + + // Span event: a timestamped annotation within the span's duration. + if (overThreshold) { + span.addEvent("threshold-crossed", {{"threshold.celsius", "28.0"}}); + OTel::Logger::logWarn("Temperature above threshold", + {{"sensor.id", "1"}, {"value.celsius", String(temperature).c_str()}}); + } + + // ── Metrics ─────────────────────────────────────────────────────────────── + // gauge: point-in-time value, no temporality required. + // Per-call labels are merged with the defaults set in setup(). + OTel::Metrics::gauge("sensor.temperature", temperature, "Cel", + {{"sensor.id", "1"}}); + + // sum: monotonic counter, DELTA temporality (required by vendors like Datadog). + static double totalReadings = 0; + totalReadings += 1.0; + OTel::Metrics::sum("sensor.readings.total", totalReadings, + /*isMonotonic=*/true, "DELTA", "1", + {{"sensor.id", "1"}}); + + span.setAttribute("reading.celsius", (double)temperature); + span.end(); + } + + // ── Health diagnostics (optional) ───────────────────────────────────────── + uint32_t dropped = OTelSender::droppedCount(); + if (dropped > 0) { + Serial.printf("[otel] warning: %u items dropped from send queue\n", dropped); + } + + delay(SEND_INTERVAL_MS); +} From 3ee78a4689d7fcc4918cd01c82adfeaa11e518cd Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 16:38:24 -0500 Subject: [PATCH 04/30] chore: update CI, docs, and project metadata validate.yml: replace OTEL_COLLECTOR_HOST/PORT with the standard OTEL_EXPORTER_OTLP_ENDPOINT variable, fix actions/checkout version (v6 does not exist, corrected to v4), add build steps for the new direct_otlp example across all three platforms. README.md: update build flags and .env examples to use OTEL_EXPORTER_OTLP_ENDPOINT; fix quick-start snippet to use the correct current API; expand configuration table with all new exporter, header, and TLS macros; mark OTEL_COLLECTOR_BASE_URL as a legacy fallback. library.json: correct repository URL. .gitignore: ignore .claude and .vscode directories. --- .github/workflows/validate.yml | 24 ++++-- .gitignore | 4 + README.md | 134 ++++++++++++++++++--------------- library.json | 2 +- 4 files changed, 95 insertions(+), 69 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index c6794d0..6f9eec6 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -21,15 +21,15 @@ jobs: - name: Install PlatformIO run: python -m pip install -U platformio - # Make repo Variables + Secrets available to all subsequent steps + # Expose repo variables and secrets as environment variables for PlatformIO + # sysenv.* references in platformio.ini build_flags pick these up at build time. - name: Export env for build run: | echo "::add-mask::${{ secrets.WIFI_PASS }}" { echo "WIFI_SSID=${{ vars.WIFI_SSID }}" echo "WIFI_PASS=${{ secrets.WIFI_PASS }}" - echo "OTEL_COLLECTOR_HOST=${{ vars.OTEL_COLLECTOR_HOST }}" - echo "OTEL_COLLECTOR_PORT=${{ vars.OTEL_COLLECTOR_PORT }}" + echo "OTEL_EXPORTER_OTLP_ENDPOINT=${{ vars.OTEL_EXPORTER_OTLP_ENDPOINT || '' }}" echo "OTEL_SERVICE_NAME=${{ vars.OTEL_SERVICE_NAME }}" echo "OTEL_SERVICE_NAMESPACE=${{ vars.OTEL_SERVICE_NAMESPACE }}" echo "OTEL_SERVICE_VERSION=${{ vars.OTEL_SERVICE_VERSION }}" @@ -40,12 +40,24 @@ jobs: - name: PlatformIO Update run: pio update - - name: Build for ESP32 (esp32dev) + # ── src/main.cpp (existing integration test) ─────────────────────────── + + - name: Build src for ESP32 (esp32dev) run: platformio ci src/main.cpp --project-conf platformio.ini --lib "." -e esp32dev - - name: Build for Pico W (rpipicow) + - name: Build src for Pico W (rpipicow) run: platformio ci src/main.cpp --project-conf platformio.ini --lib "." -e rpipicow - - name: Build for ESP8266 (esp8266 d1_mini) + - name: Build src for ESP8266 (d1_mini) run: platformio ci src/main.cpp --project-conf platformio.ini --lib "." -e esp8266 + # ── examples/direct_otlp (validates OTLP exporter configuration) ─────── + + - name: Build direct_otlp example for ESP32 (esp32dev) + run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp32dev + + - name: Build direct_otlp example for Pico W (rpipicow) + run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e rpipicow + + - name: Build direct_otlp example for ESP8266 (d1_mini) + run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp8266 diff --git a/.gitignore b/.gitignore index 585145b..534506e 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,7 @@ # Environment secrets .env + +# Local dev +.claude +.vscode \ No newline at end of file diff --git a/README.md b/README.md index faa0845..4b1272b 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,10 @@ This removes any blocking code and ensures that the HTTP POST call does not inte ```ini build_flags = - -DWIFI_SSID="${sysenv.OTEL_WIFI_SSID}" - -DWIFI_PASS="${sysenv.OTEL_WIFI_PASS}" - -DOTEL_COLLECTOR_HOST="${sysenv.OTEL_COLLECTOR_HOST}" - -DOTEL_COLLECTOR_PORT=${sysenv.OTEL_COLLECTOR_PORT} + -DWIFI_SSID="${sysenv.WIFI_SSID}" + -DWIFI_PASS="${sysenv.WIFI_PASS}" + -DOTEL_EXPORTER_OTLP_ENDPOINT="\"${sysenv.OTEL_EXPORTER_OTLP_ENDPOINT}\"" + -DOTEL_EXPORTER_OTLP_HEADERS="\"${sysenv.OTEL_EXPORTER_OTLP_HEADERS}\"" -DOTEL_SERVICE_NAME="${sysenv.OTEL_SERVICE_NAME}" -DOTEL_SERVICE_NAMESPACE="${sysenv.OTEL_SERVICE_NAMESPACE}" -DOTEL_SERVICE_VERSION="${sysenv.OTEL_SERVICE_VERSION}" @@ -59,10 +59,9 @@ This removes any blocking code and ensures that the HTTP POST call does not inte 3. **(Optional)** Use a `.env` file and load it into your shell: ```dotenv - OTEL_WIFI_SSID=default - OTEL_WIFI_PASS=default - OTEL_COLLECTOR_HOST=192.168.1.100 - OTEL_COLLECTOR_PORT=4318 + WIFI_SSID=default + WIFI_PASS=default + OTEL_EXPORTER_OTLP_ENDPOINT=http://192.168.1.100:4318 OTEL_SERVICE_NAME=demo_service OTEL_SERVICE_NAMESPACE=demo_namespace OTEL_SERVICE_VERSION=v1.0.0 @@ -87,23 +86,19 @@ This removes any blocking code and ensures that the HTTP POST call does not inte #if defined(ESP32) #include + #include #elif defined(ESP8266) #include #elif defined(ARDUINO_ARCH_RP2040) - // Earle Philhower’s Arduino-Pico core exposes a WiFi.h for Pico W #include #else #error "This example targets ESP32, ESP8266, or RP2040 (Pico W) with WiFi." #endif -// --------------------------------------------------------- -// Import Open Telemetry Libraries -// --------------------------------------------------------- -#include "OtelDefaults.h" +#include "OtelSender.h" #include "OtelTracer.h" #include "OtelLogger.h" #include "OtelMetrics.h" -#include "OtelDebug.h" static constexpr uint32_t HEARTBEAT_INTERVAL = 5000; @@ -111,52 +106,39 @@ void setup() { Serial.begin(115200); // Seed PRNG (fresh trace IDs each boot) - #if defined(ARDUINO_ARCH_ESP32) - randomSeed(esp_random()); - #else - randomSeed(micros()); - #endif - - // Connect to Wi‑Fi - WiFi.begin(OTEL_WIFI_SSID, OTEL_WIFI_PASS); +#if defined(ARDUINO_ARCH_ESP32) + randomSeed(esp_random()); +#else + randomSeed(micros()); +#endif + + // Connect to Wi-Fi + WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); } // Sync NTP configTime(0, 0, "pool.ntp.org", "time.nist.gov"); while (time(nullptr) < 1609459200UL) { delay(500); } - // Initialise Logger & Tracer - - // Set the defaults for the resources - auto &res = OTel::defaultResource(); - res.set("service", OTEL_SERVICE_NAME); - res.set("service.name", OTEL_SERVICE_NAME); - res.set("service.namespace", OTEL_SERVICE_NAMESPACE); - res.set("service.instance.id", OTEL_SERVICE_INSTANCE); - res.set("host.name", "my-embedded device"); - - // Setup our tracing engine - OTel::Tracer::begin("otel-embedded", "1.0.1"); - - // Make sure that we start with empty trace and span ID's - OTel::currentTraceContext().traceId = ""; - OTel::currentTraceContext().spanId = ""; - - // Setup the metrics engine - OTel::Metrics::begin("otel-embedded", "1.0.1"); - OTel::Metrics::setDefaultMetricLabel("device.role", "test-device"); - OTel::Metrics::setDefaultMetricLabel("device.id", "device-chip-id-or-mac"); + // Initialise tracer and metrics (scopeName, scopeVersion) + OTel::Tracer::begin("otel-embedded", "1.0.0"); + OTel::Metrics::begin("otel-embedded", "1.0.0"); + + // On RP2040, start the core-1 async send worker after Wi-Fi is ready +#if defined(ARDUINO_ARCH_RP2040) + OTelSender::beginAsyncWorker(); +#endif } void loop() { - // Heartbeat trace auto span = OTel::Tracer::startSpan("heartbeat"); OTel::Logger::logInfo("Heartbeat event"); - static OTel::OTelGauge gauge("heartbeat.gauge", "1"); - gauge.set(1.0f); - span.end(); + OTel::Metrics::gauge("heartbeat.uptime_seconds", + static_cast(millis() / 1000), "s"); + + span.end(); delay(HEARTBEAT_INTERVAL); } ``` @@ -164,8 +146,8 @@ void loop() { This will emit: * **Traces** for each `startSpan("heartbeat")` -* **Logs** with `service.*` resource attributes -* **Metrics** via `OTelGauge`, `OTelCounter` and `OTelHistogram` +* **Logs** correlated to the active span via `traceId`/`spanId` +* **Metrics** as a gauge via `Metrics::gauge()` All data is sent over OTLP/HTTP to the configured collector. @@ -173,22 +155,50 @@ All data is sent over OTLP/HTTP to the configured collector. ## 🛠 Configuration Macros -Override defaults in `OtelDefaults.h` or via `-D` flags: +Set via `-D` flags in `platformio.ini` `build_flags`. + +### Endpoint + +| Macro | Default | Description | +| ------------------------------------------ | ------------------------------ | ----------- | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | *(empty)* | Base URL for all signals; `/v1/traces`, `/v1/metrics`, `/v1/logs` are appended automatically. Follows the [OTel exporter spec](https://opentelemetry.io/docs/specs/otel/protocol/exporter/). Takes priority over `OTEL_COLLECTOR_BASE_URL`. | +| `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | *(empty)* | Per-signal endpoint override, used verbatim (no path appended). Overrides `OTEL_EXPORTER_OTLP_ENDPOINT` for logs. | +| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | *(empty)* | Same, for traces. | +| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | *(empty)* | Same, for metrics. | +| `OTEL_COLLECTOR_BASE_URL` | `http://192.168.8.50:4318` | Legacy base URL fallback. Prefer `OTEL_EXPORTER_OTLP_ENDPOINT` for new setups. | + +### Headers & Authentication + +| Macro | Default | Description | +| ------------------------------------------ | --------- | ----------- | +| `OTEL_EXPORTER_OTLP_HEADERS` | *(empty)* | Comma-separated `key=value` headers added to every request. Values containing commas must be percent-encoded. Example: `"dd-api-key=abc123"` | +| `OTEL_EXPORTER_OTLP_LOGS_HEADERS` | *(empty)* | Per-signal header overrides, merged on top of `OTEL_EXPORTER_OTLP_HEADERS`. | +| `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | *(empty)* | Same, for traces. | +| `OTEL_EXPORTER_OTLP_METRICS_HEADERS` | *(empty)* | Same, for metrics. | + +### TLS + +| Macro | Default | Description | +| -------------------- | ------- | ----------- | +| `OTEL_TLS_INSECURE` | `1` | When `1`, HTTPS connections skip certificate validation. Set to `0` for strict validation (requires a CA cert — see `OtelSender.h`). | + +### Service identity | Macro | Default | Description | | ------------------------ | ------------------ | ----------------------------------------------- | -| `WIFI_SSID` | `"default"` | Wi‑Fi SSID | -| `WIFI_PASS` | `"default"` | Wi‑Fi password | -| `OTEL_COLLECTOR_BASE_URL`| `Null` | The base URL (http://192.168.8.10:4318) of the otel collector | -| `OTEL_SERVICE_NAME` | `"demo_service"` | Name of your service | -| `OTEL_SERVICE_NAMESPACE` | `"demo_namespace"` | Service namespace | -| `OTEL_SERVICE_VERSION` | `"v1.0.0"` | Semantic version | -| `OTEL_SERVICE_INSTANCE` | `"instance-1"` | Unique instance ID | -| `OTEL_DEPLOY_ENV` | `"dev"` | Deployment environment (e.g. `prod`, `staging`) | -| `OTEL_WORKER_BURST` | `16` | The number of telemetry messages to process at a time | -| `OTEL_WORKER_SLEEP_MS` | `0` | How long to sleep between processing messages (0 is instant) | -| `OTEL_QUEUE_CAPACITY` | `128` | The maximum number of telemetry messages we can store before we start to drop data | -| `DEBUG` | `Null` | Print verbose messages including OTEL Payload to the serial port | +| `OTEL_SERVICE_NAME` | `"embedded-service"` | Name of your service | +| `OTEL_SERVICE_INSTANCE_ID` | chip ID | Unique instance identifier | +| `OTEL_HOST_NAME` | `"ESP-"` | Host name reported in resource attributes | + +### Send behaviour + +| Macro | Default | Description | +| --------------------- | ------- | ----------- | +| `OTEL_SEND_ENABLE` | `1` | Set to `0` to compile out all network sends (useful for latency benchmarking). | +| `OTEL_WORKER_BURST` | `8` | Items dequeued and sent per worker loop iteration (RP2040). | +| `OTEL_WORKER_SLEEP_MS`| `0` | Delay between worker iterations in ms. | +| `OTEL_QUEUE_CAPACITY` | `16` | SPSC queue depth for the RP2040 core-1 sender. | +| `DEBUG` | *(unset)* | Print verbose output including OTLP payloads to Serial. | --- diff --git a/library.json b/library.json index ae904c7..07bbfd9 100644 --- a/library.json +++ b/library.json @@ -35,6 +35,6 @@ ], "repository": { "type": "git", - "url": "https://github.com/your-username/otel-embedded-cpp.git" + "url": "https://github.com/proffalken/otel-embedded-cpp.git" } } From 02e296f19a1482e9607bd0e60e35eebe25314a54 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 16:56:58 -0500 Subject: [PATCH 05/30] fix(metrics): correct aggregationTemporality enum values DELTA = 1, CUMULATIVE = 2 per the OTLP proto spec. Previous commit had these reversed (CUMULATIVE = 1, DELTA = 2), which would have caused DELTA payloads to be interpreted as CUMULATIVE by spec-compliant collectors. --- src/OtelMetrics.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/OtelMetrics.cpp b/src/OtelMetrics.cpp index 487eee3..de35f1b 100644 --- a/src/OtelMetrics.cpp +++ b/src/OtelMetrics.cpp @@ -105,13 +105,15 @@ void Metrics::buildAndSendSum(const String& name, double value, metric["type"] = "sum"; // OTLP JSON encodes AggregationTemporality as an integer enum, not a string. - // AGGREGATION_TEMPORALITY_CUMULATIVE = 1, AGGREGATION_TEMPORALITY_DELTA = 2. + // From opentelemetry/proto/metrics/v1/metrics.proto: + // AGGREGATION_TEMPORALITY_DELTA = 1 + // AGGREGATION_TEMPORALITY_CUMULATIVE = 2 // Previously this sent the raw string (e.g. "DELTA"), which is not spec-compliant. // Spec-compliant collectors (including Datadog direct OTLP) require the integer. // Collectors that were previously accepting the string may silently ignore or // default the field — switching to the integer is the correct behaviour. // The public API (passing "DELTA" / "CUMULATIVE" to sum()) is unchanged. - int temporalityInt = (temporality == "CUMULATIVE") ? 1 : 2; // default to DELTA + int temporalityInt = (temporality == "CUMULATIVE") ? 2 : 1; // default to DELTA JsonObject sum = metric["sum"].to(); sum["isMonotonic"] = isMonotonic; sum["aggregationTemporality"] = temporalityInt; From 7649eb322d872cdbf0c1e19bcd87c7bed49a2b99 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 17:22:00 -0500 Subject: [PATCH 06/30] feat(proto): add full protobuf/nanopb encoding path for all three OTLP signals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements OTEL_EXPORTER_OTLP_PROTOCOL compile-time switch so callers set -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF and the library transparently encodes metrics, logs, and traces as binary protobuf instead of JSON — no API changes required. Key changes: - OtelSender: add sendProto(), per-signal headers, HTTPS auto-detection, three-level URL priority (per-signal > base > legacy), contentType in the SPSC queue item - OtelProtoEncoder: new file implementing nanopb encode helpers and all four signal senders (sendGauge, sendSum, sendLog, sendSpan); trace proto is hand-streamed using raw field numbers since it is absent from the reference generated set - Proto descriptor files copied from reference as .pb.inc to prevent PlatformIO auto-compilation; included under the protocol guard - OtelMetrics, OtelLogger, OtelTracer: #if guards route each signal to the proto encoder or JSON path depending on the protocol flag; default labels are merged at the call site for metrics and passed separately for logs - library.json / platformio.ini: add nanopb/Nanopb@^0.4.91 dependency --- include/OtelLogger.h | 10 + include/OtelProtoEncoder.h | 65 + include/OtelSender.h | 37 +- include/OtelTracer.h | 39 + library.json | 4 + platformio.ini | 3 + src/OtelMetrics.cpp | 17 + src/OtelProtoEncoder.cpp | 622 +++++++++ src/OtelSender.cpp | 43 +- .../opentelemetry/proto/common/v1/common.pb.h | 315 +++++ .../proto/common/v1/common.pb.inc | 35 + .../opentelemetry/proto/logs/v1/logs.pb.h | 371 +++++ .../opentelemetry/proto/logs/v1/logs.pb.inc | 25 + .../proto/metrics/v1/metrics.pb.h | 1244 +++++++++++++++++ .../proto/metrics/v1/metrics.pb.inc | 69 + .../proto/resource/v1/resource.pb.h | 89 ++ .../proto/resource/v1/resource.pb.inc | 12 + 17 files changed, 2987 insertions(+), 13 deletions(-) create mode 100644 include/OtelProtoEncoder.h create mode 100644 src/OtelProtoEncoder.cpp create mode 100644 src/proto/opentelemetry/proto/common/v1/common.pb.h create mode 100644 src/proto/opentelemetry/proto/common/v1/common.pb.inc create mode 100644 src/proto/opentelemetry/proto/logs/v1/logs.pb.h create mode 100644 src/proto/opentelemetry/proto/logs/v1/logs.pb.inc create mode 100644 src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h create mode 100644 src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc create mode 100644 src/proto/opentelemetry/proto/resource/v1/resource.pb.h create mode 100644 src/proto/opentelemetry/proto/resource/v1/resource.pb.inc diff --git a/include/OtelLogger.h b/include/OtelLogger.h index 78aa8de..1dd9feb 100644 --- a/include/OtelLogger.h +++ b/include/OtelLogger.h @@ -82,6 +82,15 @@ class Logger { static void buildAndSend(const String& severity, const String& message, const std::map& labels) { +#if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + { + auto& ctx = currentTraceContext(); + OTel::Proto::sendLog(severity, severityNumberFromText(severity), message, + labels, defaultLabels(), + ctx.valid() ? ctx.traceId : String(""), + ctx.valid() ? ctx.spanId : String("")); + } +#else // Build OTLP/HTTP logs payload (ArduinoJson v7) JsonDocument doc; @@ -136,6 +145,7 @@ class Logger { // Send OTelSender::sendJson("/v1/logs", doc); +#endif // OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF } }; diff --git a/include/OtelProtoEncoder.h b/include/OtelProtoEncoder.h new file mode 100644 index 0000000..cb0be03 --- /dev/null +++ b/include/OtelProtoEncoder.h @@ -0,0 +1,65 @@ +#pragma once + +// This header is only active when the protobuf protocol is selected. +// It provides encode functions called by Metrics, Logger, and Tracer +// instead of building a JsonDocument. +#if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + +#include +#include +#include +#include + +namespace OTel { +namespace Proto { + +// Typed attribute mirroring Span::Attr — forward declared here so Tracer +// can pass its internal buffer without a circular dependency. +enum class AttrType { Str, Int, Dbl, Bool }; +struct Attr { + String key; + AttrType type{AttrType::Str}; + String s; + int64_t i{0}; + double d{0.0}; + bool b{false}; +}; +struct Event { + String name; + uint64_t t{0}; + std::vector attrs; +}; + +// ── Signal encoders ────────────────────────────────────────────────────────── + +// Encode and send a Gauge metric datapoint. +void sendGauge(const String& name, double value, const String& unit, + const std::map& labels); + +// Encode and send a Sum metric datapoint. +// temporality: 1 = DELTA, 2 = CUMULATIVE +void sendSum(const String& name, double value, bool isMonotonic, + int temporality, const String& unit, + const std::map& labels); + +// Encode and send a LogRecord. +void sendLog(const String& severity, int severityNum, const String& message, + const std::map& callLabels, + const std::map& defaultLabels, + const String& traceId, const String& spanId); + +// Encode and send a Span. +// traceId / spanId / parentSpanId are 32/16/16 hex chars respectively. +void sendSpan(const String& name, + const String& traceId, + const String& spanId, + const String& parentSpanId, + uint64_t startNs, + uint64_t endNs, + const std::vector& attrs, + const std::vector& events); + +} // namespace Proto +} // namespace OTel + +#endif // OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF diff --git a/include/OtelSender.h b/include/OtelSender.h index 18f328b..2013d4a 100644 --- a/include/OtelSender.h +++ b/include/OtelSender.h @@ -71,6 +71,30 @@ #define OTEL_TLS_INSECURE 1 #endif +// OTLP wire protocol. +// Matches the standard OTEL_EXPORTER_OTLP_PROTOCOL environment variable values. +// Use the named constants below rather than raw integers. +// +// Default (JSON): +// no flag needed — http/json is the default +// +// Protobuf: +// -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF +// +// Protobuf requires the nanopb/Nanopb library in lib_deps. +#define OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON 0 +#define OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF 1 + +#ifndef OTEL_EXPORTER_OTLP_PROTOCOL +#define OTEL_EXPORTER_OTLP_PROTOCOL OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON +#endif + +// Stack buffer size for protobuf encoding (bytes). Increase if you have many +// attributes or long string values and see encoding failures. +#ifndef OTEL_PROTO_BUFFER_SIZE +#define OTEL_PROTO_BUFFER_SIZE 1024 +#endif + // Internal queue capacity for async sender on RP2040. // Keep small to bound RAM; increase if you see drops. #ifndef OTEL_QUEUE_CAPACITY @@ -78,15 +102,20 @@ #endif struct OTelQueuedItem { - const char* path; // "/v1/logs", "/v1/traces", "/v1/metrics" - String payload; // serialized JSON + const char* path; // "/v1/logs", "/v1/traces", "/v1/metrics" + const char* contentType; // "application/json" or "application/x-protobuf" + String payload; // serialized body (JSON text or raw protobuf bytes) }; class OTelSender { public: - // Main API: called by logger/tracer/metrics to send serialized JSON to OTLP/HTTP + // Send a JSON document (OTLP/HTTP JSON, Content-Type: application/json) static void sendJson(const char* path, JsonDocument& doc); + // Send a pre-encoded protobuf buffer (OTLP/HTTP Protobuf, + // Content-Type: application/x-protobuf). Used by OtelProtoEncoder. + static void sendProto(const char* path, const uint8_t* buf, size_t len); + // Start the RP2040 core-1 worker (no-op on non-RP2040). Call once after Wi-Fi is ready. static void beginAsyncWorker(); @@ -103,7 +132,7 @@ class OTelSender { static std::atomic drops_; static std::atomic worker_started_; - static bool enqueue_(const char* path, String&& payload); + static bool enqueue_(const char* path, const char* contentType, String&& payload); static bool dequeue_(OTelQueuedItem& out); // ---------- Worker ---------- diff --git a/include/OtelTracer.h b/include/OtelTracer.h index 6ddca27..adb7857 100644 --- a/include/OtelTracer.h +++ b/include/OtelTracer.h @@ -10,6 +10,7 @@ #include "OtelDebug.h" #include "OtelDefaults.h" // expects: nowUnixNano() #include "OtelSender.h" // expects: OTelSender::sendJson(const char* path, const JsonDocument&) +#include "OtelProtoEncoder.h" #if defined(ESP32) #include // esp_random, esp_fill_random @@ -553,6 +554,43 @@ class Span { const uint64_t endNs = nowUnixNano(); +#if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + { + std::vector protoAttrs; + protoAttrs.reserve(attrs_.size()); + for (const auto& a : attrs_) { + OTel::Proto::Attr pa; + pa.key = a.key; + pa.type = static_cast(static_cast(a.type)); + pa.s = a.s; + pa.i = a.i; + pa.d = a.d; + pa.b = a.b; + protoAttrs.push_back(std::move(pa)); + } + std::vector protoEvents; + protoEvents.reserve(events_.size()); + for (const auto& ev : events_) { + OTel::Proto::Event pe; + pe.name = ev.name; + pe.t = ev.t; + pe.attrs.reserve(ev.attrs.size()); + for (const auto& a : ev.attrs) { + OTel::Proto::Attr pa; + pa.key = a.key; + pa.type = static_cast(static_cast(a.type)); + pa.s = a.s; + pa.i = a.i; + pa.d = a.d; + pa.b = a.b; + pe.attrs.push_back(std::move(pa)); + } + protoEvents.push_back(std::move(pe)); + } + OTel::Proto::sendSpan(name_, traceId_, spanId_, prevSpanId_, + startNs_, endNs, protoAttrs, protoEvents); + } +#else // Build minimal OTLP/HTTP JSON payload for a single span JsonDocument doc; @@ -624,6 +662,7 @@ class Span { // Send OTelSender::sendJson("/v1/traces", doc); +#endif // OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF // Restore previous active context currentTraceContext().traceId = prevTraceId_; diff --git a/library.json b/library.json index 07bbfd9..cb579be 100644 --- a/library.json +++ b/library.json @@ -31,6 +31,10 @@ { "name": "ArduinoJson", "version": "^7.0.0" + }, + { + "name": "nanopb/Nanopb", + "version": "^0.4.91" } ], "repository": { diff --git a/platformio.ini b/platformio.ini index fa98f7f..5bdf2fc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,6 +9,7 @@ monitor_speed = 115200 lib_deps = ArduinoJson@^7.4.2 + nanopb/Nanopb@^0.4.91 WiFi HTTPClient @@ -32,6 +33,7 @@ framework = arduino ; Dependencies for micro-ROS and telemetry lib_deps = bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 build_flags = -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" @@ -53,6 +55,7 @@ framework = arduino ; Dependencies for micro-ROS and telemetry lib_deps = bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 build_flags = -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" diff --git a/src/OtelMetrics.cpp b/src/OtelMetrics.cpp index de35f1b..fe5593b 100644 --- a/src/OtelMetrics.cpp +++ b/src/OtelMetrics.cpp @@ -42,6 +42,13 @@ void Metrics::buildAndSendGauge(const String& name, double value, const String& unit, const std::map& labels) { +#if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + { + auto merged = defaultMetricLabels(); + for (const auto& kv : labels) merged[kv.first] = kv.second; + OTel::Proto::sendGauge(name, value, unit, merged); + } +#else JsonDocument doc; JsonArray resourceMetrics = doc["resourceMetrics"].to(); @@ -74,6 +81,7 @@ void Metrics::buildAndSendGauge(const String& name, double value, addPointAttributes(attrs, labels); OTelSender::sendJson("/v1/metrics", doc); +#endif // OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF } // ----------------- SUM ------------------- @@ -83,6 +91,14 @@ void Metrics::buildAndSendSum(const String& name, double value, const String& unit, const std::map& labels) { +#if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + { + auto merged = defaultMetricLabels(); + for (const auto& kv : labels) merged[kv.first] = kv.second; + int temporalityInt = (temporality == "CUMULATIVE") ? 2 : 1; + OTel::Proto::sendSum(name, value, isMonotonic, temporalityInt, unit, merged); + } +#else JsonDocument doc; JsonArray resourceMetrics = doc["resourceMetrics"].to(); @@ -128,6 +144,7 @@ void Metrics::buildAndSendSum(const String& name, double value, addPointAttributes(attrs, labels); OTelSender::sendJson("/v1/metrics", doc); +#endif // OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF } } // namespace OTel diff --git a/src/OtelProtoEncoder.cpp b/src/OtelProtoEncoder.cpp new file mode 100644 index 0000000..4fb3853 --- /dev/null +++ b/src/OtelProtoEncoder.cpp @@ -0,0 +1,622 @@ +#include "OtelSender.h" + +#if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + +// Pull in the nanopb-generated descriptor tables. Using .inc avoids +// PlatformIO auto-compiling these as independent translation units. +#include "proto/opentelemetry/proto/common/v1/common.pb.inc" +#include "proto/opentelemetry/proto/resource/v1/resource.pb.inc" +#include "proto/opentelemetry/proto/metrics/v1/metrics.pb.inc" +#include "proto/opentelemetry/proto/logs/v1/logs.pb.inc" + +#include "proto/opentelemetry/proto/common/v1/common.pb.h" +#include "proto/opentelemetry/proto/resource/v1/resource.pb.h" +#include "proto/opentelemetry/proto/metrics/v1/metrics.pb.h" +#include "proto/opentelemetry/proto/logs/v1/logs.pb.h" + +#include "OtelProtoEncoder.h" +#include "OtelTracer.h" // defaultServiceName(), defaultServiceInstanceId(), defaultHostName() + +#include + +namespace OTel { +namespace Proto { + +// ── Primitive callbacks ────────────────────────────────────────────────────── + +// Encode a single C-string as a length-delimited protobuf bytes/string field. +static bool cb_cstr(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { + const char* str = *(const char* const*)arg; + if (!str || !*str) return true; // skip empty strings + return pb_encode_tag_for_field(s, f) && + pb_encode_string(s, (const pb_byte_t*)str, strlen(str)); +} + +// Encode a std::pair as a KeyValue with stringValue. +static bool cb_kv_cstr(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { + auto* p = *(const std::pair**)arg; + + opentelemetry_proto_common_v1_KeyValue kv = opentelemetry_proto_common_v1_KeyValue_init_zero; + kv.key.funcs.encode = cb_cstr; + kv.key.arg = (void*)p->first; + kv.has_value = true; + kv.value.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; + kv.value.value.string_value.funcs.encode = cb_cstr; + kv.value.value.string_value.arg = (void*)p->second; + + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_common_v1_KeyValue_fields, &kv); +} + +// ── Map → repeated KeyValue ─────────────────────────────────── + +struct MapCtx { const std::map* m; }; + +static bool cb_map_attrs(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { + auto* ctx = *(MapCtx**)arg; + for (const auto& kv : *ctx->m) { + opentelemetry_proto_common_v1_KeyValue entry = opentelemetry_proto_common_v1_KeyValue_init_zero; + entry.key.funcs.encode = cb_cstr; + entry.key.arg = (void*)kv.first.c_str(); + entry.has_value = true; + entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; + entry.value.value.string_value.funcs.encode = cb_cstr; + entry.value.value.string_value.arg = (void*)kv.second.c_str(); + + if (!pb_encode_tag_for_field(s, f)) return false; + if (!pb_encode_submessage(s, opentelemetry_proto_common_v1_KeyValue_fields, &entry)) return false; + } + return true; +} + +// Merged map: encodes defaultLabels first, then callLabels. +struct MergedMapCtx { + const std::map* defaults; + const std::map* call; +}; + +static bool cb_merged_map_attrs(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { + auto* ctx = *(MergedMapCtx**)arg; + for (auto* m : {ctx->defaults, ctx->call}) { + for (const auto& kv : *m) { + opentelemetry_proto_common_v1_KeyValue entry = opentelemetry_proto_common_v1_KeyValue_init_zero; + entry.key.funcs.encode = cb_cstr; + entry.key.arg = (void*)kv.first.c_str(); + entry.has_value = true; + entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; + entry.value.value.string_value.funcs.encode = cb_cstr; + entry.value.value.string_value.arg = (void*)kv.second.c_str(); + + if (!pb_encode_tag_for_field(s, f)) return false; + if (!pb_encode_submessage(s, opentelemetry_proto_common_v1_KeyValue_fields, &entry)) return false; + } + } + return true; +} + +// ── Typed span/event attributes ────────────────────────────────────────────── + +struct AttrsCtx { const std::vector* attrs; }; + +static bool cb_typed_attrs(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { + auto* ctx = *(AttrsCtx**)arg; + for (const auto& a : *ctx->attrs) { + opentelemetry_proto_common_v1_KeyValue entry = opentelemetry_proto_common_v1_KeyValue_init_zero; + entry.key.funcs.encode = cb_cstr; + entry.key.arg = (void*)a.key.c_str(); + entry.has_value = true; + + switch (a.type) { + case AttrType::Str: + entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; + entry.value.value.string_value.funcs.encode = cb_cstr; + entry.value.value.string_value.arg = (void*)a.s.c_str(); + break; + case AttrType::Int: + entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_int_value_tag; + entry.value.value.int_value = a.i; + break; + case AttrType::Dbl: + entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_double_value_tag; + entry.value.value.double_value = a.d; + break; + case AttrType::Bool: + entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_bool_value_tag; + entry.value.value.bool_value = a.b; + break; + } + + if (!pb_encode_tag_for_field(s, f)) return false; + if (!pb_encode_submessage(s, opentelemetry_proto_common_v1_KeyValue_fields, &entry)) return false; + } + return true; +} + +// ── Resource builder ───────────────────────────────────────────────────────── + +// Fills a Resource with service.name / service.instance.id / host.name. +// Backed by three static String values so the c_str() pointers are stable +// for the duration of the encode call. +static void fillResource(opentelemetry_proto_resource_v1_Resource& res, + String& svcName, String& svcInst, String& hostName) { + svcName = defaultServiceName(); + svcInst = defaultServiceInstanceId(); + hostName = defaultHostName(); + + // Inline KeyValue list via a callback that emits exactly three entries. + struct ResCtx { const char* sn; const char* si; const char* hn; }; + static ResCtx rctx; + rctx = {svcName.c_str(), svcInst.c_str(), hostName.c_str()}; + + res.attributes.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(ResCtx**)arg; + const char* keys[] = {"service.name", "service.instance.id", "host.name"}; + const char* values[] = {c->sn, c->si, c->hn}; + for (int i = 0; i < 3; ++i) { + opentelemetry_proto_common_v1_KeyValue kv = opentelemetry_proto_common_v1_KeyValue_init_zero; + kv.key.funcs.encode = cb_cstr; kv.key.arg = (void*)keys[i]; + kv.has_value = true; + kv.value.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; + kv.value.value.string_value.funcs.encode = cb_cstr; + kv.value.value.string_value.arg = (void*)values[i]; + if (!pb_encode_tag_for_field(s, f)) return false; + if (!pb_encode_submessage(s, opentelemetry_proto_common_v1_KeyValue_fields, &kv)) return false; + } + return true; + }; + res.attributes.arg = &rctx; +} + +// ── Scope builder ──────────────────────────────────────────────────────────── + +static void fillScope(opentelemetry_proto_common_v1_InstrumentationScope& scope, + const char* name, const char* version) { + scope.name.funcs.encode = cb_cstr; scope.name.arg = (void*)name; + scope.version.funcs.encode = cb_cstr; scope.version.arg = (void*)version; +} + +// ── Hex string → raw bytes ─────────────────────────────────────────────────── + +static void hexToBytes(const String& hex, uint8_t* out, size_t len) { + for (size_t i = 0; i < len; ++i) { + size_t ci = i * 2; + if (ci + 1 >= (size_t)hex.length()) { out[i] = 0; continue; } + auto n = [](char c) -> uint8_t { + return (c >= 'a') ? c - 'a' + 10 : (c >= 'A') ? c - 'A' + 10 : c - '0'; + }; + out[i] = (n(hex[ci]) << 4) | n(hex[ci + 1]); + } +} + +// ── Encode + send helpers ──────────────────────────────────────────────────── + +static void encodeAndSend(const char* path, + const pb_msgdesc_t* fields, + const void* msg) { + uint8_t buf[OTEL_PROTO_BUFFER_SIZE]; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + if (pb_encode(&stream, fields, msg)) { + OTelSender::sendProto(path, buf, stream.bytes_written); + } + // Silent drop on encode failure — same behaviour as JSON OOM. +} + +// ════════════════════════════════════════════════════════════════════════════ +// Public API +// ════════════════════════════════════════════════════════════════════════════ + +// ── Metrics ────────────────────────────────────────────────────────────────── + +static void buildAndSendNumberMetric(const String& name, double value, + const String& unit, + const std::map& labels, + bool isGauge, + bool isMonotonic, + int temporality) { + using namespace OTel; + + String svcName, svcInst, hostName; + + // DataPoint + opentelemetry_proto_metrics_v1_NumberDataPoint dp = + opentelemetry_proto_metrics_v1_NumberDataPoint_init_zero; + dp.time_unix_nano = nowUnixNano(); + dp.which_value = opentelemetry_proto_metrics_v1_NumberDataPoint_as_double_tag; + dp.value.as_double = value; + MapCtx dpCtx{&labels}; + dp.attributes.funcs.encode = cb_map_attrs; + dp.attributes.arg = &dpCtx; + + // Metric (Gauge or Sum) + opentelemetry_proto_metrics_v1_Metric metric = + opentelemetry_proto_metrics_v1_Metric_init_zero; + metric.name.funcs.encode = cb_cstr; metric.name.arg = (void*)name.c_str(); + metric.unit.funcs.encode = cb_cstr; metric.unit.arg = (void*)unit.c_str(); + + // DataPoint callback for the metric type + struct DpCtx { opentelemetry_proto_metrics_v1_NumberDataPoint* dp; }; + static DpCtx dpWrap; + dpWrap.dp = &dp; + auto dp_cb = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(DpCtx**)arg; + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_metrics_v1_NumberDataPoint_fields, c->dp); + }; + + if (isGauge) { + metric.which_data = opentelemetry_proto_metrics_v1_Metric_gauge_tag; + metric.data.gauge.data_points.funcs.encode = dp_cb; + metric.data.gauge.data_points.arg = &dpWrap; + } else { + metric.which_data = opentelemetry_proto_metrics_v1_Metric_sum_tag; + metric.data.sum.is_monotonic = isMonotonic; + metric.data.sum.aggregation_temporality = + (opentelemetry_proto_metrics_v1_AggregationTemporality)temporality; + metric.data.sum.data_points.funcs.encode = dp_cb; + metric.data.sum.data_points.arg = &dpWrap; + } + + // ScopeMetrics + opentelemetry_proto_metrics_v1_ScopeMetrics sm = + opentelemetry_proto_metrics_v1_ScopeMetrics_init_zero; + fillScope(sm.scope, metricsScopeConfig().scopeName.c_str(), + metricsScopeConfig().scopeVersion.c_str()); + struct SmCtx { opentelemetry_proto_metrics_v1_Metric* m; }; + static SmCtx smCtx; smCtx.m = &metric; + sm.metrics.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(SmCtx**)arg; + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_metrics_v1_Metric_fields, c->m); + }; + sm.metrics.arg = &smCtx; + + // ResourceMetrics + opentelemetry_proto_metrics_v1_ResourceMetrics rm = + opentelemetry_proto_metrics_v1_ResourceMetrics_init_zero; + rm.has_resource = true; + fillResource(rm.resource, svcName, svcInst, hostName); + struct RmCtx { opentelemetry_proto_metrics_v1_ScopeMetrics* sm; }; + static RmCtx rmCtx; rmCtx.sm = &sm; + rm.scope_metrics.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(RmCtx**)arg; + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_metrics_v1_ScopeMetrics_fields, c->sm); + }; + rm.scope_metrics.arg = &rmCtx; + + // MetricsData + opentelemetry_proto_metrics_v1_MetricsData data = + opentelemetry_proto_metrics_v1_MetricsData_init_zero; + struct DataCtx { opentelemetry_proto_metrics_v1_ResourceMetrics* rm; }; + static DataCtx dataCtx; dataCtx.rm = &rm; + data.resource_metrics.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(DataCtx**)arg; + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_metrics_v1_ResourceMetrics_fields, c->rm); + }; + data.resource_metrics.arg = &dataCtx; + + encodeAndSend("/v1/metrics", + opentelemetry_proto_metrics_v1_MetricsData_fields, &data); +} + +void sendGauge(const String& name, double value, const String& unit, + const std::map& labels) { + buildAndSendNumberMetric(name, value, unit, labels, true, false, 0); +} + +void sendSum(const String& name, double value, bool isMonotonic, + int temporality, const String& unit, + const std::map& labels) { + buildAndSendNumberMetric(name, value, unit, labels, false, isMonotonic, temporality); +} + +// ── Logs ───────────────────────────────────────────────────────────────────── + +void sendLog(const String& severity, int severityNum, const String& message, + const std::map& callLabels, + const std::map& defaultLabels, + const String& traceId, const String& spanId) { + + String svcName, svcInst, hostName; + + // LogRecord + opentelemetry_proto_logs_v1_LogRecord lr = + opentelemetry_proto_logs_v1_LogRecord_init_zero; + lr.time_unix_nano = nowUnixNano(); + lr.severity_number = (opentelemetry_proto_logs_v1_SeverityNumber)severityNum; + lr.severity_text.funcs.encode = cb_cstr; + lr.severity_text.arg = (void*)severity.c_str(); + lr.has_body = true; + lr.body.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; + lr.body.value.string_value.funcs.encode = cb_cstr; + lr.body.value.string_value.arg = (void*)message.c_str(); + + MergedMapCtx logAttrs{&defaultLabels, &callLabels}; + lr.attributes.funcs.encode = cb_merged_map_attrs; + lr.attributes.arg = &logAttrs; + + // Trace correlation + uint8_t traceBytes[16]{}, spanBytes[8]{}; + if (traceId.length() == 32) { + hexToBytes(traceId, traceBytes, 16); + lr.trace_id.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + return pb_encode_tag_for_field(s, f) && + pb_encode_string(s, *(const pb_byte_t* const*)arg, 16); + }; + lr.trace_id.arg = traceBytes; + } + if (spanId.length() == 16) { + hexToBytes(spanId, spanBytes, 8); + lr.span_id.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + return pb_encode_tag_for_field(s, f) && + pb_encode_string(s, *(const pb_byte_t* const*)arg, 8); + }; + lr.span_id.arg = spanBytes; + } + + // ScopeLogs + opentelemetry_proto_logs_v1_ScopeLogs sl = + opentelemetry_proto_logs_v1_ScopeLogs_init_zero; + fillScope(sl.scope, logScopeConfig().scopeName.c_str(), + logScopeConfig().scopeVersion.c_str()); + struct SlCtx { opentelemetry_proto_logs_v1_LogRecord* lr; }; + static SlCtx slCtx; slCtx.lr = &lr; + sl.log_records.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(SlCtx**)arg; + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_logs_v1_LogRecord_fields, c->lr); + }; + sl.log_records.arg = &slCtx; + + // ResourceLogs + opentelemetry_proto_logs_v1_ResourceLogs rl = + opentelemetry_proto_logs_v1_ResourceLogs_init_zero; + rl.has_resource = true; + fillResource(rl.resource, svcName, svcInst, hostName); + struct RlCtx { opentelemetry_proto_logs_v1_ScopeLogs* sl; }; + static RlCtx rlCtx; rlCtx.sl = &sl; + rl.scope_logs.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(RlCtx**)arg; + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_logs_v1_ScopeLogs_fields, c->sl); + }; + rl.scope_logs.arg = &rlCtx; + + // LogsData + opentelemetry_proto_logs_v1_LogsData ld = + opentelemetry_proto_logs_v1_LogsData_init_zero; + struct LdCtx { opentelemetry_proto_logs_v1_ResourceLogs* rl; }; + static LdCtx ldCtx; ldCtx.rl = &rl; + ld.resource_logs.funcs.encode = [](pb_ostream_t* s, const pb_field_t* f, void* const* arg) -> bool { + auto* c = *(LdCtx**)arg; + return pb_encode_tag_for_field(s, f) && + pb_encode_submessage(s, opentelemetry_proto_logs_v1_ResourceLogs_fields, c->rl); + }; + ld.resource_logs.arg = &ldCtx; + + encodeAndSend("/v1/logs", + opentelemetry_proto_logs_v1_LogsData_fields, &ld); +} + +// ── Traces ─────────────────────────────────────────────────────────────────── +// Traces are not in the reference proto set, so we encode manually using +// nanopb's streaming API with the field numbers from the OTLP trace proto. +// Field numbers: https://github.com/open-telemetry/opentelemetry-proto +// TracesData.resource_spans = 1 +// ResourceSpans.resource = 1 +// ResourceSpans.scope_spans = 2 +// ScopeSpans.scope = 1 +// ScopeSpans.spans = 2 +// Span.trace_id = 1 (bytes, 16) +// Span.span_id = 2 (bytes, 8) +// Span.parent_span_id = 4 (bytes, 8) +// Span.name = 5 +// Span.kind = 6 (SERVER = 2) +// Span.start_time_unix_nano = 7 (fixed64) +// Span.end_time_unix_nano = 8 (fixed64) +// Span.attributes = 9 (repeated KeyValue) +// Span.events = 11 (repeated Span.Event) +// Span.Event.time_unix_nano = 1 (fixed64) +// Span.Event.name = 2 +// Span.Event.attributes = 3 (repeated KeyValue) +// +// InstrumentationScope.name = 1 +// InstrumentationScope.version = 2 +// Resource.attributes = 1 (repeated KeyValue) +// KeyValue.key = 1 +// KeyValue.value = 2 → AnyValue +// AnyValue.string_value = 1 (oneof) + +static bool writeVarInt(pb_ostream_t* s, uint64_t v) { + return pb_encode_varint(s, v); +} +static bool writeTag(pb_ostream_t* s, uint32_t field, pb_wire_type_t wt) { + return pb_encode_tag(s, wt, field); +} +static bool writeFixed64(pb_ostream_t* s, uint64_t v) { + uint8_t buf[8]; + for (int i = 0; i < 8; ++i) { buf[i] = v & 0xFF; v >>= 8; } + return pb_write(s, buf, 8); +} +static bool writeBytes(pb_ostream_t* s, uint32_t field, + const uint8_t* data, size_t len) { + return writeTag(s, field, PB_WT_STRING) && + pb_encode_varint(s, len) && + pb_write(s, data, len); +} +static bool writeString(pb_ostream_t* s, uint32_t field, const char* str) { + if (!str || !*str) return true; + size_t len = strlen(str); + return writeTag(s, field, PB_WT_STRING) && + pb_encode_varint(s, len) && + pb_write(s, (const pb_byte_t*)str, len); +} + +// Encode one KeyValue with a string value into stream. +static bool writeKVStr(pb_ostream_t* s, uint32_t field, + const char* key, const char* val) { + // KV submessage: two-pass (size then content) + auto writeKVBody = [](pb_ostream_t* s, const char* key, const char* val) -> bool { + if (!writeString(s, 1, key)) return false; // KeyValue.key + // KeyValue.value = AnyValue (field 2), AnyValue.string_value (field 1) + // AnyValue submessage + size_t avLen = 0; + pb_ostream_t sz = PB_OSTREAM_SIZING; + writeString(&sz, 1, val); // AnyValue.string_value + avLen = sz.bytes_written; + if (!writeTag(s, 2, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, avLen)) return false; + return writeString(s, 1, val); + }; + + pb_ostream_t sz = PB_OSTREAM_SIZING; + writeKVBody(&sz, key, val); + if (!writeTag(s, field, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz.bytes_written)) return false; + return writeKVBody(s, key, val); +} + +// Encode one typed Attr as a KeyValue submessage. +static bool writeAttr(pb_ostream_t* s, uint32_t field, const Attr& a) { + auto writeBody = [](pb_ostream_t* s, const Attr& a) -> bool { + if (!writeString(s, 1, a.key.c_str())) return false; + // Build AnyValue body + auto writeAVBody = [](pb_ostream_t* s, const Attr& a) -> bool { + switch (a.type) { + case AttrType::Str: + return writeString(s, 1, a.s.c_str()); + case AttrType::Bool: + return writeTag(s, 4, PB_WT_VARINT) && writeVarInt(s, a.b ? 1 : 0); + case AttrType::Int: + return writeTag(s, 3, PB_WT_VARINT) && writeVarInt(s, (uint64_t)a.i); + case AttrType::Dbl: { + if (!writeTag(s, 5, PB_WT_64BIT)) return false; + uint64_t v; memcpy(&v, &a.d, 8); + return writeFixed64(s, v); + } + } + return true; + }; + pb_ostream_t sz = PB_OSTREAM_SIZING; + writeAVBody(&sz, a); + if (!writeTag(s, 2, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz.bytes_written)) return false; + return writeAVBody(s, a); + }; + + pb_ostream_t sz = PB_OSTREAM_SIZING; + writeBody(&sz, a); + if (!writeTag(s, field, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz.bytes_written)) return false; + return writeBody(s, a); +} + +// Encode a Span.Event submessage. +static bool writeEvent(pb_ostream_t* s, uint32_t field, const Event& ev) { + auto writeBody = [](pb_ostream_t* s, const Event& ev) -> bool { + if (!writeTag(s, 1, PB_WT_64BIT)) return false; // time_unix_nano + if (!writeFixed64(s, ev.t)) return false; + if (!writeString(s, 2, ev.name.c_str())) return false; // name + for (const auto& a : ev.attrs) + if (!writeAttr(s, 3, a)) return false; // attributes + return true; + }; + pb_ostream_t sz = PB_OSTREAM_SIZING; + writeBody(&sz, ev); + if (!writeTag(s, field, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz.bytes_written)) return false; + return writeBody(s, ev); +} + +void sendSpan(const String& name, + const String& traceId, + const String& spanId, + const String& parentSpanId, + uint64_t startNs, + uint64_t endNs, + const std::vector& attrs, + const std::vector& events) { + + String svcName = defaultServiceName(); + String svcInst = defaultServiceInstanceId(); + String hostName = defaultHostName(); + + uint8_t traceBytes[16]{}, spanBytes[8]{}, parentBytes[8]{}; + bool hasParent = parentSpanId.length() == 16; + if (traceId.length() == 32) hexToBytes(traceId, traceBytes, 16); + if (spanId.length() == 16) hexToBytes(spanId, spanBytes, 8); + if (hasParent) hexToBytes(parentSpanId, parentBytes, 8); + + // Two-pass encode for the entire TracesData message. + auto writeSpanBody = [&](pb_ostream_t* s) -> bool { + if (!writeBytes(s, 1, traceBytes, 16)) return false; // trace_id + if (!writeBytes(s, 2, spanBytes, 8)) return false; // span_id + if (hasParent) + if (!writeBytes(s, 4, parentBytes, 8)) return false; // parent_span_id + if (!writeString(s, 5, name.c_str())) return false; // name + if (!writeTag(s, 6, PB_WT_VARINT)) return false; // kind = SERVER + if (!writeVarInt(s, 2)) return false; + if (!writeTag(s, 7, PB_WT_64BIT)) return false; // start_time + if (!writeFixed64(s, startNs)) return false; + if (!writeTag(s, 8, PB_WT_64BIT)) return false; // end_time + if (!writeFixed64(s, endNs)) return false; + for (const auto& a : attrs) + if (!writeAttr(s, 9, a)) return false; // attributes + for (const auto& ev : events) + if (!writeEvent(s, 11, ev)) return false; // events + return true; + }; + + auto writeScopeSpansBody = [&](pb_ostream_t* s) -> bool { + // scope (field 1): InstrumentationScope.name + version + auto writeScopeBody = [](pb_ostream_t* s) -> bool { + return writeString(s, 1, tracerConfig().scopeName.c_str()) && + writeString(s, 2, tracerConfig().scopeVersion.c_str()); + }; + pb_ostream_t sz = PB_OSTREAM_SIZING; writeScopeBody(&sz); + if (!writeTag(s, 1, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz.bytes_written)) return false; + if (!writeScopeBody(s)) return false; + // spans (field 2) + pb_ostream_t sz2 = PB_OSTREAM_SIZING; writeSpanBody(&sz2); + if (!writeTag(s, 2, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz2.bytes_written)) return false; + return writeSpanBody(s); + }; + + auto writeResourceSpansBody = [&](pb_ostream_t* s) -> bool { + // resource (field 1): three KeyValue entries + auto writeResBody = [&](pb_ostream_t* s) -> bool { + return writeKVStr(s, 1, "service.name", svcName.c_str()) && + writeKVStr(s, 1, "service.instance.id", svcInst.c_str()) && + writeKVStr(s, 1, "host.name", hostName.c_str()); + }; + pb_ostream_t sz = PB_OSTREAM_SIZING; writeResBody(&sz); + if (!writeTag(s, 1, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz.bytes_written)) return false; + if (!writeResBody(s)) return false; + // scope_spans (field 2) + pb_ostream_t sz2 = PB_OSTREAM_SIZING; writeScopeSpansBody(&sz2); + if (!writeTag(s, 2, PB_WT_STRING)) return false; + if (!pb_encode_varint(s, sz2.bytes_written)) return false; + return writeScopeSpansBody(s); + }; + + // TracesData { resource_spans = 1 } + pb_ostream_t sz = PB_OSTREAM_SIZING; + writeResourceSpansBody(&sz); + + uint8_t buf[OTEL_PROTO_BUFFER_SIZE]; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + if (writeTag(&stream, 1, PB_WT_STRING) && + pb_encode_varint(&stream, sz.bytes_written) && + writeResourceSpansBody(&stream)) { + OTelSender::sendProto("/v1/traces", buf, stream.bytes_written); + } +} + +} // namespace Proto +} // namespace OTel + +#endif // OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF diff --git a/src/OtelSender.cpp b/src/OtelSender.cpp index 7ae040a..a79522f 100644 --- a/src/OtelSender.cpp +++ b/src/OtelSender.cpp @@ -110,14 +110,15 @@ String OTelSender::fullUrl_(const char* path) { // Executes a single POST. Handles plain HTTP and HTTPS transparently based on // the URL scheme. Custom headers are applied after Content-Type. -static void doPost_(const String& url, const String& payload, const char* path) { +static void doPost_(const String& url, const String& payload, + const char* path, const char* contentType) { HTTPClient http; const auto& hdrs = headersForPath_(path); // Lambda keeps the send logic in one place regardless of client type. auto fire = [&](bool ok) { if (!ok) return; - http.addHeader("Content-Type", "application/json"); + http.addHeader("Content-Type", contentType); for (const auto& h : hdrs) http.addHeader(h.first, h.second); (void)http.POST(payload); http.end(); @@ -142,7 +143,7 @@ static void doPost_(const String& url, const String& payload, const char* path) // ---------- Queue (SPSC) ---------- -bool OTelSender::enqueue_(const char* path, String&& payload) { +bool OTelSender::enqueue_(const char* path, const char* contentType, String&& payload) { size_t h = head_.load(std::memory_order_relaxed); size_t t = tail_.load(std::memory_order_acquire); size_t next = (h + 1) % QCAP; @@ -154,8 +155,9 @@ bool OTelSender::enqueue_(const char* path, String&& payload) { drops_.fetch_add(1, std::memory_order_relaxed); } - q_[h].path = path; - q_[h].payload = std::move(payload); + q_[h].path = path; + q_[h].contentType = contentType; + q_[h].payload = std::move(payload); head_.store(next, std::memory_order_release); return true; } @@ -178,7 +180,7 @@ void OTelSender::pumpOnce_() { OTelQueuedItem it; if (!dequeue_(it)) return; #if OTEL_SEND_ENABLE - doPost_(fullUrl_(it.path), it.payload, it.path); + doPost_(fullUrl_(it.path), it.payload, it.path, it.contentType); #endif } @@ -188,7 +190,7 @@ void OTelSender::workerLoop_() { OTelQueuedItem it; if (!dequeue_(it)) break; #if OTEL_SEND_ENABLE - doPost_(fullUrl_(it.path), it.payload, it.path); + doPost_(fullUrl_(it.path), it.payload, it.path, it.contentType); #endif } delay(OTEL_WORKER_SLEEP_MS); @@ -222,6 +224,9 @@ bool OTelSender::queueIsHealthy() { // ---------- Public send API ---------- +static constexpr const char* kContentTypeJson = "application/json"; +static constexpr const char* kContentTypeProto = "application/x-protobuf"; + void OTelSender::sendJson(const char* path, JsonDocument& doc) { #if !OTEL_SEND_ENABLE (void)path; (void)doc; @@ -232,9 +237,29 @@ void OTelSender::sendJson(const char* path, JsonDocument& doc) { #ifdef ARDUINO_ARCH_RP2040 launchWorkerOnce_(); - enqueue_(path, std::move(payload)); + enqueue_(path, kContentTypeJson, std::move(payload)); +#else + doPost_(fullUrl_(path), payload, path, kContentTypeJson); +#endif +#endif +} + +void OTelSender::sendProto(const char* path, const uint8_t* buf, size_t len) { +#if !OTEL_SEND_ENABLE + (void)path; (void)buf; (void)len; + return; +#else + // Copy raw bytes into a String to reuse the existing queue/send path. + // The String is treated as an opaque byte container, not a text string. + String payload; + payload.reserve(len); + for (size_t i = 0; i < len; ++i) payload += (char)buf[i]; + +#ifdef ARDUINO_ARCH_RP2040 + launchWorkerOnce_(); + enqueue_(path, kContentTypeProto, std::move(payload)); #else - doPost_(fullUrl_(path), payload, path); + doPost_(fullUrl_(path), payload, path, kContentTypeProto); #endif #endif } diff --git a/src/proto/opentelemetry/proto/common/v1/common.pb.h b/src/proto/opentelemetry/proto/common/v1/common.pb.h new file mode 100644 index 0000000..b400159 --- /dev/null +++ b/src/proto/opentelemetry/proto/common/v1/common.pb.h @@ -0,0 +1,315 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_OPENTELEMETRY_PROTO_COMMON_V1_OPENTELEMETRY_PROTO_COMMON_V1_COMMON_PB_H_INCLUDED +#define PB_OPENTELEMETRY_PROTO_COMMON_V1_OPENTELEMETRY_PROTO_COMMON_V1_COMMON_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +/* ArrayValue is a list of AnyValue messages. We need ArrayValue as a message + since oneof in AnyValue does not allow repeated fields. */ +typedef struct _opentelemetry_proto_common_v1_ArrayValue { + /* Array of values. The array may be empty (contain 0 elements). */ + pb_callback_t values; +} opentelemetry_proto_common_v1_ArrayValue; + +/* KeyValueList is a list of KeyValue messages. We need KeyValueList as a message + since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need + a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to + avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches + are semantically equivalent. */ +typedef struct _opentelemetry_proto_common_v1_KeyValueList { + /* A collection of key/value pairs of key-value pairs. The list may be empty (may + contain 0 elements). + + The keys MUST be unique (it is not allowed to have more than one + value with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t values; +} opentelemetry_proto_common_v1_KeyValueList; + +/* Represents any type of attribute value. AnyValue may contain a + primitive value such as a string or integer or it may contain an arbitrary nested + object containing arrays, key-value lists and primitives. */ +typedef struct _opentelemetry_proto_common_v1_AnyValue { + pb_size_t which_value; + union { + pb_callback_t string_value; + bool bool_value; + int64_t int_value; + double double_value; + opentelemetry_proto_common_v1_ArrayValue array_value; + opentelemetry_proto_common_v1_KeyValueList kvlist_value; + pb_callback_t bytes_value; + } value; +} opentelemetry_proto_common_v1_AnyValue; + +/* Represents a key-value pair that is used to store Span attributes, Link + attributes, etc. */ +typedef struct _opentelemetry_proto_common_v1_KeyValue { + /* The key name of the pair. */ + pb_callback_t key; + /* The value of the pair. */ + bool has_value; + opentelemetry_proto_common_v1_AnyValue value; +} opentelemetry_proto_common_v1_KeyValue; + +/* InstrumentationScope is a message representing the instrumentation scope information + such as the fully qualified name and version. */ +typedef struct _opentelemetry_proto_common_v1_InstrumentationScope { + /* A name denoting the Instrumentation scope. + An empty instrumentation scope name means the name is unknown. */ + pb_callback_t name; + /* Defines the version of the instrumentation scope. + An empty instrumentation scope version means the version is unknown. */ + pb_callback_t version; + /* Additional attributes that describe the scope. [Optional]. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t attributes; + /* The number of attributes that were discarded. Attributes + can be discarded because their keys are too long or because there are too many + attributes. If this value is 0, then no attributes were dropped. */ + uint32_t dropped_attributes_count; +} opentelemetry_proto_common_v1_InstrumentationScope; + +/* A reference to an Entity. + Entity represents an object of interest associated with produced telemetry: e.g spans, metrics, profiles, or logs. + + Status: [Development] */ +typedef struct _opentelemetry_proto_common_v1_EntityRef { + /* The Schema URL, if known. This is the identifier of the Schema that the entity data + is recorded in. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + + This schema_url applies to the data in this message and to the Resource attributes + referenced by id_keys and description_keys. + TODO: discuss if we are happy with this somewhat complicated definition of what + the schema_url applies to. + + This field obsoletes the schema_url field in ResourceMetrics/ResourceSpans/ResourceLogs. */ + pb_callback_t schema_url; + /* Defines the type of the entity. MUST not change during the lifetime of the entity. + For example: "service" or "host". This field is required and MUST not be empty + for valid entities. */ + pb_callback_t type; + /* Attribute Keys that identify the entity. + MUST not change during the lifetime of the entity. The Id must contain at least one attribute. + These keys MUST exist in the containing {message}.attributes. */ + pb_callback_t id_keys; + /* Descriptive (non-identifying) attribute keys of the entity. + MAY change over the lifetime of the entity. MAY be empty. + These attribute keys are not part of entity's identity. + These keys MUST exist in the containing {message}.attributes. */ + pb_callback_t description_keys; +} opentelemetry_proto_common_v1_EntityRef; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define opentelemetry_proto_common_v1_AnyValue_init_default {0, {{{NULL}, NULL}}} +#define opentelemetry_proto_common_v1_ArrayValue_init_default {{{NULL}, NULL}} +#define opentelemetry_proto_common_v1_KeyValueList_init_default {{{NULL}, NULL}} +#define opentelemetry_proto_common_v1_KeyValue_init_default {{{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_default} +#define opentelemetry_proto_common_v1_InstrumentationScope_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define opentelemetry_proto_common_v1_EntityRef_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_common_v1_AnyValue_init_zero {0, {{{NULL}, NULL}}} +#define opentelemetry_proto_common_v1_ArrayValue_init_zero {{{NULL}, NULL}} +#define opentelemetry_proto_common_v1_KeyValueList_init_zero {{{NULL}, NULL}} +#define opentelemetry_proto_common_v1_KeyValue_init_zero {{{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_zero} +#define opentelemetry_proto_common_v1_InstrumentationScope_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define opentelemetry_proto_common_v1_EntityRef_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define opentelemetry_proto_common_v1_ArrayValue_values_tag 1 +#define opentelemetry_proto_common_v1_KeyValueList_values_tag 1 +#define opentelemetry_proto_common_v1_AnyValue_string_value_tag 1 +#define opentelemetry_proto_common_v1_AnyValue_bool_value_tag 2 +#define opentelemetry_proto_common_v1_AnyValue_int_value_tag 3 +#define opentelemetry_proto_common_v1_AnyValue_double_value_tag 4 +#define opentelemetry_proto_common_v1_AnyValue_array_value_tag 5 +#define opentelemetry_proto_common_v1_AnyValue_kvlist_value_tag 6 +#define opentelemetry_proto_common_v1_AnyValue_bytes_value_tag 7 +#define opentelemetry_proto_common_v1_KeyValue_key_tag 1 +#define opentelemetry_proto_common_v1_KeyValue_value_tag 2 +#define opentelemetry_proto_common_v1_InstrumentationScope_name_tag 1 +#define opentelemetry_proto_common_v1_InstrumentationScope_version_tag 2 +#define opentelemetry_proto_common_v1_InstrumentationScope_attributes_tag 3 +#define opentelemetry_proto_common_v1_InstrumentationScope_dropped_attributes_count_tag 4 +#define opentelemetry_proto_common_v1_EntityRef_schema_url_tag 1 +#define opentelemetry_proto_common_v1_EntityRef_type_tag 2 +#define opentelemetry_proto_common_v1_EntityRef_id_keys_tag 3 +#define opentelemetry_proto_common_v1_EntityRef_description_keys_tag 4 + +/* Struct field encoding specification for nanopb */ +#define opentelemetry_proto_common_v1_AnyValue_FIELDLIST(X, a) \ +X(a, CALLBACK, ONEOF, STRING, (value,string_value,value.string_value), 1) \ +X(a, STATIC, ONEOF, BOOL, (value,bool_value,value.bool_value), 2) \ +X(a, STATIC, ONEOF, INT64, (value,int_value,value.int_value), 3) \ +X(a, STATIC, ONEOF, DOUBLE, (value,double_value,value.double_value), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (value,array_value,value.array_value), 5) \ +X(a, STATIC, ONEOF, MESSAGE, (value,kvlist_value,value.kvlist_value), 6) \ +X(a, CALLBACK, ONEOF, BYTES, (value,bytes_value,value.bytes_value), 7) +#define opentelemetry_proto_common_v1_AnyValue_CALLBACK pb_default_field_callback +#define opentelemetry_proto_common_v1_AnyValue_DEFAULT NULL +#define opentelemetry_proto_common_v1_AnyValue_value_array_value_MSGTYPE opentelemetry_proto_common_v1_ArrayValue +#define opentelemetry_proto_common_v1_AnyValue_value_kvlist_value_MSGTYPE opentelemetry_proto_common_v1_KeyValueList + +#define opentelemetry_proto_common_v1_ArrayValue_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, values, 1) +#define opentelemetry_proto_common_v1_ArrayValue_CALLBACK pb_default_field_callback +#define opentelemetry_proto_common_v1_ArrayValue_DEFAULT NULL +#define opentelemetry_proto_common_v1_ArrayValue_values_MSGTYPE opentelemetry_proto_common_v1_AnyValue + +#define opentelemetry_proto_common_v1_KeyValueList_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, values, 1) +#define opentelemetry_proto_common_v1_KeyValueList_CALLBACK pb_default_field_callback +#define opentelemetry_proto_common_v1_KeyValueList_DEFAULT NULL +#define opentelemetry_proto_common_v1_KeyValueList_values_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +#define opentelemetry_proto_common_v1_KeyValue_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, value, 2) +#define opentelemetry_proto_common_v1_KeyValue_CALLBACK pb_default_field_callback +#define opentelemetry_proto_common_v1_KeyValue_DEFAULT NULL +#define opentelemetry_proto_common_v1_KeyValue_value_MSGTYPE opentelemetry_proto_common_v1_AnyValue + +#define opentelemetry_proto_common_v1_InstrumentationScope_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, name, 1) \ +X(a, CALLBACK, SINGULAR, STRING, version, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 3) \ +X(a, STATIC, SINGULAR, UINT32, dropped_attributes_count, 4) +#define opentelemetry_proto_common_v1_InstrumentationScope_CALLBACK pb_default_field_callback +#define opentelemetry_proto_common_v1_InstrumentationScope_DEFAULT NULL +#define opentelemetry_proto_common_v1_InstrumentationScope_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +#define opentelemetry_proto_common_v1_EntityRef_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, schema_url, 1) \ +X(a, CALLBACK, SINGULAR, STRING, type, 2) \ +X(a, CALLBACK, REPEATED, STRING, id_keys, 3) \ +X(a, CALLBACK, REPEATED, STRING, description_keys, 4) +#define opentelemetry_proto_common_v1_EntityRef_CALLBACK pb_default_field_callback +#define opentelemetry_proto_common_v1_EntityRef_DEFAULT NULL + +extern const pb_msgdesc_t opentelemetry_proto_common_v1_AnyValue_msg; +extern const pb_msgdesc_t opentelemetry_proto_common_v1_ArrayValue_msg; +extern const pb_msgdesc_t opentelemetry_proto_common_v1_KeyValueList_msg; +extern const pb_msgdesc_t opentelemetry_proto_common_v1_KeyValue_msg; +extern const pb_msgdesc_t opentelemetry_proto_common_v1_InstrumentationScope_msg; +extern const pb_msgdesc_t opentelemetry_proto_common_v1_EntityRef_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define opentelemetry_proto_common_v1_AnyValue_fields &opentelemetry_proto_common_v1_AnyValue_msg +#define opentelemetry_proto_common_v1_ArrayValue_fields &opentelemetry_proto_common_v1_ArrayValue_msg +#define opentelemetry_proto_common_v1_KeyValueList_fields &opentelemetry_proto_common_v1_KeyValueList_msg +#define opentelemetry_proto_common_v1_KeyValue_fields &opentelemetry_proto_common_v1_KeyValue_msg +#define opentelemetry_proto_common_v1_InstrumentationScope_fields &opentelemetry_proto_common_v1_InstrumentationScope_msg +#define opentelemetry_proto_common_v1_EntityRef_fields &opentelemetry_proto_common_v1_EntityRef_msg + +/* Maximum encoded size of messages (where known) */ +/* opentelemetry_proto_common_v1_AnyValue_size depends on runtime parameters */ +/* opentelemetry_proto_common_v1_ArrayValue_size depends on runtime parameters */ +/* opentelemetry_proto_common_v1_KeyValueList_size depends on runtime parameters */ +/* opentelemetry_proto_common_v1_KeyValue_size depends on runtime parameters */ +/* opentelemetry_proto_common_v1_InstrumentationScope_size depends on runtime parameters */ +/* opentelemetry_proto_common_v1_EntityRef_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +/* Message descriptors for nanopb */ +namespace nanopb { +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 7; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_common_v1_AnyValue_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_common_v1_ArrayValue_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_common_v1_KeyValueList_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_common_v1_KeyValue_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 4; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_common_v1_InstrumentationScope_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 4; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_common_v1_EntityRef_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +} // namespace nanopb + +#endif /* __cplusplus */ + + +#endif diff --git a/src/proto/opentelemetry/proto/common/v1/common.pb.inc b/src/proto/opentelemetry/proto/common/v1/common.pb.inc new file mode 100644 index 0000000..b1beb9d --- /dev/null +++ b/src/proto/opentelemetry/proto/common/v1/common.pb.inc @@ -0,0 +1,35 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "opentelemetry/proto/common/v1/common.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(opentelemetry_proto_common_v1_AnyValue, opentelemetry_proto_common_v1_AnyValue, AUTO) + + +PB_BIND(opentelemetry_proto_common_v1_ArrayValue, opentelemetry_proto_common_v1_ArrayValue, AUTO) + + +PB_BIND(opentelemetry_proto_common_v1_KeyValueList, opentelemetry_proto_common_v1_KeyValueList, AUTO) + + +PB_BIND(opentelemetry_proto_common_v1_KeyValue, opentelemetry_proto_common_v1_KeyValue, AUTO) + + +PB_BIND(opentelemetry_proto_common_v1_InstrumentationScope, opentelemetry_proto_common_v1_InstrumentationScope, AUTO) + + +PB_BIND(opentelemetry_proto_common_v1_EntityRef, opentelemetry_proto_common_v1_EntityRef, AUTO) + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/src/proto/opentelemetry/proto/logs/v1/logs.pb.h b/src/proto/opentelemetry/proto/logs/v1/logs.pb.h new file mode 100644 index 0000000..3e63a8d --- /dev/null +++ b/src/proto/opentelemetry/proto/logs/v1/logs.pb.h @@ -0,0 +1,371 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_OPENTELEMETRY_PROTO_LOGS_V1_OPENTELEMETRY_PROTO_LOGS_V1_LOGS_PB_H_INCLUDED +#define PB_OPENTELEMETRY_PROTO_LOGS_V1_OPENTELEMETRY_PROTO_LOGS_V1_LOGS_PB_H_INCLUDED +#include +#include "opentelemetry/proto/common/v1/common.pb.h" +#include "opentelemetry/proto/resource/v1/resource.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +/* Possible values for LogRecord.SeverityNumber. */ +typedef enum _opentelemetry_proto_logs_v1_SeverityNumber { + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED = 0, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE = 1, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE2 = 2, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE3 = 3, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE4 = 4, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG = 5, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG2 = 6, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG3 = 7, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG4 = 8, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO = 9, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO2 = 10, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO3 = 11, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO4 = 12, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN = 13, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN2 = 14, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN3 = 15, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN4 = 16, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR = 17, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR2 = 18, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR3 = 19, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR4 = 20, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL = 21, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL2 = 22, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL3 = 23, + opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL4 = 24 +} opentelemetry_proto_logs_v1_SeverityNumber; + +/* LogRecordFlags represents constants used to interpret the + LogRecord.flags field, which is protobuf 'fixed32' type and is to + be used as bit-fields. Each non-zero value defined in this enum is + a bit-mask. To extract the bit-field, for example, use an + expression like: + + (logRecord.flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK) */ +typedef enum _opentelemetry_proto_logs_v1_LogRecordFlags { + /* The zero value for the enum. Should not be used for comparisons. + Instead use bitwise "and" with the appropriate mask as shown above. */ + opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_DO_NOT_USE = 0, + /* Bits 0-7 are used for trace flags. */ + opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_TRACE_FLAGS_MASK = 255 +} opentelemetry_proto_logs_v1_LogRecordFlags; + +/* Struct definitions */ +/* LogsData represents the logs data that can be stored in a persistent storage, + OR can be embedded by other protocols that transfer OTLP logs data but do not + implement the OTLP protocol. + + The main difference between this message and collector protocol is that + in this message there will not be any "control" or "metadata" specific to + OTLP protocol. + + When new fields are added into this message, the OTLP request MUST be updated + as well. */ +typedef struct _opentelemetry_proto_logs_v1_LogsData { + /* An array of ResourceLogs. + For data coming from a single resource this array will typically contain + one element. Intermediary nodes that receive data from multiple origins + typically batch the data before forwarding further and in that case this + array will contain multiple elements. */ + pb_callback_t resource_logs; +} opentelemetry_proto_logs_v1_LogsData; + +/* A collection of ScopeLogs from a Resource. */ +typedef struct _opentelemetry_proto_logs_v1_ResourceLogs { + /* The resource for the logs in this message. + If this field is not set then resource info is unknown. */ + bool has_resource; + opentelemetry_proto_resource_v1_Resource resource; + /* A list of ScopeLogs that originate from a resource. */ + pb_callback_t scope_logs; + /* The Schema URL, if known. This is the identifier of the Schema that the resource data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "resource" field. It does not apply + to the data in the "scope_logs" field which have their own schema_url field. */ + pb_callback_t schema_url; +} opentelemetry_proto_logs_v1_ResourceLogs; + +/* A collection of Logs produced by a Scope. */ +typedef struct _opentelemetry_proto_logs_v1_ScopeLogs { + /* The instrumentation scope information for the logs in this message. + Semantically when InstrumentationScope isn't set, it is equivalent with + an empty instrumentation scope name (unknown). */ + bool has_scope; + opentelemetry_proto_common_v1_InstrumentationScope scope; + /* A list of log records. */ + pb_callback_t log_records; + /* The Schema URL, if known. This is the identifier of the Schema that the log data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "scope" field and all logs in the + "log_records" field. */ + pb_callback_t schema_url; +} opentelemetry_proto_logs_v1_ScopeLogs; + +/* A log record according to OpenTelemetry Log Data Model: + https://github.com/open-telemetry/oteps/blob/main/text/logs/0097-log-data-model.md */ +typedef struct _opentelemetry_proto_logs_v1_LogRecord { + /* time_unix_nano is the time when the event occurred. + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + Value of 0 indicates unknown or missing timestamp. */ + uint64_t time_unix_nano; + /* Numerical value of the severity, normalized to values described in Log Data Model. + [Optional]. */ + opentelemetry_proto_logs_v1_SeverityNumber severity_number; + /* The severity text (also known as log level). The original string representation as + it is known at the source. [Optional]. */ + pb_callback_t severity_text; + /* A value containing the body of the log record. Can be for example a human-readable + string message (including multi-line) describing the event in a free form or it can + be a structured data composed of arrays and maps of other values. [Optional]. */ + bool has_body; + opentelemetry_proto_common_v1_AnyValue body; + /* Additional attributes that describe the specific event occurrence. [Optional]. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t attributes; + uint32_t dropped_attributes_count; + /* Flags, a bit field. 8 least significant bits are the trace flags as + defined in W3C Trace Context specification. 24 most significant bits are reserved + and must be set to 0. Readers must not assume that 24 most significant bits + will be zero and must correctly mask the bits when reading 8-bit trace flag (use + flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional]. */ + uint32_t flags; + /* A unique identifier for a trace. All logs from the same trace share + the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + is zero-length and thus is also invalid). + + This field is optional. + + The receivers SHOULD assume that the log record is not associated with a + trace if any of the following is true: + - the field is not present, + - the field contains an invalid value. */ + pb_callback_t trace_id; + /* A unique identifier for a span within a trace, assigned when the span + is created. The ID is an 8-byte array. An ID with all zeroes OR of length + other than 8 bytes is considered invalid (empty string in OTLP/JSON + is zero-length and thus is also invalid). + + This field is optional. If the sender specifies a valid span_id then it SHOULD also + specify a valid trace_id. + + The receivers SHOULD assume that the log record is not associated with a + span if any of the following is true: + - the field is not present, + - the field contains an invalid value. */ + pb_callback_t span_id; + /* Time when the event was observed by the collection system. + For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK) + this timestamp is typically set at the generation time and is equal to Timestamp. + For events originating externally and collected by OpenTelemetry (e.g. using + Collector) this is the time when OpenTelemetry's code observed the event measured + by the clock of the OpenTelemetry code. This field MUST be set once the event is + observed by OpenTelemetry. + + For converting OpenTelemetry log data to formats that support only one timestamp or + when receiving OpenTelemetry log data by recipients that support only one timestamp + internally the following logic is recommended: + - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + Value of 0 indicates unknown or missing timestamp. */ + uint64_t observed_time_unix_nano; + /* A unique identifier of event category/type. + All events with the same event_name are expected to conform to the same + schema for both their attributes and their body. + + Recommended to be fully qualified and short (no longer than 256 characters). + + Presence of event_name on the log record identifies this record + as an event. + + [Optional]. */ + pb_callback_t event_name; +} opentelemetry_proto_logs_v1_LogRecord; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _opentelemetry_proto_logs_v1_SeverityNumber_MIN opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED +#define _opentelemetry_proto_logs_v1_SeverityNumber_MAX opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL4 +#define _opentelemetry_proto_logs_v1_SeverityNumber_ARRAYSIZE ((opentelemetry_proto_logs_v1_SeverityNumber)(opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL4+1)) + +#define _opentelemetry_proto_logs_v1_LogRecordFlags_MIN opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_DO_NOT_USE +#define _opentelemetry_proto_logs_v1_LogRecordFlags_MAX opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_TRACE_FLAGS_MASK +#define _opentelemetry_proto_logs_v1_LogRecordFlags_ARRAYSIZE ((opentelemetry_proto_logs_v1_LogRecordFlags)(opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_TRACE_FLAGS_MASK+1)) + + + + +#define opentelemetry_proto_logs_v1_LogRecord_severity_number_ENUMTYPE opentelemetry_proto_logs_v1_SeverityNumber + + +/* Initializer values for message structs */ +#define opentelemetry_proto_logs_v1_LogsData_init_default {{{NULL}, NULL}} +#define opentelemetry_proto_logs_v1_ResourceLogs_init_default {false, opentelemetry_proto_resource_v1_Resource_init_default, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_logs_v1_ScopeLogs_init_default {false, opentelemetry_proto_common_v1_InstrumentationScope_init_default, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_logs_v1_LogRecord_init_default {0, _opentelemetry_proto_logs_v1_SeverityNumber_MIN, {{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_default, {{NULL}, NULL}, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}} +#define opentelemetry_proto_logs_v1_LogsData_init_zero {{{NULL}, NULL}} +#define opentelemetry_proto_logs_v1_ResourceLogs_init_zero {false, opentelemetry_proto_resource_v1_Resource_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_logs_v1_ScopeLogs_init_zero {false, opentelemetry_proto_common_v1_InstrumentationScope_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_logs_v1_LogRecord_init_zero {0, _opentelemetry_proto_logs_v1_SeverityNumber_MIN, {{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_zero, {{NULL}, NULL}, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define opentelemetry_proto_logs_v1_LogsData_resource_logs_tag 1 +#define opentelemetry_proto_logs_v1_ResourceLogs_resource_tag 1 +#define opentelemetry_proto_logs_v1_ResourceLogs_scope_logs_tag 2 +#define opentelemetry_proto_logs_v1_ResourceLogs_schema_url_tag 3 +#define opentelemetry_proto_logs_v1_ScopeLogs_scope_tag 1 +#define opentelemetry_proto_logs_v1_ScopeLogs_log_records_tag 2 +#define opentelemetry_proto_logs_v1_ScopeLogs_schema_url_tag 3 +#define opentelemetry_proto_logs_v1_LogRecord_time_unix_nano_tag 1 +#define opentelemetry_proto_logs_v1_LogRecord_severity_number_tag 2 +#define opentelemetry_proto_logs_v1_LogRecord_severity_text_tag 3 +#define opentelemetry_proto_logs_v1_LogRecord_body_tag 5 +#define opentelemetry_proto_logs_v1_LogRecord_attributes_tag 6 +#define opentelemetry_proto_logs_v1_LogRecord_dropped_attributes_count_tag 7 +#define opentelemetry_proto_logs_v1_LogRecord_flags_tag 8 +#define opentelemetry_proto_logs_v1_LogRecord_trace_id_tag 9 +#define opentelemetry_proto_logs_v1_LogRecord_span_id_tag 10 +#define opentelemetry_proto_logs_v1_LogRecord_observed_time_unix_nano_tag 11 +#define opentelemetry_proto_logs_v1_LogRecord_event_name_tag 12 + +/* Struct field encoding specification for nanopb */ +#define opentelemetry_proto_logs_v1_LogsData_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, resource_logs, 1) +#define opentelemetry_proto_logs_v1_LogsData_CALLBACK pb_default_field_callback +#define opentelemetry_proto_logs_v1_LogsData_DEFAULT NULL +#define opentelemetry_proto_logs_v1_LogsData_resource_logs_MSGTYPE opentelemetry_proto_logs_v1_ResourceLogs + +#define opentelemetry_proto_logs_v1_ResourceLogs_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, resource, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, scope_logs, 2) \ +X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) +#define opentelemetry_proto_logs_v1_ResourceLogs_CALLBACK pb_default_field_callback +#define opentelemetry_proto_logs_v1_ResourceLogs_DEFAULT NULL +#define opentelemetry_proto_logs_v1_ResourceLogs_resource_MSGTYPE opentelemetry_proto_resource_v1_Resource +#define opentelemetry_proto_logs_v1_ResourceLogs_scope_logs_MSGTYPE opentelemetry_proto_logs_v1_ScopeLogs + +#define opentelemetry_proto_logs_v1_ScopeLogs_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, scope, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, log_records, 2) \ +X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) +#define opentelemetry_proto_logs_v1_ScopeLogs_CALLBACK pb_default_field_callback +#define opentelemetry_proto_logs_v1_ScopeLogs_DEFAULT NULL +#define opentelemetry_proto_logs_v1_ScopeLogs_scope_MSGTYPE opentelemetry_proto_common_v1_InstrumentationScope +#define opentelemetry_proto_logs_v1_ScopeLogs_log_records_MSGTYPE opentelemetry_proto_logs_v1_LogRecord + +#define opentelemetry_proto_logs_v1_LogRecord_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 1) \ +X(a, STATIC, SINGULAR, UENUM, severity_number, 2) \ +X(a, CALLBACK, SINGULAR, STRING, severity_text, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, body, 5) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 6) \ +X(a, STATIC, SINGULAR, UINT32, dropped_attributes_count, 7) \ +X(a, STATIC, SINGULAR, FIXED32, flags, 8) \ +X(a, CALLBACK, SINGULAR, BYTES, trace_id, 9) \ +X(a, CALLBACK, SINGULAR, BYTES, span_id, 10) \ +X(a, STATIC, SINGULAR, FIXED64, observed_time_unix_nano, 11) \ +X(a, CALLBACK, SINGULAR, STRING, event_name, 12) +#define opentelemetry_proto_logs_v1_LogRecord_CALLBACK pb_default_field_callback +#define opentelemetry_proto_logs_v1_LogRecord_DEFAULT NULL +#define opentelemetry_proto_logs_v1_LogRecord_body_MSGTYPE opentelemetry_proto_common_v1_AnyValue +#define opentelemetry_proto_logs_v1_LogRecord_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +extern const pb_msgdesc_t opentelemetry_proto_logs_v1_LogsData_msg; +extern const pb_msgdesc_t opentelemetry_proto_logs_v1_ResourceLogs_msg; +extern const pb_msgdesc_t opentelemetry_proto_logs_v1_ScopeLogs_msg; +extern const pb_msgdesc_t opentelemetry_proto_logs_v1_LogRecord_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define opentelemetry_proto_logs_v1_LogsData_fields &opentelemetry_proto_logs_v1_LogsData_msg +#define opentelemetry_proto_logs_v1_ResourceLogs_fields &opentelemetry_proto_logs_v1_ResourceLogs_msg +#define opentelemetry_proto_logs_v1_ScopeLogs_fields &opentelemetry_proto_logs_v1_ScopeLogs_msg +#define opentelemetry_proto_logs_v1_LogRecord_fields &opentelemetry_proto_logs_v1_LogRecord_msg + +/* Maximum encoded size of messages (where known) */ +/* opentelemetry_proto_logs_v1_LogsData_size depends on runtime parameters */ +/* opentelemetry_proto_logs_v1_ResourceLogs_size depends on runtime parameters */ +/* opentelemetry_proto_logs_v1_ScopeLogs_size depends on runtime parameters */ +/* opentelemetry_proto_logs_v1_LogRecord_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +/* Message descriptors for nanopb */ +namespace nanopb { +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_logs_v1_LogsData_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_logs_v1_ResourceLogs_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_logs_v1_ScopeLogs_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 11; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_logs_v1_LogRecord_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +} // namespace nanopb + +#endif /* __cplusplus */ + + +#endif diff --git a/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc b/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc new file mode 100644 index 0000000..e56612e --- /dev/null +++ b/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc @@ -0,0 +1,25 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "opentelemetry/proto/logs/v1/logs.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(opentelemetry_proto_logs_v1_LogsData, opentelemetry_proto_logs_v1_LogsData, AUTO) + + +PB_BIND(opentelemetry_proto_logs_v1_ResourceLogs, opentelemetry_proto_logs_v1_ResourceLogs, AUTO) + + +PB_BIND(opentelemetry_proto_logs_v1_ScopeLogs, opentelemetry_proto_logs_v1_ScopeLogs, AUTO) + + +PB_BIND(opentelemetry_proto_logs_v1_LogRecord, opentelemetry_proto_logs_v1_LogRecord, AUTO) + + + + + + + diff --git a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h new file mode 100644 index 0000000..ad0a687 --- /dev/null +++ b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h @@ -0,0 +1,1244 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_INCLUDED +#define PB_OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_INCLUDED +#include +#include "opentelemetry/proto/common/v1/common.pb.h" +#include "opentelemetry/proto/resource/v1/resource.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +/* AggregationTemporality defines how a metric aggregator reports aggregated + values. It describes how those values relate to the time interval over + which they are aggregated. */ +typedef enum _opentelemetry_proto_metrics_v1_AggregationTemporality { + /* UNSPECIFIED is the default AggregationTemporality, it MUST not be used. */ + opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED = 0, + /* DELTA is an AggregationTemporality for a metric aggregator which reports + changes since last report time. Successive metrics contain aggregation of + values from continuous and non-overlapping intervals. + + The values for a DELTA metric are based only on the time interval + associated with one measurement cycle. There is no dependency on + previous measurements like is the case for CUMULATIVE metrics. + + For example, consider a system measuring the number of requests that + it receives and reports the sum of these requests every second as a + DELTA metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0+1 to + t_0+2 with a value of 2. */ + opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA = 1, + /* CUMULATIVE is an AggregationTemporality for a metric aggregator which + reports changes since a fixed start time. This means that current values + of a CUMULATIVE metric depend on all previous measurements since the + start time. Because of this, the sender is required to retain this state + in some form. If this state is lost or invalidated, the CUMULATIVE metric + values MUST be reset and a new fixed start time following the last + reported measurement time sent MUST be used. + + For example, consider a system measuring the number of requests that + it receives and reports the sum of these requests every second as a + CUMULATIVE metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+2 with a value of 5. + 9. The system experiences a fault and loses state. + 10. The system recovers and resumes receiving at time=t_1. + 11. A request is received, the system measures 1 request. + 12. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_1 to + t_0+1 with a value of 1. + + Note: Even though, when reporting changes since last report time, using + CUMULATIVE is valid, it is not recommended. This may cause problems for + systems that do not use start_time to determine when the aggregation + value was reset (e.g. Prometheus). */ + opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE = 2 +} opentelemetry_proto_metrics_v1_AggregationTemporality; + +/* DataPointFlags is defined as a protobuf 'uint32' type and is to be used as a + bit-field representing 32 distinct boolean flags. Each flag defined in this + enum is a bit-mask. To test the presence of a single flag in the flags of + a data point, for example, use an expression like: + + (point.flags & DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK) == DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK */ +typedef enum _opentelemetry_proto_metrics_v1_DataPointFlags { + /* The zero value for the enum. Should not be used for comparisons. + Instead use bitwise "and" with the appropriate mask as shown above. */ + opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_DO_NOT_USE = 0, + /* This DataPoint is valid but has no recorded value. This value + SHOULD be used to reflect explicitly missing data in a series, as + for an equivalent to the Prometheus "staleness marker". */ + opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK = 1 +} opentelemetry_proto_metrics_v1_DataPointFlags; + +/* Struct definitions */ +/* MetricsData represents the metrics data that can be stored in a persistent + storage, OR can be embedded by other protocols that transfer OTLP metrics + data but do not implement the OTLP protocol. + + MetricsData + └─── ResourceMetrics + ├── Resource + ├── SchemaURL + └── ScopeMetrics + ├── Scope + ├── SchemaURL + └── Metric + ├── Name + ├── Description + ├── Unit + └── data + ├── Gauge + ├── Sum + ├── Histogram + ├── ExponentialHistogram + └── Summary + + The main difference between this message and collector protocol is that + in this message there will not be any "control" or "metadata" specific to + OTLP protocol. + + When new fields are added into this message, the OTLP request MUST be updated + as well. */ +typedef struct _opentelemetry_proto_metrics_v1_MetricsData { + /* An array of ResourceMetrics. + For data coming from a single resource this array will typically contain + one element. Intermediary nodes that receive data from multiple origins + typically batch the data before forwarding further and in that case this + array will contain multiple elements. */ + pb_callback_t resource_metrics; +} opentelemetry_proto_metrics_v1_MetricsData; + +/* A collection of ScopeMetrics from a Resource. */ +typedef struct _opentelemetry_proto_metrics_v1_ResourceMetrics { + /* The resource for the metrics in this message. + If this field is not set then no resource info is known. */ + bool has_resource; + opentelemetry_proto_resource_v1_Resource resource; + /* A list of metrics that originate from a resource. */ + pb_callback_t scope_metrics; + /* The Schema URL, if known. This is the identifier of the Schema that the resource data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "resource" field. It does not apply + to the data in the "scope_metrics" field which have their own schema_url field. */ + pb_callback_t schema_url; +} opentelemetry_proto_metrics_v1_ResourceMetrics; + +/* A collection of Metrics produced by an Scope. */ +typedef struct _opentelemetry_proto_metrics_v1_ScopeMetrics { + /* The instrumentation scope information for the metrics in this message. + Semantically when InstrumentationScope isn't set, it is equivalent with + an empty instrumentation scope name (unknown). */ + bool has_scope; + opentelemetry_proto_common_v1_InstrumentationScope scope; + /* A list of metrics that originate from an instrumentation library. */ + pb_callback_t metrics; + /* The Schema URL, if known. This is the identifier of the Schema that the metric data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "scope" field and all metrics in the + "metrics" field. */ + pb_callback_t schema_url; +} opentelemetry_proto_metrics_v1_ScopeMetrics; + +/* Gauge represents the type of a scalar metric that always exports the + "current value" for every data point. It should be used for an "unknown" + aggregation. + + A Gauge does not support different aggregation temporalities. Given the + aggregation is unknown, points cannot be combined using the same + aggregation, regardless of aggregation temporalities. Therefore, + AggregationTemporality is not included. Consequently, this also means + "StartTimeUnixNano" is ignored for all data points. */ +typedef struct _opentelemetry_proto_metrics_v1_Gauge { + /* The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). */ + pb_callback_t data_points; +} opentelemetry_proto_metrics_v1_Gauge; + +/* Sum represents the type of a scalar metric that is calculated as a sum of all + reported measurements over a time interval. */ +typedef struct _opentelemetry_proto_metrics_v1_Sum { + /* The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). */ + pb_callback_t data_points; + /* aggregation_temporality describes if the aggregator reports delta changes + since last report time, or cumulative changes since a fixed start time. */ + opentelemetry_proto_metrics_v1_AggregationTemporality aggregation_temporality; + /* Represents whether the sum is monotonic. */ + bool is_monotonic; +} opentelemetry_proto_metrics_v1_Sum; + +/* Histogram represents the type of a metric that is calculated by aggregating + as a Histogram of all reported measurements over a time interval. */ +typedef struct _opentelemetry_proto_metrics_v1_Histogram { + /* The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). */ + pb_callback_t data_points; + /* aggregation_temporality describes if the aggregator reports delta changes + since last report time, or cumulative changes since a fixed start time. */ + opentelemetry_proto_metrics_v1_AggregationTemporality aggregation_temporality; +} opentelemetry_proto_metrics_v1_Histogram; + +/* ExponentialHistogram represents the type of a metric that is calculated by aggregating + as a ExponentialHistogram of all reported double measurements over a time interval. */ +typedef struct _opentelemetry_proto_metrics_v1_ExponentialHistogram { + /* The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). */ + pb_callback_t data_points; + /* aggregation_temporality describes if the aggregator reports delta changes + since last report time, or cumulative changes since a fixed start time. */ + opentelemetry_proto_metrics_v1_AggregationTemporality aggregation_temporality; +} opentelemetry_proto_metrics_v1_ExponentialHistogram; + +/* Summary metric data are used to convey quantile summaries, + a Prometheus (see: https://prometheus.io/docs/concepts/metric_types/#summary) + and OpenMetrics (see: https://github.com/prometheus/OpenMetrics/blob/4dbf6075567ab43296eed941037c12951faafb92/protos/prometheus.proto#L45) + data type. These data points cannot always be merged in a meaningful way. + While they can be useful in some applications, histogram data points are + recommended for new applications. + Summary metrics do not have an aggregation temporality field. This is + because the count and sum fields of a SummaryDataPoint are assumed to be + cumulative values. */ +typedef struct _opentelemetry_proto_metrics_v1_Summary { + /* The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). */ + pb_callback_t data_points; +} opentelemetry_proto_metrics_v1_Summary; + +/* Defines a Metric which has one or more timeseries. The following is a + brief summary of the Metric data model. For more details, see: + + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md + + The data model and relation between entities is shown in the + diagram below. Here, "DataPoint" is the term used to refer to any + one of the specific data point value types, and "points" is the term used + to refer to any one of the lists of points contained in the Metric. + + - Metric is composed of a metadata and data. + - Metadata part contains a name, description, unit. + - Data is one of the possible types (Sum, Gauge, Histogram, Summary). + - DataPoint contains timestamps, attributes, and one of the possible value type + fields. + + Metric + +------------+ + |name | + |description | + |unit | +------------------------------------+ + |data |---> |Gauge, Sum, Histogram, Summary, ... | + +------------+ +------------------------------------+ + + Data [One of Gauge, Sum, Histogram, Summary, ...] + +-----------+ + |... | // Metadata about the Data. + |points |--+ + +-----------+ | + | +---------------------------+ + | |DataPoint 1 | + v |+------+------+ +------+ | + +-----+ ||label |label |...|label | | + | 1 |-->||value1|value2|...|valueN| | + +-----+ |+------+------+ +------+ | + | . | |+-----+ | + | . | ||value| | + | . | |+-----+ | + | . | +---------------------------+ + | . | . + | . | . + | . | . + | . | +---------------------------+ + | . | |DataPoint M | + +-----+ |+------+------+ +------+ | + | M |-->||label |label |...|label | | + +-----+ ||value1|value2|...|valueN| | + |+------+------+ +------+ | + |+-----+ | + ||value| | + |+-----+ | + +---------------------------+ + + Each distinct type of DataPoint represents the output of a specific + aggregation function, the result of applying the DataPoint's + associated function of to one or more measurements. + + All DataPoint types have three common fields: + - Attributes includes key-value pairs associated with the data point + - TimeUnixNano is required, set to the end time of the aggregation + - StartTimeUnixNano is optional, but strongly encouraged for DataPoints + having an AggregationTemporality field, as discussed below. + + Both TimeUnixNano and StartTimeUnixNano values are expressed as + UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + + # TimeUnixNano + + This field is required, having consistent interpretation across + DataPoint types. TimeUnixNano is the moment corresponding to when + the data point's aggregate value was captured. + + Data points with the 0 value for TimeUnixNano SHOULD be rejected + by consumers. + + # StartTimeUnixNano + + StartTimeUnixNano in general allows detecting when a sequence of + observations is unbroken. This field indicates to consumers the + start time for points with cumulative and delta + AggregationTemporality, and it should be included whenever possible + to support correct rate calculation. Although it may be omitted + when the start time is truly unknown, setting StartTimeUnixNano is + strongly encouraged. */ +typedef struct _opentelemetry_proto_metrics_v1_Metric { + /* The name of the metric. */ + pb_callback_t name; + /* A description of the metric, which can be used in documentation. */ + pb_callback_t description; + /* The unit in which the metric value is reported. Follows the format + described by https://unitsofmeasure.org/ucum.html. */ + pb_callback_t unit; + pb_size_t which_data; + union { + opentelemetry_proto_metrics_v1_Gauge gauge; + opentelemetry_proto_metrics_v1_Sum sum; + opentelemetry_proto_metrics_v1_Histogram histogram; + opentelemetry_proto_metrics_v1_ExponentialHistogram exponential_histogram; + opentelemetry_proto_metrics_v1_Summary summary; + } data; + /* Additional metadata attributes that describe the metric. [Optional]. + Attributes are non-identifying. + Consumers SHOULD NOT need to be aware of these attributes. + These attributes MAY be used to encode information allowing + for lossless roundtrip translation to / from another data model. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t metadata; +} opentelemetry_proto_metrics_v1_Metric; + +/* NumberDataPoint is a single data point in a timeseries that describes the + time-varying scalar value of a metric. */ +typedef struct _opentelemetry_proto_metrics_v1_NumberDataPoint { + /* StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t start_time_unix_nano; + /* TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t time_unix_nano; + pb_size_t which_value; + union { + double as_double; + int64_t as_int; + } value; + /* (Optional) List of exemplars collected from + measurements that were used to form the data point */ + pb_callback_t exemplars; + /* The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t attributes; + /* Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. */ + uint32_t flags; +} opentelemetry_proto_metrics_v1_NumberDataPoint; + +/* HistogramDataPoint is a single data point in a timeseries that describes the + time-varying values of a Histogram. A Histogram contains summary statistics + for a population of values, it may optionally contain the distribution of + those values across a set of buckets. + + If the histogram contains the distribution of values, then both + "explicit_bounds" and "bucket counts" fields must be defined. + If the histogram does not contain the distribution of values, then both + "explicit_bounds" and "bucket_counts" must be omitted and only "count" and + "sum" are known. */ +typedef struct _opentelemetry_proto_metrics_v1_HistogramDataPoint { + /* StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t start_time_unix_nano; + /* TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t time_unix_nano; + /* count is the number of values in the population. Must be non-negative. This + value must be equal to the sum of the "count" fields in buckets if a + histogram is provided. */ + uint64_t count; + /* sum of the values in the population. If count is zero then this field + must be zero. + + Note: Sum should only be filled out when measuring non-negative discrete + events, and is assumed to be monotonic over the values of these events. + Negative events *can* be recorded, but sum should not be filled out when + doing so. This is specifically to enforce compatibility w/ OpenMetrics, + see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram */ + bool has_sum; + double sum; + /* bucket_counts is an optional field contains the count values of histogram + for each bucket. + + The sum of the bucket_counts must equal the value in the count field. + + The number of elements in bucket_counts array must be by one greater than + the number of elements in explicit_bounds array. The exception to this rule + is when the length of bucket_counts is 0, then the length of explicit_bounds + must also be 0. */ + pb_callback_t bucket_counts; + /* explicit_bounds specifies buckets with explicitly defined bounds for values. + + The boundaries for bucket at index i are: + + (-infinity, explicit_bounds[i]] for i == 0 + (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds) + (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds) + + The values in the explicit_bounds array must be strictly increasing. + + Histogram buckets are inclusive of their upper boundary, except the last + bucket where the boundary is at infinity. This format is intentionally + compatible with the OpenMetrics histogram definition. + + If bucket_counts length is 0 then explicit_bounds length must also be 0, + otherwise the data point is invalid. */ + pb_callback_t explicit_bounds; + /* (Optional) List of exemplars collected from + measurements that were used to form the data point */ + pb_callback_t exemplars; + /* The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t attributes; + /* Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. */ + uint32_t flags; + /* min is the minimum value over (start_time, end_time]. */ + bool has_min; + double min; + /* max is the maximum value over (start_time, end_time]. */ + bool has_max; + double max; +} opentelemetry_proto_metrics_v1_HistogramDataPoint; + +/* Buckets are a set of bucket counts, encoded in a contiguous array + of counts. */ +typedef struct _opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets { + /* The bucket index of the first entry in the bucket_counts array. + + Note: This uses a varint encoding as a simple form of compression. */ + int32_t offset; + /* An array of count values, where bucket_counts[i] carries + the count of the bucket at index (offset+i). bucket_counts[i] is the count + of values greater than base^(offset+i) and less than or equal to + base^(offset+i+1). + + Note: By contrast, the explicit HistogramDataPoint uses + fixed64. This field is expected to have many buckets, + especially zeros, so uint64 has been selected to ensure + varint encoding. */ + pb_callback_t bucket_counts; +} opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets; + +/* ExponentialHistogramDataPoint is a single data point in a timeseries that describes the + time-varying values of a ExponentialHistogram of double values. A ExponentialHistogram contains + summary statistics for a population of values, it may optionally contain the + distribution of those values across a set of buckets. */ +typedef struct _opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint { + /* The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t attributes; + /* StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t start_time_unix_nano; + /* TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t time_unix_nano; + /* The number of values in the population. Must be + non-negative. This value must be equal to the sum of the "bucket_counts" + values in the positive and negative Buckets plus the "zero_count" field. */ + uint64_t count; + /* The sum of the values in the population. If count is zero then this field + must be zero. + + Note: Sum should only be filled out when measuring non-negative discrete + events, and is assumed to be monotonic over the values of these events. + Negative events *can* be recorded, but sum should not be filled out when + doing so. This is specifically to enforce compatibility w/ OpenMetrics, + see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram */ + bool has_sum; + double sum; + /* scale describes the resolution of the histogram. Boundaries are + located at powers of the base, where: + + base = (2^(2^-scale)) + + The histogram bucket identified by `index`, a signed integer, + contains values that are greater than (base^index) and + less than or equal to (base^(index+1)). + + The positive and negative ranges of the histogram are expressed + separately. Negative values are mapped by their absolute value + into the negative range using the same scale as the positive range. + + scale is not restricted by the protocol, as the permissible + values depend on the range of the data. */ + int32_t scale; + /* The count of values that are either exactly zero or + within the region considered zero by the instrumentation at the + tolerated degree of precision. This bucket stores values that + cannot be expressed using the standard exponential formula as + well as values that have been rounded to zero. + + Implementations MAY consider the zero bucket to have probability + mass equal to (zero_count / count). */ + uint64_t zero_count; + /* positive carries the positive range of exponential bucket counts. */ + bool has_positive; + opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets positive; + /* negative carries the negative range of exponential bucket counts. */ + bool has_negative; + opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets negative; + /* Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. */ + uint32_t flags; + /* (Optional) List of exemplars collected from + measurements that were used to form the data point */ + pb_callback_t exemplars; + /* The minimum value over (start_time, end_time]. */ + bool has_min; + double min; + /* The maximum value over (start_time, end_time]. */ + bool has_max; + double max; + /* ZeroThreshold may be optionally set to convey the width of the zero + region. Where the zero region is defined as the closed interval + [-ZeroThreshold, ZeroThreshold]. + When ZeroThreshold is 0, zero count bucket stores values that cannot be + expressed using the standard exponential formula as well as values that + have been rounded to zero. */ + double zero_threshold; +} opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint; + +/* SummaryDataPoint is a single data point in a timeseries that describes the + time-varying values of a Summary metric. The count and sum fields represent + cumulative values. */ +typedef struct _opentelemetry_proto_metrics_v1_SummaryDataPoint { + /* StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t start_time_unix_nano; + /* TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t time_unix_nano; + /* count is the number of values in the population. Must be non-negative. */ + uint64_t count; + /* sum of the values in the population. If count is zero then this field + must be zero. + + Note: Sum should only be filled out when measuring non-negative discrete + events, and is assumed to be monotonic over the values of these events. + Negative events *can* be recorded, but sum should not be filled out when + doing so. This is specifically to enforce compatibility w/ OpenMetrics, + see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary */ + double sum; + /* (Optional) list of values at different quantiles of the distribution calculated + from the current snapshot. The quantiles must be strictly increasing. */ + pb_callback_t quantile_values; + /* The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t attributes; + /* Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. */ + uint32_t flags; +} opentelemetry_proto_metrics_v1_SummaryDataPoint; + +/* Represents the value at a given quantile of a distribution. + + To record Min and Max values following conventions are used: + - The 1.0 quantile is equivalent to the maximum value observed. + - The 0.0 quantile is equivalent to the minimum value observed. + + See the following issue for more context: + https://github.com/open-telemetry/opentelemetry-proto/issues/125 */ +typedef struct _opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile { + /* The quantile of a distribution. Must be in the interval + [0.0, 1.0]. */ + double quantile; + /* The value at the given quantile of a distribution. + + Quantile values must NOT be negative. */ + double value; +} opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile; + +/* A representation of an exemplar, which is a sample input measurement. + Exemplars also hold information about the environment when the measurement + was recorded, for example the span and trace ID of the active span when the + exemplar was recorded. */ +typedef struct _opentelemetry_proto_metrics_v1_Exemplar { + /* time_unix_nano is the exact time when this exemplar was recorded + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. */ + uint64_t time_unix_nano; + pb_size_t which_value; + union { + double as_double; + int64_t as_int; + } value; + /* (Optional) Span ID of the exemplar trace. + span_id may be missing if the measurement is not recorded inside a trace + or if the trace is not sampled. */ + pb_callback_t span_id; + /* (Optional) Trace ID of the exemplar trace. + trace_id may be missing if the measurement is not recorded inside a trace + or if the trace is not sampled. */ + pb_callback_t trace_id; + /* The set of key/value pairs that were filtered out by the aggregator, but + recorded alongside the original measurement. Only key/value pairs that were + filtered out by the aggregator should be included */ + pb_callback_t filtered_attributes; +} opentelemetry_proto_metrics_v1_Exemplar; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED +#define _opentelemetry_proto_metrics_v1_AggregationTemporality_MAX opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE +#define _opentelemetry_proto_metrics_v1_AggregationTemporality_ARRAYSIZE ((opentelemetry_proto_metrics_v1_AggregationTemporality)(opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE+1)) + +#define _opentelemetry_proto_metrics_v1_DataPointFlags_MIN opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_DO_NOT_USE +#define _opentelemetry_proto_metrics_v1_DataPointFlags_MAX opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK +#define _opentelemetry_proto_metrics_v1_DataPointFlags_ARRAYSIZE ((opentelemetry_proto_metrics_v1_DataPointFlags)(opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK+1)) + + + + + + +#define opentelemetry_proto_metrics_v1_Sum_aggregation_temporality_ENUMTYPE opentelemetry_proto_metrics_v1_AggregationTemporality + +#define opentelemetry_proto_metrics_v1_Histogram_aggregation_temporality_ENUMTYPE opentelemetry_proto_metrics_v1_AggregationTemporality + +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_aggregation_temporality_ENUMTYPE opentelemetry_proto_metrics_v1_AggregationTemporality + + + + + + + + + + +/* Initializer values for message structs */ +#define opentelemetry_proto_metrics_v1_MetricsData_init_default {{{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_ResourceMetrics_init_default {false, opentelemetry_proto_resource_v1_Resource_init_default, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_ScopeMetrics_init_default {false, opentelemetry_proto_common_v1_InstrumentationScope_init_default, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_Metric_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {opentelemetry_proto_metrics_v1_Gauge_init_default}, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_Gauge_init_default {{{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_Sum_init_default {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN, 0} +#define opentelemetry_proto_metrics_v1_Histogram_init_default {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_init_default {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} +#define opentelemetry_proto_metrics_v1_Summary_init_default {{{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_NumberDataPoint_init_default {0, 0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_init_default {0, 0, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, false, 0, false, 0} +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_init_default {{{NULL}, NULL}, 0, 0, 0, false, 0, 0, 0, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_default, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_default, 0, {{NULL}, NULL}, false, 0, false, 0, 0} +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_default {0, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_init_default {0, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_init_default {0, 0} +#define opentelemetry_proto_metrics_v1_Exemplar_init_default {0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_MetricsData_init_zero {{{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_ResourceMetrics_init_zero {false, opentelemetry_proto_resource_v1_Resource_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_ScopeMetrics_init_zero {false, opentelemetry_proto_common_v1_InstrumentationScope_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_Metric_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {opentelemetry_proto_metrics_v1_Gauge_init_zero}, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_Gauge_init_zero {{{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_Sum_init_zero {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN, 0} +#define opentelemetry_proto_metrics_v1_Histogram_init_zero {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_init_zero {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} +#define opentelemetry_proto_metrics_v1_Summary_init_zero {{{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_NumberDataPoint_init_zero {0, 0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_init_zero {0, 0, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, false, 0, false, 0} +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_init_zero {{{NULL}, NULL}, 0, 0, 0, false, 0, 0, 0, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_zero, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_zero, 0, {{NULL}, NULL}, false, 0, false, 0, 0} +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_zero {0, {{NULL}, NULL}} +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_init_zero {0, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_init_zero {0, 0} +#define opentelemetry_proto_metrics_v1_Exemplar_init_zero {0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define opentelemetry_proto_metrics_v1_MetricsData_resource_metrics_tag 1 +#define opentelemetry_proto_metrics_v1_ResourceMetrics_resource_tag 1 +#define opentelemetry_proto_metrics_v1_ResourceMetrics_scope_metrics_tag 2 +#define opentelemetry_proto_metrics_v1_ResourceMetrics_schema_url_tag 3 +#define opentelemetry_proto_metrics_v1_ScopeMetrics_scope_tag 1 +#define opentelemetry_proto_metrics_v1_ScopeMetrics_metrics_tag 2 +#define opentelemetry_proto_metrics_v1_ScopeMetrics_schema_url_tag 3 +#define opentelemetry_proto_metrics_v1_Gauge_data_points_tag 1 +#define opentelemetry_proto_metrics_v1_Sum_data_points_tag 1 +#define opentelemetry_proto_metrics_v1_Sum_aggregation_temporality_tag 2 +#define opentelemetry_proto_metrics_v1_Sum_is_monotonic_tag 3 +#define opentelemetry_proto_metrics_v1_Histogram_data_points_tag 1 +#define opentelemetry_proto_metrics_v1_Histogram_aggregation_temporality_tag 2 +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_data_points_tag 1 +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_aggregation_temporality_tag 2 +#define opentelemetry_proto_metrics_v1_Summary_data_points_tag 1 +#define opentelemetry_proto_metrics_v1_Metric_name_tag 1 +#define opentelemetry_proto_metrics_v1_Metric_description_tag 2 +#define opentelemetry_proto_metrics_v1_Metric_unit_tag 3 +#define opentelemetry_proto_metrics_v1_Metric_gauge_tag 5 +#define opentelemetry_proto_metrics_v1_Metric_sum_tag 7 +#define opentelemetry_proto_metrics_v1_Metric_histogram_tag 9 +#define opentelemetry_proto_metrics_v1_Metric_exponential_histogram_tag 10 +#define opentelemetry_proto_metrics_v1_Metric_summary_tag 11 +#define opentelemetry_proto_metrics_v1_Metric_metadata_tag 12 +#define opentelemetry_proto_metrics_v1_NumberDataPoint_start_time_unix_nano_tag 2 +#define opentelemetry_proto_metrics_v1_NumberDataPoint_time_unix_nano_tag 3 +#define opentelemetry_proto_metrics_v1_NumberDataPoint_as_double_tag 4 +#define opentelemetry_proto_metrics_v1_NumberDataPoint_as_int_tag 6 +#define opentelemetry_proto_metrics_v1_NumberDataPoint_exemplars_tag 5 +#define opentelemetry_proto_metrics_v1_NumberDataPoint_attributes_tag 7 +#define opentelemetry_proto_metrics_v1_NumberDataPoint_flags_tag 8 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_start_time_unix_nano_tag 2 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_time_unix_nano_tag 3 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_count_tag 4 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_sum_tag 5 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_bucket_counts_tag 6 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_explicit_bounds_tag 7 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_exemplars_tag 8 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_attributes_tag 9 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_flags_tag 10 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_min_tag 11 +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_max_tag 12 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_offset_tag 1 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_bucket_counts_tag 2 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_attributes_tag 1 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_start_time_unix_nano_tag 2 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_time_unix_nano_tag 3 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_count_tag 4 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_sum_tag 5 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_scale_tag 6 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_zero_count_tag 7 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_positive_tag 8 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_negative_tag 9 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_flags_tag 10 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_exemplars_tag 11 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_min_tag 12 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_max_tag 13 +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_zero_threshold_tag 14 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_start_time_unix_nano_tag 2 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_time_unix_nano_tag 3 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_count_tag 4 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_sum_tag 5 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_quantile_values_tag 6 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_attributes_tag 7 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_flags_tag 8 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_quantile_tag 1 +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_value_tag 2 +#define opentelemetry_proto_metrics_v1_Exemplar_time_unix_nano_tag 2 +#define opentelemetry_proto_metrics_v1_Exemplar_as_double_tag 3 +#define opentelemetry_proto_metrics_v1_Exemplar_as_int_tag 6 +#define opentelemetry_proto_metrics_v1_Exemplar_span_id_tag 4 +#define opentelemetry_proto_metrics_v1_Exemplar_trace_id_tag 5 +#define opentelemetry_proto_metrics_v1_Exemplar_filtered_attributes_tag 7 + +/* Struct field encoding specification for nanopb */ +#define opentelemetry_proto_metrics_v1_MetricsData_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, resource_metrics, 1) +#define opentelemetry_proto_metrics_v1_MetricsData_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_MetricsData_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_MetricsData_resource_metrics_MSGTYPE opentelemetry_proto_metrics_v1_ResourceMetrics + +#define opentelemetry_proto_metrics_v1_ResourceMetrics_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, resource, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, scope_metrics, 2) \ +X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) +#define opentelemetry_proto_metrics_v1_ResourceMetrics_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_ResourceMetrics_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_ResourceMetrics_resource_MSGTYPE opentelemetry_proto_resource_v1_Resource +#define opentelemetry_proto_metrics_v1_ResourceMetrics_scope_metrics_MSGTYPE opentelemetry_proto_metrics_v1_ScopeMetrics + +#define opentelemetry_proto_metrics_v1_ScopeMetrics_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, scope, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, metrics, 2) \ +X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) +#define opentelemetry_proto_metrics_v1_ScopeMetrics_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_ScopeMetrics_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_ScopeMetrics_scope_MSGTYPE opentelemetry_proto_common_v1_InstrumentationScope +#define opentelemetry_proto_metrics_v1_ScopeMetrics_metrics_MSGTYPE opentelemetry_proto_metrics_v1_Metric + +#define opentelemetry_proto_metrics_v1_Metric_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, name, 1) \ +X(a, CALLBACK, SINGULAR, STRING, description, 2) \ +X(a, CALLBACK, SINGULAR, STRING, unit, 3) \ +X(a, STATIC, ONEOF, MESSAGE, (data,gauge,data.gauge), 5) \ +X(a, STATIC, ONEOF, MESSAGE, (data,sum,data.sum), 7) \ +X(a, STATIC, ONEOF, MESSAGE, (data,histogram,data.histogram), 9) \ +X(a, STATIC, ONEOF, MESSAGE, (data,exponential_histogram,data.exponential_histogram), 10) \ +X(a, STATIC, ONEOF, MESSAGE, (data,summary,data.summary), 11) \ +X(a, CALLBACK, REPEATED, MESSAGE, metadata, 12) +#define opentelemetry_proto_metrics_v1_Metric_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_Metric_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_Metric_data_gauge_MSGTYPE opentelemetry_proto_metrics_v1_Gauge +#define opentelemetry_proto_metrics_v1_Metric_data_sum_MSGTYPE opentelemetry_proto_metrics_v1_Sum +#define opentelemetry_proto_metrics_v1_Metric_data_histogram_MSGTYPE opentelemetry_proto_metrics_v1_Histogram +#define opentelemetry_proto_metrics_v1_Metric_data_exponential_histogram_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogram +#define opentelemetry_proto_metrics_v1_Metric_data_summary_MSGTYPE opentelemetry_proto_metrics_v1_Summary +#define opentelemetry_proto_metrics_v1_Metric_metadata_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +#define opentelemetry_proto_metrics_v1_Gauge_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) +#define opentelemetry_proto_metrics_v1_Gauge_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_Gauge_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_Gauge_data_points_MSGTYPE opentelemetry_proto_metrics_v1_NumberDataPoint + +#define opentelemetry_proto_metrics_v1_Sum_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) \ +X(a, STATIC, SINGULAR, UENUM, aggregation_temporality, 2) \ +X(a, STATIC, SINGULAR, BOOL, is_monotonic, 3) +#define opentelemetry_proto_metrics_v1_Sum_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_Sum_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_Sum_data_points_MSGTYPE opentelemetry_proto_metrics_v1_NumberDataPoint + +#define opentelemetry_proto_metrics_v1_Histogram_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) \ +X(a, STATIC, SINGULAR, UENUM, aggregation_temporality, 2) +#define opentelemetry_proto_metrics_v1_Histogram_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_Histogram_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_Histogram_data_points_MSGTYPE opentelemetry_proto_metrics_v1_HistogramDataPoint + +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) \ +X(a, STATIC, SINGULAR, UENUM, aggregation_temporality, 2) +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_data_points_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint + +#define opentelemetry_proto_metrics_v1_Summary_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) +#define opentelemetry_proto_metrics_v1_Summary_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_Summary_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_Summary_data_points_MSGTYPE opentelemetry_proto_metrics_v1_SummaryDataPoint + +#define opentelemetry_proto_metrics_v1_NumberDataPoint_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ +X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ +X(a, STATIC, ONEOF, DOUBLE, (value,as_double,value.as_double), 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, exemplars, 5) \ +X(a, STATIC, ONEOF, SFIXED64, (value,as_int,value.as_int), 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 7) \ +X(a, STATIC, SINGULAR, UINT32, flags, 8) +#define opentelemetry_proto_metrics_v1_NumberDataPoint_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_NumberDataPoint_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_NumberDataPoint_exemplars_MSGTYPE opentelemetry_proto_metrics_v1_Exemplar +#define opentelemetry_proto_metrics_v1_NumberDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ +X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ +X(a, STATIC, SINGULAR, FIXED64, count, 4) \ +X(a, STATIC, OPTIONAL, DOUBLE, sum, 5) \ +X(a, CALLBACK, REPEATED, FIXED64, bucket_counts, 6) \ +X(a, CALLBACK, REPEATED, DOUBLE, explicit_bounds, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, exemplars, 8) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 9) \ +X(a, STATIC, SINGULAR, UINT32, flags, 10) \ +X(a, STATIC, OPTIONAL, DOUBLE, min, 11) \ +X(a, STATIC, OPTIONAL, DOUBLE, max, 12) +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_exemplars_MSGTYPE opentelemetry_proto_metrics_v1_Exemplar +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 1) \ +X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ +X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ +X(a, STATIC, SINGULAR, FIXED64, count, 4) \ +X(a, STATIC, OPTIONAL, DOUBLE, sum, 5) \ +X(a, STATIC, SINGULAR, SINT32, scale, 6) \ +X(a, STATIC, SINGULAR, FIXED64, zero_count, 7) \ +X(a, STATIC, OPTIONAL, MESSAGE, positive, 8) \ +X(a, STATIC, OPTIONAL, MESSAGE, negative, 9) \ +X(a, STATIC, SINGULAR, UINT32, flags, 10) \ +X(a, CALLBACK, REPEATED, MESSAGE, exemplars, 11) \ +X(a, STATIC, OPTIONAL, DOUBLE, min, 12) \ +X(a, STATIC, OPTIONAL, DOUBLE, max, 13) \ +X(a, STATIC, SINGULAR, DOUBLE, zero_threshold, 14) +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_positive_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_negative_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_exemplars_MSGTYPE opentelemetry_proto_metrics_v1_Exemplar + +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, SINT32, offset, 1) \ +X(a, CALLBACK, REPEATED, UINT64, bucket_counts, 2) +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_DEFAULT NULL + +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ +X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ +X(a, STATIC, SINGULAR, FIXED64, count, 4) \ +X(a, STATIC, SINGULAR, DOUBLE, sum, 5) \ +X(a, CALLBACK, REPEATED, MESSAGE, quantile_values, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 7) \ +X(a, STATIC, SINGULAR, UINT32, flags, 8) +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_quantile_values_MSGTYPE opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, DOUBLE, quantile, 1) \ +X(a, STATIC, SINGULAR, DOUBLE, value, 2) +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_CALLBACK NULL +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_DEFAULT NULL + +#define opentelemetry_proto_metrics_v1_Exemplar_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 2) \ +X(a, STATIC, ONEOF, DOUBLE, (value,as_double,value.as_double), 3) \ +X(a, CALLBACK, SINGULAR, BYTES, span_id, 4) \ +X(a, CALLBACK, SINGULAR, BYTES, trace_id, 5) \ +X(a, STATIC, ONEOF, SFIXED64, (value,as_int,value.as_int), 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, filtered_attributes, 7) +#define opentelemetry_proto_metrics_v1_Exemplar_CALLBACK pb_default_field_callback +#define opentelemetry_proto_metrics_v1_Exemplar_DEFAULT NULL +#define opentelemetry_proto_metrics_v1_Exemplar_filtered_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue + +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_MetricsData_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ResourceMetrics_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ScopeMetrics_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Metric_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Gauge_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Sum_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Histogram_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ExponentialHistogram_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Summary_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_NumberDataPoint_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_HistogramDataPoint_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_SummaryDataPoint_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_msg; +extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Exemplar_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define opentelemetry_proto_metrics_v1_MetricsData_fields &opentelemetry_proto_metrics_v1_MetricsData_msg +#define opentelemetry_proto_metrics_v1_ResourceMetrics_fields &opentelemetry_proto_metrics_v1_ResourceMetrics_msg +#define opentelemetry_proto_metrics_v1_ScopeMetrics_fields &opentelemetry_proto_metrics_v1_ScopeMetrics_msg +#define opentelemetry_proto_metrics_v1_Metric_fields &opentelemetry_proto_metrics_v1_Metric_msg +#define opentelemetry_proto_metrics_v1_Gauge_fields &opentelemetry_proto_metrics_v1_Gauge_msg +#define opentelemetry_proto_metrics_v1_Sum_fields &opentelemetry_proto_metrics_v1_Sum_msg +#define opentelemetry_proto_metrics_v1_Histogram_fields &opentelemetry_proto_metrics_v1_Histogram_msg +#define opentelemetry_proto_metrics_v1_ExponentialHistogram_fields &opentelemetry_proto_metrics_v1_ExponentialHistogram_msg +#define opentelemetry_proto_metrics_v1_Summary_fields &opentelemetry_proto_metrics_v1_Summary_msg +#define opentelemetry_proto_metrics_v1_NumberDataPoint_fields &opentelemetry_proto_metrics_v1_NumberDataPoint_msg +#define opentelemetry_proto_metrics_v1_HistogramDataPoint_fields &opentelemetry_proto_metrics_v1_HistogramDataPoint_msg +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_fields &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_msg +#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_fields &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_msg +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_fields &opentelemetry_proto_metrics_v1_SummaryDataPoint_msg +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_fields &opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_msg +#define opentelemetry_proto_metrics_v1_Exemplar_fields &opentelemetry_proto_metrics_v1_Exemplar_msg + +/* Maximum encoded size of messages (where known) */ +/* opentelemetry_proto_metrics_v1_MetricsData_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_ResourceMetrics_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_ScopeMetrics_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_Metric_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_Gauge_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_Sum_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_Histogram_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_ExponentialHistogram_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_Summary_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_NumberDataPoint_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_HistogramDataPoint_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_SummaryDataPoint_size depends on runtime parameters */ +/* opentelemetry_proto_metrics_v1_Exemplar_size depends on runtime parameters */ +#define OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_MAX_SIZE opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_size +#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_size 18 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +/* Message descriptors for nanopb */ +namespace nanopb { +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_MetricsData_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_ResourceMetrics_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_ScopeMetrics_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 9; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_Metric_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_Gauge_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_Sum_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_Histogram_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_ExponentialHistogram_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_Summary_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 7; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_NumberDataPoint_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 11; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_HistogramDataPoint_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 14; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 7; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_SummaryDataPoint_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; + static PB_INLINE_CONSTEXPR const pb_size_t size = opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_size; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 6; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_metrics_v1_Exemplar_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +} // namespace nanopb + +#endif /* __cplusplus */ + + +#endif diff --git a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc new file mode 100644 index 0000000..914ea0c --- /dev/null +++ b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc @@ -0,0 +1,69 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "opentelemetry/proto/metrics/v1/metrics.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(opentelemetry_proto_metrics_v1_MetricsData, opentelemetry_proto_metrics_v1_MetricsData, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_ResourceMetrics, opentelemetry_proto_metrics_v1_ResourceMetrics, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_ScopeMetrics, opentelemetry_proto_metrics_v1_ScopeMetrics, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_Metric, opentelemetry_proto_metrics_v1_Metric, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_Gauge, opentelemetry_proto_metrics_v1_Gauge, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_Sum, opentelemetry_proto_metrics_v1_Sum, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_Histogram, opentelemetry_proto_metrics_v1_Histogram, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_ExponentialHistogram, opentelemetry_proto_metrics_v1_ExponentialHistogram, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_Summary, opentelemetry_proto_metrics_v1_Summary, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_NumberDataPoint, opentelemetry_proto_metrics_v1_NumberDataPoint, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_HistogramDataPoint, opentelemetry_proto_metrics_v1_HistogramDataPoint, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_SummaryDataPoint, opentelemetry_proto_metrics_v1_SummaryDataPoint, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile, opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile, AUTO) + + +PB_BIND(opentelemetry_proto_metrics_v1_Exemplar, opentelemetry_proto_metrics_v1_Exemplar, AUTO) + + + + + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/src/proto/opentelemetry/proto/resource/v1/resource.pb.h b/src/proto/opentelemetry/proto/resource/v1/resource.pb.h new file mode 100644 index 0000000..58c1000 --- /dev/null +++ b/src/proto/opentelemetry/proto/resource/v1/resource.pb.h @@ -0,0 +1,89 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_OPENTELEMETRY_PROTO_RESOURCE_V1_OPENTELEMETRY_PROTO_RESOURCE_V1_RESOURCE_PB_H_INCLUDED +#define PB_OPENTELEMETRY_PROTO_RESOURCE_V1_OPENTELEMETRY_PROTO_RESOURCE_V1_RESOURCE_PB_H_INCLUDED +#include +#include "opentelemetry/proto/common/v1/common.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +/* Resource information. */ +typedef struct _opentelemetry_proto_resource_v1_Resource { + /* Set of attributes that describe the resource. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. */ + pb_callback_t attributes; + /* The number of dropped attributes. If the value is 0, then + no attributes were dropped. */ + uint32_t dropped_attributes_count; + /* Set of entities that participate in this Resource. + + Note: keys in the references MUST exist in attributes of this message. + + Status: [Development] */ + pb_callback_t entity_refs; +} opentelemetry_proto_resource_v1_Resource; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define opentelemetry_proto_resource_v1_Resource_init_default {{{NULL}, NULL}, 0, {{NULL}, NULL}} +#define opentelemetry_proto_resource_v1_Resource_init_zero {{{NULL}, NULL}, 0, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define opentelemetry_proto_resource_v1_Resource_attributes_tag 1 +#define opentelemetry_proto_resource_v1_Resource_dropped_attributes_count_tag 2 +#define opentelemetry_proto_resource_v1_Resource_entity_refs_tag 3 + +/* Struct field encoding specification for nanopb */ +#define opentelemetry_proto_resource_v1_Resource_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 1) \ +X(a, STATIC, SINGULAR, UINT32, dropped_attributes_count, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, entity_refs, 3) +#define opentelemetry_proto_resource_v1_Resource_CALLBACK pb_default_field_callback +#define opentelemetry_proto_resource_v1_Resource_DEFAULT NULL +#define opentelemetry_proto_resource_v1_Resource_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue +#define opentelemetry_proto_resource_v1_Resource_entity_refs_MSGTYPE opentelemetry_proto_common_v1_EntityRef + +extern const pb_msgdesc_t opentelemetry_proto_resource_v1_Resource_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define opentelemetry_proto_resource_v1_Resource_fields &opentelemetry_proto_resource_v1_Resource_msg + +/* Maximum encoded size of messages (where known) */ +/* opentelemetry_proto_resource_v1_Resource_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +/* Message descriptors for nanopb */ +namespace nanopb { +template <> +struct MessageDescriptor { + static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; + static inline const pb_msgdesc_t* fields() { + return &opentelemetry_proto_resource_v1_Resource_msg; + } + static inline bool has_msgid() { + return false; + } + static inline uint32_t msgid() { + return 0; + } +}; +} // namespace nanopb + +#endif /* __cplusplus */ + + +#endif diff --git a/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc b/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc new file mode 100644 index 0000000..befc5d7 --- /dev/null +++ b/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc @@ -0,0 +1,12 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "opentelemetry/proto/resource/v1/resource.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(opentelemetry_proto_resource_v1_Resource, opentelemetry_proto_resource_v1_Resource, AUTO) + + + From 4c69ba15e2ef2403d7642ff9eccf5009e6e058a6 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 17:27:03 -0500 Subject: [PATCH 07/30] feat(examples): add proto_otlp example and declare examples in library.json - examples/proto_otlp/main.cpp: demonstrates protobuf encoding mode with the same three-signal (trace/metric/log) pattern as direct_otlp; the only difference from the caller's perspective is the build flag -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF - library.json: add "examples" array so the PlatformIO registry lists all three examples (basic, direct_otlp, proto_otlp) on the library page - platformio.ini: add esp32dev-proto, rpipicow-proto, esp8266-proto envs that extend their base envs and enable the protobuf flag via `extends` - validate.yml: build the basic example (was missing), and build proto_otlp against the new *-proto environments on all three platforms --- .github/workflows/validate.yml | 26 ++++- examples/proto_otlp/main.cpp | 170 +++++++++++++++++++++++++++++++++ library.json | 19 +++- platformio.ini | 21 ++++ 4 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 examples/proto_otlp/main.cpp diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 6f9eec6..5ab9b4d 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -51,7 +51,18 @@ jobs: - name: Build src for ESP8266 (d1_mini) run: platformio ci src/main.cpp --project-conf platformio.ini --lib "." -e esp8266 - # ── examples/direct_otlp (validates OTLP exporter configuration) ─────── + # ── examples/basic (minimal heartbeat, collector-based setup) ──────────── + + - name: Build basic example for ESP32 (esp32dev) + run: platformio ci examples/basic/main.cpp --project-conf platformio.ini --lib "." -e esp32dev + + - name: Build basic example for Pico W (rpipicow) + run: platformio ci examples/basic/main.cpp --project-conf platformio.ini --lib "." -e rpipicow + + - name: Build basic example for ESP8266 (d1_mini) + run: platformio ci examples/basic/main.cpp --project-conf platformio.ini --lib "." -e esp8266 + + # ── examples/direct_otlp (validates OTLP exporter configuration) ───────── - name: Build direct_otlp example for ESP32 (esp32dev) run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp32dev @@ -61,3 +72,16 @@ jobs: - name: Build direct_otlp example for ESP8266 (d1_mini) run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp8266 + + # ── examples/proto_otlp (validates protobuf encoding path) ─────────────── + # Uses dedicated *-proto envs in platformio.ini that inherit their base + # env and add -DOTEL_EXPORTER_OTLP_PROTOCOL=...HTTP_PROTOBUF. + + - name: Build proto_otlp example for ESP32 (esp32dev-proto) + run: platformio ci examples/proto_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp32dev-proto + + - name: Build proto_otlp example for Pico W (rpipicow-proto) + run: platformio ci examples/proto_otlp/main.cpp --project-conf platformio.ini --lib "." -e rpipicow-proto + + - name: Build proto_otlp example for ESP8266 (esp8266-proto) + run: platformio ci examples/proto_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp8266-proto diff --git a/examples/proto_otlp/main.cpp b/examples/proto_otlp/main.cpp new file mode 100644 index 0000000..9d9b8c3 --- /dev/null +++ b/examples/proto_otlp/main.cpp @@ -0,0 +1,170 @@ +/* + * proto_otlp — send traces, metrics, and logs over OTLP/HTTP using binary + * protobuf encoding instead of the default JSON. + * + * Protobuf produces smaller payloads (typically 30–60 % smaller than JSON for + * the same telemetry) and is faster to serialise on-device. It is the + * preferred encoding for bandwidth-constrained devices or high-frequency + * telemetry. + * + * To enable protobuf, add ONE build flag: + * + * build_flags = + * -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + * + * All other configuration (endpoint, headers, TLS) is identical to JSON mode. + * The application code below is exactly the same as the direct_otlp example — + * only the build flag differs. + * + * Protobuf also requires the nanopb library: + * + * lib_deps = + * bblanchon/ArduinoJson@^7.0.0 + * nanopb/Nanopb@^0.4.91 + * + * ── Datadog (US1) example ──────────────────────────────────────────────────── + * + * build_flags = + * -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + * -DOTEL_EXPORTER_OTLP_ENDPOINT="\"https://otlp.datadoghq.com\"" + * -DOTEL_EXPORTER_OTLP_HEADERS="\"dd-api-key=${sysenv.DD_API_KEY}\"" + * + * ── Grafana Cloud example ──────────────────────────────────────────────────── + * + * build_flags = + * -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + * -DOTEL_EXPORTER_OTLP_ENDPOINT="\"https://.grafana.net/otlp\"" + * -DOTEL_EXPORTER_OTLP_HEADERS="\"Authorization=Basic \"" + * + * ── Local OpenTelemetry Collector ──────────────────────────────────────────── + * + * build_flags = + * -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + * -DOTEL_EXPORTER_OTLP_ENDPOINT="\"http://192.168.1.10:4318\"" + * + * ───────────────────────────────────────────────────────────────────────────── + * + * Buffer sizing: the protobuf encoder uses a stack buffer for each signal. + * The default is 1024 bytes. Increase it if you have many attributes or long + * string values and see silent drops: + * + * -DOTEL_PROTO_BUFFER_SIZE=2048 + * + * ───────────────────────────────────────────────────────────────────────────── + */ + +#include + +#if defined(ESP8266) + #include +#elif defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_RP2040) + #include +#else + #error "Unsupported platform: must be ESP8266, ESP32, or RP2040" +#endif + +#include + +#include "OtelSender.h" +#include "OtelLogger.h" +#include "OtelTracer.h" +#include "OtelMetrics.h" + +// ── Build-time defaults (override via -D flags in platformio.ini) ───────────── + +#ifndef WIFI_SSID +#define WIFI_SSID "your-ssid" +#endif + +#ifndef WIFI_PASS +#define WIFI_PASS "your-password" +#endif + +#ifndef OTEL_DEPLOY_ENV +#define OTEL_DEPLOY_ENV "dev" +#endif + +// ───────────────────────────────────────────────────────────────────────────── + +static constexpr uint32_t SEND_INTERVAL_MS = 10000; + +void setup() { + Serial.begin(115200); + + Serial.printf("Connecting to %s\n", WIFI_SSID); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print('.'); + } + Serial.println("\nWi-Fi connected"); + + configTime(0, 0, "pool.ntp.org", "time.nist.gov"); + Serial.print("Waiting for NTP"); + while (time(nullptr) < 1609459200UL) { + delay(500); + Serial.print('.'); + } + Serial.println(); + + OTel::Tracer::begin("proto-otlp-example", "1.0.0"); + OTel::Metrics::begin("proto-otlp-example", "1.0.0"); + + OTel::Metrics::setDefaultMetricLabel("device.id", "esp32-abc123"); + OTel::Metrics::setDefaultMetricLabel("deploy.env", OTEL_DEPLOY_ENV); + OTel::Logger::setDefaultLabel("device.id", "esp32-abc123"); + OTel::Logger::setDefaultLabel("deploy.env", OTEL_DEPLOY_ENV); + +#if defined(ARDUINO_ARCH_RP2040) + OTelSender::beginAsyncWorker(); +#endif + +#if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + Serial.println("OTel ready — encoding: protobuf"); +#else + Serial.println("OTel ready — encoding: JSON"); +#endif + Serial.println("Endpoint: " OTEL_EXPORTER_OTLP_ENDPOINT); +} + +void loop() { + { + auto span = OTel::Tracer::startSpan("sensor-read"); + + span.setAttribute("sensor.id", "1"); + span.setAttribute("sensor.type", "temperature"); + span.setAttribute("sensor.channel", (int64_t)0); + span.setAttribute("sensor.enabled", true); + + OTel::Logger::logInfo("Reading sensor", {{"sensor.id", "1"}}); + + float temperature = 20.0f + (random(0, 100) / 10.0f); + bool overThreshold = temperature > 28.0f; + + if (overThreshold) { + span.addEvent("threshold-crossed", {{"threshold.celsius", "28.0"}}); + OTel::Logger::logWarn("Temperature above threshold", + {{"sensor.id", "1"}, + {"value.celsius", String(temperature).c_str()}}); + } + + OTel::Metrics::gauge("sensor.temperature", temperature, "Cel", + {{"sensor.id", "1"}}); + + static double totalReadings = 0; + totalReadings += 1.0; + OTel::Metrics::sum("sensor.readings.total", totalReadings, + /*isMonotonic=*/true, "DELTA", "1", + {{"sensor.id", "1"}}); + + span.setAttribute("reading.celsius", (double)temperature); + span.end(); + } + + uint32_t dropped = OTelSender::droppedCount(); + if (dropped > 0) { + Serial.printf("[otel] warning: %u items dropped from send queue\n", dropped); + } + + delay(SEND_INTERVAL_MS); +} diff --git a/library.json b/library.json index cb579be..6ba1fba 100644 --- a/library.json +++ b/library.json @@ -40,5 +40,22 @@ "repository": { "type": "git", "url": "https://github.com/proffalken/otel-embedded-cpp.git" - } + }, + "examples": [ + { + "name": "Basic", + "base": "examples/basic", + "files": ["main.cpp", "README.md"] + }, + { + "name": "Direct OTLP", + "base": "examples/direct_otlp", + "files": ["main.cpp", "README.md"] + }, + { + "name": "Protobuf OTLP", + "base": "examples/proto_otlp", + "files": ["main.cpp"] + } + ] } diff --git a/platformio.ini b/platformio.ini index 5bdf2fc..a93cadc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -66,3 +66,24 @@ build_flags = -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" -DOTEL_SERVICE_INSTANCE=\"esp8266\" -DOTEL_DEPLOY_ENV=\"esp8266\" + +; ── Protobuf variants — used by CI to validate the protobuf encoding path ───── +; Each env extends its base and adds the protocol flag on top of existing flags. + +[env:esp32dev-proto] +extends = esp32dev +build_flags = + ${env:esp32dev.build_flags} + -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + +[env:rpipicow-proto] +extends = rpipicow +build_flags = + ${env:rpipicow.build_flags} + -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + +[env:esp8266-proto] +extends = esp8266 +build_flags = + ${env:esp8266.build_flags} + -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF From 7b9491df605be0e409a0a62474513a8f3e619aa5 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 17:46:50 -0500 Subject: [PATCH 08/30] ci: add workflow_dispatch trigger for manual test runs --- .github/workflows/validate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 5ab9b4d..b127211 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -5,6 +5,7 @@ on: branches: [main] pull_request: branches: [main] + workflow_dispatch: # allow manual runs from the GitHub Actions tab jobs: build: From 0d0d91d0c050069125d7cfd7cf9d2a461dfd20d7 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 17:49:41 -0500 Subject: [PATCH 09/30] ci: add validate workflow with workflow_dispatch trigger --- .github/workflows/validate.yml | 49 +++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index c6794d0..b127211 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -5,6 +5,7 @@ on: branches: [main] pull_request: branches: [main] + workflow_dispatch: # allow manual runs from the GitHub Actions tab jobs: build: @@ -21,15 +22,15 @@ jobs: - name: Install PlatformIO run: python -m pip install -U platformio - # Make repo Variables + Secrets available to all subsequent steps + # Expose repo variables and secrets as environment variables for PlatformIO + # sysenv.* references in platformio.ini build_flags pick these up at build time. - name: Export env for build run: | echo "::add-mask::${{ secrets.WIFI_PASS }}" { echo "WIFI_SSID=${{ vars.WIFI_SSID }}" echo "WIFI_PASS=${{ secrets.WIFI_PASS }}" - echo "OTEL_COLLECTOR_HOST=${{ vars.OTEL_COLLECTOR_HOST }}" - echo "OTEL_COLLECTOR_PORT=${{ vars.OTEL_COLLECTOR_PORT }}" + echo "OTEL_EXPORTER_OTLP_ENDPOINT=${{ vars.OTEL_EXPORTER_OTLP_ENDPOINT || '' }}" echo "OTEL_SERVICE_NAME=${{ vars.OTEL_SERVICE_NAME }}" echo "OTEL_SERVICE_NAMESPACE=${{ vars.OTEL_SERVICE_NAMESPACE }}" echo "OTEL_SERVICE_VERSION=${{ vars.OTEL_SERVICE_VERSION }}" @@ -40,12 +41,48 @@ jobs: - name: PlatformIO Update run: pio update - - name: Build for ESP32 (esp32dev) + # ── src/main.cpp (existing integration test) ─────────────────────────── + + - name: Build src for ESP32 (esp32dev) run: platformio ci src/main.cpp --project-conf platformio.ini --lib "." -e esp32dev - - name: Build for Pico W (rpipicow) + - name: Build src for Pico W (rpipicow) run: platformio ci src/main.cpp --project-conf platformio.ini --lib "." -e rpipicow - - name: Build for ESP8266 (esp8266 d1_mini) + - name: Build src for ESP8266 (d1_mini) run: platformio ci src/main.cpp --project-conf platformio.ini --lib "." -e esp8266 + # ── examples/basic (minimal heartbeat, collector-based setup) ──────────── + + - name: Build basic example for ESP32 (esp32dev) + run: platformio ci examples/basic/main.cpp --project-conf platformio.ini --lib "." -e esp32dev + + - name: Build basic example for Pico W (rpipicow) + run: platformio ci examples/basic/main.cpp --project-conf platformio.ini --lib "." -e rpipicow + + - name: Build basic example for ESP8266 (d1_mini) + run: platformio ci examples/basic/main.cpp --project-conf platformio.ini --lib "." -e esp8266 + + # ── examples/direct_otlp (validates OTLP exporter configuration) ───────── + + - name: Build direct_otlp example for ESP32 (esp32dev) + run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp32dev + + - name: Build direct_otlp example for Pico W (rpipicow) + run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e rpipicow + + - name: Build direct_otlp example for ESP8266 (d1_mini) + run: platformio ci examples/direct_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp8266 + + # ── examples/proto_otlp (validates protobuf encoding path) ─────────────── + # Uses dedicated *-proto envs in platformio.ini that inherit their base + # env and add -DOTEL_EXPORTER_OTLP_PROTOCOL=...HTTP_PROTOBUF. + + - name: Build proto_otlp example for ESP32 (esp32dev-proto) + run: platformio ci examples/proto_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp32dev-proto + + - name: Build proto_otlp example for Pico W (rpipicow-proto) + run: platformio ci examples/proto_otlp/main.cpp --project-conf platformio.ini --lib "." -e rpipicow-proto + + - name: Build proto_otlp example for ESP8266 (esp8266-proto) + run: platformio ci examples/proto_otlp/main.cpp --project-conf platformio.ini --lib "." -e esp8266-proto From 4483e43d11b7f69ef1aff4147d70b4d67ac4f811 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 20:07:39 -0500 Subject: [PATCH 10/30] fix(ci): prevent registry library from shadowing local source PlatformIO cached otel-embedded-cpp@1.0.2 from the registry during pio update. Because library.json also declared version 1.0.2, the LDF used the cached copy (with old API calls) instead of --lib ".". - Bump library version to 1.1.0 so no registry match is possible - Add build.srcFilter to exclude main.cpp from library compilation; src/main.cpp is an integration test, not a reusable library source - Flush the cached package before each CI build run as belt-and-suspenders --- .github/workflows/validate.yml | 9 +++++++-- library.json | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index b127211..d7572f2 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -12,10 +12,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' @@ -41,6 +41,11 @@ jobs: - name: PlatformIO Update run: pio update + # Remove any cached registry version so --lib "." always wins and we + # compile the local source, not the published package at the same version. + - name: Flush cached library + run: pio pkg uninstall --global --library "otel-embedded-cpp" 2>/dev/null || true + # ── src/main.cpp (existing integration test) ─────────────────────────── - name: Build src for ESP32 (esp32dev) diff --git a/library.json b/library.json index 6ba1fba..254567c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "otel-embedded-cpp", - "version": "1.0.2", + "version": "1.1.0", "description": "OpenTelemetry logging, tracing, and metrics for embedded C++ devices (ESP32, RP2040 Pico W, ESP8266).", "keywords": [ "OpenTelemetry", @@ -37,6 +37,9 @@ "version": "^0.4.91" } ], + "build": { + "srcFilter": ["+<*.cpp>", "-"] + }, "repository": { "type": "git", "url": "https://github.com/proffalken/otel-embedded-cpp.git" From 590742dcf4581f88878e4240ca35667672ae2227 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 20:18:27 -0500 Subject: [PATCH 11/30] fix(ci): fully specify proto envs instead of using extends platformio ci does not reliably propagate platform/board/framework through the extends mechanism, causing UndefinedEnvPlatformError. Replace all three *-proto environments with explicit full definitions. --- platformio.ini | 55 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/platformio.ini b/platformio.ini index a93cadc..1cb4d23 100644 --- a/platformio.ini +++ b/platformio.ini @@ -68,22 +68,63 @@ build_flags = -DOTEL_DEPLOY_ENV=\"esp8266\" ; ── Protobuf variants — used by CI to validate the protobuf encoding path ───── -; Each env extends its base and adds the protocol flag on top of existing flags. +; Fully specified (not using `extends`) because platformio ci does not reliably +; inherit platform/board/framework through the extends mechanism. [env:esp32dev-proto] -extends = esp32dev +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 +lib_deps = + ArduinoJson@^7.4.2 + nanopb/Nanopb@^0.4.91 + WiFi + HTTPClient build_flags = - ${env:esp32dev.build_flags} + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" + -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF [env:rpipicow-proto] -extends = rpipicow +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = rpipicow +framework = arduino +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 build_flags = - ${env:rpipicow.build_flags} + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" + -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" + -DDEBUG -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF [env:esp8266-proto] -extends = esp8266 +platform = espressif8266 +board = d1_mini +framework = arduino +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 build_flags = - ${env:esp8266.build_flags} + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"esp8266\" + -DOTEL_DEPLOY_ENV=\"esp8266\" -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF From ffe2fb92efdf6af0fbf51a9a635358745f3c53e0 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 20:36:07 -0500 Subject: [PATCH 12/30] fix(proto): use relative includes in .pb.inc files The nanopb-generated .pb.inc files used full project-rooted paths (e.g. opentelemetry/proto/common/v1/common.pb.h) that required src/proto/ on the include path. PlatformIO only adds src/ by default, causing fatal include errors during CI. Switch to sibling-relative includes since each .pb.h lives in the same directory as its .pb.inc. Co-Authored-By: Claude Sonnet 4.6 --- src/proto/opentelemetry/proto/common/v1/common.pb.inc | 2 +- src/proto/opentelemetry/proto/logs/v1/logs.pb.inc | 2 +- src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc | 2 +- src/proto/opentelemetry/proto/resource/v1/resource.pb.inc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/proto/opentelemetry/proto/common/v1/common.pb.inc b/src/proto/opentelemetry/proto/common/v1/common.pb.inc index b1beb9d..1835279 100644 --- a/src/proto/opentelemetry/proto/common/v1/common.pb.inc +++ b/src/proto/opentelemetry/proto/common/v1/common.pb.inc @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ /* Generated by nanopb-0.4.9.1 */ -#include "opentelemetry/proto/common/v1/common.pb.h" +#include "common.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif diff --git a/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc b/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc index e56612e..41875ce 100644 --- a/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc +++ b/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ /* Generated by nanopb-0.4.9.1 */ -#include "opentelemetry/proto/logs/v1/logs.pb.h" +#include "logs.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif diff --git a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc index 914ea0c..e800058 100644 --- a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc +++ b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ /* Generated by nanopb-0.4.9.1 */ -#include "opentelemetry/proto/metrics/v1/metrics.pb.h" +#include "metrics.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif diff --git a/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc b/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc index befc5d7..ed5c9e4 100644 --- a/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc +++ b/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ /* Generated by nanopb-0.4.9.1 */ -#include "opentelemetry/proto/resource/v1/resource.pb.h" +#include "resource.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif From f61796d50a6aeae5c42d634e2469c5e3b89cd678 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 21:19:01 -0500 Subject: [PATCH 13/30] fix(proto): use relative includes in .pb.h cross-references The nanopb-generated .pb.h files referenced sibling proto headers using full project-rooted paths (e.g. opentelemetry/proto/common/v1/...) that require src/proto/ on the include path. Switch to relative paths (../../common/v1/common.pb.h etc.) so the files resolve correctly from their own directory, regardless of which include paths the build system adds. Co-Authored-By: Claude Sonnet 4.6 --- src/proto/opentelemetry/proto/logs/v1/logs.pb.h | 4 ++-- src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h | 4 ++-- src/proto/opentelemetry/proto/resource/v1/resource.pb.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/proto/opentelemetry/proto/logs/v1/logs.pb.h b/src/proto/opentelemetry/proto/logs/v1/logs.pb.h index 3e63a8d..231f36c 100644 --- a/src/proto/opentelemetry/proto/logs/v1/logs.pb.h +++ b/src/proto/opentelemetry/proto/logs/v1/logs.pb.h @@ -4,8 +4,8 @@ #ifndef PB_OPENTELEMETRY_PROTO_LOGS_V1_OPENTELEMETRY_PROTO_LOGS_V1_LOGS_PB_H_INCLUDED #define PB_OPENTELEMETRY_PROTO_LOGS_V1_OPENTELEMETRY_PROTO_LOGS_V1_LOGS_PB_H_INCLUDED #include -#include "opentelemetry/proto/common/v1/common.pb.h" -#include "opentelemetry/proto/resource/v1/resource.pb.h" +#include "../../common/v1/common.pb.h" +#include "../../resource/v1/resource.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. diff --git a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h index ad0a687..08c0f36 100644 --- a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h +++ b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h @@ -4,8 +4,8 @@ #ifndef PB_OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_INCLUDED #define PB_OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_INCLUDED #include -#include "opentelemetry/proto/common/v1/common.pb.h" -#include "opentelemetry/proto/resource/v1/resource.pb.h" +#include "../../common/v1/common.pb.h" +#include "../../resource/v1/resource.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. diff --git a/src/proto/opentelemetry/proto/resource/v1/resource.pb.h b/src/proto/opentelemetry/proto/resource/v1/resource.pb.h index 58c1000..1eb114b 100644 --- a/src/proto/opentelemetry/proto/resource/v1/resource.pb.h +++ b/src/proto/opentelemetry/proto/resource/v1/resource.pb.h @@ -4,7 +4,7 @@ #ifndef PB_OPENTELEMETRY_PROTO_RESOURCE_V1_OPENTELEMETRY_PROTO_RESOURCE_V1_RESOURCE_PB_H_INCLUDED #define PB_OPENTELEMETRY_PROTO_RESOURCE_V1_OPENTELEMETRY_PROTO_RESOURCE_V1_RESOURCE_PB_H_INCLUDED #include -#include "opentelemetry/proto/common/v1/common.pb.h" +#include "../../common/v1/common.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. From 67eb345f6db66ecc38972d9b14fcb27aa9bc2c92 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 21:37:52 -0500 Subject: [PATCH 14/30] fix(proto): include OtelMetrics.h and OtelLogger.h in OtelProtoEncoder.cpp metricsScopeConfig() and logScopeConfig() are defined in their respective signal headers, but OtelProtoEncoder.cpp didn't include them, causing undeclared-identifier errors in the proto build path. Co-Authored-By: Claude Sonnet 4.6 --- src/OtelProtoEncoder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OtelProtoEncoder.cpp b/src/OtelProtoEncoder.cpp index 4fb3853..c0b12af 100644 --- a/src/OtelProtoEncoder.cpp +++ b/src/OtelProtoEncoder.cpp @@ -16,6 +16,8 @@ #include "OtelProtoEncoder.h" #include "OtelTracer.h" // defaultServiceName(), defaultServiceInstanceId(), defaultHostName() +#include "OtelMetrics.h" // metricsScopeConfig() +#include "OtelLogger.h" // logScopeConfig() #include From da618c4294e16e0f0ab7b827d8219e4e4acd1cac Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 22:10:37 -0500 Subject: [PATCH 15/30] fix(warnings): remove unused static functions in proto build path - Guard the three JSON-only helpers in OtelMetrics.cpp (addPointAttributes, addCommonResource, addCommonScope) with #if !HTTP_PROTOBUF so they are not compiled when the proto path is active, eliminating -Wunused-function warnings. - Delete cb_kv_cstr and cb_typed_attrs from OtelProtoEncoder.cpp; both were defined but never called (dead code from an earlier draft). Co-Authored-By: Claude Sonnet 4.6 --- src/OtelMetrics.cpp | 8 +++--- src/OtelProtoEncoder.cpp | 54 ---------------------------------------- 2 files changed, 4 insertions(+), 58 deletions(-) diff --git a/src/OtelMetrics.cpp b/src/OtelMetrics.cpp index fe5593b..9c83443 100644 --- a/src/OtelMetrics.cpp +++ b/src/OtelMetrics.cpp @@ -2,16 +2,15 @@ namespace OTel { -// Helper: merge default + per-call labels into a datapoint attributes array +#if OTEL_EXPORTER_OTLP_PROTOCOL != OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + static void addPointAttributes(JsonArray& attrArray, const std::map& callLabels) { - // Defaults first for (const auto& kv : defaultMetricLabels()) { JsonObject a = attrArray.add(); a["key"] = kv.first; a["value"].to()["stringValue"] = kv.second; } - // Then per-call (override by reusing key downstream in the stack) for (const auto& kv : callLabels) { JsonObject a = attrArray.add(); a["key"] = kv.first; @@ -25,7 +24,6 @@ static void addCommonResource(JsonObject& resource) { res.addResourceAttributes(resource); return; } - JsonArray rattrs = resource["attributes"].to(); addResAttr(rattrs, "service.name", defaultServiceName()); addResAttr(rattrs, "service.instance.id", defaultServiceInstanceId()); @@ -37,6 +35,8 @@ static void addCommonScope(JsonObject& scope) { scope["version"] = metricsScopeConfig().scopeVersion; } +#endif // OTEL_EXPORTER_OTLP_PROTOCOL != OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF + // ----------------- GAUGE ----------------- void Metrics::buildAndSendGauge(const String& name, double value, const String& unit, diff --git a/src/OtelProtoEncoder.cpp b/src/OtelProtoEncoder.cpp index c0b12af..81e0e5e 100644 --- a/src/OtelProtoEncoder.cpp +++ b/src/OtelProtoEncoder.cpp @@ -34,22 +34,6 @@ static bool cb_cstr(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { pb_encode_string(s, (const pb_byte_t*)str, strlen(str)); } -// Encode a std::pair as a KeyValue with stringValue. -static bool cb_kv_cstr(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { - auto* p = *(const std::pair**)arg; - - opentelemetry_proto_common_v1_KeyValue kv = opentelemetry_proto_common_v1_KeyValue_init_zero; - kv.key.funcs.encode = cb_cstr; - kv.key.arg = (void*)p->first; - kv.has_value = true; - kv.value.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; - kv.value.value.string_value.funcs.encode = cb_cstr; - kv.value.value.string_value.arg = (void*)p->second; - - return pb_encode_tag_for_field(s, f) && - pb_encode_submessage(s, opentelemetry_proto_common_v1_KeyValue_fields, &kv); -} - // ── Map → repeated KeyValue ─────────────────────────────────── struct MapCtx { const std::map* m; }; @@ -96,44 +80,6 @@ static bool cb_merged_map_attrs(pb_ostream_t* s, const pb_field_t* f, void* cons return true; } -// ── Typed span/event attributes ────────────────────────────────────────────── - -struct AttrsCtx { const std::vector* attrs; }; - -static bool cb_typed_attrs(pb_ostream_t* s, const pb_field_t* f, void* const* arg) { - auto* ctx = *(AttrsCtx**)arg; - for (const auto& a : *ctx->attrs) { - opentelemetry_proto_common_v1_KeyValue entry = opentelemetry_proto_common_v1_KeyValue_init_zero; - entry.key.funcs.encode = cb_cstr; - entry.key.arg = (void*)a.key.c_str(); - entry.has_value = true; - - switch (a.type) { - case AttrType::Str: - entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_string_value_tag; - entry.value.value.string_value.funcs.encode = cb_cstr; - entry.value.value.string_value.arg = (void*)a.s.c_str(); - break; - case AttrType::Int: - entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_int_value_tag; - entry.value.value.int_value = a.i; - break; - case AttrType::Dbl: - entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_double_value_tag; - entry.value.value.double_value = a.d; - break; - case AttrType::Bool: - entry.value.which_value = opentelemetry_proto_common_v1_AnyValue_bool_value_tag; - entry.value.value.bool_value = a.b; - break; - } - - if (!pb_encode_tag_for_field(s, f)) return false; - if (!pb_encode_submessage(s, opentelemetry_proto_common_v1_KeyValue_fields, &entry)) return false; - } - return true; -} - // ── Resource builder ───────────────────────────────────────────────────────── // Fills a Resource with service.name / service.instance.id / host.name. From 75513259406f73ce9e68a4c55c9d61812404391e Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 22:36:21 -0500 Subject: [PATCH 16/30] feat(test): add native unit test skeleton for OTLP payload validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a host-compilable test suite (pio test -e native) that exercises the JSON encoding path of all three signals without real hardware: - test/stubs/Arduino.h — minimal String/Serial/millis shim so library headers compile under host GCC - test/native/fake_sender.cpp — replaces OtelSender (WiFi/HTTP) with a stub that captures the last emitted JSON/proto payload for assertion - test/native/test_otlp.cpp — 13 Unity tests covering gauge name/value/ unit/labels, log severity text+number+body, and span name+traceId - [env:native] in platformio.ini with OtelSender.cpp excluded from the src filter and test/stubs/ on the include path - CI step "Run native unit tests" added before the cross-compile steps The tests will need iteration as the stubs are exercised; they are intentionally conservative (no proto path, no collector service) as a starting point. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/validate.yml | 8 ++ platformio.ini | 23 +++++ test/native/fake_sender.cpp | 50 ++++++++++ test/native/fake_sender.h | 15 +++ test/native/test_otlp.cpp | 173 +++++++++++++++++++++++++++++++++ test/stubs/Arduino.h | 79 +++++++++++++++ 6 files changed, 348 insertions(+) create mode 100644 test/native/fake_sender.cpp create mode 100644 test/native/fake_sender.h create mode 100644 test/native/test_otlp.cpp create mode 100644 test/stubs/Arduino.h diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index d7572f2..9c91c74 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -46,6 +46,14 @@ jobs: - name: Flush cached library run: pio pkg uninstall --global --library "otel-embedded-cpp" 2>/dev/null || true + # ── Native unit tests (host GCC, no hardware required) ────────────────── + # Compiles and runs test/native/test_otlp.cpp against a fake OTelSender + # that captures emitted JSON so tests can assert on payload structure. + # GCC is pre-installed on ubuntu-latest; no extra toolchain step needed. + + - name: Run native unit tests + run: pio test -e native --project-dir . --project-conf platformio.ini + # ── src/main.cpp (existing integration test) ─────────────────────────── - name: Build src for ESP32 (esp32dev) diff --git a/platformio.ini b/platformio.ini index 1cb4d23..7209d93 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,29 @@ [platformio] default_envs = esp32dev, rpipicow, esp8266 +; ── Native host tests ───────────────────────────────────────────────────────── +; Compiles the library on the host GCC toolchain and runs Unity unit tests. +; OtelSender.cpp (WiFi/HTTP) is excluded; test/native/fake_sender.cpp is used +; instead so tests can assert on emitted JSON payloads without real hardware. +[env:native] +platform = native +build_flags = + -std=c++14 + -Itest/stubs + -DWIFI_SSID=\"ci-build\" + -DWIFI_PASS=\"ci-build\" + -DOTEL_SERVICE_NAME=\"test-service\" + -DOTEL_SERVICE_NAMESPACE=\"test\" + -DOTEL_SERVICE_VERSION=\"0.0.1\" + -DOTEL_SERVICE_INSTANCE=\"test-001\" + -DOTEL_DEPLOY_ENV=\"test\" +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 +; Exclude OtelSender.cpp (WiFi/HTTP) and main.cpp from the test build. +; fake_sender.cpp in test/native/ provides OTelSender stubs instead. +build_src_filter = +<*.cpp> - - + [env:esp32dev] platform = espressif32 board = esp32dev diff --git a/test/native/fake_sender.cpp b/test/native/fake_sender.cpp new file mode 100644 index 0000000..e8a1ff4 --- /dev/null +++ b/test/native/fake_sender.cpp @@ -0,0 +1,50 @@ +#include "fake_sender.h" +#include "OtelSender.h" +#include +#include + +// ── Captured state ──────────────────────────────────────────────────────────── +namespace FakeSender { + std::string lastPath; + std::string lastJson; + std::vector lastProto; + + void reset() { + lastPath.clear(); + lastJson.clear(); + lastProto.clear(); + } +} + +// ── OTelSender static member definitions ───────────────────────────────────── +// These must live in exactly one translation unit; the real OtelSender.cpp is +// excluded from the native build, so we define them here instead. +OTelQueuedItem OTelSender::q_[OTelSender::QCAP]; +std::atomic OTelSender::head_{0}; +std::atomic OTelSender::tail_{0}; +std::atomic OTelSender::drops_{0}; +std::atomic OTelSender::worker_started_{false}; + +// ── Public API stubs ────────────────────────────────────────────────────────── + +void OTelSender::sendJson(const char* path, JsonDocument& doc) { + FakeSender::lastPath = path; + serializeJson(doc, FakeSender::lastJson); +} + +void OTelSender::sendProto(const char* path, const uint8_t* buf, size_t len) { + FakeSender::lastPath = path; + FakeSender::lastProto.assign(buf, buf + len); +} + +void OTelSender::beginAsyncWorker() {} +uint32_t OTelSender::droppedCount() { return 0; } +bool OTelSender::queueIsHealthy() { return true; } + +// ── Private helper stubs ────────────────────────────────────────────────────── +bool OTelSender::enqueue_(const char*, const char*, String&&) { return true; } +bool OTelSender::dequeue_(OTelQueuedItem&) { return false; } +void OTelSender::pumpOnce_() {} +void OTelSender::workerLoop_() {} +void OTelSender::launchWorkerOnce_() {} +String OTelSender::fullUrl_(const char* path) { return String(path); } diff --git a/test/native/fake_sender.h b/test/native/fake_sender.h new file mode 100644 index 0000000..115fab7 --- /dev/null +++ b/test/native/fake_sender.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include +#include + +// Test double for OTelSender. +// Call reset() in setUp(); then inspect lastPath / lastJson / lastProto +// after exercising library code to assert on the emitted payload. +namespace FakeSender { + extern std::string lastPath; + extern std::string lastJson; + extern std::vector lastProto; + + void reset(); +} diff --git a/test/native/test_otlp.cpp b/test/native/test_otlp.cpp new file mode 100644 index 0000000..d9d57e9 --- /dev/null +++ b/test/native/test_otlp.cpp @@ -0,0 +1,173 @@ +#include +#include + +#include "OtelDefaults.h" +#include "OtelMetrics.h" +#include "OtelLogger.h" +#include "OtelTracer.h" +#include "fake_sender.h" + +// ── Fixture ─────────────────────────────────────────────────────────────────── + +void setUp() { + FakeSender::reset(); + + auto& res = OTel::defaultResource(); + res.set("service.name", "test-service"); + res.set("service.namespace", "test-ns"); + res.set("service.instance.id", "test-001"); + res.set("host.name", "test-host"); + + OTel::Metrics::begin("otel-embedded-cpp", "0.0.1"); + OTel::Tracer::begin("otel-embedded-cpp", "0.0.1"); +} + +void tearDown() {} + +// ── Metrics: gauge ──────────────────────────────────────────────────────────── + +void test_gauge_sends_to_metrics_endpoint() { + OTel::Metrics::gauge("cpu.usage", 0.42, "1"); + TEST_ASSERT_EQUAL_STRING("/v1/metrics", FakeSender::lastPath.c_str()); +} + +void test_gauge_payload_has_metric_name() { + OTel::Metrics::gauge("cpu.usage", 0.42, "1"); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + const char* name = + doc["resourceMetrics"][0]["scopeMetrics"][0]["metrics"][0]["name"]; + TEST_ASSERT_EQUAL_STRING("cpu.usage", name); +} + +void test_gauge_payload_has_value() { + OTel::Metrics::gauge("cpu.usage", 0.42, "1"); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + double val = + doc["resourceMetrics"][0]["scopeMetrics"][0]["metrics"][0] + ["gauge"]["dataPoints"][0]["asDouble"]; + TEST_ASSERT_FLOAT_WITHIN(0.001, 0.42, val); +} + +void test_gauge_payload_has_unit() { + OTel::Metrics::gauge("disk.reads", 100.0, "By"); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + const char* unit = + doc["resourceMetrics"][0]["scopeMetrics"][0]["metrics"][0]["unit"]; + TEST_ASSERT_EQUAL_STRING("By", unit); +} + +void test_gauge_payload_includes_call_labels() { + OTel::Metrics::gauge("cpu.usage", 0.5, "1", {{"core", "0"}}); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + JsonArray attrs = + doc["resourceMetrics"][0]["scopeMetrics"][0]["metrics"][0] + ["gauge"]["dataPoints"][0]["attributes"]; + bool found = false; + for (JsonObject a : attrs) { + if (strcmp(a["key"], "core") == 0) { found = true; break; } + } + TEST_ASSERT_TRUE(found); +} + +// ── Logs ────────────────────────────────────────────────────────────────────── + +void test_log_sends_to_logs_endpoint() { + OTel::Logger::logInfo("hello"); + TEST_ASSERT_EQUAL_STRING("/v1/logs", FakeSender::lastPath.c_str()); +} + +void test_log_info_severity_text() { + OTel::Logger::logInfo("hello"); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + const char* sev = + doc["resourceLogs"][0]["scopeLogs"][0]["logRecords"][0]["severityText"]; + TEST_ASSERT_EQUAL_STRING("INFO", sev); +} + +void test_log_warn_severity_number() { + OTel::Logger::logWarn("watch out"); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + int num = + doc["resourceLogs"][0]["scopeLogs"][0]["logRecords"][0]["severityNumber"]; + TEST_ASSERT_EQUAL_INT(13, num); // WARN = 13 per OTLP spec +} + +void test_log_body_string_value() { + OTel::Logger::logInfo("hello world"); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + const char* body = + doc["resourceLogs"][0]["scopeLogs"][0]["logRecords"][0]["body"]["stringValue"]; + TEST_ASSERT_EQUAL_STRING("hello world", body); +} + +void test_log_error_severity_text() { + OTel::Logger::logError("boom"); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + const char* sev = + doc["resourceLogs"][0]["scopeLogs"][0]["logRecords"][0]["severityText"]; + TEST_ASSERT_EQUAL_STRING("ERROR", sev); +} + +// ── Traces ──────────────────────────────────────────────────────────────────── + +void test_span_sends_to_traces_endpoint() { + auto span = OTel::Tracer::startSpan("my-op"); + span.end(); + TEST_ASSERT_EQUAL_STRING("/v1/traces", FakeSender::lastPath.c_str()); +} + +void test_span_name_in_payload() { + auto span = OTel::Tracer::startSpan("my-op"); + span.end(); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + const char* name = + doc["resourceSpans"][0]["scopeSpans"][0]["spans"][0]["name"]; + TEST_ASSERT_EQUAL_STRING("my-op", name); +} + +void test_span_has_non_empty_trace_id() { + auto span = OTel::Tracer::startSpan("my-op"); + span.end(); + JsonDocument doc; + deserializeJson(doc, FakeSender::lastJson); + const char* tid = + doc["resourceSpans"][0]["scopeSpans"][0]["spans"][0]["traceId"]; + TEST_ASSERT_NOT_NULL(tid); + TEST_ASSERT_TRUE(strlen(tid) > 0); +} + +// ── Main ────────────────────────────────────────────────────────────────────── + +int main() { + UNITY_BEGIN(); + + // Metrics + RUN_TEST(test_gauge_sends_to_metrics_endpoint); + RUN_TEST(test_gauge_payload_has_metric_name); + RUN_TEST(test_gauge_payload_has_value); + RUN_TEST(test_gauge_payload_has_unit); + RUN_TEST(test_gauge_payload_includes_call_labels); + + // Logs + RUN_TEST(test_log_sends_to_logs_endpoint); + RUN_TEST(test_log_info_severity_text); + RUN_TEST(test_log_warn_severity_number); + RUN_TEST(test_log_body_string_value); + RUN_TEST(test_log_error_severity_text); + + // Traces + RUN_TEST(test_span_sends_to_traces_endpoint); + RUN_TEST(test_span_name_in_payload); + RUN_TEST(test_span_has_non_empty_trace_id); + + return UNITY_END(); +} diff --git a/test/stubs/Arduino.h b/test/stubs/Arduino.h new file mode 100644 index 0000000..fed2c00 --- /dev/null +++ b/test/stubs/Arduino.h @@ -0,0 +1,79 @@ +#pragma once +// Minimal Arduino shim for native (Linux/macOS) unit tests. +// Provides just enough of the Arduino API surface that otel-embedded-cpp +// headers can be compiled on a host toolchain without modification. + +#ifndef ARDUINO +#define ARDUINO 100 +#endif + +#include +#include +#include +#include +#include + +// ── String ──────────────────────────────────────────────────────────────────── +// std::string subclass that adds the Arduino-compatible constructors and +// helpers used throughout the library (c_str, length, indexOf, etc.). +class String : public std::string { +public: + String() = default; + String(const char* s) : std::string(s ? s : "") {} + String(const std::string& s) : std::string(s) {} + String(char c) : std::string(1, c) {} + explicit String(int v) : std::string(std::to_string(v)) {} + explicit String(long v) : std::string(std::to_string(v)) {} + explicit String(unsigned int v) : std::string(std::to_string(v)) {} + explicit String(unsigned long v) : std::string(std::to_string(v)) {} + explicit String(float v) : std::string(std::to_string(v)) {} + explicit String(double v) : std::string(std::to_string(v)) {} + + unsigned int length() const { return (unsigned int)size(); } + + int indexOf(char c, unsigned int from = 0) const { + auto p = find(c, from); + return p == npos ? -1 : (int)p; + } + String substring(unsigned int from, unsigned int to = 0) const { + return to ? String(substr(from, to - from)) : String(substr(from)); + } + int toInt() const { return empty() ? 0 : std::stoi(*this); } + float toFloat() const { return empty() ? 0.f : std::stof(*this); } + + String& operator+=(const char* s) { std::string::operator+=(s); return *this; } + String& operator+=(const String& s) { std::string::operator+=(s); return *this; } + String operator+(const String& s) const { return String(std::string(*this) + std::string(s)); } + String operator+(const char* s) const { return String(std::string(*this) + s); } + bool operator==(const char* s) const { return compare(s) == 0; } + bool operator!=(const char* s) const { return compare(s) != 0; } +}; + +inline String operator+(const char* a, const String& b) { + return String(std::string(a) + std::string(b)); +} + +// ── Flash string ────────────────────────────────────────────────────────────── +#define F(x) (x) + +// ── Serial stub ─────────────────────────────────────────────────────────────── +struct HardwareSerial { + void begin(unsigned long) {} + template size_t print(T) { return 0; } + template size_t println(T) { return 0; } + template size_t print(T, int) { return 0; } + template size_t println(T, int) { return 0; } + size_t println() { return 0; } + void printf(const char*, ...) {} +}; +static HardwareSerial Serial; + +// ── Timing stubs ────────────────────────────────────────────────────────────── +static inline unsigned long millis() { return 0; } +static inline unsigned long micros() { return 0; } +static inline void delay(unsigned long) {} + +// ── PRNG ────────────────────────────────────────────────────────────────────── +static inline void randomSeed(unsigned long seed) { srand((unsigned int)seed); } +static inline long random(long max) { return (long)(rand() % max); } +static inline long random(long min, long max) { return min + (long)(rand() % (max - min)); } From f3d5b0b775eb3f0c1f5aa83ec8e69024cd9cc976 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 22:51:43 -0500 Subject: [PATCH 17/30] refactor(proto): replace local pb files with aodtorusan/opentelemetry_proto library The OTel proto files (common, resource, metrics, logs) are now sourced from the published PlatformIO library aodtorusan/opentelemetry_proto@^1.9.0 rather than being vendored in src/proto/. Changes: - Delete src/proto/ (8 files: 4 .pb.h + 4 .pb.inc) - OtelProtoEncoder.cpp: remove manual .pb.inc includes (the library's .pb.c files are compiled automatically by PlatformIO's LDF) and update .pb.h include paths from "proto/opentelemetry/..." to "opentelemetry/..." (library src/ is on the include path) - library.json: add aodtorusan/opentelemetry_proto@^1.9.0 as a dependency - platformio.ini already has the library in all env lib_deps (added by user) Co-Authored-By: Claude Sonnet 4.6 --- library.json | 4 + src/OtelProtoEncoder.cpp | 17 +- .../opentelemetry/proto/common/v1/common.pb.h | 315 ----- .../proto/common/v1/common.pb.inc | 35 - .../opentelemetry/proto/logs/v1/logs.pb.h | 371 ----- .../opentelemetry/proto/logs/v1/logs.pb.inc | 25 - .../proto/metrics/v1/metrics.pb.h | 1244 ----------------- .../proto/metrics/v1/metrics.pb.inc | 69 - .../proto/resource/v1/resource.pb.h | 89 -- .../proto/resource/v1/resource.pb.inc | 12 - 10 files changed, 10 insertions(+), 2171 deletions(-) delete mode 100644 src/proto/opentelemetry/proto/common/v1/common.pb.h delete mode 100644 src/proto/opentelemetry/proto/common/v1/common.pb.inc delete mode 100644 src/proto/opentelemetry/proto/logs/v1/logs.pb.h delete mode 100644 src/proto/opentelemetry/proto/logs/v1/logs.pb.inc delete mode 100644 src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h delete mode 100644 src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc delete mode 100644 src/proto/opentelemetry/proto/resource/v1/resource.pb.h delete mode 100644 src/proto/opentelemetry/proto/resource/v1/resource.pb.inc diff --git a/library.json b/library.json index 254567c..dfb32a8 100644 --- a/library.json +++ b/library.json @@ -35,6 +35,10 @@ { "name": "nanopb/Nanopb", "version": "^0.4.91" + }, + { + "name": "aodtorusan/opentelemetry_proto", + "version": "^1.9.0" } ], "build": { diff --git a/src/OtelProtoEncoder.cpp b/src/OtelProtoEncoder.cpp index 81e0e5e..7002a6d 100644 --- a/src/OtelProtoEncoder.cpp +++ b/src/OtelProtoEncoder.cpp @@ -2,17 +2,12 @@ #if OTEL_EXPORTER_OTLP_PROTOCOL == OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF -// Pull in the nanopb-generated descriptor tables. Using .inc avoids -// PlatformIO auto-compiling these as independent translation units. -#include "proto/opentelemetry/proto/common/v1/common.pb.inc" -#include "proto/opentelemetry/proto/resource/v1/resource.pb.inc" -#include "proto/opentelemetry/proto/metrics/v1/metrics.pb.inc" -#include "proto/opentelemetry/proto/logs/v1/logs.pb.inc" - -#include "proto/opentelemetry/proto/common/v1/common.pb.h" -#include "proto/opentelemetry/proto/resource/v1/resource.pb.h" -#include "proto/opentelemetry/proto/metrics/v1/metrics.pb.h" -#include "proto/opentelemetry/proto/logs/v1/logs.pb.h" +// Nanopb-generated OTLP descriptors — provided by aodtorusan/opentelemetry_proto. +// PlatformIO compiles the library's .pb.c files automatically; no .pb.inc hack needed. +#include "opentelemetry/proto/common/v1/common.pb.h" +#include "opentelemetry/proto/resource/v1/resource.pb.h" +#include "opentelemetry/proto/metrics/v1/metrics.pb.h" +#include "opentelemetry/proto/logs/v1/logs.pb.h" #include "OtelProtoEncoder.h" #include "OtelTracer.h" // defaultServiceName(), defaultServiceInstanceId(), defaultHostName() diff --git a/src/proto/opentelemetry/proto/common/v1/common.pb.h b/src/proto/opentelemetry/proto/common/v1/common.pb.h deleted file mode 100644 index b400159..0000000 --- a/src/proto/opentelemetry/proto/common/v1/common.pb.h +++ /dev/null @@ -1,315 +0,0 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9.1 */ - -#ifndef PB_OPENTELEMETRY_PROTO_COMMON_V1_OPENTELEMETRY_PROTO_COMMON_V1_COMMON_PB_H_INCLUDED -#define PB_OPENTELEMETRY_PROTO_COMMON_V1_OPENTELEMETRY_PROTO_COMMON_V1_COMMON_PB_H_INCLUDED -#include - -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -/* Struct definitions */ -/* ArrayValue is a list of AnyValue messages. We need ArrayValue as a message - since oneof in AnyValue does not allow repeated fields. */ -typedef struct _opentelemetry_proto_common_v1_ArrayValue { - /* Array of values. The array may be empty (contain 0 elements). */ - pb_callback_t values; -} opentelemetry_proto_common_v1_ArrayValue; - -/* KeyValueList is a list of KeyValue messages. We need KeyValueList as a message - since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need - a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to - avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches - are semantically equivalent. */ -typedef struct _opentelemetry_proto_common_v1_KeyValueList { - /* A collection of key/value pairs of key-value pairs. The list may be empty (may - contain 0 elements). - - The keys MUST be unique (it is not allowed to have more than one - value with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t values; -} opentelemetry_proto_common_v1_KeyValueList; - -/* Represents any type of attribute value. AnyValue may contain a - primitive value such as a string or integer or it may contain an arbitrary nested - object containing arrays, key-value lists and primitives. */ -typedef struct _opentelemetry_proto_common_v1_AnyValue { - pb_size_t which_value; - union { - pb_callback_t string_value; - bool bool_value; - int64_t int_value; - double double_value; - opentelemetry_proto_common_v1_ArrayValue array_value; - opentelemetry_proto_common_v1_KeyValueList kvlist_value; - pb_callback_t bytes_value; - } value; -} opentelemetry_proto_common_v1_AnyValue; - -/* Represents a key-value pair that is used to store Span attributes, Link - attributes, etc. */ -typedef struct _opentelemetry_proto_common_v1_KeyValue { - /* The key name of the pair. */ - pb_callback_t key; - /* The value of the pair. */ - bool has_value; - opentelemetry_proto_common_v1_AnyValue value; -} opentelemetry_proto_common_v1_KeyValue; - -/* InstrumentationScope is a message representing the instrumentation scope information - such as the fully qualified name and version. */ -typedef struct _opentelemetry_proto_common_v1_InstrumentationScope { - /* A name denoting the Instrumentation scope. - An empty instrumentation scope name means the name is unknown. */ - pb_callback_t name; - /* Defines the version of the instrumentation scope. - An empty instrumentation scope version means the version is unknown. */ - pb_callback_t version; - /* Additional attributes that describe the scope. [Optional]. - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t attributes; - /* The number of attributes that were discarded. Attributes - can be discarded because their keys are too long or because there are too many - attributes. If this value is 0, then no attributes were dropped. */ - uint32_t dropped_attributes_count; -} opentelemetry_proto_common_v1_InstrumentationScope; - -/* A reference to an Entity. - Entity represents an object of interest associated with produced telemetry: e.g spans, metrics, profiles, or logs. - - Status: [Development] */ -typedef struct _opentelemetry_proto_common_v1_EntityRef { - /* The Schema URL, if known. This is the identifier of the Schema that the entity data - is recorded in. To learn more about Schema URL see - https://opentelemetry.io/docs/specs/otel/schemas/#schema-url - - This schema_url applies to the data in this message and to the Resource attributes - referenced by id_keys and description_keys. - TODO: discuss if we are happy with this somewhat complicated definition of what - the schema_url applies to. - - This field obsoletes the schema_url field in ResourceMetrics/ResourceSpans/ResourceLogs. */ - pb_callback_t schema_url; - /* Defines the type of the entity. MUST not change during the lifetime of the entity. - For example: "service" or "host". This field is required and MUST not be empty - for valid entities. */ - pb_callback_t type; - /* Attribute Keys that identify the entity. - MUST not change during the lifetime of the entity. The Id must contain at least one attribute. - These keys MUST exist in the containing {message}.attributes. */ - pb_callback_t id_keys; - /* Descriptive (non-identifying) attribute keys of the entity. - MAY change over the lifetime of the entity. MAY be empty. - These attribute keys are not part of entity's identity. - These keys MUST exist in the containing {message}.attributes. */ - pb_callback_t description_keys; -} opentelemetry_proto_common_v1_EntityRef; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Initializer values for message structs */ -#define opentelemetry_proto_common_v1_AnyValue_init_default {0, {{{NULL}, NULL}}} -#define opentelemetry_proto_common_v1_ArrayValue_init_default {{{NULL}, NULL}} -#define opentelemetry_proto_common_v1_KeyValueList_init_default {{{NULL}, NULL}} -#define opentelemetry_proto_common_v1_KeyValue_init_default {{{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_default} -#define opentelemetry_proto_common_v1_InstrumentationScope_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} -#define opentelemetry_proto_common_v1_EntityRef_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_common_v1_AnyValue_init_zero {0, {{{NULL}, NULL}}} -#define opentelemetry_proto_common_v1_ArrayValue_init_zero {{{NULL}, NULL}} -#define opentelemetry_proto_common_v1_KeyValueList_init_zero {{{NULL}, NULL}} -#define opentelemetry_proto_common_v1_KeyValue_init_zero {{{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_zero} -#define opentelemetry_proto_common_v1_InstrumentationScope_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} -#define opentelemetry_proto_common_v1_EntityRef_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} - -/* Field tags (for use in manual encoding/decoding) */ -#define opentelemetry_proto_common_v1_ArrayValue_values_tag 1 -#define opentelemetry_proto_common_v1_KeyValueList_values_tag 1 -#define opentelemetry_proto_common_v1_AnyValue_string_value_tag 1 -#define opentelemetry_proto_common_v1_AnyValue_bool_value_tag 2 -#define opentelemetry_proto_common_v1_AnyValue_int_value_tag 3 -#define opentelemetry_proto_common_v1_AnyValue_double_value_tag 4 -#define opentelemetry_proto_common_v1_AnyValue_array_value_tag 5 -#define opentelemetry_proto_common_v1_AnyValue_kvlist_value_tag 6 -#define opentelemetry_proto_common_v1_AnyValue_bytes_value_tag 7 -#define opentelemetry_proto_common_v1_KeyValue_key_tag 1 -#define opentelemetry_proto_common_v1_KeyValue_value_tag 2 -#define opentelemetry_proto_common_v1_InstrumentationScope_name_tag 1 -#define opentelemetry_proto_common_v1_InstrumentationScope_version_tag 2 -#define opentelemetry_proto_common_v1_InstrumentationScope_attributes_tag 3 -#define opentelemetry_proto_common_v1_InstrumentationScope_dropped_attributes_count_tag 4 -#define opentelemetry_proto_common_v1_EntityRef_schema_url_tag 1 -#define opentelemetry_proto_common_v1_EntityRef_type_tag 2 -#define opentelemetry_proto_common_v1_EntityRef_id_keys_tag 3 -#define opentelemetry_proto_common_v1_EntityRef_description_keys_tag 4 - -/* Struct field encoding specification for nanopb */ -#define opentelemetry_proto_common_v1_AnyValue_FIELDLIST(X, a) \ -X(a, CALLBACK, ONEOF, STRING, (value,string_value,value.string_value), 1) \ -X(a, STATIC, ONEOF, BOOL, (value,bool_value,value.bool_value), 2) \ -X(a, STATIC, ONEOF, INT64, (value,int_value,value.int_value), 3) \ -X(a, STATIC, ONEOF, DOUBLE, (value,double_value,value.double_value), 4) \ -X(a, STATIC, ONEOF, MESSAGE, (value,array_value,value.array_value), 5) \ -X(a, STATIC, ONEOF, MESSAGE, (value,kvlist_value,value.kvlist_value), 6) \ -X(a, CALLBACK, ONEOF, BYTES, (value,bytes_value,value.bytes_value), 7) -#define opentelemetry_proto_common_v1_AnyValue_CALLBACK pb_default_field_callback -#define opentelemetry_proto_common_v1_AnyValue_DEFAULT NULL -#define opentelemetry_proto_common_v1_AnyValue_value_array_value_MSGTYPE opentelemetry_proto_common_v1_ArrayValue -#define opentelemetry_proto_common_v1_AnyValue_value_kvlist_value_MSGTYPE opentelemetry_proto_common_v1_KeyValueList - -#define opentelemetry_proto_common_v1_ArrayValue_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, values, 1) -#define opentelemetry_proto_common_v1_ArrayValue_CALLBACK pb_default_field_callback -#define opentelemetry_proto_common_v1_ArrayValue_DEFAULT NULL -#define opentelemetry_proto_common_v1_ArrayValue_values_MSGTYPE opentelemetry_proto_common_v1_AnyValue - -#define opentelemetry_proto_common_v1_KeyValueList_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, values, 1) -#define opentelemetry_proto_common_v1_KeyValueList_CALLBACK pb_default_field_callback -#define opentelemetry_proto_common_v1_KeyValueList_DEFAULT NULL -#define opentelemetry_proto_common_v1_KeyValueList_values_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -#define opentelemetry_proto_common_v1_KeyValue_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, STRING, key, 1) \ -X(a, STATIC, OPTIONAL, MESSAGE, value, 2) -#define opentelemetry_proto_common_v1_KeyValue_CALLBACK pb_default_field_callback -#define opentelemetry_proto_common_v1_KeyValue_DEFAULT NULL -#define opentelemetry_proto_common_v1_KeyValue_value_MSGTYPE opentelemetry_proto_common_v1_AnyValue - -#define opentelemetry_proto_common_v1_InstrumentationScope_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, STRING, name, 1) \ -X(a, CALLBACK, SINGULAR, STRING, version, 2) \ -X(a, CALLBACK, REPEATED, MESSAGE, attributes, 3) \ -X(a, STATIC, SINGULAR, UINT32, dropped_attributes_count, 4) -#define opentelemetry_proto_common_v1_InstrumentationScope_CALLBACK pb_default_field_callback -#define opentelemetry_proto_common_v1_InstrumentationScope_DEFAULT NULL -#define opentelemetry_proto_common_v1_InstrumentationScope_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -#define opentelemetry_proto_common_v1_EntityRef_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, STRING, schema_url, 1) \ -X(a, CALLBACK, SINGULAR, STRING, type, 2) \ -X(a, CALLBACK, REPEATED, STRING, id_keys, 3) \ -X(a, CALLBACK, REPEATED, STRING, description_keys, 4) -#define opentelemetry_proto_common_v1_EntityRef_CALLBACK pb_default_field_callback -#define opentelemetry_proto_common_v1_EntityRef_DEFAULT NULL - -extern const pb_msgdesc_t opentelemetry_proto_common_v1_AnyValue_msg; -extern const pb_msgdesc_t opentelemetry_proto_common_v1_ArrayValue_msg; -extern const pb_msgdesc_t opentelemetry_proto_common_v1_KeyValueList_msg; -extern const pb_msgdesc_t opentelemetry_proto_common_v1_KeyValue_msg; -extern const pb_msgdesc_t opentelemetry_proto_common_v1_InstrumentationScope_msg; -extern const pb_msgdesc_t opentelemetry_proto_common_v1_EntityRef_msg; - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define opentelemetry_proto_common_v1_AnyValue_fields &opentelemetry_proto_common_v1_AnyValue_msg -#define opentelemetry_proto_common_v1_ArrayValue_fields &opentelemetry_proto_common_v1_ArrayValue_msg -#define opentelemetry_proto_common_v1_KeyValueList_fields &opentelemetry_proto_common_v1_KeyValueList_msg -#define opentelemetry_proto_common_v1_KeyValue_fields &opentelemetry_proto_common_v1_KeyValue_msg -#define opentelemetry_proto_common_v1_InstrumentationScope_fields &opentelemetry_proto_common_v1_InstrumentationScope_msg -#define opentelemetry_proto_common_v1_EntityRef_fields &opentelemetry_proto_common_v1_EntityRef_msg - -/* Maximum encoded size of messages (where known) */ -/* opentelemetry_proto_common_v1_AnyValue_size depends on runtime parameters */ -/* opentelemetry_proto_common_v1_ArrayValue_size depends on runtime parameters */ -/* opentelemetry_proto_common_v1_KeyValueList_size depends on runtime parameters */ -/* opentelemetry_proto_common_v1_KeyValue_size depends on runtime parameters */ -/* opentelemetry_proto_common_v1_InstrumentationScope_size depends on runtime parameters */ -/* opentelemetry_proto_common_v1_EntityRef_size depends on runtime parameters */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#ifdef __cplusplus -/* Message descriptors for nanopb */ -namespace nanopb { -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 7; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_common_v1_AnyValue_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_common_v1_ArrayValue_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_common_v1_KeyValueList_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_common_v1_KeyValue_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 4; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_common_v1_InstrumentationScope_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 4; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_common_v1_EntityRef_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -} // namespace nanopb - -#endif /* __cplusplus */ - - -#endif diff --git a/src/proto/opentelemetry/proto/common/v1/common.pb.inc b/src/proto/opentelemetry/proto/common/v1/common.pb.inc deleted file mode 100644 index 1835279..0000000 --- a/src/proto/opentelemetry/proto/common/v1/common.pb.inc +++ /dev/null @@ -1,35 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9.1 */ - -#include "common.pb.h" -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -PB_BIND(opentelemetry_proto_common_v1_AnyValue, opentelemetry_proto_common_v1_AnyValue, AUTO) - - -PB_BIND(opentelemetry_proto_common_v1_ArrayValue, opentelemetry_proto_common_v1_ArrayValue, AUTO) - - -PB_BIND(opentelemetry_proto_common_v1_KeyValueList, opentelemetry_proto_common_v1_KeyValueList, AUTO) - - -PB_BIND(opentelemetry_proto_common_v1_KeyValue, opentelemetry_proto_common_v1_KeyValue, AUTO) - - -PB_BIND(opentelemetry_proto_common_v1_InstrumentationScope, opentelemetry_proto_common_v1_InstrumentationScope, AUTO) - - -PB_BIND(opentelemetry_proto_common_v1_EntityRef, opentelemetry_proto_common_v1_EntityRef, AUTO) - - - -#ifndef PB_CONVERT_DOUBLE_FLOAT -/* On some platforms (such as AVR), double is really float. - * To be able to encode/decode double on these platforms, you need. - * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. - */ -PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) -#endif - diff --git a/src/proto/opentelemetry/proto/logs/v1/logs.pb.h b/src/proto/opentelemetry/proto/logs/v1/logs.pb.h deleted file mode 100644 index 231f36c..0000000 --- a/src/proto/opentelemetry/proto/logs/v1/logs.pb.h +++ /dev/null @@ -1,371 +0,0 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9.1 */ - -#ifndef PB_OPENTELEMETRY_PROTO_LOGS_V1_OPENTELEMETRY_PROTO_LOGS_V1_LOGS_PB_H_INCLUDED -#define PB_OPENTELEMETRY_PROTO_LOGS_V1_OPENTELEMETRY_PROTO_LOGS_V1_LOGS_PB_H_INCLUDED -#include -#include "../../common/v1/common.pb.h" -#include "../../resource/v1/resource.pb.h" - -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -/* Enum definitions */ -/* Possible values for LogRecord.SeverityNumber. */ -typedef enum _opentelemetry_proto_logs_v1_SeverityNumber { - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED = 0, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE = 1, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE2 = 2, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE3 = 3, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_TRACE4 = 4, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG = 5, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG2 = 6, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG3 = 7, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_DEBUG4 = 8, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO = 9, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO2 = 10, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO3 = 11, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_INFO4 = 12, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN = 13, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN2 = 14, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN3 = 15, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_WARN4 = 16, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR = 17, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR2 = 18, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR3 = 19, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_ERROR4 = 20, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL = 21, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL2 = 22, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL3 = 23, - opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL4 = 24 -} opentelemetry_proto_logs_v1_SeverityNumber; - -/* LogRecordFlags represents constants used to interpret the - LogRecord.flags field, which is protobuf 'fixed32' type and is to - be used as bit-fields. Each non-zero value defined in this enum is - a bit-mask. To extract the bit-field, for example, use an - expression like: - - (logRecord.flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK) */ -typedef enum _opentelemetry_proto_logs_v1_LogRecordFlags { - /* The zero value for the enum. Should not be used for comparisons. - Instead use bitwise "and" with the appropriate mask as shown above. */ - opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_DO_NOT_USE = 0, - /* Bits 0-7 are used for trace flags. */ - opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_TRACE_FLAGS_MASK = 255 -} opentelemetry_proto_logs_v1_LogRecordFlags; - -/* Struct definitions */ -/* LogsData represents the logs data that can be stored in a persistent storage, - OR can be embedded by other protocols that transfer OTLP logs data but do not - implement the OTLP protocol. - - The main difference between this message and collector protocol is that - in this message there will not be any "control" or "metadata" specific to - OTLP protocol. - - When new fields are added into this message, the OTLP request MUST be updated - as well. */ -typedef struct _opentelemetry_proto_logs_v1_LogsData { - /* An array of ResourceLogs. - For data coming from a single resource this array will typically contain - one element. Intermediary nodes that receive data from multiple origins - typically batch the data before forwarding further and in that case this - array will contain multiple elements. */ - pb_callback_t resource_logs; -} opentelemetry_proto_logs_v1_LogsData; - -/* A collection of ScopeLogs from a Resource. */ -typedef struct _opentelemetry_proto_logs_v1_ResourceLogs { - /* The resource for the logs in this message. - If this field is not set then resource info is unknown. */ - bool has_resource; - opentelemetry_proto_resource_v1_Resource resource; - /* A list of ScopeLogs that originate from a resource. */ - pb_callback_t scope_logs; - /* The Schema URL, if known. This is the identifier of the Schema that the resource data - is recorded in. Notably, the last part of the URL path is the version number of the - schema: http[s]://server[:port]/path/. To learn more about Schema URL see - https://opentelemetry.io/docs/specs/otel/schemas/#schema-url - This schema_url applies to the data in the "resource" field. It does not apply - to the data in the "scope_logs" field which have their own schema_url field. */ - pb_callback_t schema_url; -} opentelemetry_proto_logs_v1_ResourceLogs; - -/* A collection of Logs produced by a Scope. */ -typedef struct _opentelemetry_proto_logs_v1_ScopeLogs { - /* The instrumentation scope information for the logs in this message. - Semantically when InstrumentationScope isn't set, it is equivalent with - an empty instrumentation scope name (unknown). */ - bool has_scope; - opentelemetry_proto_common_v1_InstrumentationScope scope; - /* A list of log records. */ - pb_callback_t log_records; - /* The Schema URL, if known. This is the identifier of the Schema that the log data - is recorded in. Notably, the last part of the URL path is the version number of the - schema: http[s]://server[:port]/path/. To learn more about Schema URL see - https://opentelemetry.io/docs/specs/otel/schemas/#schema-url - This schema_url applies to the data in the "scope" field and all logs in the - "log_records" field. */ - pb_callback_t schema_url; -} opentelemetry_proto_logs_v1_ScopeLogs; - -/* A log record according to OpenTelemetry Log Data Model: - https://github.com/open-telemetry/oteps/blob/main/text/logs/0097-log-data-model.md */ -typedef struct _opentelemetry_proto_logs_v1_LogRecord { - /* time_unix_nano is the time when the event occurred. - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - Value of 0 indicates unknown or missing timestamp. */ - uint64_t time_unix_nano; - /* Numerical value of the severity, normalized to values described in Log Data Model. - [Optional]. */ - opentelemetry_proto_logs_v1_SeverityNumber severity_number; - /* The severity text (also known as log level). The original string representation as - it is known at the source. [Optional]. */ - pb_callback_t severity_text; - /* A value containing the body of the log record. Can be for example a human-readable - string message (including multi-line) describing the event in a free form or it can - be a structured data composed of arrays and maps of other values. [Optional]. */ - bool has_body; - opentelemetry_proto_common_v1_AnyValue body; - /* Additional attributes that describe the specific event occurrence. [Optional]. - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t attributes; - uint32_t dropped_attributes_count; - /* Flags, a bit field. 8 least significant bits are the trace flags as - defined in W3C Trace Context specification. 24 most significant bits are reserved - and must be set to 0. Readers must not assume that 24 most significant bits - will be zero and must correctly mask the bits when reading 8-bit trace flag (use - flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional]. */ - uint32_t flags; - /* A unique identifier for a trace. All logs from the same trace share - the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR - of length other than 16 bytes is considered invalid (empty string in OTLP/JSON - is zero-length and thus is also invalid). - - This field is optional. - - The receivers SHOULD assume that the log record is not associated with a - trace if any of the following is true: - - the field is not present, - - the field contains an invalid value. */ - pb_callback_t trace_id; - /* A unique identifier for a span within a trace, assigned when the span - is created. The ID is an 8-byte array. An ID with all zeroes OR of length - other than 8 bytes is considered invalid (empty string in OTLP/JSON - is zero-length and thus is also invalid). - - This field is optional. If the sender specifies a valid span_id then it SHOULD also - specify a valid trace_id. - - The receivers SHOULD assume that the log record is not associated with a - span if any of the following is true: - - the field is not present, - - the field contains an invalid value. */ - pb_callback_t span_id; - /* Time when the event was observed by the collection system. - For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK) - this timestamp is typically set at the generation time and is equal to Timestamp. - For events originating externally and collected by OpenTelemetry (e.g. using - Collector) this is the time when OpenTelemetry's code observed the event measured - by the clock of the OpenTelemetry code. This field MUST be set once the event is - observed by OpenTelemetry. - - For converting OpenTelemetry log data to formats that support only one timestamp or - when receiving OpenTelemetry log data by recipients that support only one timestamp - internally the following logic is recommended: - - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - Value of 0 indicates unknown or missing timestamp. */ - uint64_t observed_time_unix_nano; - /* A unique identifier of event category/type. - All events with the same event_name are expected to conform to the same - schema for both their attributes and their body. - - Recommended to be fully qualified and short (no longer than 256 characters). - - Presence of event_name on the log record identifies this record - as an event. - - [Optional]. */ - pb_callback_t event_name; -} opentelemetry_proto_logs_v1_LogRecord; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Helper constants for enums */ -#define _opentelemetry_proto_logs_v1_SeverityNumber_MIN opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED -#define _opentelemetry_proto_logs_v1_SeverityNumber_MAX opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL4 -#define _opentelemetry_proto_logs_v1_SeverityNumber_ARRAYSIZE ((opentelemetry_proto_logs_v1_SeverityNumber)(opentelemetry_proto_logs_v1_SeverityNumber_SEVERITY_NUMBER_FATAL4+1)) - -#define _opentelemetry_proto_logs_v1_LogRecordFlags_MIN opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_DO_NOT_USE -#define _opentelemetry_proto_logs_v1_LogRecordFlags_MAX opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_TRACE_FLAGS_MASK -#define _opentelemetry_proto_logs_v1_LogRecordFlags_ARRAYSIZE ((opentelemetry_proto_logs_v1_LogRecordFlags)(opentelemetry_proto_logs_v1_LogRecordFlags_LOG_RECORD_FLAGS_TRACE_FLAGS_MASK+1)) - - - - -#define opentelemetry_proto_logs_v1_LogRecord_severity_number_ENUMTYPE opentelemetry_proto_logs_v1_SeverityNumber - - -/* Initializer values for message structs */ -#define opentelemetry_proto_logs_v1_LogsData_init_default {{{NULL}, NULL}} -#define opentelemetry_proto_logs_v1_ResourceLogs_init_default {false, opentelemetry_proto_resource_v1_Resource_init_default, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_logs_v1_ScopeLogs_init_default {false, opentelemetry_proto_common_v1_InstrumentationScope_init_default, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_logs_v1_LogRecord_init_default {0, _opentelemetry_proto_logs_v1_SeverityNumber_MIN, {{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_default, {{NULL}, NULL}, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}} -#define opentelemetry_proto_logs_v1_LogsData_init_zero {{{NULL}, NULL}} -#define opentelemetry_proto_logs_v1_ResourceLogs_init_zero {false, opentelemetry_proto_resource_v1_Resource_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_logs_v1_ScopeLogs_init_zero {false, opentelemetry_proto_common_v1_InstrumentationScope_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_logs_v1_LogRecord_init_zero {0, _opentelemetry_proto_logs_v1_SeverityNumber_MIN, {{NULL}, NULL}, false, opentelemetry_proto_common_v1_AnyValue_init_zero, {{NULL}, NULL}, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}} - -/* Field tags (for use in manual encoding/decoding) */ -#define opentelemetry_proto_logs_v1_LogsData_resource_logs_tag 1 -#define opentelemetry_proto_logs_v1_ResourceLogs_resource_tag 1 -#define opentelemetry_proto_logs_v1_ResourceLogs_scope_logs_tag 2 -#define opentelemetry_proto_logs_v1_ResourceLogs_schema_url_tag 3 -#define opentelemetry_proto_logs_v1_ScopeLogs_scope_tag 1 -#define opentelemetry_proto_logs_v1_ScopeLogs_log_records_tag 2 -#define opentelemetry_proto_logs_v1_ScopeLogs_schema_url_tag 3 -#define opentelemetry_proto_logs_v1_LogRecord_time_unix_nano_tag 1 -#define opentelemetry_proto_logs_v1_LogRecord_severity_number_tag 2 -#define opentelemetry_proto_logs_v1_LogRecord_severity_text_tag 3 -#define opentelemetry_proto_logs_v1_LogRecord_body_tag 5 -#define opentelemetry_proto_logs_v1_LogRecord_attributes_tag 6 -#define opentelemetry_proto_logs_v1_LogRecord_dropped_attributes_count_tag 7 -#define opentelemetry_proto_logs_v1_LogRecord_flags_tag 8 -#define opentelemetry_proto_logs_v1_LogRecord_trace_id_tag 9 -#define opentelemetry_proto_logs_v1_LogRecord_span_id_tag 10 -#define opentelemetry_proto_logs_v1_LogRecord_observed_time_unix_nano_tag 11 -#define opentelemetry_proto_logs_v1_LogRecord_event_name_tag 12 - -/* Struct field encoding specification for nanopb */ -#define opentelemetry_proto_logs_v1_LogsData_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, resource_logs, 1) -#define opentelemetry_proto_logs_v1_LogsData_CALLBACK pb_default_field_callback -#define opentelemetry_proto_logs_v1_LogsData_DEFAULT NULL -#define opentelemetry_proto_logs_v1_LogsData_resource_logs_MSGTYPE opentelemetry_proto_logs_v1_ResourceLogs - -#define opentelemetry_proto_logs_v1_ResourceLogs_FIELDLIST(X, a) \ -X(a, STATIC, OPTIONAL, MESSAGE, resource, 1) \ -X(a, CALLBACK, REPEATED, MESSAGE, scope_logs, 2) \ -X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) -#define opentelemetry_proto_logs_v1_ResourceLogs_CALLBACK pb_default_field_callback -#define opentelemetry_proto_logs_v1_ResourceLogs_DEFAULT NULL -#define opentelemetry_proto_logs_v1_ResourceLogs_resource_MSGTYPE opentelemetry_proto_resource_v1_Resource -#define opentelemetry_proto_logs_v1_ResourceLogs_scope_logs_MSGTYPE opentelemetry_proto_logs_v1_ScopeLogs - -#define opentelemetry_proto_logs_v1_ScopeLogs_FIELDLIST(X, a) \ -X(a, STATIC, OPTIONAL, MESSAGE, scope, 1) \ -X(a, CALLBACK, REPEATED, MESSAGE, log_records, 2) \ -X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) -#define opentelemetry_proto_logs_v1_ScopeLogs_CALLBACK pb_default_field_callback -#define opentelemetry_proto_logs_v1_ScopeLogs_DEFAULT NULL -#define opentelemetry_proto_logs_v1_ScopeLogs_scope_MSGTYPE opentelemetry_proto_common_v1_InstrumentationScope -#define opentelemetry_proto_logs_v1_ScopeLogs_log_records_MSGTYPE opentelemetry_proto_logs_v1_LogRecord - -#define opentelemetry_proto_logs_v1_LogRecord_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 1) \ -X(a, STATIC, SINGULAR, UENUM, severity_number, 2) \ -X(a, CALLBACK, SINGULAR, STRING, severity_text, 3) \ -X(a, STATIC, OPTIONAL, MESSAGE, body, 5) \ -X(a, CALLBACK, REPEATED, MESSAGE, attributes, 6) \ -X(a, STATIC, SINGULAR, UINT32, dropped_attributes_count, 7) \ -X(a, STATIC, SINGULAR, FIXED32, flags, 8) \ -X(a, CALLBACK, SINGULAR, BYTES, trace_id, 9) \ -X(a, CALLBACK, SINGULAR, BYTES, span_id, 10) \ -X(a, STATIC, SINGULAR, FIXED64, observed_time_unix_nano, 11) \ -X(a, CALLBACK, SINGULAR, STRING, event_name, 12) -#define opentelemetry_proto_logs_v1_LogRecord_CALLBACK pb_default_field_callback -#define opentelemetry_proto_logs_v1_LogRecord_DEFAULT NULL -#define opentelemetry_proto_logs_v1_LogRecord_body_MSGTYPE opentelemetry_proto_common_v1_AnyValue -#define opentelemetry_proto_logs_v1_LogRecord_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -extern const pb_msgdesc_t opentelemetry_proto_logs_v1_LogsData_msg; -extern const pb_msgdesc_t opentelemetry_proto_logs_v1_ResourceLogs_msg; -extern const pb_msgdesc_t opentelemetry_proto_logs_v1_ScopeLogs_msg; -extern const pb_msgdesc_t opentelemetry_proto_logs_v1_LogRecord_msg; - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define opentelemetry_proto_logs_v1_LogsData_fields &opentelemetry_proto_logs_v1_LogsData_msg -#define opentelemetry_proto_logs_v1_ResourceLogs_fields &opentelemetry_proto_logs_v1_ResourceLogs_msg -#define opentelemetry_proto_logs_v1_ScopeLogs_fields &opentelemetry_proto_logs_v1_ScopeLogs_msg -#define opentelemetry_proto_logs_v1_LogRecord_fields &opentelemetry_proto_logs_v1_LogRecord_msg - -/* Maximum encoded size of messages (where known) */ -/* opentelemetry_proto_logs_v1_LogsData_size depends on runtime parameters */ -/* opentelemetry_proto_logs_v1_ResourceLogs_size depends on runtime parameters */ -/* opentelemetry_proto_logs_v1_ScopeLogs_size depends on runtime parameters */ -/* opentelemetry_proto_logs_v1_LogRecord_size depends on runtime parameters */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#ifdef __cplusplus -/* Message descriptors for nanopb */ -namespace nanopb { -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_logs_v1_LogsData_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_logs_v1_ResourceLogs_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_logs_v1_ScopeLogs_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 11; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_logs_v1_LogRecord_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -} // namespace nanopb - -#endif /* __cplusplus */ - - -#endif diff --git a/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc b/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc deleted file mode 100644 index 41875ce..0000000 --- a/src/proto/opentelemetry/proto/logs/v1/logs.pb.inc +++ /dev/null @@ -1,25 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9.1 */ - -#include "logs.pb.h" -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -PB_BIND(opentelemetry_proto_logs_v1_LogsData, opentelemetry_proto_logs_v1_LogsData, AUTO) - - -PB_BIND(opentelemetry_proto_logs_v1_ResourceLogs, opentelemetry_proto_logs_v1_ResourceLogs, AUTO) - - -PB_BIND(opentelemetry_proto_logs_v1_ScopeLogs, opentelemetry_proto_logs_v1_ScopeLogs, AUTO) - - -PB_BIND(opentelemetry_proto_logs_v1_LogRecord, opentelemetry_proto_logs_v1_LogRecord, AUTO) - - - - - - - diff --git a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h deleted file mode 100644 index 08c0f36..0000000 --- a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.h +++ /dev/null @@ -1,1244 +0,0 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9.1 */ - -#ifndef PB_OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_INCLUDED -#define PB_OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_INCLUDED -#include -#include "../../common/v1/common.pb.h" -#include "../../resource/v1/resource.pb.h" - -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -/* Enum definitions */ -/* AggregationTemporality defines how a metric aggregator reports aggregated - values. It describes how those values relate to the time interval over - which they are aggregated. */ -typedef enum _opentelemetry_proto_metrics_v1_AggregationTemporality { - /* UNSPECIFIED is the default AggregationTemporality, it MUST not be used. */ - opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED = 0, - /* DELTA is an AggregationTemporality for a metric aggregator which reports - changes since last report time. Successive metrics contain aggregation of - values from continuous and non-overlapping intervals. - - The values for a DELTA metric are based only on the time interval - associated with one measurement cycle. There is no dependency on - previous measurements like is the case for CUMULATIVE metrics. - - For example, consider a system measuring the number of requests that - it receives and reports the sum of these requests every second as a - DELTA metric: - - 1. The system starts receiving at time=t_0. - 2. A request is received, the system measures 1 request. - 3. A request is received, the system measures 1 request. - 4. A request is received, the system measures 1 request. - 5. The 1 second collection cycle ends. A metric is exported for the - number of requests received over the interval of time t_0 to - t_0+1 with a value of 3. - 6. A request is received, the system measures 1 request. - 7. A request is received, the system measures 1 request. - 8. The 1 second collection cycle ends. A metric is exported for the - number of requests received over the interval of time t_0+1 to - t_0+2 with a value of 2. */ - opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA = 1, - /* CUMULATIVE is an AggregationTemporality for a metric aggregator which - reports changes since a fixed start time. This means that current values - of a CUMULATIVE metric depend on all previous measurements since the - start time. Because of this, the sender is required to retain this state - in some form. If this state is lost or invalidated, the CUMULATIVE metric - values MUST be reset and a new fixed start time following the last - reported measurement time sent MUST be used. - - For example, consider a system measuring the number of requests that - it receives and reports the sum of these requests every second as a - CUMULATIVE metric: - - 1. The system starts receiving at time=t_0. - 2. A request is received, the system measures 1 request. - 3. A request is received, the system measures 1 request. - 4. A request is received, the system measures 1 request. - 5. The 1 second collection cycle ends. A metric is exported for the - number of requests received over the interval of time t_0 to - t_0+1 with a value of 3. - 6. A request is received, the system measures 1 request. - 7. A request is received, the system measures 1 request. - 8. The 1 second collection cycle ends. A metric is exported for the - number of requests received over the interval of time t_0 to - t_0+2 with a value of 5. - 9. The system experiences a fault and loses state. - 10. The system recovers and resumes receiving at time=t_1. - 11. A request is received, the system measures 1 request. - 12. The 1 second collection cycle ends. A metric is exported for the - number of requests received over the interval of time t_1 to - t_0+1 with a value of 1. - - Note: Even though, when reporting changes since last report time, using - CUMULATIVE is valid, it is not recommended. This may cause problems for - systems that do not use start_time to determine when the aggregation - value was reset (e.g. Prometheus). */ - opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE = 2 -} opentelemetry_proto_metrics_v1_AggregationTemporality; - -/* DataPointFlags is defined as a protobuf 'uint32' type and is to be used as a - bit-field representing 32 distinct boolean flags. Each flag defined in this - enum is a bit-mask. To test the presence of a single flag in the flags of - a data point, for example, use an expression like: - - (point.flags & DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK) == DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK */ -typedef enum _opentelemetry_proto_metrics_v1_DataPointFlags { - /* The zero value for the enum. Should not be used for comparisons. - Instead use bitwise "and" with the appropriate mask as shown above. */ - opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_DO_NOT_USE = 0, - /* This DataPoint is valid but has no recorded value. This value - SHOULD be used to reflect explicitly missing data in a series, as - for an equivalent to the Prometheus "staleness marker". */ - opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK = 1 -} opentelemetry_proto_metrics_v1_DataPointFlags; - -/* Struct definitions */ -/* MetricsData represents the metrics data that can be stored in a persistent - storage, OR can be embedded by other protocols that transfer OTLP metrics - data but do not implement the OTLP protocol. - - MetricsData - └─── ResourceMetrics - ├── Resource - ├── SchemaURL - └── ScopeMetrics - ├── Scope - ├── SchemaURL - └── Metric - ├── Name - ├── Description - ├── Unit - └── data - ├── Gauge - ├── Sum - ├── Histogram - ├── ExponentialHistogram - └── Summary - - The main difference between this message and collector protocol is that - in this message there will not be any "control" or "metadata" specific to - OTLP protocol. - - When new fields are added into this message, the OTLP request MUST be updated - as well. */ -typedef struct _opentelemetry_proto_metrics_v1_MetricsData { - /* An array of ResourceMetrics. - For data coming from a single resource this array will typically contain - one element. Intermediary nodes that receive data from multiple origins - typically batch the data before forwarding further and in that case this - array will contain multiple elements. */ - pb_callback_t resource_metrics; -} opentelemetry_proto_metrics_v1_MetricsData; - -/* A collection of ScopeMetrics from a Resource. */ -typedef struct _opentelemetry_proto_metrics_v1_ResourceMetrics { - /* The resource for the metrics in this message. - If this field is not set then no resource info is known. */ - bool has_resource; - opentelemetry_proto_resource_v1_Resource resource; - /* A list of metrics that originate from a resource. */ - pb_callback_t scope_metrics; - /* The Schema URL, if known. This is the identifier of the Schema that the resource data - is recorded in. Notably, the last part of the URL path is the version number of the - schema: http[s]://server[:port]/path/. To learn more about Schema URL see - https://opentelemetry.io/docs/specs/otel/schemas/#schema-url - This schema_url applies to the data in the "resource" field. It does not apply - to the data in the "scope_metrics" field which have their own schema_url field. */ - pb_callback_t schema_url; -} opentelemetry_proto_metrics_v1_ResourceMetrics; - -/* A collection of Metrics produced by an Scope. */ -typedef struct _opentelemetry_proto_metrics_v1_ScopeMetrics { - /* The instrumentation scope information for the metrics in this message. - Semantically when InstrumentationScope isn't set, it is equivalent with - an empty instrumentation scope name (unknown). */ - bool has_scope; - opentelemetry_proto_common_v1_InstrumentationScope scope; - /* A list of metrics that originate from an instrumentation library. */ - pb_callback_t metrics; - /* The Schema URL, if known. This is the identifier of the Schema that the metric data - is recorded in. Notably, the last part of the URL path is the version number of the - schema: http[s]://server[:port]/path/. To learn more about Schema URL see - https://opentelemetry.io/docs/specs/otel/schemas/#schema-url - This schema_url applies to the data in the "scope" field and all metrics in the - "metrics" field. */ - pb_callback_t schema_url; -} opentelemetry_proto_metrics_v1_ScopeMetrics; - -/* Gauge represents the type of a scalar metric that always exports the - "current value" for every data point. It should be used for an "unknown" - aggregation. - - A Gauge does not support different aggregation temporalities. Given the - aggregation is unknown, points cannot be combined using the same - aggregation, regardless of aggregation temporalities. Therefore, - AggregationTemporality is not included. Consequently, this also means - "StartTimeUnixNano" is ignored for all data points. */ -typedef struct _opentelemetry_proto_metrics_v1_Gauge { - /* The time series data points. - Note: Multiple time series may be included (same timestamp, different attributes). */ - pb_callback_t data_points; -} opentelemetry_proto_metrics_v1_Gauge; - -/* Sum represents the type of a scalar metric that is calculated as a sum of all - reported measurements over a time interval. */ -typedef struct _opentelemetry_proto_metrics_v1_Sum { - /* The time series data points. - Note: Multiple time series may be included (same timestamp, different attributes). */ - pb_callback_t data_points; - /* aggregation_temporality describes if the aggregator reports delta changes - since last report time, or cumulative changes since a fixed start time. */ - opentelemetry_proto_metrics_v1_AggregationTemporality aggregation_temporality; - /* Represents whether the sum is monotonic. */ - bool is_monotonic; -} opentelemetry_proto_metrics_v1_Sum; - -/* Histogram represents the type of a metric that is calculated by aggregating - as a Histogram of all reported measurements over a time interval. */ -typedef struct _opentelemetry_proto_metrics_v1_Histogram { - /* The time series data points. - Note: Multiple time series may be included (same timestamp, different attributes). */ - pb_callback_t data_points; - /* aggregation_temporality describes if the aggregator reports delta changes - since last report time, or cumulative changes since a fixed start time. */ - opentelemetry_proto_metrics_v1_AggregationTemporality aggregation_temporality; -} opentelemetry_proto_metrics_v1_Histogram; - -/* ExponentialHistogram represents the type of a metric that is calculated by aggregating - as a ExponentialHistogram of all reported double measurements over a time interval. */ -typedef struct _opentelemetry_proto_metrics_v1_ExponentialHistogram { - /* The time series data points. - Note: Multiple time series may be included (same timestamp, different attributes). */ - pb_callback_t data_points; - /* aggregation_temporality describes if the aggregator reports delta changes - since last report time, or cumulative changes since a fixed start time. */ - opentelemetry_proto_metrics_v1_AggregationTemporality aggregation_temporality; -} opentelemetry_proto_metrics_v1_ExponentialHistogram; - -/* Summary metric data are used to convey quantile summaries, - a Prometheus (see: https://prometheus.io/docs/concepts/metric_types/#summary) - and OpenMetrics (see: https://github.com/prometheus/OpenMetrics/blob/4dbf6075567ab43296eed941037c12951faafb92/protos/prometheus.proto#L45) - data type. These data points cannot always be merged in a meaningful way. - While they can be useful in some applications, histogram data points are - recommended for new applications. - Summary metrics do not have an aggregation temporality field. This is - because the count and sum fields of a SummaryDataPoint are assumed to be - cumulative values. */ -typedef struct _opentelemetry_proto_metrics_v1_Summary { - /* The time series data points. - Note: Multiple time series may be included (same timestamp, different attributes). */ - pb_callback_t data_points; -} opentelemetry_proto_metrics_v1_Summary; - -/* Defines a Metric which has one or more timeseries. The following is a - brief summary of the Metric data model. For more details, see: - - https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md - - The data model and relation between entities is shown in the - diagram below. Here, "DataPoint" is the term used to refer to any - one of the specific data point value types, and "points" is the term used - to refer to any one of the lists of points contained in the Metric. - - - Metric is composed of a metadata and data. - - Metadata part contains a name, description, unit. - - Data is one of the possible types (Sum, Gauge, Histogram, Summary). - - DataPoint contains timestamps, attributes, and one of the possible value type - fields. - - Metric - +------------+ - |name | - |description | - |unit | +------------------------------------+ - |data |---> |Gauge, Sum, Histogram, Summary, ... | - +------------+ +------------------------------------+ - - Data [One of Gauge, Sum, Histogram, Summary, ...] - +-----------+ - |... | // Metadata about the Data. - |points |--+ - +-----------+ | - | +---------------------------+ - | |DataPoint 1 | - v |+------+------+ +------+ | - +-----+ ||label |label |...|label | | - | 1 |-->||value1|value2|...|valueN| | - +-----+ |+------+------+ +------+ | - | . | |+-----+ | - | . | ||value| | - | . | |+-----+ | - | . | +---------------------------+ - | . | . - | . | . - | . | . - | . | +---------------------------+ - | . | |DataPoint M | - +-----+ |+------+------+ +------+ | - | M |-->||label |label |...|label | | - +-----+ ||value1|value2|...|valueN| | - |+------+------+ +------+ | - |+-----+ | - ||value| | - |+-----+ | - +---------------------------+ - - Each distinct type of DataPoint represents the output of a specific - aggregation function, the result of applying the DataPoint's - associated function of to one or more measurements. - - All DataPoint types have three common fields: - - Attributes includes key-value pairs associated with the data point - - TimeUnixNano is required, set to the end time of the aggregation - - StartTimeUnixNano is optional, but strongly encouraged for DataPoints - having an AggregationTemporality field, as discussed below. - - Both TimeUnixNano and StartTimeUnixNano values are expressed as - UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - - # TimeUnixNano - - This field is required, having consistent interpretation across - DataPoint types. TimeUnixNano is the moment corresponding to when - the data point's aggregate value was captured. - - Data points with the 0 value for TimeUnixNano SHOULD be rejected - by consumers. - - # StartTimeUnixNano - - StartTimeUnixNano in general allows detecting when a sequence of - observations is unbroken. This field indicates to consumers the - start time for points with cumulative and delta - AggregationTemporality, and it should be included whenever possible - to support correct rate calculation. Although it may be omitted - when the start time is truly unknown, setting StartTimeUnixNano is - strongly encouraged. */ -typedef struct _opentelemetry_proto_metrics_v1_Metric { - /* The name of the metric. */ - pb_callback_t name; - /* A description of the metric, which can be used in documentation. */ - pb_callback_t description; - /* The unit in which the metric value is reported. Follows the format - described by https://unitsofmeasure.org/ucum.html. */ - pb_callback_t unit; - pb_size_t which_data; - union { - opentelemetry_proto_metrics_v1_Gauge gauge; - opentelemetry_proto_metrics_v1_Sum sum; - opentelemetry_proto_metrics_v1_Histogram histogram; - opentelemetry_proto_metrics_v1_ExponentialHistogram exponential_histogram; - opentelemetry_proto_metrics_v1_Summary summary; - } data; - /* Additional metadata attributes that describe the metric. [Optional]. - Attributes are non-identifying. - Consumers SHOULD NOT need to be aware of these attributes. - These attributes MAY be used to encode information allowing - for lossless roundtrip translation to / from another data model. - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t metadata; -} opentelemetry_proto_metrics_v1_Metric; - -/* NumberDataPoint is a single data point in a timeseries that describes the - time-varying scalar value of a metric. */ -typedef struct _opentelemetry_proto_metrics_v1_NumberDataPoint { - /* StartTimeUnixNano is optional but strongly encouraged, see the - the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t start_time_unix_nano; - /* TimeUnixNano is required, see the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t time_unix_nano; - pb_size_t which_value; - union { - double as_double; - int64_t as_int; - } value; - /* (Optional) List of exemplars collected from - measurements that were used to form the data point */ - pb_callback_t exemplars; - /* The set of key/value pairs that uniquely identify the timeseries from - where this point belongs. The list may be empty (may contain 0 elements). - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t attributes; - /* Flags that apply to this specific data point. See DataPointFlags - for the available flags and their meaning. */ - uint32_t flags; -} opentelemetry_proto_metrics_v1_NumberDataPoint; - -/* HistogramDataPoint is a single data point in a timeseries that describes the - time-varying values of a Histogram. A Histogram contains summary statistics - for a population of values, it may optionally contain the distribution of - those values across a set of buckets. - - If the histogram contains the distribution of values, then both - "explicit_bounds" and "bucket counts" fields must be defined. - If the histogram does not contain the distribution of values, then both - "explicit_bounds" and "bucket_counts" must be omitted and only "count" and - "sum" are known. */ -typedef struct _opentelemetry_proto_metrics_v1_HistogramDataPoint { - /* StartTimeUnixNano is optional but strongly encouraged, see the - the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t start_time_unix_nano; - /* TimeUnixNano is required, see the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t time_unix_nano; - /* count is the number of values in the population. Must be non-negative. This - value must be equal to the sum of the "count" fields in buckets if a - histogram is provided. */ - uint64_t count; - /* sum of the values in the population. If count is zero then this field - must be zero. - - Note: Sum should only be filled out when measuring non-negative discrete - events, and is assumed to be monotonic over the values of these events. - Negative events *can* be recorded, but sum should not be filled out when - doing so. This is specifically to enforce compatibility w/ OpenMetrics, - see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram */ - bool has_sum; - double sum; - /* bucket_counts is an optional field contains the count values of histogram - for each bucket. - - The sum of the bucket_counts must equal the value in the count field. - - The number of elements in bucket_counts array must be by one greater than - the number of elements in explicit_bounds array. The exception to this rule - is when the length of bucket_counts is 0, then the length of explicit_bounds - must also be 0. */ - pb_callback_t bucket_counts; - /* explicit_bounds specifies buckets with explicitly defined bounds for values. - - The boundaries for bucket at index i are: - - (-infinity, explicit_bounds[i]] for i == 0 - (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds) - (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds) - - The values in the explicit_bounds array must be strictly increasing. - - Histogram buckets are inclusive of their upper boundary, except the last - bucket where the boundary is at infinity. This format is intentionally - compatible with the OpenMetrics histogram definition. - - If bucket_counts length is 0 then explicit_bounds length must also be 0, - otherwise the data point is invalid. */ - pb_callback_t explicit_bounds; - /* (Optional) List of exemplars collected from - measurements that were used to form the data point */ - pb_callback_t exemplars; - /* The set of key/value pairs that uniquely identify the timeseries from - where this point belongs. The list may be empty (may contain 0 elements). - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t attributes; - /* Flags that apply to this specific data point. See DataPointFlags - for the available flags and their meaning. */ - uint32_t flags; - /* min is the minimum value over (start_time, end_time]. */ - bool has_min; - double min; - /* max is the maximum value over (start_time, end_time]. */ - bool has_max; - double max; -} opentelemetry_proto_metrics_v1_HistogramDataPoint; - -/* Buckets are a set of bucket counts, encoded in a contiguous array - of counts. */ -typedef struct _opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets { - /* The bucket index of the first entry in the bucket_counts array. - - Note: This uses a varint encoding as a simple form of compression. */ - int32_t offset; - /* An array of count values, where bucket_counts[i] carries - the count of the bucket at index (offset+i). bucket_counts[i] is the count - of values greater than base^(offset+i) and less than or equal to - base^(offset+i+1). - - Note: By contrast, the explicit HistogramDataPoint uses - fixed64. This field is expected to have many buckets, - especially zeros, so uint64 has been selected to ensure - varint encoding. */ - pb_callback_t bucket_counts; -} opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets; - -/* ExponentialHistogramDataPoint is a single data point in a timeseries that describes the - time-varying values of a ExponentialHistogram of double values. A ExponentialHistogram contains - summary statistics for a population of values, it may optionally contain the - distribution of those values across a set of buckets. */ -typedef struct _opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint { - /* The set of key/value pairs that uniquely identify the timeseries from - where this point belongs. The list may be empty (may contain 0 elements). - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t attributes; - /* StartTimeUnixNano is optional but strongly encouraged, see the - the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t start_time_unix_nano; - /* TimeUnixNano is required, see the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t time_unix_nano; - /* The number of values in the population. Must be - non-negative. This value must be equal to the sum of the "bucket_counts" - values in the positive and negative Buckets plus the "zero_count" field. */ - uint64_t count; - /* The sum of the values in the population. If count is zero then this field - must be zero. - - Note: Sum should only be filled out when measuring non-negative discrete - events, and is assumed to be monotonic over the values of these events. - Negative events *can* be recorded, but sum should not be filled out when - doing so. This is specifically to enforce compatibility w/ OpenMetrics, - see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram */ - bool has_sum; - double sum; - /* scale describes the resolution of the histogram. Boundaries are - located at powers of the base, where: - - base = (2^(2^-scale)) - - The histogram bucket identified by `index`, a signed integer, - contains values that are greater than (base^index) and - less than or equal to (base^(index+1)). - - The positive and negative ranges of the histogram are expressed - separately. Negative values are mapped by their absolute value - into the negative range using the same scale as the positive range. - - scale is not restricted by the protocol, as the permissible - values depend on the range of the data. */ - int32_t scale; - /* The count of values that are either exactly zero or - within the region considered zero by the instrumentation at the - tolerated degree of precision. This bucket stores values that - cannot be expressed using the standard exponential formula as - well as values that have been rounded to zero. - - Implementations MAY consider the zero bucket to have probability - mass equal to (zero_count / count). */ - uint64_t zero_count; - /* positive carries the positive range of exponential bucket counts. */ - bool has_positive; - opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets positive; - /* negative carries the negative range of exponential bucket counts. */ - bool has_negative; - opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets negative; - /* Flags that apply to this specific data point. See DataPointFlags - for the available flags and their meaning. */ - uint32_t flags; - /* (Optional) List of exemplars collected from - measurements that were used to form the data point */ - pb_callback_t exemplars; - /* The minimum value over (start_time, end_time]. */ - bool has_min; - double min; - /* The maximum value over (start_time, end_time]. */ - bool has_max; - double max; - /* ZeroThreshold may be optionally set to convey the width of the zero - region. Where the zero region is defined as the closed interval - [-ZeroThreshold, ZeroThreshold]. - When ZeroThreshold is 0, zero count bucket stores values that cannot be - expressed using the standard exponential formula as well as values that - have been rounded to zero. */ - double zero_threshold; -} opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint; - -/* SummaryDataPoint is a single data point in a timeseries that describes the - time-varying values of a Summary metric. The count and sum fields represent - cumulative values. */ -typedef struct _opentelemetry_proto_metrics_v1_SummaryDataPoint { - /* StartTimeUnixNano is optional but strongly encouraged, see the - the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t start_time_unix_nano; - /* TimeUnixNano is required, see the detailed comments above Metric. - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t time_unix_nano; - /* count is the number of values in the population. Must be non-negative. */ - uint64_t count; - /* sum of the values in the population. If count is zero then this field - must be zero. - - Note: Sum should only be filled out when measuring non-negative discrete - events, and is assumed to be monotonic over the values of these events. - Negative events *can* be recorded, but sum should not be filled out when - doing so. This is specifically to enforce compatibility w/ OpenMetrics, - see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary */ - double sum; - /* (Optional) list of values at different quantiles of the distribution calculated - from the current snapshot. The quantiles must be strictly increasing. */ - pb_callback_t quantile_values; - /* The set of key/value pairs that uniquely identify the timeseries from - where this point belongs. The list may be empty (may contain 0 elements). - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t attributes; - /* Flags that apply to this specific data point. See DataPointFlags - for the available flags and their meaning. */ - uint32_t flags; -} opentelemetry_proto_metrics_v1_SummaryDataPoint; - -/* Represents the value at a given quantile of a distribution. - - To record Min and Max values following conventions are used: - - The 1.0 quantile is equivalent to the maximum value observed. - - The 0.0 quantile is equivalent to the minimum value observed. - - See the following issue for more context: - https://github.com/open-telemetry/opentelemetry-proto/issues/125 */ -typedef struct _opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile { - /* The quantile of a distribution. Must be in the interval - [0.0, 1.0]. */ - double quantile; - /* The value at the given quantile of a distribution. - - Quantile values must NOT be negative. */ - double value; -} opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile; - -/* A representation of an exemplar, which is a sample input measurement. - Exemplars also hold information about the environment when the measurement - was recorded, for example the span and trace ID of the active span when the - exemplar was recorded. */ -typedef struct _opentelemetry_proto_metrics_v1_Exemplar { - /* time_unix_nano is the exact time when this exemplar was recorded - - Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January - 1970. */ - uint64_t time_unix_nano; - pb_size_t which_value; - union { - double as_double; - int64_t as_int; - } value; - /* (Optional) Span ID of the exemplar trace. - span_id may be missing if the measurement is not recorded inside a trace - or if the trace is not sampled. */ - pb_callback_t span_id; - /* (Optional) Trace ID of the exemplar trace. - trace_id may be missing if the measurement is not recorded inside a trace - or if the trace is not sampled. */ - pb_callback_t trace_id; - /* The set of key/value pairs that were filtered out by the aggregator, but - recorded alongside the original measurement. Only key/value pairs that were - filtered out by the aggregator should be included */ - pb_callback_t filtered_attributes; -} opentelemetry_proto_metrics_v1_Exemplar; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Helper constants for enums */ -#define _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED -#define _opentelemetry_proto_metrics_v1_AggregationTemporality_MAX opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE -#define _opentelemetry_proto_metrics_v1_AggregationTemporality_ARRAYSIZE ((opentelemetry_proto_metrics_v1_AggregationTemporality)(opentelemetry_proto_metrics_v1_AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE+1)) - -#define _opentelemetry_proto_metrics_v1_DataPointFlags_MIN opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_DO_NOT_USE -#define _opentelemetry_proto_metrics_v1_DataPointFlags_MAX opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK -#define _opentelemetry_proto_metrics_v1_DataPointFlags_ARRAYSIZE ((opentelemetry_proto_metrics_v1_DataPointFlags)(opentelemetry_proto_metrics_v1_DataPointFlags_DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK+1)) - - - - - - -#define opentelemetry_proto_metrics_v1_Sum_aggregation_temporality_ENUMTYPE opentelemetry_proto_metrics_v1_AggregationTemporality - -#define opentelemetry_proto_metrics_v1_Histogram_aggregation_temporality_ENUMTYPE opentelemetry_proto_metrics_v1_AggregationTemporality - -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_aggregation_temporality_ENUMTYPE opentelemetry_proto_metrics_v1_AggregationTemporality - - - - - - - - - - -/* Initializer values for message structs */ -#define opentelemetry_proto_metrics_v1_MetricsData_init_default {{{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_ResourceMetrics_init_default {false, opentelemetry_proto_resource_v1_Resource_init_default, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_ScopeMetrics_init_default {false, opentelemetry_proto_common_v1_InstrumentationScope_init_default, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_Metric_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {opentelemetry_proto_metrics_v1_Gauge_init_default}, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_Gauge_init_default {{{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_Sum_init_default {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN, 0} -#define opentelemetry_proto_metrics_v1_Histogram_init_default {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_init_default {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} -#define opentelemetry_proto_metrics_v1_Summary_init_default {{{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_NumberDataPoint_init_default {0, 0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, 0} -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_init_default {0, 0, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, false, 0, false, 0} -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_init_default {{{NULL}, NULL}, 0, 0, 0, false, 0, 0, 0, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_default, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_default, 0, {{NULL}, NULL}, false, 0, false, 0, 0} -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_default {0, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_init_default {0, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_init_default {0, 0} -#define opentelemetry_proto_metrics_v1_Exemplar_init_default {0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_MetricsData_init_zero {{{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_ResourceMetrics_init_zero {false, opentelemetry_proto_resource_v1_Resource_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_ScopeMetrics_init_zero {false, opentelemetry_proto_common_v1_InstrumentationScope_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_Metric_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {opentelemetry_proto_metrics_v1_Gauge_init_zero}, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_Gauge_init_zero {{{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_Sum_init_zero {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN, 0} -#define opentelemetry_proto_metrics_v1_Histogram_init_zero {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_init_zero {{{NULL}, NULL}, _opentelemetry_proto_metrics_v1_AggregationTemporality_MIN} -#define opentelemetry_proto_metrics_v1_Summary_init_zero {{{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_NumberDataPoint_init_zero {0, 0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, 0} -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_init_zero {0, 0, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, false, 0, false, 0} -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_init_zero {{{NULL}, NULL}, 0, 0, 0, false, 0, 0, 0, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_zero, false, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_zero, 0, {{NULL}, NULL}, false, 0, false, 0, 0} -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_init_zero {0, {{NULL}, NULL}} -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_init_zero {0, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_init_zero {0, 0} -#define opentelemetry_proto_metrics_v1_Exemplar_init_zero {0, 0, {0}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} - -/* Field tags (for use in manual encoding/decoding) */ -#define opentelemetry_proto_metrics_v1_MetricsData_resource_metrics_tag 1 -#define opentelemetry_proto_metrics_v1_ResourceMetrics_resource_tag 1 -#define opentelemetry_proto_metrics_v1_ResourceMetrics_scope_metrics_tag 2 -#define opentelemetry_proto_metrics_v1_ResourceMetrics_schema_url_tag 3 -#define opentelemetry_proto_metrics_v1_ScopeMetrics_scope_tag 1 -#define opentelemetry_proto_metrics_v1_ScopeMetrics_metrics_tag 2 -#define opentelemetry_proto_metrics_v1_ScopeMetrics_schema_url_tag 3 -#define opentelemetry_proto_metrics_v1_Gauge_data_points_tag 1 -#define opentelemetry_proto_metrics_v1_Sum_data_points_tag 1 -#define opentelemetry_proto_metrics_v1_Sum_aggregation_temporality_tag 2 -#define opentelemetry_proto_metrics_v1_Sum_is_monotonic_tag 3 -#define opentelemetry_proto_metrics_v1_Histogram_data_points_tag 1 -#define opentelemetry_proto_metrics_v1_Histogram_aggregation_temporality_tag 2 -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_data_points_tag 1 -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_aggregation_temporality_tag 2 -#define opentelemetry_proto_metrics_v1_Summary_data_points_tag 1 -#define opentelemetry_proto_metrics_v1_Metric_name_tag 1 -#define opentelemetry_proto_metrics_v1_Metric_description_tag 2 -#define opentelemetry_proto_metrics_v1_Metric_unit_tag 3 -#define opentelemetry_proto_metrics_v1_Metric_gauge_tag 5 -#define opentelemetry_proto_metrics_v1_Metric_sum_tag 7 -#define opentelemetry_proto_metrics_v1_Metric_histogram_tag 9 -#define opentelemetry_proto_metrics_v1_Metric_exponential_histogram_tag 10 -#define opentelemetry_proto_metrics_v1_Metric_summary_tag 11 -#define opentelemetry_proto_metrics_v1_Metric_metadata_tag 12 -#define opentelemetry_proto_metrics_v1_NumberDataPoint_start_time_unix_nano_tag 2 -#define opentelemetry_proto_metrics_v1_NumberDataPoint_time_unix_nano_tag 3 -#define opentelemetry_proto_metrics_v1_NumberDataPoint_as_double_tag 4 -#define opentelemetry_proto_metrics_v1_NumberDataPoint_as_int_tag 6 -#define opentelemetry_proto_metrics_v1_NumberDataPoint_exemplars_tag 5 -#define opentelemetry_proto_metrics_v1_NumberDataPoint_attributes_tag 7 -#define opentelemetry_proto_metrics_v1_NumberDataPoint_flags_tag 8 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_start_time_unix_nano_tag 2 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_time_unix_nano_tag 3 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_count_tag 4 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_sum_tag 5 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_bucket_counts_tag 6 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_explicit_bounds_tag 7 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_exemplars_tag 8 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_attributes_tag 9 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_flags_tag 10 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_min_tag 11 -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_max_tag 12 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_offset_tag 1 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_bucket_counts_tag 2 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_attributes_tag 1 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_start_time_unix_nano_tag 2 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_time_unix_nano_tag 3 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_count_tag 4 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_sum_tag 5 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_scale_tag 6 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_zero_count_tag 7 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_positive_tag 8 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_negative_tag 9 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_flags_tag 10 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_exemplars_tag 11 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_min_tag 12 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_max_tag 13 -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_zero_threshold_tag 14 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_start_time_unix_nano_tag 2 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_time_unix_nano_tag 3 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_count_tag 4 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_sum_tag 5 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_quantile_values_tag 6 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_attributes_tag 7 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_flags_tag 8 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_quantile_tag 1 -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_value_tag 2 -#define opentelemetry_proto_metrics_v1_Exemplar_time_unix_nano_tag 2 -#define opentelemetry_proto_metrics_v1_Exemplar_as_double_tag 3 -#define opentelemetry_proto_metrics_v1_Exemplar_as_int_tag 6 -#define opentelemetry_proto_metrics_v1_Exemplar_span_id_tag 4 -#define opentelemetry_proto_metrics_v1_Exemplar_trace_id_tag 5 -#define opentelemetry_proto_metrics_v1_Exemplar_filtered_attributes_tag 7 - -/* Struct field encoding specification for nanopb */ -#define opentelemetry_proto_metrics_v1_MetricsData_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, resource_metrics, 1) -#define opentelemetry_proto_metrics_v1_MetricsData_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_MetricsData_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_MetricsData_resource_metrics_MSGTYPE opentelemetry_proto_metrics_v1_ResourceMetrics - -#define opentelemetry_proto_metrics_v1_ResourceMetrics_FIELDLIST(X, a) \ -X(a, STATIC, OPTIONAL, MESSAGE, resource, 1) \ -X(a, CALLBACK, REPEATED, MESSAGE, scope_metrics, 2) \ -X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) -#define opentelemetry_proto_metrics_v1_ResourceMetrics_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_ResourceMetrics_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_ResourceMetrics_resource_MSGTYPE opentelemetry_proto_resource_v1_Resource -#define opentelemetry_proto_metrics_v1_ResourceMetrics_scope_metrics_MSGTYPE opentelemetry_proto_metrics_v1_ScopeMetrics - -#define opentelemetry_proto_metrics_v1_ScopeMetrics_FIELDLIST(X, a) \ -X(a, STATIC, OPTIONAL, MESSAGE, scope, 1) \ -X(a, CALLBACK, REPEATED, MESSAGE, metrics, 2) \ -X(a, CALLBACK, SINGULAR, STRING, schema_url, 3) -#define opentelemetry_proto_metrics_v1_ScopeMetrics_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_ScopeMetrics_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_ScopeMetrics_scope_MSGTYPE opentelemetry_proto_common_v1_InstrumentationScope -#define opentelemetry_proto_metrics_v1_ScopeMetrics_metrics_MSGTYPE opentelemetry_proto_metrics_v1_Metric - -#define opentelemetry_proto_metrics_v1_Metric_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, STRING, name, 1) \ -X(a, CALLBACK, SINGULAR, STRING, description, 2) \ -X(a, CALLBACK, SINGULAR, STRING, unit, 3) \ -X(a, STATIC, ONEOF, MESSAGE, (data,gauge,data.gauge), 5) \ -X(a, STATIC, ONEOF, MESSAGE, (data,sum,data.sum), 7) \ -X(a, STATIC, ONEOF, MESSAGE, (data,histogram,data.histogram), 9) \ -X(a, STATIC, ONEOF, MESSAGE, (data,exponential_histogram,data.exponential_histogram), 10) \ -X(a, STATIC, ONEOF, MESSAGE, (data,summary,data.summary), 11) \ -X(a, CALLBACK, REPEATED, MESSAGE, metadata, 12) -#define opentelemetry_proto_metrics_v1_Metric_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_Metric_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_Metric_data_gauge_MSGTYPE opentelemetry_proto_metrics_v1_Gauge -#define opentelemetry_proto_metrics_v1_Metric_data_sum_MSGTYPE opentelemetry_proto_metrics_v1_Sum -#define opentelemetry_proto_metrics_v1_Metric_data_histogram_MSGTYPE opentelemetry_proto_metrics_v1_Histogram -#define opentelemetry_proto_metrics_v1_Metric_data_exponential_histogram_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogram -#define opentelemetry_proto_metrics_v1_Metric_data_summary_MSGTYPE opentelemetry_proto_metrics_v1_Summary -#define opentelemetry_proto_metrics_v1_Metric_metadata_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -#define opentelemetry_proto_metrics_v1_Gauge_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) -#define opentelemetry_proto_metrics_v1_Gauge_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_Gauge_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_Gauge_data_points_MSGTYPE opentelemetry_proto_metrics_v1_NumberDataPoint - -#define opentelemetry_proto_metrics_v1_Sum_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) \ -X(a, STATIC, SINGULAR, UENUM, aggregation_temporality, 2) \ -X(a, STATIC, SINGULAR, BOOL, is_monotonic, 3) -#define opentelemetry_proto_metrics_v1_Sum_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_Sum_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_Sum_data_points_MSGTYPE opentelemetry_proto_metrics_v1_NumberDataPoint - -#define opentelemetry_proto_metrics_v1_Histogram_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) \ -X(a, STATIC, SINGULAR, UENUM, aggregation_temporality, 2) -#define opentelemetry_proto_metrics_v1_Histogram_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_Histogram_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_Histogram_data_points_MSGTYPE opentelemetry_proto_metrics_v1_HistogramDataPoint - -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) \ -X(a, STATIC, SINGULAR, UENUM, aggregation_temporality, 2) -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_data_points_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint - -#define opentelemetry_proto_metrics_v1_Summary_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, data_points, 1) -#define opentelemetry_proto_metrics_v1_Summary_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_Summary_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_Summary_data_points_MSGTYPE opentelemetry_proto_metrics_v1_SummaryDataPoint - -#define opentelemetry_proto_metrics_v1_NumberDataPoint_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ -X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ -X(a, STATIC, ONEOF, DOUBLE, (value,as_double,value.as_double), 4) \ -X(a, CALLBACK, REPEATED, MESSAGE, exemplars, 5) \ -X(a, STATIC, ONEOF, SFIXED64, (value,as_int,value.as_int), 6) \ -X(a, CALLBACK, REPEATED, MESSAGE, attributes, 7) \ -X(a, STATIC, SINGULAR, UINT32, flags, 8) -#define opentelemetry_proto_metrics_v1_NumberDataPoint_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_NumberDataPoint_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_NumberDataPoint_exemplars_MSGTYPE opentelemetry_proto_metrics_v1_Exemplar -#define opentelemetry_proto_metrics_v1_NumberDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ -X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ -X(a, STATIC, SINGULAR, FIXED64, count, 4) \ -X(a, STATIC, OPTIONAL, DOUBLE, sum, 5) \ -X(a, CALLBACK, REPEATED, FIXED64, bucket_counts, 6) \ -X(a, CALLBACK, REPEATED, DOUBLE, explicit_bounds, 7) \ -X(a, CALLBACK, REPEATED, MESSAGE, exemplars, 8) \ -X(a, CALLBACK, REPEATED, MESSAGE, attributes, 9) \ -X(a, STATIC, SINGULAR, UINT32, flags, 10) \ -X(a, STATIC, OPTIONAL, DOUBLE, min, 11) \ -X(a, STATIC, OPTIONAL, DOUBLE, max, 12) -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_exemplars_MSGTYPE opentelemetry_proto_metrics_v1_Exemplar -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, attributes, 1) \ -X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ -X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ -X(a, STATIC, SINGULAR, FIXED64, count, 4) \ -X(a, STATIC, OPTIONAL, DOUBLE, sum, 5) \ -X(a, STATIC, SINGULAR, SINT32, scale, 6) \ -X(a, STATIC, SINGULAR, FIXED64, zero_count, 7) \ -X(a, STATIC, OPTIONAL, MESSAGE, positive, 8) \ -X(a, STATIC, OPTIONAL, MESSAGE, negative, 9) \ -X(a, STATIC, SINGULAR, UINT32, flags, 10) \ -X(a, CALLBACK, REPEATED, MESSAGE, exemplars, 11) \ -X(a, STATIC, OPTIONAL, DOUBLE, min, 12) \ -X(a, STATIC, OPTIONAL, DOUBLE, max, 13) \ -X(a, STATIC, SINGULAR, DOUBLE, zero_threshold, 14) -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_positive_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_negative_MSGTYPE opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_exemplars_MSGTYPE opentelemetry_proto_metrics_v1_Exemplar - -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, SINT32, offset, 1) \ -X(a, CALLBACK, REPEATED, UINT64, bucket_counts, 2) -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_DEFAULT NULL - -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FIXED64, start_time_unix_nano, 2) \ -X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 3) \ -X(a, STATIC, SINGULAR, FIXED64, count, 4) \ -X(a, STATIC, SINGULAR, DOUBLE, sum, 5) \ -X(a, CALLBACK, REPEATED, MESSAGE, quantile_values, 6) \ -X(a, CALLBACK, REPEATED, MESSAGE, attributes, 7) \ -X(a, STATIC, SINGULAR, UINT32, flags, 8) -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_quantile_values_MSGTYPE opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, DOUBLE, quantile, 1) \ -X(a, STATIC, SINGULAR, DOUBLE, value, 2) -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_CALLBACK NULL -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_DEFAULT NULL - -#define opentelemetry_proto_metrics_v1_Exemplar_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FIXED64, time_unix_nano, 2) \ -X(a, STATIC, ONEOF, DOUBLE, (value,as_double,value.as_double), 3) \ -X(a, CALLBACK, SINGULAR, BYTES, span_id, 4) \ -X(a, CALLBACK, SINGULAR, BYTES, trace_id, 5) \ -X(a, STATIC, ONEOF, SFIXED64, (value,as_int,value.as_int), 6) \ -X(a, CALLBACK, REPEATED, MESSAGE, filtered_attributes, 7) -#define opentelemetry_proto_metrics_v1_Exemplar_CALLBACK pb_default_field_callback -#define opentelemetry_proto_metrics_v1_Exemplar_DEFAULT NULL -#define opentelemetry_proto_metrics_v1_Exemplar_filtered_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue - -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_MetricsData_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ResourceMetrics_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ScopeMetrics_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Metric_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Gauge_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Sum_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Histogram_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ExponentialHistogram_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Summary_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_NumberDataPoint_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_HistogramDataPoint_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_SummaryDataPoint_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_msg; -extern const pb_msgdesc_t opentelemetry_proto_metrics_v1_Exemplar_msg; - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define opentelemetry_proto_metrics_v1_MetricsData_fields &opentelemetry_proto_metrics_v1_MetricsData_msg -#define opentelemetry_proto_metrics_v1_ResourceMetrics_fields &opentelemetry_proto_metrics_v1_ResourceMetrics_msg -#define opentelemetry_proto_metrics_v1_ScopeMetrics_fields &opentelemetry_proto_metrics_v1_ScopeMetrics_msg -#define opentelemetry_proto_metrics_v1_Metric_fields &opentelemetry_proto_metrics_v1_Metric_msg -#define opentelemetry_proto_metrics_v1_Gauge_fields &opentelemetry_proto_metrics_v1_Gauge_msg -#define opentelemetry_proto_metrics_v1_Sum_fields &opentelemetry_proto_metrics_v1_Sum_msg -#define opentelemetry_proto_metrics_v1_Histogram_fields &opentelemetry_proto_metrics_v1_Histogram_msg -#define opentelemetry_proto_metrics_v1_ExponentialHistogram_fields &opentelemetry_proto_metrics_v1_ExponentialHistogram_msg -#define opentelemetry_proto_metrics_v1_Summary_fields &opentelemetry_proto_metrics_v1_Summary_msg -#define opentelemetry_proto_metrics_v1_NumberDataPoint_fields &opentelemetry_proto_metrics_v1_NumberDataPoint_msg -#define opentelemetry_proto_metrics_v1_HistogramDataPoint_fields &opentelemetry_proto_metrics_v1_HistogramDataPoint_msg -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_fields &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_msg -#define opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_fields &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_msg -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_fields &opentelemetry_proto_metrics_v1_SummaryDataPoint_msg -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_fields &opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_msg -#define opentelemetry_proto_metrics_v1_Exemplar_fields &opentelemetry_proto_metrics_v1_Exemplar_msg - -/* Maximum encoded size of messages (where known) */ -/* opentelemetry_proto_metrics_v1_MetricsData_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_ResourceMetrics_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_ScopeMetrics_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_Metric_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_Gauge_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_Sum_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_Histogram_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_ExponentialHistogram_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_Summary_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_NumberDataPoint_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_HistogramDataPoint_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_SummaryDataPoint_size depends on runtime parameters */ -/* opentelemetry_proto_metrics_v1_Exemplar_size depends on runtime parameters */ -#define OPENTELEMETRY_PROTO_METRICS_V1_OPENTELEMETRY_PROTO_METRICS_V1_METRICS_PB_H_MAX_SIZE opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_size -#define opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_size 18 - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#ifdef __cplusplus -/* Message descriptors for nanopb */ -namespace nanopb { -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_MetricsData_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_ResourceMetrics_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_ScopeMetrics_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 9; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_Metric_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_Gauge_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_Sum_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_Histogram_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_ExponentialHistogram_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 1; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_Summary_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 7; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_NumberDataPoint_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 11; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_HistogramDataPoint_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 14; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 7; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_SummaryDataPoint_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 2; - static PB_INLINE_CONSTEXPR const pb_size_t size = opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_size; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 6; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_metrics_v1_Exemplar_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -} // namespace nanopb - -#endif /* __cplusplus */ - - -#endif diff --git a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc b/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc deleted file mode 100644 index e800058..0000000 --- a/src/proto/opentelemetry/proto/metrics/v1/metrics.pb.inc +++ /dev/null @@ -1,69 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9.1 */ - -#include "metrics.pb.h" -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -PB_BIND(opentelemetry_proto_metrics_v1_MetricsData, opentelemetry_proto_metrics_v1_MetricsData, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_ResourceMetrics, opentelemetry_proto_metrics_v1_ResourceMetrics, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_ScopeMetrics, opentelemetry_proto_metrics_v1_ScopeMetrics, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_Metric, opentelemetry_proto_metrics_v1_Metric, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_Gauge, opentelemetry_proto_metrics_v1_Gauge, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_Sum, opentelemetry_proto_metrics_v1_Sum, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_Histogram, opentelemetry_proto_metrics_v1_Histogram, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_ExponentialHistogram, opentelemetry_proto_metrics_v1_ExponentialHistogram, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_Summary, opentelemetry_proto_metrics_v1_Summary, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_NumberDataPoint, opentelemetry_proto_metrics_v1_NumberDataPoint, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_HistogramDataPoint, opentelemetry_proto_metrics_v1_HistogramDataPoint, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets, opentelemetry_proto_metrics_v1_ExponentialHistogramDataPoint_Buckets, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_SummaryDataPoint, opentelemetry_proto_metrics_v1_SummaryDataPoint, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile, opentelemetry_proto_metrics_v1_SummaryDataPoint_ValueAtQuantile, AUTO) - - -PB_BIND(opentelemetry_proto_metrics_v1_Exemplar, opentelemetry_proto_metrics_v1_Exemplar, AUTO) - - - - - - - -#ifndef PB_CONVERT_DOUBLE_FLOAT -/* On some platforms (such as AVR), double is really float. - * To be able to encode/decode double on these platforms, you need. - * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. - */ -PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) -#endif - diff --git a/src/proto/opentelemetry/proto/resource/v1/resource.pb.h b/src/proto/opentelemetry/proto/resource/v1/resource.pb.h deleted file mode 100644 index 1eb114b..0000000 --- a/src/proto/opentelemetry/proto/resource/v1/resource.pb.h +++ /dev/null @@ -1,89 +0,0 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9.1 */ - -#ifndef PB_OPENTELEMETRY_PROTO_RESOURCE_V1_OPENTELEMETRY_PROTO_RESOURCE_V1_RESOURCE_PB_H_INCLUDED -#define PB_OPENTELEMETRY_PROTO_RESOURCE_V1_OPENTELEMETRY_PROTO_RESOURCE_V1_RESOURCE_PB_H_INCLUDED -#include -#include "../../common/v1/common.pb.h" - -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -/* Struct definitions */ -/* Resource information. */ -typedef struct _opentelemetry_proto_resource_v1_Resource { - /* Set of attributes that describe the resource. - Attribute keys MUST be unique (it is not allowed to have more than one - attribute with the same key). - The behavior of software that receives duplicated keys can be unpredictable. */ - pb_callback_t attributes; - /* The number of dropped attributes. If the value is 0, then - no attributes were dropped. */ - uint32_t dropped_attributes_count; - /* Set of entities that participate in this Resource. - - Note: keys in the references MUST exist in attributes of this message. - - Status: [Development] */ - pb_callback_t entity_refs; -} opentelemetry_proto_resource_v1_Resource; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Initializer values for message structs */ -#define opentelemetry_proto_resource_v1_Resource_init_default {{{NULL}, NULL}, 0, {{NULL}, NULL}} -#define opentelemetry_proto_resource_v1_Resource_init_zero {{{NULL}, NULL}, 0, {{NULL}, NULL}} - -/* Field tags (for use in manual encoding/decoding) */ -#define opentelemetry_proto_resource_v1_Resource_attributes_tag 1 -#define opentelemetry_proto_resource_v1_Resource_dropped_attributes_count_tag 2 -#define opentelemetry_proto_resource_v1_Resource_entity_refs_tag 3 - -/* Struct field encoding specification for nanopb */ -#define opentelemetry_proto_resource_v1_Resource_FIELDLIST(X, a) \ -X(a, CALLBACK, REPEATED, MESSAGE, attributes, 1) \ -X(a, STATIC, SINGULAR, UINT32, dropped_attributes_count, 2) \ -X(a, CALLBACK, REPEATED, MESSAGE, entity_refs, 3) -#define opentelemetry_proto_resource_v1_Resource_CALLBACK pb_default_field_callback -#define opentelemetry_proto_resource_v1_Resource_DEFAULT NULL -#define opentelemetry_proto_resource_v1_Resource_attributes_MSGTYPE opentelemetry_proto_common_v1_KeyValue -#define opentelemetry_proto_resource_v1_Resource_entity_refs_MSGTYPE opentelemetry_proto_common_v1_EntityRef - -extern const pb_msgdesc_t opentelemetry_proto_resource_v1_Resource_msg; - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define opentelemetry_proto_resource_v1_Resource_fields &opentelemetry_proto_resource_v1_Resource_msg - -/* Maximum encoded size of messages (where known) */ -/* opentelemetry_proto_resource_v1_Resource_size depends on runtime parameters */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#ifdef __cplusplus -/* Message descriptors for nanopb */ -namespace nanopb { -template <> -struct MessageDescriptor { - static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = 3; - static inline const pb_msgdesc_t* fields() { - return &opentelemetry_proto_resource_v1_Resource_msg; - } - static inline bool has_msgid() { - return false; - } - static inline uint32_t msgid() { - return 0; - } -}; -} // namespace nanopb - -#endif /* __cplusplus */ - - -#endif diff --git a/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc b/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc deleted file mode 100644 index ed5c9e4..0000000 --- a/src/proto/opentelemetry/proto/resource/v1/resource.pb.inc +++ /dev/null @@ -1,12 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9.1 */ - -#include "resource.pb.h" -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -PB_BIND(opentelemetry_proto_resource_v1_Resource, opentelemetry_proto_resource_v1_Resource, AUTO) - - - From 63adcb8d20f80bcb3a4855c84258ca38575858ee Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 22:58:20 -0500 Subject: [PATCH 18/30] chore(config): add opentelemetry_proto to all envs, restore comments - Add aodtorusan/opentelemetry_proto@^1.9.0 to lib_deps in every env (including non-proto envs; the library is a declared dep in library.json so having it available everywhere is consistent) - Restore section comments removed by pio auto-formatting: native env explanation, rpipicow board note, proto variants header Co-Authored-By: Claude Sonnet 4.6 --- platformio.ini | 218 +++++++++++++++++++++++++------------------------ 1 file changed, 111 insertions(+), 107 deletions(-) diff --git a/platformio.ini b/platformio.ini index 7209d93..1e43539 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,3 +1,13 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + [platformio] default_envs = esp32dev, rpipicow, esp8266 @@ -7,21 +17,20 @@ default_envs = esp32dev, rpipicow, esp8266 ; instead so tests can assert on emitted JSON payloads without real hardware. [env:native] platform = native -build_flags = - -std=c++14 - -Itest/stubs - -DWIFI_SSID=\"ci-build\" - -DWIFI_PASS=\"ci-build\" - -DOTEL_SERVICE_NAME=\"test-service\" - -DOTEL_SERVICE_NAMESPACE=\"test\" - -DOTEL_SERVICE_VERSION=\"0.0.1\" - -DOTEL_SERVICE_INSTANCE=\"test-001\" - -DOTEL_DEPLOY_ENV=\"test\" -lib_deps = - bblanchon/ArduinoJson@^7.0.0 - nanopb/Nanopb@^0.4.91 -; Exclude OtelSender.cpp (WiFi/HTTP) and main.cpp from the test build. -; fake_sender.cpp in test/native/ provides OTelSender stubs instead. +build_flags = + -std=c++14 + -Itest/stubs + -DWIFI_SSID=\"ci-build\" + -DWIFI_PASS=\"ci-build\" + -DOTEL_SERVICE_NAME=\"test-service\" + -DOTEL_SERVICE_NAMESPACE=\"test\" + -DOTEL_SERVICE_VERSION=\"0.0.1\" + -DOTEL_SERVICE_INSTANCE=\"test-001\" + -DOTEL_DEPLOY_ENV=\"test\" +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 + aodtorusan/opentelemetry_proto@^1.9.0 build_src_filter = +<*.cpp> - - [env:esp32dev] @@ -29,125 +38,120 @@ platform = espressif32 board = esp32dev framework = arduino monitor_speed = 115200 - -lib_deps = - ArduinoJson@^7.4.2 - nanopb/Nanopb@^0.4.91 - WiFi - HTTPClient - -build_flags = - -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" - -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" - -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" - -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" - -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" - -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" - -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" - -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" - +lib_deps = + ArduinoJson@^7.4.2 + nanopb/Nanopb@^0.4.91 + WiFi + HTTPClient + aodtorusan/opentelemetry_proto@^1.9.0 +build_flags = + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" + -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" [env:rpipicow] ; Community Raspberry Pi Pico W platform and board platform = https://github.com/maxgerhardt/platform-raspberrypi.git board = rpipicow framework = arduino - -; Dependencies for micro-ROS and telemetry -lib_deps = - bblanchon/ArduinoJson@^7.0.0 - nanopb/Nanopb@^0.4.91 - -build_flags = - -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" - -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" - -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" - -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" - -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" - -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" - -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" - -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" - -DDEBUG +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 + aodtorusan/opentelemetry_proto@^1.9.0 +build_flags = + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" + -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" + -DDEBUG [env:esp8266] platform = espressif8266 board = d1_mini framework = arduino - - -; Dependencies for micro-ROS and telemetry -lib_deps = - bblanchon/ArduinoJson@^7.0.0 - nanopb/Nanopb@^0.4.91 - -build_flags = - -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" - -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" - -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" - -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" - -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" - -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" - -DOTEL_SERVICE_INSTANCE=\"esp8266\" - -DOTEL_DEPLOY_ENV=\"esp8266\" +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 + aodtorusan/opentelemetry_proto@^1.9.0 +build_flags = + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"esp8266\" + -DOTEL_DEPLOY_ENV=\"esp8266\" ; ── Protobuf variants — used by CI to validate the protobuf encoding path ───── ; Fully specified (not using `extends`) because platformio ci does not reliably ; inherit platform/board/framework through the extends mechanism. - [env:esp32dev-proto] platform = espressif32 board = esp32dev framework = arduino monitor_speed = 115200 -lib_deps = - ArduinoJson@^7.4.2 - nanopb/Nanopb@^0.4.91 - WiFi - HTTPClient -build_flags = - -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" - -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" - -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" - -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" - -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" - -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" - -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" - -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" - -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF +lib_deps = + ArduinoJson@^7.4.2 + nanopb/Nanopb@^0.4.91 + WiFi + HTTPClient + aodtorusan/opentelemetry_proto@^1.9.0 +build_flags = + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" + -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" + -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF [env:rpipicow-proto] platform = https://github.com/maxgerhardt/platform-raspberrypi.git board = rpipicow framework = arduino -lib_deps = - bblanchon/ArduinoJson@^7.0.0 - nanopb/Nanopb@^0.4.91 -build_flags = - -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" - -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" - -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" - -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" - -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" - -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" - -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" - -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" - -DDEBUG - -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 + aodtorusan/opentelemetry_proto@^1.9.0 +build_flags = + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"${sysenv.OTEL_SERVICE_INSTANCE}\" + -DOTEL_DEPLOY_ENV=\"${sysenv.OTEL_DEPLOY_ENV}\" + -DDEBUG + -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF [env:esp8266-proto] platform = espressif8266 board = d1_mini framework = arduino -lib_deps = - bblanchon/ArduinoJson@^7.0.0 - nanopb/Nanopb@^0.4.91 -build_flags = - -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" - -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" - -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" - -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" - -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" - -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" - -DOTEL_SERVICE_INSTANCE=\"esp8266\" - -DOTEL_DEPLOY_ENV=\"esp8266\" - -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + nanopb/Nanopb@^0.4.91 + aodtorusan/opentelemetry_proto@^1.9.0 +build_flags = + -DWIFI_SSID=\"${sysenv.WIFI_SSID}\" + -DWIFI_PASS=\"${sysenv.WIFI_PASS}\" + -DOTEL_COLLECTOR_BASE_URL="\"http://192.168.8.10:4318\"" + -DOTEL_SERVICE_NAME=\"${sysenv.OTEL_SERVICE_NAME}\" + -DOTEL_SERVICE_NAMESPACE=\"${sysenv.OTEL_SERVICE_NAMESPACE}\" + -DOTEL_SERVICE_VERSION=\"${sysenv.OTEL_SERVICE_VERSION}\" + -DOTEL_SERVICE_INSTANCE=\"esp8266\" + -DOTEL_DEPLOY_ENV=\"esp8266\" + -DOTEL_EXPORTER_OTLP_PROTOCOL=OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF From a05cd1ccadde856124b5b0fa0402aa73c577acc1 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 23:10:49 -0500 Subject: [PATCH 19/30] fix(ci): rename test suite dir to test_otlp so PlatformIO discovers it PlatformIO's native test runner skipped test/native/ because the directory name clashed with the platform name. Renaming to test/test_otlp/ makes the suite discoverable. Also simplified the pio test CI command to remove redundant --project-dir / --project-conf flags that default to the checked-out root. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/validate.yml | 2 +- platformio.ini | 2 +- test/{native => test_otlp}/fake_sender.cpp | 0 test/{native => test_otlp}/fake_sender.h | 0 test/{native => test_otlp}/test_otlp.cpp | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename test/{native => test_otlp}/fake_sender.cpp (100%) rename test/{native => test_otlp}/fake_sender.h (100%) rename test/{native => test_otlp}/test_otlp.cpp (100%) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 9c91c74..dee72c1 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -52,7 +52,7 @@ jobs: # GCC is pre-installed on ubuntu-latest; no extra toolchain step needed. - name: Run native unit tests - run: pio test -e native --project-dir . --project-conf platformio.ini + run: pio test -e native # ── src/main.cpp (existing integration test) ─────────────────────────── diff --git a/platformio.ini b/platformio.ini index 1e43539..65ec140 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ default_envs = esp32dev, rpipicow, esp8266 ; ── Native host tests ───────────────────────────────────────────────────────── ; Compiles the library on the host GCC toolchain and runs Unity unit tests. -; OtelSender.cpp (WiFi/HTTP) is excluded; test/native/fake_sender.cpp is used +; OtelSender.cpp (WiFi/HTTP) is excluded; test/test_otlp/fake_sender.cpp is used ; instead so tests can assert on emitted JSON payloads without real hardware. [env:native] platform = native diff --git a/test/native/fake_sender.cpp b/test/test_otlp/fake_sender.cpp similarity index 100% rename from test/native/fake_sender.cpp rename to test/test_otlp/fake_sender.cpp diff --git a/test/native/fake_sender.h b/test/test_otlp/fake_sender.h similarity index 100% rename from test/native/fake_sender.h rename to test/test_otlp/fake_sender.h diff --git a/test/native/test_otlp.cpp b/test/test_otlp/test_otlp.cpp similarity index 100% rename from test/native/test_otlp.cpp rename to test/test_otlp/test_otlp.cpp From a83291e3d6d35ec2e03e144e64f0aa841b12caec Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 23:19:06 -0500 Subject: [PATCH 20/30] fix(native-tests): resolve String type errors in host GCC build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OtelDefaults.h used String without including Arduino.h, so when test_otlp.cpp included it directly (before OtelMetrics.h brought in the stub), GCC parsed String as its error-recovery int type — making attrs appear as int and breaking all String-accepting set() overloads. Adding #include to OtelDefaults.h makes it self-contained regardless of include order (on embedded targets the header is already included by the framework, so this is a no-op there). Also adds String indexOf(const String&) overload to the Arduino stub so OtelTracer.h's indexOf("01") string-literal call compiles cleanly. Co-Authored-By: Claude Sonnet 4.6 --- include/OtelDefaults.h | 1 + test/stubs/Arduino.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/OtelDefaults.h b/include/OtelDefaults.h index 7cfde02..b7b2d21 100644 --- a/include/OtelDefaults.h +++ b/include/OtelDefaults.h @@ -1,6 +1,7 @@ #ifndef OTEL_DEFAULTS_H #define OTEL_DEFAULTS_H +#include #include #include #include // gettimeofday() diff --git a/test/stubs/Arduino.h b/test/stubs/Arduino.h index fed2c00..ce0b301 100644 --- a/test/stubs/Arduino.h +++ b/test/stubs/Arduino.h @@ -35,6 +35,10 @@ class String : public std::string { auto p = find(c, from); return p == npos ? -1 : (int)p; } + int indexOf(const String& s, unsigned int from = 0) const { + auto p = find(s, from); + return p == npos ? -1 : (int)p; + } String substring(unsigned int from, unsigned int to = 0) const { return to ? String(substr(from, to - from)) : String(substr(from)); } From a466facca7843672dbea2a1f79ee428e62768cf7 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 23:40:51 -0500 Subject: [PATCH 21/30] This command is deprecated and will be removed in the next releases. Please use `pio pkg update` instead. --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index dee72c1..d44a2e2 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -39,7 +39,7 @@ jobs: } >> "$GITHUB_ENV" - name: PlatformIO Update - run: pio update + run: pio pkg update # Remove any cached registry version so --lib "." always wins and we # compile the local source, not the published package at the same version. From 40c9b38ee513efed422fcfb4c4a4298f5a58af92 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 23:44:17 -0500 Subject: [PATCH 22/30] fix(native-tests): add missing Arduino polyfills to host stub When ARDUINO is defined, ArduinoJson expects the Arduino framework to provide pgm_read_byte, __FlashStringHelper, Print, Printable, and Stream. Our stub only had String/Serial/timing, so ArduinoJson's pgmspace.hpp, FlashString.hpp, PrintWriter.hpp, and related headers failed to compile on native/GCC. Adds the full set of types ArduinoJson needs in Arduino mode: - pgm_read_byte/word/ptr macros (identity reads on native) - __FlashStringHelper struct + correct F() cast - Print / Printable / Stream base classes Co-Authored-By: Claude Sonnet 4.6 --- test/stubs/Arduino.h | 67 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/test/stubs/Arduino.h b/test/stubs/Arduino.h index ce0b301..af53087 100644 --- a/test/stubs/Arduino.h +++ b/test/stubs/Arduino.h @@ -1,7 +1,8 @@ #pragma once // Minimal Arduino shim for native (Linux/macOS) unit tests. // Provides just enough of the Arduino API surface that otel-embedded-cpp -// headers can be compiled on a host toolchain without modification. +// headers (and ArduinoJson's Arduino-mode polyfills) compile on a host +// toolchain without modification. #ifndef ARDUINO #define ARDUINO 100 @@ -12,6 +13,45 @@ #include #include #include +#include + +// ── pgmspace (AVR flash memory — identity on native) ───────────────────────── +#define pgm_read_byte(addr) (*((const uint8_t *)(addr))) +#define pgm_read_word(addr) (*((const uint16_t *)(addr))) +#define pgm_read_ptr(addr) (*((const void* const*)(addr))) +#define PROGMEM +#define PGM_P const char* +#define PSTR(s) (s) + +// ── Flash string helper ─────────────────────────────────────────────────────── +// ArduinoJson checks for __FlashStringHelper in several headers. +// On native it is just an opaque type; cast via F(). +struct __FlashStringHelper {}; +#define F(x) (reinterpret_cast(x)) + +// ── Print / Printable / Stream ──────────────────────────────────────────────── +// ArduinoJson uses these as base-class constraints in its adapters. +class Print { +public: + virtual size_t write(uint8_t) { return 0; } + virtual size_t write(const uint8_t*, size_t n) { return n; } + size_t print(const char* s) { return write((const uint8_t*)s, strlen(s)); } + size_t println(const char* s = "") { return print(s) + write((uint8_t)'\n'); } + virtual ~Print() = default; +}; + +class Printable { +public: + virtual size_t printTo(Print&) const = 0; + virtual ~Printable() = default; +}; + +class Stream : public Print { +public: + virtual int available() { return 0; } + virtual int read() { return -1; } + virtual int peek() { return -1; } +}; // ── String ──────────────────────────────────────────────────────────────────── // std::string subclass that adds the Arduino-compatible constructors and @@ -57,27 +97,24 @@ inline String operator+(const char* a, const String& b) { return String(std::string(a) + std::string(b)); } -// ── Flash string ────────────────────────────────────────────────────────────── -#define F(x) (x) - // ── Serial stub ─────────────────────────────────────────────────────────────── struct HardwareSerial { void begin(unsigned long) {} - template size_t print(T) { return 0; } - template size_t println(T) { return 0; } - template size_t print(T, int) { return 0; } - template size_t println(T, int) { return 0; } - size_t println() { return 0; } - void printf(const char*, ...) {} + template size_t print(T) { return 0; } + template size_t println(T) { return 0; } + template size_t print(T, int) { return 0; } + template size_t println(T, int) { return 0; } + size_t println() { return 0; } + void printf(const char*, ...) {} }; static HardwareSerial Serial; // ── Timing stubs ────────────────────────────────────────────────────────────── -static inline unsigned long millis() { return 0; } -static inline unsigned long micros() { return 0; } -static inline void delay(unsigned long) {} +static inline unsigned long millis() { return 0; } +static inline unsigned long micros() { return 0; } +static inline void delay(unsigned long) {} // ── PRNG ────────────────────────────────────────────────────────────────────── static inline void randomSeed(unsigned long seed) { srand((unsigned int)seed); } -static inline long random(long max) { return (long)(rand() % max); } -static inline long random(long min, long max) { return min + (long)(rand() % (max - min)); } +static inline long random(long hi) { return (long)(rand() % hi); } +static inline long random(long lo, long hi) { return lo + (long)(rand() % (hi - lo)); } From 916d86d70df5b2f6f7e7b3eddd5f527512303711 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 28 Apr 2026 23:55:42 -0500 Subject: [PATCH 23/30] fix(native-tests): add String::concat and Stream::readBytes to stub ArduinoJson's ArduinoStringWriter calls String::concat() to flush its buffer, and ArduinoStreamReader calls Stream::readBytes() to deserialize from a stream. Both were missing from the host test stub. Co-Authored-By: Claude Sonnet 4.6 --- test/stubs/Arduino.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/stubs/Arduino.h b/test/stubs/Arduino.h index af53087..efa96b7 100644 --- a/test/stubs/Arduino.h +++ b/test/stubs/Arduino.h @@ -48,9 +48,11 @@ class Printable { class Stream : public Print { public: - virtual int available() { return 0; } - virtual int read() { return -1; } - virtual int peek() { return -1; } + virtual int available() { return 0; } + virtual int read() { return -1; } + virtual int peek() { return -1; } + virtual size_t readBytes(char*, size_t) { return 0; } + virtual size_t readBytes(uint8_t* b, size_t n) { return readBytes((char*)b, n); } }; // ── String ──────────────────────────────────────────────────────────────────── @@ -91,6 +93,9 @@ class String : public std::string { String operator+(const char* s) const { return String(std::string(*this) + s); } bool operator==(const char* s) const { return compare(s) == 0; } bool operator!=(const char* s) const { return compare(s) != 0; } + + bool concat(const char* s) { if (s) append(s); return true; } + bool concat(const String& s) { append(s); return true; } }; inline String operator+(const char* a, const String& b) { From 62611820e07e09d49c60fa0874521fbe6e0c9547 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Wed, 29 Apr 2026 00:54:12 -0500 Subject: [PATCH 24/30] fix(tests): get all 13 native Unity tests passing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add `#include ` first in test_otlp.cpp so ArduinoJson uses the same inline namespace (V743PB42) as fake_sender.cpp, fixing linker errors from namespace mismatch. - Include OtelMetrics.cpp directly in test_otlp.cpp so static-inline singletons (defaultResource, metricsScopeConfig) are shared with test fixtures; project_src.cpp is now a no-op comment. - Set ARDUINOJSON_DEFAULT_NESTING_LIMIT=15 in native build flags — OTLP JSON nests 11 levels deep, exceeding the default of 10, which caused deserializeJson to silently drop the attributes array contents. - Fix deserializeJson(doc, json) ambiguity in OtelTracer.h (String satisfies both IteratorReader and ArduinoStringReader); use json.c_str() for unambiguous const char* reader. Co-Authored-By: Claude Sonnet 4.6 --- include/OtelTracer.h | 2 +- platformio.ini | 3 ++- test/test_otlp/project_src.cpp | 2 ++ test/test_otlp/test_otlp.cpp | 5 +++++ 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 test/test_otlp/project_src.cpp diff --git a/include/OtelTracer.h b/include/OtelTracer.h index adb7857..176e8a5 100644 --- a/include/OtelTracer.h +++ b/include/OtelTracer.h @@ -119,7 +119,7 @@ struct Propagators { // Use a small document to avoid heap bloat; adjust if payloads are larger JsonDocument doc; - DeserializationError err = deserializeJson(doc, json); + DeserializationError err = deserializeJson(doc, json.c_str()); if (err) return out; if (doc["traceparent"].is()) { diff --git a/platformio.ini b/platformio.ini index 65ec140..5ba7d01 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ default_envs = esp32dev, rpipicow, esp8266 ; instead so tests can assert on emitted JSON payloads without real hardware. [env:native] platform = native -build_flags = +build_flags = -std=c++14 -Itest/stubs -DWIFI_SSID=\"ci-build\" @@ -27,6 +27,7 @@ build_flags = -DOTEL_SERVICE_VERSION=\"0.0.1\" -DOTEL_SERVICE_INSTANCE=\"test-001\" -DOTEL_DEPLOY_ENV=\"test\" + -DARDUINOJSON_DEFAULT_NESTING_LIMIT=15 lib_deps = bblanchon/ArduinoJson@^7.0.0 nanopb/Nanopb@^0.4.91 diff --git a/test/test_otlp/project_src.cpp b/test/test_otlp/project_src.cpp new file mode 100644 index 0000000..9c3f98e --- /dev/null +++ b/test/test_otlp/project_src.cpp @@ -0,0 +1,2 @@ +// OtelMetrics.cpp is included directly in test_otlp.cpp to keep all +// static-inline singletons in the same translation unit as the test fixtures. diff --git a/test/test_otlp/test_otlp.cpp b/test/test_otlp/test_otlp.cpp index d9d57e9..a0e49ce 100644 --- a/test/test_otlp/test_otlp.cpp +++ b/test/test_otlp/test_otlp.cpp @@ -1,3 +1,4 @@ +#include // must come before ArduinoJson.h to get matching inline namespace #include #include @@ -7,6 +8,10 @@ #include "OtelTracer.h" #include "fake_sender.h" +// Pull in non-inline implementations in the same TU so static-inline singletons +// (defaultResource, metricsScopeConfig, etc.) are shared with the test fixtures. +#include "../../src/OtelMetrics.cpp" + // ── Fixture ─────────────────────────────────────────────────────────────────── void setUp() { From 09d473fabfa79cf634de7d2a829ade2d790722f5 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Wed, 29 Apr 2026 02:18:35 -0500 Subject: [PATCH 25/30] docs: add Doxygen docstrings to reach 80% coverage threshold Add /** */ docstrings to all public classes, structs, and methods across OtelSender.h, OtelDefaults.h, OtelMetrics.h, OtelLogger.h, OtelTracer.h, OtelProtoEncoder.h, and src/OtelMetrics.cpp. Converts existing inline // comments on public API to proper Doxygen /** */ blocks, and adds new docstrings to previously undocumented items (TraceContext, ExtractedContext, KeyValuePairs, Propagators, RemoteParentScope, Span, Tracer, utility functions, and all signal encoder declarations). Co-Authored-By: Claude Sonnet 4.6 --- include/OtelDefaults.h | 12 ++- include/OtelLogger.h | 49 +++++++++-- include/OtelMetrics.h | 70 +++++++++++++--- include/OtelProtoEncoder.h | 56 +++++++++++-- include/OtelSender.h | 43 ++++++++-- include/OtelTracer.h | 167 +++++++++++++++++++++++++++++-------- src/OtelMetrics.cpp | 3 + 7 files changed, 330 insertions(+), 70 deletions(-) diff --git a/include/OtelDefaults.h b/include/OtelDefaults.h index b7b2d21..cea3476 100644 --- a/include/OtelDefaults.h +++ b/include/OtelDefaults.h @@ -37,7 +37,7 @@ static inline uint64_t nowUnixMillis() { + static_cast(tv.tv_usec) / 1000ULL; } -// Portable uint64 -> String (no printf/ULL reliance; RP2040-safe) +/** Convert a uint64 to its decimal String representation without printf/ULL (RP2040-safe). */ inline String u64ToString(uint64_t v) { if (v == 0) return String("0"); char buf[21]; // max 20 digits + NUL @@ -97,15 +97,21 @@ inline void serializeKeyInt(JsonArray &arr, const String &key, int64_t value) { struct OTelResourceConfig { std::map attrs; - // Newer API + /** @{ Set or update a resource attribute. */ void set(const String &k, const String &v) { attrs[k] = v; } void set(const char *k, const String &v) { attrs[String(k)] = v; } + /** @} */ + + /** Remove all resource attributes. */ void clear() { attrs.clear(); } + + /** @return True if no resource attributes have been set. */ bool empty() const { return attrs.empty(); } - // Backwards-compatible API expected by existing Metrics/Tracer code + /** @{ Backwards-compatible attribute setter used by Metrics/Tracer paths. */ void setAttribute(const String &k, const String &v) { attrs[k] = v; } void setAttribute(const char *k, const String &v) { attrs[String(k)] = v; } + /** @} */ /** * Legacy helper used by Metrics/Tracer paths: diff --git a/include/OtelLogger.h b/include/OtelLogger.h index 1dd9feb..2d8fa14 100644 --- a/include/OtelLogger.h +++ b/include/OtelLogger.h @@ -12,7 +12,12 @@ namespace OTel { -// ---- Severity mapping ------------------------------------------------------- +/** + * Map a severity text string to its OTLP severityNumber enum value. + * Follows the OpenTelemetry Log Data Model specification. + * @param s Severity string: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", or "FATAL". + * @return OTLP integer severity number, or 0 if unknown. + */ static inline int severityNumberFromText(const String& s) { if (s == "TRACE") return 1; if (s == "DEBUG") return 5; @@ -23,39 +28,66 @@ static inline int severityNumberFromText(const String& s) { return 0; } -// ---- Instrumentation scope for logs ----------------------------------------- +/** Instrumentation scope name and version emitted on every log payload. */ struct LogScopeConfig { String scopeName{"otel-embedded-cpp"}; String scopeVersion{""}; // optional }; + +/** Returns the process-wide LogScopeConfig singleton. */ static inline LogScopeConfig& logScopeConfig() { static LogScopeConfig cfg; return cfg; } -// ---- Default labels (merged into each log record's attributes) -------------- +/** Returns the process-wide default log labels map (merged into every log record). */ static inline std::map& defaultLabels() { static std::map labels; return labels; } +/** + * Static façade for emitting OTLP log records. + * + * Automatically correlates log records with the currently active span + * (traceId / spanId) when called from within a @c Span's lifetime. + */ class Logger { public: - // Set/merge defaults + /** + * Replace the full set of default labels added to every log record. + * @param labels Map of attribute key/value pairs. + */ static void setDefaultLabels(const std::map& labels) { defaultLabels() = labels; } + + /** + * Set or update a single default label added to every log record. + * @param key Attribute key. + * @param value Attribute value. + */ static void setDefaultLabel(const String& key, const String& value) { defaultLabels()[key] = value; } - // Map-based API + /** + * Emit a log record at the given severity. + * @param severity Severity string ("TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"). + * @param message Log body text. + * @param labels Per-call attributes merged on top of default labels. + */ static void log(const String& severity, const String& message, const std::map& labels = {}) { buildAndSend(severity, message, labels); } - // Convenience overload: initializer_list of key/value pairs + /** + * Emit a log record using an initializer-list of attributes. + * @param severity Severity string. + * @param message Log body text. + * @param kvs Brace-enclosed attribute pairs, e.g. {{"key","val"}}. + */ static void log(const String& severity, const String& message, std::initializer_list> kvs) { std::map labels; @@ -63,7 +95,7 @@ class Logger { buildAndSend(severity, message, labels); } - // Helpers by severity + /** @{ Convenience helpers that hard-code the severity level. */ static void logTrace(const String &m, const std::map &l = {}) { log("TRACE", m, l); } static void logDebug(const String &m, const std::map &l = {}) { log("DEBUG", m, l); } static void logInfo (const String &m, const std::map &l = {}) { log("INFO", m, l); } @@ -77,8 +109,10 @@ class Logger { static void logWarn (const String &m, std::initializer_list> kvs) { log("WARN", m, kvs); } static void logError(const String &m, std::initializer_list> kvs) { log("ERROR", m, kvs); } static void logFatal(const String &m, std::initializer_list> kvs) { log("FATAL", m, kvs); } + /** @} */ private: + /** Build the OTLP log payload and hand it to OTelSender. */ static void buildAndSend(const String& severity, const String& message, const std::map& labels) { @@ -152,4 +186,3 @@ class Logger { } // namespace OTel #endif // OTEL_LOGGER_H - diff --git a/include/OtelMetrics.h b/include/OtelMetrics.h index 10b3e62..613293a 100644 --- a/include/OtelMetrics.h +++ b/include/OtelMetrics.h @@ -12,48 +12,79 @@ namespace OTel { -// ---- Instrumentation scope for metrics -------------------------------------- +/** Instrumentation scope name and version emitted on every metrics payload. */ struct MetricsScopeConfig { String scopeName{"otel-embedded"}; String scopeVersion{"0.1.0"}; }; +/** Returns the process-wide MetricsScopeConfig singleton. */ static inline MetricsScopeConfig& metricsScopeConfig() { static MetricsScopeConfig cfg; return cfg; } -// ---- Default metric labels (merged into each datapoint's attributes) -------- +/** Returns the process-wide default metric labels map (merged into every datapoint). */ static inline std::map& defaultMetricLabels() { static std::map labels; return labels; } +/** + * Static façade for emitting OTLP metrics (gauges and sums). + * + * Call @c begin() once after connecting to Wi-Fi to set the instrumentation + * scope name/version. Then call @c gauge() or @c sum() freely from loop(). + */ class Metrics { public: - // Configure the instrumentation scope name/version for metrics + /** + * Configure the instrumentation scope name and version for all metrics. + * @param scopeName Library/component name, e.g. "my-firmware". + * @param scopeVersion Semver string, e.g. "1.0.0". + */ static void begin(const String& scopeName, const String& scopeVersion) { metricsScopeConfig().scopeName = scopeName; metricsScopeConfig().scopeVersion = scopeVersion; } - // Set/merge defaults applied to *every* datapoint + /** + * Replace the full set of default labels applied to every datapoint. + * @param labels Map of attribute key/value pairs. + */ static void setDefaultMetricLabels(const std::map& labels) { defaultMetricLabels() = labels; } + + /** + * Set or update a single default label applied to every datapoint. + * @param key Attribute key. + * @param value Attribute value. + */ static void setDefaultMetricLabel(const String& key, const String& value) { defaultMetricLabels()[key] = value; } - // --------- GAUGE (double) ---------- - // Convenience with std::map + /** + * Emit a gauge datapoint. + * @param name Metric name (e.g. "cpu.usage"). + * @param value Measured value. + * @param unit UCUM unit string, e.g. "1", "By", "ms". + * @param labels Per-call attributes merged on top of default labels. + */ static void gauge(const String& name, double value, const String& unit = "1", const std::map& labels = {}) { buildAndSendGauge(name, value, unit, labels); } - // Convenience with initializer_list + /** + * Emit a gauge datapoint using an initializer-list of attributes. + * @param name Metric name. + * @param value Measured value. + * @param unit UCUM unit string. + * @param kvs Brace-enclosed attribute pairs, e.g. {{"core","0"},{"host","esp1"}}. + */ static void gauge(const String& name, double value, const String& unit, std::initializer_list> kvs) { @@ -62,16 +93,32 @@ class Metrics { buildAndSendGauge(name, value, unit, labels); } - // --------- SUM (double) ------------- - // OTLP requires temporality + monotonic flags for Sum + /** + * Emit a sum (counter/cumulative) datapoint. + * @param name Metric name. + * @param value Measured value. + * @param isMonotonic True for counters that only increase. + * @param temporality "DELTA" or "CUMULATIVE". + * @param unit UCUM unit string. + * @param labels Per-call attributes merged on top of default labels. + */ static void sum(const String& name, double value, bool isMonotonic = false, - const String& temporality = "DELTA", // or "CUMULATIVE" + const String& temporality = "DELTA", const String& unit = "1", const std::map& labels = {}) { buildAndSendSum(name, value, isMonotonic, temporality, unit, labels); } + /** + * Emit a sum datapoint using an initializer-list of attributes. + * @param name Metric name. + * @param value Measured value. + * @param isMonotonic True for monotonic counters. + * @param temporality "DELTA" or "CUMULATIVE". + * @param unit UCUM unit string. + * @param kvs Brace-enclosed attribute pairs. + */ static void sum(const String& name, double value, bool isMonotonic, const String& temporality, @@ -83,10 +130,12 @@ class Metrics { } private: + /** Build the OTLP gauge payload and hand it to OTelSender. */ static void buildAndSendGauge(const String& name, double value, const String& unit, const std::map& labels); + /** Build the OTLP sum payload and hand it to OTelSender. */ static void buildAndSendSum(const String& name, double value, bool isMonotonic, const String& temporality, @@ -97,4 +146,3 @@ class Metrics { } // namespace OTel #endif // OTEL_METRICS_H - diff --git a/include/OtelProtoEncoder.h b/include/OtelProtoEncoder.h index cb0be03..626914d 100644 --- a/include/OtelProtoEncoder.h +++ b/include/OtelProtoEncoder.h @@ -13,9 +13,14 @@ namespace OTel { namespace Proto { -// Typed attribute mirroring Span::Attr — forward declared here so Tracer -// can pass its internal buffer without a circular dependency. +/** Discriminator for typed span/log attributes. */ enum class AttrType { Str, Int, Dbl, Bool }; + +/** + * A single typed key/value attribute used in spans and log records. + * Mirrors @c Span::Attr; forward-declared here to avoid a circular dependency + * between OtelTracer.h and OtelProtoEncoder.h. + */ struct Attr { String key; AttrType type{AttrType::Str}; @@ -24,6 +29,10 @@ struct Attr { double d{0.0}; bool b{false}; }; + +/** + * A span event (timestamped annotation) with an optional set of attributes. + */ struct Event { String name; uint64_t t{0}; @@ -32,24 +41,55 @@ struct Event { // ── Signal encoders ────────────────────────────────────────────────────────── -// Encode and send a Gauge metric datapoint. +/** + * Encode and transmit a Gauge metric datapoint via OTLP/Protobuf. + * @param name Metric name. + * @param value Gauge value. + * @param unit UCUM unit string. + * @param labels Merged default + per-call attributes. + */ void sendGauge(const String& name, double value, const String& unit, const std::map& labels); -// Encode and send a Sum metric datapoint. -// temporality: 1 = DELTA, 2 = CUMULATIVE +/** + * Encode and transmit a Sum metric datapoint via OTLP/Protobuf. + * @param name Metric name. + * @param value Sum value. + * @param isMonotonic True for monotonically increasing counters. + * @param temporality 1 = DELTA, 2 = CUMULATIVE. + * @param unit UCUM unit string. + * @param labels Merged default + per-call attributes. + */ void sendSum(const String& name, double value, bool isMonotonic, int temporality, const String& unit, const std::map& labels); -// Encode and send a LogRecord. +/** + * Encode and transmit a LogRecord via OTLP/Protobuf. + * @param severity Severity text ("INFO", "WARN", etc.). + * @param severityNum OTLP severity number (see severityNumberFromText()). + * @param message Log body. + * @param callLabels Per-call attributes. + * @param defaultLabels Process-wide default labels. + * @param traceId Active trace ID (32 hex chars), or empty string. + * @param spanId Active span ID (16 hex chars), or empty string. + */ void sendLog(const String& severity, int severityNum, const String& message, const std::map& callLabels, const std::map& defaultLabels, const String& traceId, const String& spanId); -// Encode and send a Span. -// traceId / spanId / parentSpanId are 32/16/16 hex chars respectively. +/** + * Encode and transmit a Span via OTLP/Protobuf. + * @param name Operation name. + * @param traceId 32 hex-char trace identifier. + * @param spanId 16 hex-char span identifier. + * @param parentSpanId 16 hex-char parent span ID, or empty string for root spans. + * @param startNs Span start time in nanoseconds since UNIX epoch. + * @param endNs Span end time in nanoseconds since UNIX epoch. + * @param attrs Span attributes. + * @param events Span events (timestamped annotations). + */ void sendSpan(const String& name, const String& traceId, const String& spanId, diff --git a/include/OtelSender.h b/include/OtelSender.h index 2013d4a..83162b8 100644 --- a/include/OtelSender.h +++ b/include/OtelSender.h @@ -101,27 +101,56 @@ #define OTEL_QUEUE_CAPACITY 16 #endif +/** Internal queue item for the RP2040 SPSC ring buffer (core-0 → core-1). */ struct OTelQueuedItem { const char* path; // "/v1/logs", "/v1/traces", "/v1/metrics" const char* contentType; // "application/json" or "application/x-protobuf" String payload; // serialized body (JSON text or raw protobuf bytes) }; +/** + * Low-level OTLP/HTTP sender used by Logger, Tracer, and Metrics. + * + * On RP2040 the actual HTTP calls run on core 1 via a SPSC ring buffer so + * that loop() on core 0 is never blocked. On ESP32/ESP8266 the send is + * synchronous. Call @c beginAsyncWorker() once after Wi-Fi comes up. + */ class OTelSender { public: - // Send a JSON document (OTLP/HTTP JSON, Content-Type: application/json) + /** + * Serialise @p doc to JSON and send it to the collector at @p path. + * @param path OTLP signal path: "/v1/logs", "/v1/metrics", or "/v1/traces". + * @param doc ArduinoJson document representing the OTLP payload. + */ static void sendJson(const char* path, JsonDocument& doc); - // Send a pre-encoded protobuf buffer (OTLP/HTTP Protobuf, - // Content-Type: application/x-protobuf). Used by OtelProtoEncoder. + /** + * Send a pre-encoded protobuf buffer to the collector at @p path. + * Used by OtelProtoEncoder when @c OTEL_EXPORTER_OTLP_PROTOCOL is set to + * @c OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF. + * @param path OTLP signal path. + * @param buf Pointer to the encoded protobuf bytes. + * @param len Length of the buffer in bytes. + */ static void sendProto(const char* path, const uint8_t* buf, size_t len); - // Start the RP2040 core-1 worker (no-op on non-RP2040). Call once after Wi-Fi is ready. + /** + * Start the RP2040 core-1 worker thread. + * No-op on ESP32/ESP8266. Call once after Wi-Fi is ready. + */ static void beginAsyncWorker(); - // Diagnostics (published via your health metrics if you like) - static uint32_t droppedCount(); // number of items dropped due to full queue - static bool queueIsHealthy(); // worker started? + /** + * @return Number of payloads dropped because the internal queue was full. + * Monitor this with a gauge metric to detect back-pressure. + */ + static uint32_t droppedCount(); + + /** + * @return True if the async worker has been started (RP2040) or always + * true on synchronous platforms. + */ + static bool queueIsHealthy(); private: // ---------- SPSC ring buffer (core0 producer -> core1 consumer) ---------- diff --git a/include/OtelTracer.h b/include/OtelTracer.h index 176e8a5..5b597d4 100644 --- a/include/OtelTracer.h +++ b/include/OtelTracer.h @@ -24,33 +24,49 @@ namespace OTel { -// ---- Active Trace Context --------------------------------------------------- +/** + * Active W3C trace context (traceId + spanId) for the current execution scope. + * Updated automatically when a @c Span is created or destroyed. + */ struct TraceContext { String traceId; // 32 hex chars String spanId; // 16 hex chars + /** @return True when both IDs have the correct lengths per the W3C spec. */ bool valid() const { return traceId.length() == 32 && spanId.length() == 16; } }; +/** Returns the process-wide active TraceContext singleton. */ static inline TraceContext& currentTraceContext() { static TraceContext ctx; return ctx; } -// --- New: Context Propagation (extract + scope) ------------------------------ +/** + * Result of extracting a remote trace context from inbound headers or a payload. + * Pass to @c RemoteParentScope to make it the active context for child spans. + */ struct ExtractedContext { TraceContext ctx; String tracestate; // optional; unused for now but kept for future injection bool sampled = true; // from flags; default true if unknown + /** @return True when the embedded TraceContext is valid. */ bool valid() const { return ctx.valid(); } }; -// Simple key/value view for header-like maps (HTTP headers, MQTT user props) +/** + * Thin abstraction over a key/value lookup used by context propagators. + * Wrap HTTP headers, MQTT user properties, or any string-keyed map. + */ struct KeyValuePairs { - // Provide a lambda to look up case-insensitive keys. Returns empty String if missing. + /** Callable that returns the value for a key, or an empty String if absent. */ std::function get; }; -// W3C "traceparent": 00-<32 hex traceId>-<16 hex parentId>-<2 hex flags> +/** + * Parse a W3C @c traceparent header value into @p out. + * Format: "00-<32 hex traceId>-<16 hex parentId>-<2 hex flags>". + * @return True on success, false if the string is malformed. + */ static inline bool parseTraceparent(const String& tp, ExtractedContext& out) { // Minimal, allocation-light parser // Expect 55 chars with version "00" or at least the 4 parts separated by '-' @@ -73,7 +89,11 @@ static inline bool parseTraceparent(const String& tp, ExtractedContext& out) { return out.valid(); } -// B3 single header: b3 = traceId-spanId-sampled +/** + * Parse a B3 single-header value into @p out. + * Format: "<32 hex traceId>-<16 hex spanId>[-]". + * @return True on success, false if the string is malformed. + */ static inline bool parseB3Single(const String& b3, ExtractedContext& out) { // Minimal split (traceId-spanId-[sampling?]) int p1 = b3.indexOf('-'); if (p1 < 0) return false; @@ -90,8 +110,18 @@ static inline bool parseB3Single(const String& b3, ExtractedContext& out) { return out.valid(); } +/** + * W3C TraceContext and B3 context propagation helpers. + * Use @c extract() / @c extractFromJson() to read a remote parent context + * and @c inject() / @c injectToJson() / @c injectToHeaders() to forward it. + */ struct Propagators { - // 1) Extract from header-like key/values (HTTP headers, MQTT v5 user props) + /** + * Extract a remote trace context from header-like key/value pairs. + * Tries W3C @c traceparent first, then B3 single-header as a fallback. + * @param kv Wrapper providing a case-insensitive key lookup. + * @return Extracted context; check @c valid() before use. + */ static ExtractedContext extract(const KeyValuePairs& kv) { ExtractedContext out; @@ -112,7 +142,12 @@ struct Propagators { return out; // invalid } - // 2) Extract directly from JSON payload + /** + * Extract a remote trace context from a JSON payload string. + * Recognises @c traceparent, @c trace_id/@c span_id, and @c b3 fields. + * @param json Serialised JSON string. + * @return Extracted context; check @c valid() before use. + */ static ExtractedContext extractFromJson(const String& json) { ExtractedContext out; if (json.length() == 0) return out; @@ -156,11 +191,14 @@ struct Propagators { return out; // invalid if none matched } -// --- ADD these inside: struct OTel::Propagators { ... } --- - -// Generic injector: pass a setter that accepts (key, value). -template -static inline void inject(Setter set, uint8_t flags = 0x01) { + /** + * Inject the active trace context into any key/value store via a setter callable. + * Writes a W3C @c traceparent value. No-op if no active span is present. + * @param set Callable accepting @c (const char* key, const char* value). + * @param flags W3C trace flags byte (bit 0 = sampled; default 0x01). + */ + template + static inline void inject(Setter set, uint8_t flags = 0x01) { const auto& ctx = OTel::currentTraceContext(); // Only inject if we actually have a valid active context @@ -181,21 +219,35 @@ static inline void inject(Setter set, uint8_t flags = 0x01) { // If you add tracestate in future, you can forward it here. } -// Convenience: inject into ArduinoJson JsonDocument payloads -static inline void injectToJson(JsonDocument& doc, uint8_t flags = 0x01) { + /** + * Inject the active trace context as a @c traceparent key into an ArduinoJson document. + * @param doc JSON document to write into. + * @param flags W3C trace flags byte. + */ + static inline void injectToJson(JsonDocument& doc, uint8_t flags = 0x01) { inject([&](const char* k, const char* v){ doc[k] = v; }, flags); } -// Convenience: inject into HTTP headers via a generic adder (e.g., http.addHeader) -template -static inline void injectToHeaders(HeaderAdder add, uint8_t flags = 0x01) { + /** + * Inject the active trace context into HTTP headers via a generic adder callable. + * @param add Callable accepting @c (const char* name, const char* value) + * — e.g. pass @c http.addHeader directly. + * @param flags W3C trace flags byte. + */ + template + static inline void injectToHeaders(HeaderAdder add, uint8_t flags = 0x01) { inject(add, flags); } }; -// RAII helper: temporarily install a remote parent context as the active one +/** + * RAII guard that installs a remote parent context as the active TraceContext + * for the duration of its lifetime, then restores the previous context on + * destruction. Use when processing inbound requests or messages that carry + * a W3C traceparent so child spans are linked to the upstream trace. + */ class RemoteParentScope { public: RemoteParentScope(const TraceContext& incoming) { @@ -221,6 +273,8 @@ class RemoteParentScope { // ---- Utilities -------------------------------------------------------------- + +/** Convert uint64 to its decimal String representation without printf (RP2040-safe). */ static inline String u64ToStr(uint64_t v) { // Avoid ambiguous String(uint64_t) on some cores char buf[32]; @@ -233,8 +287,7 @@ static inline String u64ToStr(uint64_t v) { } return String(p); } -// -// Best-effort chip id (used for defaults) +/** Best-effort chip ID as a hex string; used as a default for service.instance.id. */ static inline String chipIdHex() { #if defined(ESP8266) uint32_t id = ESP.getChipId(); @@ -249,7 +302,7 @@ static inline String chipIdHex() { #endif } -// Defaults for resource fields (compile-time overrides win) +/** @return Default service name from @c OTEL_SERVICE_NAME, or "embedded-service". */ static inline String defaultServiceName() { #ifdef OTEL_SERVICE_NAME return String(OTEL_SERVICE_NAME); @@ -257,6 +310,7 @@ static inline String defaultServiceName() { return String("embedded-service"); #endif } +/** @return Default service instance ID from @c OTEL_SERVICE_INSTANCE_ID, or the chip ID hex. */ static inline String defaultServiceInstanceId() { #ifdef OTEL_SERVICE_INSTANCE_ID return String(OTEL_SERVICE_INSTANCE_ID); @@ -264,6 +318,7 @@ static inline String defaultServiceInstanceId() { return chipIdHex(); #endif } +/** @return Default host name from @c OTEL_HOST_NAME, or "ESP-" + chip ID hex. */ static inline String defaultHostName() { #ifdef OTEL_HOST_NAME return String(OTEL_HOST_NAME); @@ -273,7 +328,11 @@ static inline String defaultHostName() { } // ---- Entropy + ID helpers --------------------------------------------------- -// + +/** + * XOR a boot-time salt derived from the system clock and service instance ID + * into @p b to reduce cross-boot ID collisions on platforms with weak RNGs. + */ static inline void mix_boot_salt(uint8_t* b, size_t len) { uint64_t t = nowUnixNano(); uint32_t salt = (uint32_t)t ^ (uint32_t)(t >> 32); @@ -295,6 +354,7 @@ static inline void mix_boot_salt(uint8_t* b, size_t len) { } +/** Seed the PRNG with hardware entropy sources and mix in boot-time jitter. */ static inline void seedEntropy() { uint32_t seed = 0; @@ -322,6 +382,10 @@ static inline void seedEntropy() { for (int i = 0; i < 8; ++i) (void)random(); } +/** + * Fill @p out with @p len cryptographically random bytes using the best + * hardware source available for the target platform. + */ static inline void fillRandom(uint8_t* out, size_t len) { #if defined(ESP32) esp_fill_random(out, len); @@ -342,6 +406,7 @@ static inline void fillRandom(uint8_t* out, size_t len) { #endif } +/** Encode @p len bytes of @p data as a lowercase hex string. */ static inline String toHex(const uint8_t* data, size_t len) { static const char* hex = "0123456789abcdef"; String out; out.reserve(len * 2); @@ -352,6 +417,7 @@ static inline String toHex(const uint8_t* data, size_t len) { return out; } +/** Generate a random 128-bit trace ID as a 32-char lowercase hex string. */ static inline String generateTraceId() { uint8_t b[16]; fillRandom(b, sizeof b); @@ -382,6 +448,7 @@ static inline String generateTraceId() { return h; } +/** Generate a random 64-bit span ID as a 16-char lowercase hex string. */ static inline String generateSpanId() { uint8_t b[8]; fillRandom(b, sizeof b); @@ -406,25 +473,32 @@ static inline String generateSpanId() { -// Add one string attribute to a resource attributes array +/** Append a string-valued OTLP KeyValue object to a resource attributes array. */ static inline void addResAttr(JsonArray& arr, const char* key, const String& value) { JsonObject a = arr.add(); a["key"] = key; a["value"].to()["stringValue"] = value; } -// ---- Tracer configuration --------------------------------------------------- +/** Instrumentation scope name and version emitted on every trace payload. */ struct TracerConfig { String scopeName{"otel-embedded"}; String scopeVersion{"0.1.0"}; }; +/** Returns the process-wide TracerConfig singleton. */ static inline TracerConfig& tracerConfig() { static TracerConfig cfg; return cfg; } -// ---- Span ------------------------------------------------------------------- +/** + * A single OTLP span. Create via @c Tracer::startSpan(); the span is + * automatically sent when @c end() is called or the object goes out of scope. + * + * Spans are not copyable. They are movable so you can store them in a + * container or return them from a factory function. + */ class Span { public: explicit Span(const String& name) @@ -487,8 +561,7 @@ class Span { return *this; } - // ---------- NEW: span attributes API --------------------------------------- - // These buffer attributes until end() and are rendered into OTLP JSON. + /** @{ Add a typed attribute to the span. Attributes are buffered until @c end(). */ Span& setAttribute(const String& key, const String& v) { //attrs_.push_back(Attr{key, Type::Str, v, 0, 0.0, false}); Attr a; @@ -513,9 +586,12 @@ class Span { Span& setAttribute(const String& key, bool v) { Attr a; a.key=key; a.type=Type::Bool; a.b=v; attrs_.push_back(a); return *this; } + /** @} */ - // ---------- NEW: span events API ------------------------------------------- - // 1) Event without attributes + /** + * Record a timestamped event on this span. + * @param name Human-readable event name, e.g. "cache.miss". + */ Span& addEvent(const String& name) { //events_.push_back(Event{name, nowUnixNano(), {}}); Event e; @@ -524,7 +600,11 @@ class Span { events_.push_back(e); return *this; } - // 2) Event with simple (string) attributes — minimal footprint + /** + * Record a timestamped event with string attributes. + * @param name Human-readable event name. + * @param attrs Key/value pairs attached to the event. + */ Span& addEvent(const String& name, const std::vector>& attrs) { //Event e{name, nowUnixNano(), {}}; // NEW @@ -547,7 +627,11 @@ class Span { return *this; } - // You can still call this manually; it's safe to call more than once. + /** + * Finalise and transmit the span to the collector. + * Safe to call more than once (idempotent). Called automatically by the + * destructor if the user does not call it explicitly. + */ void end() { if (ended_) return; // idempotent guard ended_ = true; @@ -722,9 +806,21 @@ class Span { bool ended_ = false; }; -// ---- Tracer facade ---------------------------------------------------------- +/** + * Static façade for creating OTLP spans. + * + * Call @c begin() once after connecting to Wi-Fi to seed the PRNG and set the + * instrumentation scope name/version. Then call @c startSpan() to instrument + * any operation. + */ class Tracer { public: + /** + * Initialise the tracer: seed entropy, clear any stale context, and + * configure the instrumentation scope. + * @param scopeName Library/component name, e.g. "my-firmware". + * @param scopeVersion Semver string, e.g. "1.0.0". + */ static void begin(const String& scopeName, const String& scopeVersion) { seedEntropy(); @@ -736,6 +832,11 @@ class Tracer { tracerConfig().scopeVersion = scopeVersion; } + /** + * Start a new span. If a span is already active its IDs become the parent. + * @param name Human-readable operation name, e.g. "mqtt.publish". + * @return A @c Span object; call @c end() or let it go out of scope. + */ static Span startSpan(const String& name) { return Span(name); } diff --git a/src/OtelMetrics.cpp b/src/OtelMetrics.cpp index 9c83443..3153929 100644 --- a/src/OtelMetrics.cpp +++ b/src/OtelMetrics.cpp @@ -4,6 +4,7 @@ namespace OTel { #if OTEL_EXPORTER_OTLP_PROTOCOL != OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF +/** Append default metric labels then per-call labels to @p attrArray as OTLP KeyValue objects. */ static void addPointAttributes(JsonArray& attrArray, const std::map& callLabels) { for (const auto& kv : defaultMetricLabels()) { @@ -18,6 +19,7 @@ static void addPointAttributes(JsonArray& attrArray, } } +/** Populate the OTLP resource object from defaultResource() or compile-time defaults. */ static void addCommonResource(JsonObject& resource) { auto &res = OTel::defaultResource(); if (!res.empty()) { @@ -30,6 +32,7 @@ static void addCommonResource(JsonObject& resource) { addResAttr(rattrs, "host.name", defaultHostName()); } +/** Write the instrumentation scope name and version into @p scope. */ static void addCommonScope(JsonObject& scope) { scope["name"] = metricsScopeConfig().scopeName; scope["version"] = metricsScopeConfig().scopeVersion; From 854b2c41a16ea2d34a287d51f9d0d1fae25672ee Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Mon, 4 May 2026 16:30:40 -0500 Subject: [PATCH 26/30] Remove broken OTel type aliases that referenced nonexistent OTelLogger/Tracer/Gauge names The namespace OTel block declared using aliases for OTelLogger, OTelTracer, OTelGauge etc., but those prefixed names don't exist. The actual classes (Logger, Tracer, Metrics) are already defined inside namespace OTel by their respective headers and need no further aliasing. Also removed getDefaultResource() which constructed OTelResourceConfig with 5 positional args that the struct no longer supports. --- include/OtelEmbeddedCpp.h | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/include/OtelEmbeddedCpp.h b/include/OtelEmbeddedCpp.h index d4c42ae..6d0dd1a 100644 --- a/include/OtelEmbeddedCpp.h +++ b/include/OtelEmbeddedCpp.h @@ -26,22 +26,5 @@ #define OTEL_DEPLOY_ENV "dev" #endif -namespace OTel { - using Logger = OTelLogger; - using Tracer = OTelTracer; - using Gauge = OTelGauge; - using Counter = OTelCounter; - using Histogram = OTelHistogram; - using ResourceConfig = OTelResourceConfig; - - inline OTelResourceConfig getDefaultResource() { - return OTelResourceConfig( - OTEL_SERVICE_NAME, - OTEL_SERVICE_NAMESPACE, - OTEL_SERVICE_VERSION, - OTEL_SERVICE_INSTANCE, - OTEL_DEPLOY_ENV - ); - } -} +// OTel::Logger, OTel::Tracer, OTel::Metrics are defined in their respective headers above. From 31718666e323e2c205f4352a33f79c2ac5f95f21 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Tue, 5 May 2026 22:26:33 -0500 Subject: [PATCH 27/30] fix: address CodeRabbit PR review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - examples/direct_otlp/main.cpp: pass 1.0 per call with DELTA temporality instead of accumulating a running total; the old code sent a monotonically growing value under DELTA, which is a semantic error (DELTA expects the increment since the last report, not the cumulative sum). - README.md / include/OtelTracer.h: align macro name — the README table, platformio.ini, OtelEmbeddedCpp.h, and src/main.cpp all use OTEL_SERVICE_INSTANCE, but OtelTracer.h::defaultServiceInstanceId() checked OTEL_SERVICE_INSTANCE_ID. Fixed OtelTracer.h and README to use the consistent OTEL_SERVICE_INSTANCE name. - include/OtelMetrics.h, OtelLogger.h, OtelTracer.h: remove `static` from singleton inline helpers (metricsScopeConfig, defaultMetricLabels, logScopeConfig, defaultLabels, currentTraceContext, tracerConfig). The `static` keyword gives internal linkage, so each translation unit that includes the header gets its own private copy of the function-local static. Calls to Metrics::begin() or Tracer::begin() in one TU were invisible to the encoder in another TU — silently reverting scope metadata and default labels to defaults. Removing `static` while keeping `inline` gives a single process-wide instance as required by the C++ ODR for inline functions. - include/OtelProtoEncoder.h: include OtelSender.h before the protocol guard so the macros are always defined. Without it, if the header were included standalone both OTEL_EXPORTER_OTLP_PROTOCOL and OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF would expand to 0, making the guard evaluate to true and silently activating the protobuf path. - src/OtelSender.cpp: replace the byte-by-byte append loop in sendProto() with String(const char*, len) to correctly handle null bytes in protobuf payloads and avoid repeated heap reallocations. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 2 +- examples/direct_otlp/main.cpp | 8 ++++---- include/OtelLogger.h | 4 ++-- include/OtelMetrics.h | 4 ++-- include/OtelProtoEncoder.h | 7 +++++++ include/OtelTracer.h | 10 +++++----- src/OtelSender.cpp | 6 +++--- 7 files changed, 24 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4b1272b..09fcce1 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ Set via `-D` flags in `platformio.ini` `build_flags`. | Macro | Default | Description | | ------------------------ | ------------------ | ----------------------------------------------- | | `OTEL_SERVICE_NAME` | `"embedded-service"` | Name of your service | -| `OTEL_SERVICE_INSTANCE_ID` | chip ID | Unique instance identifier | +| `OTEL_SERVICE_INSTANCE` | chip ID | Unique instance identifier | | `OTEL_HOST_NAME` | `"ESP-"` | Host name reported in resource attributes | ### Send behaviour diff --git a/examples/direct_otlp/main.cpp b/examples/direct_otlp/main.cpp index 2249e57..472ffc7 100644 --- a/examples/direct_otlp/main.cpp +++ b/examples/direct_otlp/main.cpp @@ -159,10 +159,10 @@ void loop() { OTel::Metrics::gauge("sensor.temperature", temperature, "Cel", {{"sensor.id", "1"}}); - // sum: monotonic counter, DELTA temporality (required by vendors like Datadog). - static double totalReadings = 0; - totalReadings += 1.0; - OTel::Metrics::sum("sensor.readings.total", totalReadings, + // sum: monotonic counter. Pass 1.0 per call with DELTA temporality so + // the value represents the increment since the last report, not a running + // total. Use CUMULATIVE + an accumulator if your backend requires it. + OTel::Metrics::sum("sensor.readings.total", 1.0, /*isMonotonic=*/true, "DELTA", "1", {{"sensor.id", "1"}}); diff --git a/include/OtelLogger.h b/include/OtelLogger.h index 2d8fa14..464ac03 100644 --- a/include/OtelLogger.h +++ b/include/OtelLogger.h @@ -35,13 +35,13 @@ struct LogScopeConfig { }; /** Returns the process-wide LogScopeConfig singleton. */ -static inline LogScopeConfig& logScopeConfig() { +inline LogScopeConfig& logScopeConfig() { static LogScopeConfig cfg; return cfg; } /** Returns the process-wide default log labels map (merged into every log record). */ -static inline std::map& defaultLabels() { +inline std::map& defaultLabels() { static std::map labels; return labels; } diff --git a/include/OtelMetrics.h b/include/OtelMetrics.h index 613293a..8ee87e6 100644 --- a/include/OtelMetrics.h +++ b/include/OtelMetrics.h @@ -19,13 +19,13 @@ struct MetricsScopeConfig { }; /** Returns the process-wide MetricsScopeConfig singleton. */ -static inline MetricsScopeConfig& metricsScopeConfig() { +inline MetricsScopeConfig& metricsScopeConfig() { static MetricsScopeConfig cfg; return cfg; } /** Returns the process-wide default metric labels map (merged into every datapoint). */ -static inline std::map& defaultMetricLabels() { +inline std::map& defaultMetricLabels() { static std::map labels; return labels; } diff --git a/include/OtelProtoEncoder.h b/include/OtelProtoEncoder.h index 626914d..da7b858 100644 --- a/include/OtelProtoEncoder.h +++ b/include/OtelProtoEncoder.h @@ -1,5 +1,12 @@ #pragma once +// Ensure the protocol macros are defined before we evaluate the guard below. +// OTEL_EXPORTER_OTLP_PROTOCOL defaults to 0 (JSON) and +// OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF is 1, both defined in OtelSender.h. +// Without this include, an undefined macro would expand to 0 on both sides, +// making the guard evaluate to true and silently activating the protobuf path. +#include "OtelSender.h" + // This header is only active when the protobuf protocol is selected. // It provides encode functions called by Metrics, Logger, and Tracer // instead of building a JsonDocument. diff --git a/include/OtelTracer.h b/include/OtelTracer.h index 5b597d4..38fee5a 100644 --- a/include/OtelTracer.h +++ b/include/OtelTracer.h @@ -36,7 +36,7 @@ struct TraceContext { }; /** Returns the process-wide active TraceContext singleton. */ -static inline TraceContext& currentTraceContext() { +inline TraceContext& currentTraceContext() { static TraceContext ctx; return ctx; } @@ -310,10 +310,10 @@ static inline String defaultServiceName() { return String("embedded-service"); #endif } -/** @return Default service instance ID from @c OTEL_SERVICE_INSTANCE_ID, or the chip ID hex. */ +/** @return Default service instance ID from @c OTEL_SERVICE_INSTANCE, or the chip ID hex. */ static inline String defaultServiceInstanceId() { -#ifdef OTEL_SERVICE_INSTANCE_ID - return String(OTEL_SERVICE_INSTANCE_ID); +#ifdef OTEL_SERVICE_INSTANCE + return String(OTEL_SERVICE_INSTANCE); #else return chipIdHex(); #endif @@ -487,7 +487,7 @@ struct TracerConfig { }; /** Returns the process-wide TracerConfig singleton. */ -static inline TracerConfig& tracerConfig() { +inline TracerConfig& tracerConfig() { static TracerConfig cfg; return cfg; } diff --git a/src/OtelSender.cpp b/src/OtelSender.cpp index a79522f..930a515 100644 --- a/src/OtelSender.cpp +++ b/src/OtelSender.cpp @@ -251,9 +251,9 @@ void OTelSender::sendProto(const char* path, const uint8_t* buf, size_t len) { #else // Copy raw bytes into a String to reuse the existing queue/send path. // The String is treated as an opaque byte container, not a text string. - String payload; - payload.reserve(len); - for (size_t i = 0; i < len; ++i) payload += (char)buf[i]; + // Using the length-based constructor avoids the O(n) append loop and + // correctly handles null bytes that are valid in protobuf payloads. + String payload(reinterpret_cast(buf), len); #ifdef ARDUINO_ARCH_RP2040 launchWorkerOnce_(); From 0bda033f4cff4086cc3c11ac2d429d1b63462b65 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Wed, 6 May 2026 01:24:34 -0500 Subject: [PATCH 28/30] Add OTelSender::addHeader() runtime API for backend-specific headers --- include/OtelSender.h | 21 +++++ src/OtelSender.cpp | 210 ++++++++++++++++++++++++++++--------------- 2 files changed, 157 insertions(+), 74 deletions(-) diff --git a/include/OtelSender.h b/include/OtelSender.h index 83162b8..20996da 100644 --- a/include/OtelSender.h +++ b/include/OtelSender.h @@ -152,6 +152,27 @@ class OTelSender { */ static bool queueIsHealthy(); + /** + * Add a custom HTTP header that will be sent on every OTLP request for the + * given signal path. Useful for backend-specific configuration when the + * value is awkward to embed via -DOTEL_EXPORTER_OTLP_*_HEADERS build flag + * (e.g. JSON values containing quotes/commas). + * + * Headers added at runtime are layered on top of any parsed from the build + * flags. Call from setup() or after `Tracer::begin()` — order doesn't matter + * relative to send calls because headers are looked up per request. + * + * @param path OTLP signal path: "/v1/logs", "/v1/metrics", or "/v1/traces". + * @param key HTTP header name. + * @param value HTTP header value (sent verbatim — caller is responsible + * for any required encoding). + * + * Example (Datadog OTLP-to-tag promotion): + * OTelSender::addHeader("/v1/metrics", "dd-otel-metric-config", + * "{\"resource_attributes_as_tags\":true}"); + */ + static void addHeader(const char* path, const String& key, const String& value); + private: // ---------- SPSC ring buffer (core0 producer -> core1 consumer) ---------- static constexpr size_t QCAP = OTEL_QUEUE_CAPACITY; diff --git a/src/OtelSender.cpp b/src/OtelSender.cpp index 930a515..3e18023 100644 --- a/src/OtelSender.cpp +++ b/src/OtelSender.cpp @@ -4,23 +4,23 @@ // --- HTTP + WiFi includes (portable) --- #if defined(ESP8266) - #include - #include - #include +#include +#include +#include #elif defined(ESP32) - #include - #include - #include +#include +#include +#include #elif defined(ARDUINO_ARCH_RP2040) - #include // Earle Philhower core - #include // Arduino HTTPClient - #include +#include // Earle Philhower core +#include // Arduino HTTPClient +#include #else - #error "Unsupported platform: need WiFi + HTTPClient" +#error "Unsupported platform: need WiFi + HTTPClient" #endif #ifdef ARDUINO_ARCH_RP2040 - #include "pico/multicore.h" +#include "pico/multicore.h" #endif // ===== statics ===== @@ -28,51 +28,82 @@ OTelQueuedItem OTelSender::q_[QCAP]; std::atomic OTelSender::head_{0}; std::atomic OTelSender::tail_{0}; std::atomic OTelSender::drops_{0}; -std::atomic OTelSender::worker_started_{false}; +std::atomic OTelSender::worker_started_{false}; // ---------- Header parsing ---------- // Append "key=value,…" pairs from raw into out. Existing entries are preserved // so callers can layer global headers then per-signal headers. -static void appendParsedHeaders_(const char* raw, - std::vector>& out) { - if (!raw || !*raw) return; +static void appendParsedHeaders_(const char *raw, + std::vector> &out) +{ + if (!raw || !*raw) + return; String s(raw); int start = 0; - while (start <= (int)s.length()) { + while (start <= (int)s.length()) + { int comma = s.indexOf(',', start); - if (comma < 0) comma = (int)s.length(); + if (comma < 0) + comma = (int)s.length(); String token = s.substring(start, comma); token.trim(); int eq = token.indexOf('='); - if (eq > 0) { + if (eq > 0) + { String k = token.substring(0, eq); String v = token.substring(eq + 1); - k.trim(); v.trim(); - if (k.length()) out.push_back({k, v}); + k.trim(); + v.trim(); + if (k.length()) + out.push_back({k, v}); } start = comma + 1; } } -// Returns the merged header list for the given OTLP path (/v1/logs etc.). -// Built once on first call and cached for the lifetime of the program. -static const std::vector>& headersForPath_(const char* path) { - static bool init = false; - static std::vector> logH, traceH, metricH; - if (!init) { - init = true; - // Global headers first, then per-signal overrides - appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, logH); - appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, traceH); - appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, metricH); - appendParsedHeaders_(OTEL_EXPORTER_OTLP_LOGS_HEADERS, logH); - appendParsedHeaders_(OTEL_EXPORTER_OTLP_TRACES_HEADERS, traceH); - appendParsedHeaders_(OTEL_EXPORTER_OTLP_METRICS_HEADERS, metricH); +// Per-signal header lists. Initialized lazily from build flags on first +// access, and extended at runtime via OTelSender::addHeader(). +struct SignalHeaders { + std::vector> log, trace, metric; +}; + +static SignalHeaders &mutableHeaders_() +{ + static SignalHeaders s; + static bool initialized = false; + if (!initialized) + { + initialized = true; + // Global headers first, then per-signal overrides from build flags. + appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, s.log); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, s.trace); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_HEADERS, s.metric); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_LOGS_HEADERS, s.log); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_TRACES_HEADERS, s.trace); + appendParsedHeaders_(OTEL_EXPORTER_OTLP_METRICS_HEADERS, s.metric); } - if (path && strcmp(path, "/v1/logs") == 0) return logH; - if (path && strcmp(path, "/v1/traces") == 0) return traceH; - return metricH; + return s; +} + +// Returns the merged header list for the given OTLP path. +static const std::vector> &headersForPath_(const char *path) +{ + auto &s = mutableHeaders_(); + if (path && strcmp(path, "/v1/logs") == 0) + return s.log; + if (path && strcmp(path, "/v1/traces") == 0) + return s.trace; + return s.metric; +} + +// Public API: add a header at runtime to a specific OTLP signal path. +void OTelSender::addHeader(const char *path, const String &key, const String &value) +{ + auto &s = mutableHeaders_(); + if (path && strcmp(path, "/v1/logs") == 0) s.log.push_back({key, value}); + else if (path && strcmp(path, "/v1/traces") == 0) s.trace.push_back({key, value}); + else if (path && strcmp(path, "/v1/metrics") == 0) s.metric.push_back({key, value}); } // ---------- URL resolution ---------- @@ -86,9 +117,11 @@ static const std::vector>& headersForPath_(const char* // → signal path appended automatically // 3. Legacy base URL (OTEL_COLLECTOR_BASE_URL) // → signal path appended automatically -String OTelSender::fullUrl_(const char* path) { +String OTelSender::fullUrl_(const char *path) +{ // 1. Per-signal overrides (used verbatim per spec) - if (path) { + if (path) + { if (strcmp(path, "/v1/logs") == 0 && strlen(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) > 0) return String(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT); if (strcmp(path, "/v1/traces") == 0 && strlen(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) > 0) @@ -99,10 +132,12 @@ String OTelSender::fullUrl_(const char* path) { // 2/3. Base URL with signal path appended String base = strlen(OTEL_EXPORTER_OTLP_ENDPOINT) > 0 - ? String(OTEL_EXPORTER_OTLP_ENDPOINT) - : String(OTEL_COLLECTOR_BASE_URL); - if (base.endsWith("/")) base.remove(base.length() - 1); - if (path && *path == '/') return base + String(path); + ? String(OTEL_EXPORTER_OTLP_ENDPOINT) + : String(OTEL_COLLECTOR_BASE_URL); + if (base.endsWith("/")) + base.remove(base.length() - 1); + if (path && *path == '/') + return base + String(path); return base + "/" + String(path ? path : ""); } @@ -110,28 +145,35 @@ String OTelSender::fullUrl_(const char* path) { // Executes a single POST. Handles plain HTTP and HTTPS transparently based on // the URL scheme. Custom headers are applied after Content-Type. -static void doPost_(const String& url, const String& payload, - const char* path, const char* contentType) { +static void doPost_(const String &url, const String &payload, + const char *path, const char *contentType) +{ HTTPClient http; - const auto& hdrs = headersForPath_(path); + const auto &hdrs = headersForPath_(path); // Lambda keeps the send logic in one place regardless of client type. - auto fire = [&](bool ok) { - if (!ok) return; + auto fire = [&](bool ok) + { + if (!ok) + return; http.addHeader("Content-Type", contentType); - for (const auto& h : hdrs) http.addHeader(h.first, h.second); + for (const auto &h : hdrs) + http.addHeader(h.first, h.second); (void)http.POST(payload); http.end(); }; - if (url.startsWith("https://")) { + if (url.startsWith("https://")) + { // WiFiClientSecure must remain in scope until after http.end() (fire()). WiFiClientSecure sc; #if OTEL_TLS_INSECURE sc.setInsecure(); #endif fire(http.begin(sc, url)); - } else { + } + else + { #if defined(ESP8266) WiFiClient wc; fire(http.begin(wc, url)); @@ -143,29 +185,33 @@ static void doPost_(const String& url, const String& payload, // ---------- Queue (SPSC) ---------- -bool OTelSender::enqueue_(const char* path, const char* contentType, String&& payload) { +bool OTelSender::enqueue_(const char *path, const char *contentType, String &&payload) +{ size_t h = head_.load(std::memory_order_relaxed); size_t t = tail_.load(std::memory_order_acquire); size_t next = (h + 1) % QCAP; - if (next == t) { + if (next == t) + { // Full: drop oldest (advance tail) size_t new_t = (t + 1) % QCAP; tail_.store(new_t, std::memory_order_release); drops_.fetch_add(1, std::memory_order_relaxed); } - q_[h].path = path; + q_[h].path = path; q_[h].contentType = contentType; - q_[h].payload = std::move(payload); + q_[h].payload = std::move(payload); head_.store(next, std::memory_order_release); return true; } -bool OTelSender::dequeue_(OTelQueuedItem& out) { +bool OTelSender::dequeue_(OTelQueuedItem &out) +{ size_t t = tail_.load(std::memory_order_relaxed); size_t h = head_.load(std::memory_order_acquire); - if (t == h) return false; + if (t == h) + return false; out = std::move(q_[t]); q_[t].payload = String(); @@ -176,19 +222,25 @@ bool OTelSender::dequeue_(OTelQueuedItem& out) { // ---------- Worker ---------- -void OTelSender::pumpOnce_() { +void OTelSender::pumpOnce_() +{ OTelQueuedItem it; - if (!dequeue_(it)) return; + if (!dequeue_(it)) + return; #if OTEL_SEND_ENABLE doPost_(fullUrl_(it.path), it.payload, it.path, it.contentType); #endif } -void OTelSender::workerLoop_() { - for (;;) { - for (int i = 0; i < OTEL_WORKER_BURST; ++i) { +void OTelSender::workerLoop_() +{ + for (;;) + { + for (int i = 0; i < OTEL_WORKER_BURST; ++i) + { OTelQueuedItem it; - if (!dequeue_(it)) break; + if (!dequeue_(it)) + break; #if OTEL_SEND_ENABLE doPost_(fullUrl_(it.path), it.payload, it.path, it.contentType); #endif @@ -201,35 +253,42 @@ void OTelSender::workerLoop_() { void otel_worker_entry() { OTelSender::workerLoop_(); } #endif -void OTelSender::launchWorkerOnce_() { +void OTelSender::launchWorkerOnce_() +{ #ifdef ARDUINO_ARCH_RP2040 bool expected = false; - if (worker_started_.compare_exchange_strong(expected, true)) { + if (worker_started_.compare_exchange_strong(expected, true)) + { multicore_launch_core1(otel_worker_entry); } #endif } -void OTelSender::beginAsyncWorker() { +void OTelSender::beginAsyncWorker() +{ launchWorkerOnce_(); } -uint32_t OTelSender::droppedCount() { +uint32_t OTelSender::droppedCount() +{ return drops_.load(std::memory_order_relaxed); } -bool OTelSender::queueIsHealthy() { +bool OTelSender::queueIsHealthy() +{ return worker_started_.load(std::memory_order_relaxed); } // ---------- Public send API ---------- -static constexpr const char* kContentTypeJson = "application/json"; -static constexpr const char* kContentTypeProto = "application/x-protobuf"; +static constexpr const char *kContentTypeJson = "application/json"; +static constexpr const char *kContentTypeProto = "application/x-protobuf"; -void OTelSender::sendJson(const char* path, JsonDocument& doc) { +void OTelSender::sendJson(const char *path, JsonDocument &doc) +{ #if !OTEL_SEND_ENABLE - (void)path; (void)doc; + (void)path; + (void)doc; return; #else String payload; @@ -244,9 +303,12 @@ void OTelSender::sendJson(const char* path, JsonDocument& doc) { #endif } -void OTelSender::sendProto(const char* path, const uint8_t* buf, size_t len) { +void OTelSender::sendProto(const char *path, const uint8_t *buf, size_t len) +{ #if !OTEL_SEND_ENABLE - (void)path; (void)buf; (void)len; + (void)path; + (void)buf; + (void)len; return; #else // Copy raw bytes into a String to reuse the existing queue/send path. From 6c8f9ab8042f869b80c200bc7a1a1f0f93f79634 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Sat, 9 May 2026 22:54:48 -0500 Subject: [PATCH 29/30] fix(sender): address CodeRabbit findings on OtelSender.cpp - Race in addHeader() vs RP2040 worker: addHeader() pushes to per-signal std::vectors that doPost_() iterates from core 1. A concurrent push_back() could reallocate the vector mid-iteration, which is UB. Freeze the headers (set an atomic flag) at the first send so the worker reads a stable list, and reject later addHeader() calls. Documented the new contract: call addHeader() before the first send. - Documented TLS CA cert path was a no-op: the header promised "OTEL_TLS_INSECURE=0 + OTEL_TLS_CA_CERT enables certificate validation", but the implementation only ever called setInsecure() and never read OTEL_TLS_CA_CERT. Now: when OTEL_TLS_INSECURE=0, call setCACert(); if OTEL_TLS_CA_CERT is undefined in that case, fail at compile time with a clear #error rather than silently constructing a client with no trust anchors. - queueIsHealthy() returned false on synchronous platforms because worker_started_ is only set on RP2040. Header docs say "always true on synchronous platforms"; implementation now matches. Co-Authored-By: Claude Sonnet 4.6 --- include/OtelSender.h | 6 ++++-- src/OtelSender.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/include/OtelSender.h b/include/OtelSender.h index 20996da..b0a5ce1 100644 --- a/include/OtelSender.h +++ b/include/OtelSender.h @@ -159,8 +159,10 @@ class OTelSender { * (e.g. JSON values containing quotes/commas). * * Headers added at runtime are layered on top of any parsed from the build - * flags. Call from setup() or after `Tracer::begin()` — order doesn't matter - * relative to send calls because headers are looked up per request. + * flags. Call from setup() or after `Tracer::begin()` — must be called + * before the first send. Once the first send happens the header lists are + * frozen so the RP2040 worker on core 1 can read them without locking; + * later addHeader() calls are silently dropped. * * @param path OTLP signal path: "/v1/logs", "/v1/metrics", or "/v1/traces". * @param key HTTP header name. diff --git a/src/OtelSender.cpp b/src/OtelSender.cpp index 3e18023..da04384 100644 --- a/src/OtelSender.cpp +++ b/src/OtelSender.cpp @@ -68,6 +68,11 @@ struct SignalHeaders { std::vector> log, trace, metric; }; +// Once a send happens, headers are read by the RP2040 worker on core 1. +// We freeze the SignalHeaders vectors at that point so further addHeader() +// calls from core 0 cannot reallocate them mid-iteration (UB). +static std::atomic headers_frozen_{false}; + static SignalHeaders &mutableHeaders_() { static SignalHeaders s; @@ -86,6 +91,18 @@ static SignalHeaders &mutableHeaders_() return s; } +// Touch the headers on the current core to force lazy init, then freeze. +// After this, the RP2040 worker on core 1 only ever reads the vectors so +// no synchronisation is required for the read path in doPost_(). +static void freezeHeaders_() +{ + if (!headers_frozen_.load(std::memory_order_acquire)) + { + (void)mutableHeaders_(); + headers_frozen_.store(true, std::memory_order_release); + } +} + // Returns the merged header list for the given OTLP path. static const std::vector> &headersForPath_(const char *path) { @@ -98,8 +115,11 @@ static const std::vector> &headersForPath_(const char } // Public API: add a header at runtime to a specific OTLP signal path. +// Must be called before the first send; once headers are frozen the call +// is rejected to avoid racing with the RP2040 worker reading the vectors. void OTelSender::addHeader(const char *path, const String &key, const String &value) { + if (headers_frozen_.load(std::memory_order_acquire)) return; auto &s = mutableHeaders_(); if (path && strcmp(path, "/v1/logs") == 0) s.log.push_back({key, value}); else if (path && strcmp(path, "/v1/traces") == 0) s.trace.push_back({key, value}); @@ -169,6 +189,13 @@ static void doPost_(const String &url, const String &payload, WiFiClientSecure sc; #if OTEL_TLS_INSECURE sc.setInsecure(); +#elif defined(OTEL_TLS_CA_CERT) + // Validated TLS: caller must -DOTEL_TLS_CA_CERT="..." with a PEM-encoded + // root CA. Without this, setting OTEL_TLS_INSECURE=0 leaves the client + // with no trust anchors and the handshake will fail at runtime. + sc.setCACert(OTEL_TLS_CA_CERT); +#else +#error "OTEL_TLS_INSECURE=0 requires -DOTEL_TLS_CA_CERT=\"...PEM...\" to be defined." #endif fire(http.begin(sc, url)); } @@ -276,7 +303,13 @@ uint32_t OTelSender::droppedCount() bool OTelSender::queueIsHealthy() { +#ifdef ARDUINO_ARCH_RP2040 return worker_started_.load(std::memory_order_relaxed); +#else + // No async worker on synchronous platforms — sends happen inline, so + // there is no queue health to report. Always healthy by definition. + return true; +#endif } // ---------- Public send API ---------- @@ -291,6 +324,7 @@ void OTelSender::sendJson(const char *path, JsonDocument &doc) (void)doc; return; #else + freezeHeaders_(); String payload; serializeJson(doc, payload); @@ -311,6 +345,7 @@ void OTelSender::sendProto(const char *path, const uint8_t *buf, size_t len) (void)len; return; #else + freezeHeaders_(); // Copy raw bytes into a String to reuse the existing queue/send path. // The String is treated as an opaque byte container, not a text string. // Using the length-based constructor avoids the O(n) append loop and From 202ec213eec766cda526b606a95c576a4a077ff9 Mon Sep 17 00:00:00 2001 From: Kyle Taylor Date: Sat, 9 May 2026 23:19:08 -0500 Subject: [PATCH 30/30] fix(sender): use binary buffer for queue payloads + bounds-check tunables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review 4258823480. - Storing protobuf bodies in an Arduino String and passing them to HTTPClient::POST(String) relies on the platform's HTTPClient using String::length() rather than strlen() internally. ESP32, ESP8266, and Arduino-Pico happen to do the right thing, but the type signature says "text" while the contents are arbitrary bytes including embedded NULs. Switch OTelQueuedItem::payload to std::vector and use HTTPClient::POST(uint8_t*, size_t) directly so the binary contract is enforced at the type system level. JSON callers serialise to a temporary String and copy the bytes into the vector (JSON is null-byte-free, so the copy is safe and the cost is one allocation per send). Free the queue slot's allocation on dequeue (swap with empty vector) rather than holding it until the next overwrite, so a long idle period doesn't pin RAM proportional to the largest payload ever sent. Updated fake_sender.cpp's enqueue_ stub signature to match. - Add compile-time bounds checks for OTEL_PROTO_BUFFER_SIZE (must be > 0) and OTEL_QUEUE_CAPACITY (must be >= 2 — one slot is unusable in an SPSC ring buffer). Misconfiguration now fails fast with a clear #error. Co-Authored-By: Claude Sonnet 4.6 --- include/OtelSender.h | 23 ++++++++++++++++++----- src/OtelSender.cpp | 33 ++++++++++++++++++++++----------- test/test_otlp/fake_sender.cpp | 2 +- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/include/OtelSender.h b/include/OtelSender.h index b0a5ce1..0ef9224 100644 --- a/include/OtelSender.h +++ b/include/OtelSender.h @@ -2,6 +2,8 @@ #include #include #include +#include +#include // Optional compile-time on/off switch for all network sends. // You can set -DOTEL_SEND_ENABLE=0 in platformio.ini for latency tests. #ifndef OTEL_SEND_ENABLE @@ -94,18 +96,29 @@ #ifndef OTEL_PROTO_BUFFER_SIZE #define OTEL_PROTO_BUFFER_SIZE 1024 #endif +#if OTEL_PROTO_BUFFER_SIZE <= 0 +#error "OTEL_PROTO_BUFFER_SIZE must be > 0" +#endif // Internal queue capacity for async sender on RP2040. // Keep small to bound RAM; increase if you see drops. #ifndef OTEL_QUEUE_CAPACITY #define OTEL_QUEUE_CAPACITY 16 #endif +#if OTEL_QUEUE_CAPACITY < 2 +#error "OTEL_QUEUE_CAPACITY must be >= 2 (one slot is unusable in an SPSC ring buffer)" +#endif -/** Internal queue item for the RP2040 SPSC ring buffer (core-0 → core-1). */ +/** + * Internal queue item for the RP2040 SPSC ring buffer (core-0 → core-1). + * The payload is stored as a raw byte buffer so that protobuf bodies (which + * may contain embedded null bytes) round-trip through the queue and the + * binary HTTPClient::POST(uint8_t*, size_t) overload without truncation. + */ struct OTelQueuedItem { - const char* path; // "/v1/logs", "/v1/traces", "/v1/metrics" - const char* contentType; // "application/json" or "application/x-protobuf" - String payload; // serialized body (JSON text or raw protobuf bytes) + const char* path; // "/v1/logs", "/v1/traces", "/v1/metrics" + const char* contentType; // "application/json" or "application/x-protobuf" + std::vector payload; // serialized body (JSON text or raw protobuf bytes) }; /** @@ -184,7 +197,7 @@ class OTelSender { static std::atomic drops_; static std::atomic worker_started_; - static bool enqueue_(const char* path, const char* contentType, String&& payload); + static bool enqueue_(const char* path, const char* contentType, std::vector&& payload); static bool dequeue_(OTelQueuedItem& out); // ---------- Worker ---------- diff --git a/src/OtelSender.cpp b/src/OtelSender.cpp index da04384..8e37b98 100644 --- a/src/OtelSender.cpp +++ b/src/OtelSender.cpp @@ -165,7 +165,7 @@ String OTelSender::fullUrl_(const char *path) // Executes a single POST. Handles plain HTTP and HTTPS transparently based on // the URL scheme. Custom headers are applied after Content-Type. -static void doPost_(const String &url, const String &payload, +static void doPost_(const String &url, const std::vector &payload, const char *path, const char *contentType) { HTTPClient http; @@ -179,7 +179,10 @@ static void doPost_(const String &url, const String &payload, http.addHeader("Content-Type", contentType); for (const auto &h : hdrs) http.addHeader(h.first, h.second); - (void)http.POST(payload); + // Use the binary-safe (uint8_t*, size_t) overload so embedded null bytes + // in protobuf bodies are preserved. The String-based overload on some + // HTTPClient implementations would truncate at the first null byte. + (void)http.POST(const_cast(payload.data()), payload.size()); http.end(); }; @@ -212,7 +215,7 @@ static void doPost_(const String &url, const String &payload, // ---------- Queue (SPSC) ---------- -bool OTelSender::enqueue_(const char *path, const char *contentType, String &&payload) +bool OTelSender::enqueue_(const char *path, const char *contentType, std::vector &&payload) { size_t h = head_.load(std::memory_order_relaxed); size_t t = tail_.load(std::memory_order_acquire); @@ -241,7 +244,10 @@ bool OTelSender::dequeue_(OTelQueuedItem &out) return false; out = std::move(q_[t]); - q_[t].payload = String(); + // Free the slot's allocation now rather than waiting for the next + // overwrite, so a long idle period doesn't pin RAM proportional to + // the largest payload ever sent. + std::vector().swap(q_[t].payload); size_t next = (t + 1) % QCAP; tail_.store(next, std::memory_order_release); return true; @@ -325,8 +331,14 @@ void OTelSender::sendJson(const char *path, JsonDocument &doc) return; #else freezeHeaders_(); - String payload; - serializeJson(doc, payload); + // Serialise JSON to a temporary String, then copy bytes into a binary + // buffer for the queue/send path. JSON is UTF-8 text without embedded + // null bytes, so the copy is safe; we use a vector here for symmetry + // with the protobuf path and to share the binary-safe POST overload. + String text; + serializeJson(doc, text); + const uint8_t *begin = reinterpret_cast(text.c_str()); + std::vector payload(begin, begin + text.length()); #ifdef ARDUINO_ARCH_RP2040 launchWorkerOnce_(); @@ -346,11 +358,10 @@ void OTelSender::sendProto(const char *path, const uint8_t *buf, size_t len) return; #else freezeHeaders_(); - // Copy raw bytes into a String to reuse the existing queue/send path. - // The String is treated as an opaque byte container, not a text string. - // Using the length-based constructor avoids the O(n) append loop and - // correctly handles null bytes that are valid in protobuf payloads. - String payload(reinterpret_cast(buf), len); + // Binary buffer round-trips embedded null bytes in protobuf payloads + // through the queue and the HTTPClient::POST(uint8_t*, size_t) overload + // without truncation. + std::vector payload(buf, buf + len); #ifdef ARDUINO_ARCH_RP2040 launchWorkerOnce_(); diff --git a/test/test_otlp/fake_sender.cpp b/test/test_otlp/fake_sender.cpp index e8a1ff4..82fd344 100644 --- a/test/test_otlp/fake_sender.cpp +++ b/test/test_otlp/fake_sender.cpp @@ -42,7 +42,7 @@ uint32_t OTelSender::droppedCount() { return 0; } bool OTelSender::queueIsHealthy() { return true; } // ── Private helper stubs ────────────────────────────────────────────────────── -bool OTelSender::enqueue_(const char*, const char*, String&&) { return true; } +bool OTelSender::enqueue_(const char*, const char*, std::vector&&) { return true; } bool OTelSender::dequeue_(OTelQueuedItem&) { return false; } void OTelSender::pumpOnce_() {} void OTelSender::workerLoop_() {}