Conversation
The OTLP HTTP and file clients each carried their own copy of the same reflection-based protobuf-to-JSON converter. Replace both with a single converter that emits through a JsonWriter interface, and let callers supply their own writer through a JsonWriterFactory. The emitted JSON is unchanged. The factory is injected the way each family already injects thread_instrumentation: as a field on OtlpHttpClientOptions for the HTTP exporters, and on OtlpFileClientRuntimeOptions for the file ones. No existing constructor or factory signature changes, and a null factory selects the default backend.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4556 +/- ##
==========================================
- Coverage 86.50% 86.38% -0.12%
==========================================
Files 525 530 +5
Lines 20465 20135 -330
==========================================
- Hits 17701 17391 -310
+ Misses 2764 2744 -20
🚀 New features to boost your workflow:
|
…ction Advances third_party/opentelemetry-cpp to ltowarek/opentelemetry-cpp@2fb8632c, otlp-json-no-protobuf rebased onto the current json-writer-seam (open-telemetry/opentelemetry-cpp#4556). The previous pin, 4a8e7bf4, is no longer on any fork branch. PR1 now takes the JsonWriterFactory as a json_writer_factory field on the exporters' runtime options instead of constructor overloads, so the JTAG file exporters and the protobuf-encoding OTLP/HTTP exporters set it there through CjsonRuntimeOptions<T>(). The OTLP/JSON exporters still take both factories as constructor arguments and are unchanged. Not yet built with ESP-IDF. Part of #102 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015FvAjrPuAUXJLv8ZpUxh96
|
|
||
| std::string post_body_json = | ||
| json_request.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); | ||
| std::string post_body_json = json_writer->ToString(); |
There was a problem hiding this comment.
Could we check ok() after ToString() too? That call can fail, and we currently continue with an empty body. The file client needs the same check. Also better to have a test with a writer that fails during ToString() for both.
There was a problem hiding this comment.
Done in 1a9780e: both clients now fail the export when ok() is false after ToString() or when Create() returns nullptr, with tests for each case in both clients.
| : is_shutdown_(false), | ||
| options_(std::move(options)), | ||
| http_client_(std::move(http_client)), | ||
| json_writer_factory_(options_.json_writer_factory ? options_.json_writer_factory |
There was a problem hiding this comment.
Could we resolve the default JSON writer only when the content type is JSON? With nlohmann disabled and no custom writer, this currently calls std::terminate() even for binary HTTP export. Please also cover binary export without a JSON backend in a test.
There was a problem hiding this comment.
Done in fc0f530: the default factory is resolved only for kJson content, and BinaryExportDoesNotNeedAJsonWriterFactory covers binary export.
| opentelemetry_add_pkgconfig( | ||
| exporter_otlp_json_converter | ||
| "OpenTelemetry OTLP - JSON Converter" | ||
| "Shared reflection-based protobuf-to-JSON converter for OTLP/JSON exporters." | ||
| "opentelemetry_sdk opentelemetry_common opentelemetry_exporter_otlp_json_writer" |
There was a problem hiding this comment.
Could we update the full pkg-config dependency chain for these new libraries? The HTTP/file client packages do not include the converter, the writer package does not include the enabled nlohmann backend, and opentelemetry_sdk has no .pc file. An installed static consumer should be able to link using pkg-config with the backend enabled or disabled.
There was a problem hiding this comment.
Done in 803fcce: the client packages now require the writer and converter, the writer requires the nlohmann backend when enabled, the converter requires opentelemetry_proto instead of opentelemetry_sdk, and ci/verify_packages.sh checks the new packages.
| if (nlohmann::json *slot = ClaimSlot()) | ||
| { | ||
| *slot = opentelemetry::sdk::common::Base64Escape( | ||
| std::string(reinterpret_cast<const char *>(data), size)); |
There was a problem hiding this comment.
Could we pass a nostd::string_view directly to Base64Escape? It already accepts one, so creating a string here adds a full copy of every bytes value before encoding it.
| Fail("Key() called twice without an intervening value"); | ||
| return; | ||
| } | ||
| pending_key_ = std::string(key.data(), key.size()); |
There was a problem hiding this comment.
Could we use pending_key_.assign(key.data(), key.size()) here? That lets us reuse the existing capacity instead of constructing another string for every key. Longer field names can otherwise allocate on each call.
| virtual void BeginArray() noexcept = 0; | ||
| virtual void EndArray() noexcept = 0; | ||
|
|
||
| virtual void Key(nostd::string_view key) noexcept = 0; |
There was a problem hiding this comment.
Could we document the expectations for custom writers here? In particular, string views must be consumed or copied before the call returns, WriteBytes produces base64, and callers need to know how ok() and ToString() behave after a failure.
The factory should also say whether Create() can return null or throw, and whether calls may happen concurrently.
|
Thanks for the PR. The approach looks find at initial glance. Could we add a benchmark comparing JSON serialization before and after this change? It would help check whether the new writer adds any overhead for users keeping the default nlohmann backend. Ideally, measure serialization without HTTP or file I/O. |
Added in f35605b (serialization only, no I/O); after the optimizations in b9530db, 94b2f64 and ff92b5b, the PR is on par with main for 1 span and faster for larger batches (median of 8 interleaved runs, -O3, i5-4590):
|
The OTLP HTTP and file clients each carried their own copy of the same reflection-based protobuf-to-JSON converter. Replace both with a single converter that emits through a JsonWriter interface, and let callers supply their own writer through a JsonWriterFactory, as they already can for the HTTP client. The emitted JSON is unchanged.
Fixes #2541
Changes
The factory is injected as a
json_writer_factoryfield on the existingruntime options, rather than as new constructor and factory overloads - those
would have added roughly 50 parallel entry points across the exporters for one
injectable. No existing signature changes; a null factory selects the default
nlohmann backend.
OTELCPP_WITH_JSON_WRITER_NLOHMANNcontrols whether that backend is compiled.OTELCPP_WITH_OTLP_HTTPandOTELCPP_WITH_OTLP_FILEno longer forcenlohmann-json on, so a consumer supplying their own writer can drop it from the
build entirely.
Golden-body tests pin the complete request body for traces, metrics and logs on
both exporters, so the unchanged-output claim rests on the emitted bytes.
For significant contributions please make sure you have completed the following items:
CHANGELOG.mdupdated for non-trivial changes