Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions include/OtelDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,33 @@ inline OTelResourceConfig& defaultResource() {
return rc;
}

/**
* Populate an OTLP resource attributes array by merging runtime defaultResource()
* values with compile-time fallbacks. Runtime values always win: if a key is
* set via defaultResource().set(), the fallback for that key is suppressed.
*/
inline void buildResourceAttributes(JsonArray& attrs,
const String& fallbackServiceName,
const String& fallbackInstanceId,
const String& fallbackHostName)
{
static const String kServiceName("service.name");
static const String kServiceInstanceId("service.instance.id");
static const String kHostName("host.name");

const auto& res = defaultResource();

if (res.attrs.find(kServiceName) == res.attrs.end())
serializeKeyValue(attrs, kServiceName, fallbackServiceName);
if (res.attrs.find(kServiceInstanceId) == res.attrs.end())
serializeKeyValue(attrs, kServiceInstanceId, fallbackInstanceId);
if (res.attrs.find(kHostName) == res.attrs.end())
serializeKeyValue(attrs, kHostName, fallbackHostName);

for (const auto& p : res.attrs)
serializeKeyValue(attrs, p.first, p.second);
}
Comment thread
proffalken marked this conversation as resolved.

} // namespace OTel

#endif // OTEL_DEFAULTS_H
6 changes: 2 additions & 4 deletions include/OtelLogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <ArduinoJson.h>
#include "OtelDefaults.h" // expects: nowUnixNano()
#include "OtelSender.h" // expects: OTelSender::sendJson(path, doc)
#include "OtelTracer.h" // provides: currentTraceContext(), u64ToStr(), defaults & addResAttr helpers
#include "OtelTracer.h" // provides: currentTraceContext(), u64ToStr(), buildResourceAttributes(), defaults

