diff --git a/sdk/include/opentelemetry/sdk/configuration/ryml_document.h b/sdk/include/opentelemetry/sdk/configuration/ryml_document.h index 9d464e63be..7f22660902 100644 --- a/sdk/include/opentelemetry/sdk/configuration/ryml_document.h +++ b/sdk/include/opentelemetry/sdk/configuration/ryml_document.h @@ -22,7 +22,7 @@ class RymlDocument : public Document public: static std::unique_ptr Parse(const std::string &source, const std::string &content); - RymlDocument() {} + RymlDocument() : event_handler_(MakeCallbacks()) {} RymlDocument(RymlDocument &&) = delete; RymlDocument(const RymlDocument &) = delete; RymlDocument &operator=(RymlDocument &&) = delete; @@ -36,6 +36,12 @@ class RymlDocument : public Document DocumentNodeLocation Location(ryml::ConstNodeRef node) const; private: + static ryml::Callbacks MakeCallbacks(); + [[noreturn]] static void OnError(const char *msg, + size_t msg_len, + ryml::Location location, + void *user_data); + ryml::ParserOptions opts_; ryml::Parser::handler_type event_handler_; std::unique_ptr parser_; diff --git a/sdk/src/configuration/ryml_document.cc b/sdk/src/configuration/ryml_document.cc index 83a067455e..ac0e051c2e 100644 --- a/sdk/src/configuration/ryml_document.cc +++ b/sdk/src/configuration/ryml_document.cc @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#include #include #include #include @@ -11,6 +12,7 @@ #include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/configuration/document.h" #include "opentelemetry/sdk/configuration/document_node.h" +#include "opentelemetry/sdk/configuration/invalid_schema_exception.h" #include "opentelemetry/sdk/configuration/ryml_document.h" #include "opentelemetry/sdk/configuration/ryml_document_node.h" #include "opentelemetry/version.h" @@ -21,6 +23,30 @@ namespace sdk namespace configuration { +// Custom ryml error callback that throws instead of calling abort(). +// This ensures the try-catch in ParseDocument works regardless of how +// ryml was compiled (with or without RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS). +void RymlDocument::OnError(const char *msg, + size_t msg_len, + ryml::Location location, + void * /*user_data*/) +{ + DocumentNodeLocation loc; + loc.offset = location.offset; + loc.line = location.line; + loc.col = location.col; + loc.filename = std::string(location.name.str, location.name.len); + + throw InvalidSchemaException(loc, std::string(msg, msg_len)); +} + +ryml::Callbacks RymlDocument::MakeCallbacks() +{ + ryml::Callbacks cb = ryml::get_callbacks(); + cb.m_error = &RymlDocument::OnError; + return cb; +} + std::unique_ptr RymlDocument::Parse(const std::string &source, const std::string &content) { auto doc = std::make_unique(); diff --git a/sdk/test/configuration/yaml_test.cc b/sdk/test/configuration/yaml_test.cc index 9b8f2212f9..180dbe1b6e 100644 --- a/sdk/test/configuration/yaml_test.cc +++ b/sdk/test/configuration/yaml_test.cc @@ -717,3 +717,26 @@ file_format: "1.0" auto config = DoParse(yaml); ASSERT_EQ(config, nullptr); } + +TEST(Yaml, malformed_yaml) +{ + // "headers" is indented under "client_certificate" instead of "otlp_http" + std::string yaml = R"( +file_format: "1.0" +tracer_provider: + processors: + - simple: + exporter: + otlp_http: + endpoint: http://localhost:4318/v1/traces + certificate: /app/cert.pem + client_key: /app/cert.pem + client_certificate: /app/cert.pem + headers: + - name: header_name + value: header_value +)"; + + auto config = DoParse(yaml); + ASSERT_EQ(config, nullptr); +}