namespace OTel {

Expand Down Expand Up @@ -134,9 +134,7 @@ class Logger {
// Resource (with attributes to ensure service.name lands)
JsonObject resource = rl["resource"].to<JsonObject>();
JsonArray rattrs = resource["attributes"].to<JsonArray>();
addResAttr(rattrs, "service.name", defaultServiceName());
addResAttr(rattrs, "service.instance.id", defaultServiceInstanceId());
addResAttr(rattrs, "host.name", defaultHostName());
buildResourceAttributes(rattrs, defaultServiceName(), defaultServiceInstanceId(), defaultHostName());

// Scope
JsonObject sl = rl["scopeLogs"].to<JsonArray>().add<JsonObject>();
Expand Down
2 changes: 1 addition & 1 deletion include/OtelMetrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <ArduinoJson.h>
#include "OtelDefaults.h" // expects: nowUnixNano()
#include "OtelSender.h" // expects: OTelSender::sendJson(path, doc)
#include "OtelTracer.h" // reuses: u64ToStr(), defaultServiceName(), defaultServiceInstanceId(), defaultHostName(), addResAttr()
#include "OtelTracer.h" // reuses: u64ToStr(), defaultServiceName(), defaultServiceInstanceId(), defaultHostName()

namespace OTel {

Expand Down
83 changes: 72 additions & 11 deletions include/OtelTracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,13 +473,30 @@ static inline String generateSpanId() {



/** Append a string-valued OTLP KeyValue object to a resource attributes array. */
/** @deprecated Use buildResourceAttributes() instead. */
[[deprecated("Use buildResourceAttributes() instead")]]
static inline void addResAttr(JsonArray& arr, const char* key, const String& value) {
JsonObject a = arr.add<JsonObject>();
a["key"] = key;
a["value"].to<JsonObject>()["stringValue"] = value;
}

/** OTLP SpanKind enum values. */
namespace SpanKind {
constexpr int INTERNAL = 1;
constexpr int SERVER = 2;
constexpr int CLIENT = 3;
constexpr int PRODUCER = 4;
constexpr int CONSUMER = 5;
}

/** OTLP StatusCode enum values. */
namespace StatusCode {
constexpr int UNSET = 0;
constexpr int OK = 1;
constexpr int ERROR = 2;
}

/** Instrumentation scope name and version emitted on every trace payload. */
struct TracerConfig {
String scopeName{"otel-embedded"};
Expand Down Expand Up @@ -535,6 +552,9 @@ class Span {
prevSpanId_(std::move(o.prevSpanId_)),
attrs_(std::move(o.attrs_)),
events_(std::move(o.events_)),
kind_(o.kind_),
statusCode_(o.statusCode_),
statusMessage_(std::move(o.statusMessage_)),
ended_(o.ended_)
{
o.ended_ = true; // source dtor becomes a no-op
Expand All @@ -551,16 +571,49 @@ class Span {
startNs_ = o.startNs_;
prevTraceId_ = std::move(o.prevTraceId_);
prevSpanId_ = std::move(o.prevSpanId_);
attrs_ = std::move(o.attrs_);
events_ = std::move(o.events_);
ended_ = o.ended_;
o.ended_ = true; // source won't end() again
attrs_ = std::move(o.attrs_);
events_ = std::move(o.events_);
kind_ = o.kind_;
statusCode_ = o.statusCode_;
statusMessage_ = std::move(o.statusMessage_);
Comment thread
proffalken marked this conversation as resolved.
ended_ = o.ended_;
o.ended_ = true; // source won't end() again
o.prevTraceId_ = "";
o.prevSpanId_ = "";
}
return *this;
}

/** Set the OTLP SpanKind. Use the SpanKind:: constants. */
Span& setKind(int kind) {
if (kind >= SpanKind::INTERNAL && kind <= SpanKind::CONSUMER) {
kind_ = kind;
} else {
DBG_PRINT("[otel] WARNING: invalid span kind "); DBG_PRINT(kind);
DBG_PRINT(", keeping current ("); DBG_PRINT(kind_); DBG_PRINTLN(")");
}
return *this;
}

/** Set span status explicitly. Use the StatusCode:: constants. */
Span& setStatus(int code, const String& message = "") {
if (code >= StatusCode::UNSET && code <= StatusCode::ERROR) {
statusCode_ = code;
statusMessage_ = message;
} else {
DBG_PRINT("[otel] WARNING: invalid status code "); DBG_PRINT(code);
DBG_PRINTLN(", defaulting to UNSET");
statusCode_ = StatusCode::UNSET;
}
return *this;
}

/** Shorthand for setStatus(StatusCode::ERROR, message). */
Span& setError(const String& message = "") { return setStatus(StatusCode::ERROR, message); }

/** Shorthand for setStatus(StatusCode::OK). */
Span& setOk() { return setStatus(StatusCode::OK); }

/** @{ 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});
Expand Down Expand Up @@ -680,9 +733,7 @@ class Span {

// resourceSpans[0].resource.attributes[...]
JsonArray rattrs = doc["resourceSpans"][0]["resource"]["attributes"].to<JsonArray>();
addResAttr(rattrs, "service.name", defaultServiceName());
addResAttr(rattrs, "service.instance.id", defaultServiceInstanceId());
addResAttr(rattrs, "host.name", defaultHostName());
buildResourceAttributes(rattrs, defaultServiceName(), defaultServiceInstanceId(), defaultHostName());

// instrumentation scope
JsonObject scope = doc["resourceSpans"][0]["scopeSpans"][0]["scope"].to<JsonObject>();
Expand All @@ -694,7 +745,7 @@ class Span {
s["traceId"] = traceId_;
s["spanId"] = spanId_;
s["name"] = name_;
s["kind"] = 2; // SERVER by default; adjust if you have a setter
s["kind"] = kind_;
s["startTimeUnixNano"] = u64ToStr(startNs_);
s["endTimeUnixNano"] = u64ToStr(endNs);

Expand Down Expand Up @@ -744,6 +795,14 @@ class Span {
}
}

// Span status — only serialise if explicitly set (UNSET is the default)
if (statusCode_ != StatusCode::UNSET) {
JsonObject status = s["status"].to<JsonObject>();
status["code"] = statusCode_;
if (statusCode_ == StatusCode::ERROR && statusMessage_.length() > 0)
status["message"] = statusMessage_;
}
Comment thread
proffalken marked this conversation as resolved.

// Send
OTelSender::sendJson("/v1/traces", doc);
#endif // OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF
Expand Down Expand Up @@ -798,11 +857,13 @@ class Span {
String prevTraceId_;
String prevSpanId_;

// NEW: buffers
std::vector<Attr> attrs_;
std::vector<Event> events_;

// RAII guard
int kind_ = SpanKind::SERVER;
int statusCode_ = StatusCode::UNSET;
String statusMessage_;

bool ended_ = false;
};

Expand Down
11 changes: 2 additions & 9 deletions src/OtelMetrics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,10 @@ static void addPointAttributes(JsonArray& attrArray,
}
}

/** Populate the OTLP resource object from defaultResource() or compile-time defaults. */
/** Populate the OTLP resource object, merging runtime and compile-time defaults. */
static void addCommonResource(JsonObject& resource) {
auto &res = OTel::defaultResource();
if (!res.empty()) {
res.addResourceAttributes(resource);
return;
}
JsonArray rattrs = resource["attributes"].to<JsonArray>();
addResAttr(rattrs, "service.name", defaultServiceName());
addResAttr(rattrs, "service.instance.id", defaultServiceInstanceId());
addResAttr(rattrs, "host.name", defaultHostName());
buildResourceAttributes(rattrs, defaultServiceName(), defaultServiceInstanceId(), defaultHostName());
}

/** Write the instrumentation scope name and version into @p scope. */
Expand Down