diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a9c1da79..78617adaab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Increment the: ## [Unreleased] +* [CONFIGURATION] Add YAML parsing support for Composable Samplers + [#3914](https://github.com/open-telemetry/opentelemetry-cpp/issues/3914) * [SDK] Fix PeriodicExportingMetricReader shutdown race on destruction [#4008](https://github.com/open-telemetry/opentelemetry-cpp/pull/4008) diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h new file mode 100644 index 0000000000..ed26b45ff2 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// 1. Include our new base class +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ + +// 2. Inherit from ComposableSamplerConfiguration +class ComposableAlwaysOffSamplerConfiguration : public ComposableSamplerConfiguration +{ +public: + ComposableAlwaysOffSamplerConfiguration() = default; + void Accept(SamplerConfigurationVisitor *visitor) const override; +}; + +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h new file mode 100644 index 0000000000..d2ecab0d21 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// 1. Include our new base class +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ + +// 2. Inherit from ComposableSamplerConfiguration +class ComposableAlwaysOnSamplerConfiguration : public ComposableSamplerConfiguration +{ +public: + ComposableAlwaysOnSamplerConfiguration() = default; + void Accept(SamplerConfigurationVisitor *visitor) const override; +}; + +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h new file mode 100644 index 0000000000..ba967ffc61 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h @@ -0,0 +1,33 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +// Include the new base class +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ + +// Inherit from ComposableSamplerConfiguration +class ComposableParentThresholdSamplerConfiguration : public ComposableSamplerConfiguration +{ +public: + ComposableParentThresholdSamplerConfiguration() = default; + + // Enforce strong typing for the root pointer + std::unique_ptr root; + + void Accept(SamplerConfigurationVisitor *visitor) const override; +}; + +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h new file mode 100644 index 0000000000..5085b99bc5 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// 1. Include our new base class +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ + +// 2. Inherit from ComposableSamplerConfiguration +class ComposableProbabilitySamplerConfiguration : public ComposableSamplerConfiguration +{ +public: + ComposableProbabilitySamplerConfiguration() = default; + double ratio{1.0}; + void Accept(SamplerConfigurationVisitor *visitor) const override; +}; + +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h new file mode 100644 index 0000000000..e54a14cb9c --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h @@ -0,0 +1,32 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_configuration.h" +// 1. Include our new base class +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ + +// 2. Inherit from ComposableSamplerConfiguration +class ComposableRuleBasedSamplerConfiguration : public ComposableSamplerConfiguration +{ +public: + ComposableRuleBasedSamplerConfiguration() = default; + std::vector> rules; + void Accept(SamplerConfigurationVisitor *visitor) const override; +}; + +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_patterns_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_patterns_configuration.h new file mode 100644 index 0000000000..827148cd5f --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_patterns_configuration.h @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include +#include "opentelemetry/version.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +class ComposableRuleBasedSamplerRuleAttributePatternsConfiguration +{ +public: + ComposableRuleBasedSamplerRuleAttributePatternsConfiguration() = default; + std::string key; + std::vector included; + std::vector excluded; +}; +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_values_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_values_configuration.h new file mode 100644 index 0000000000..1c194e7541 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_values_configuration.h @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include +#include "opentelemetry/version.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +class ComposableRuleBasedSamplerRuleAttributeValuesConfiguration +{ +public: + ComposableRuleBasedSamplerRuleAttributeValuesConfiguration() = default; + std::string key; + std::vector values; +}; +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_configuration.h new file mode 100644 index 0000000000..da58a10c1d --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_configuration.h @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_patterns_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_values_configuration.h" +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ + +class ComposableRuleBasedSamplerRuleConfiguration +{ +public: + ComposableRuleBasedSamplerRuleConfiguration() = default; + + std::unique_ptr attribute_values; + std::unique_ptr attribute_patterns; + + bool match_parent_none{false}; + bool match_parent_remote{false}; + bool match_parent_local{false}; + + bool match_span_kind_internal{false}; + bool match_span_kind_server{false}; + bool match_span_kind_client{false}; + bool match_span_kind_producer{false}; + bool match_span_kind_consumer{false}; + + std::unique_ptr sampler; +}; + +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_sampler_configuration.h new file mode 100644 index 0000000000..1aa5d69add --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/composable_sampler_configuration.h @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include "opentelemetry/sdk/configuration/sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +#include "opentelemetry/version.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +class ComposableSamplerConfiguration : public SamplerConfiguration +{ +public: + ComposableSamplerConfiguration() = default; + ~ComposableSamplerConfiguration() override = default; +}; +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h b/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h index 43caaed960..91b6bff5eb 100644 --- a/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h +++ b/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h @@ -17,6 +17,12 @@ #include "opentelemetry/sdk/configuration/boolean_array_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/cardinality_limits_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" #include "opentelemetry/sdk/configuration/configuration.h" #include "opentelemetry/sdk/configuration/console_log_record_exporter_configuration.h" #include "opentelemetry/sdk/configuration/console_push_metric_exporter_configuration.h" @@ -313,6 +319,42 @@ class ConfigurationParser const std::unique_ptr &node, size_t depth) const; + std::unique_ptr + ParseComposableAlwaysOffSamplerConfiguration(const std::unique_ptr &node, + size_t depth) const; + + std::unique_ptr + ParseComposableAlwaysOnSamplerConfiguration(const std::unique_ptr &node, + size_t depth) const; + + std::unique_ptr + ParseComposableProbabilitySamplerConfiguration(const std::unique_ptr &node, + size_t depth) const; + + std::unique_ptr + ParseComposableParentThresholdSamplerConfiguration(const std::unique_ptr &node, + size_t depth) const; + + std::unique_ptr + ParseComposableRuleBasedSamplerRuleAttributeValuesConfiguration( + const std::unique_ptr &node) const; + + std::unique_ptr + ParseComposableRuleBasedSamplerRuleAttributePatternsConfiguration( + const std::unique_ptr &node) const; + + std::unique_ptr + ParseComposableRuleBasedSamplerRuleConfiguration(const std::unique_ptr &node, + size_t depth) const; + + std::unique_ptr + ParseComposableRuleBasedSamplerConfiguration(const std::unique_ptr &node, + size_t depth) const; + + std::unique_ptr ParseComposableSamplerConfiguration( + const std::unique_ptr &node, + size_t depth) const; + std::unique_ptr ParseSamplerExtensionConfiguration( const std::string &name, std::unique_ptr node, diff --git a/sdk/include/opentelemetry/sdk/configuration/sampler_configuration_visitor.h b/sdk/include/opentelemetry/sdk/configuration/sampler_configuration_visitor.h index 930e447325..c27f0839a8 100644 --- a/sdk/include/opentelemetry/sdk/configuration/sampler_configuration_visitor.h +++ b/sdk/include/opentelemetry/sdk/configuration/sampler_configuration_visitor.h @@ -17,6 +17,12 @@ class JaegerRemoteSamplerConfiguration; class ParentBasedSamplerConfiguration; class TraceIdRatioBasedSamplerConfiguration; class ExtensionSamplerConfiguration; +class ComposableAlwaysOffSamplerConfiguration; +class ComposableAlwaysOnSamplerConfiguration; +class ComposableProbabilitySamplerConfiguration; +class ComposableParentThresholdSamplerConfiguration; +class ComposableRuleBasedSamplerConfiguration; +class ComposableSamplerConfiguration; class SamplerConfigurationVisitor { @@ -34,6 +40,17 @@ class SamplerConfigurationVisitor virtual void VisitParentBased(const ParentBasedSamplerConfiguration *model) = 0; virtual void VisitTraceIdRatioBased(const TraceIdRatioBasedSamplerConfiguration *model) = 0; virtual void VisitExtension(const ExtensionSamplerConfiguration *model) = 0; + virtual void VisitComposableAlwaysOff(const ComposableAlwaysOffSamplerConfiguration * /*model*/) + {} + virtual void VisitComposableAlwaysOn(const ComposableAlwaysOnSamplerConfiguration * /*model*/) {} + virtual void VisitComposableProbability( + const ComposableProbabilitySamplerConfiguration * /*model*/) + {} + virtual void VisitComposableParentThreshold( + const ComposableParentThresholdSamplerConfiguration * /*model*/) + {} + virtual void VisitComposableRuleBased(const ComposableRuleBasedSamplerConfiguration * /*model*/) + {} }; } // namespace configuration diff --git a/sdk/src/configuration/CMakeLists.txt b/sdk/src/configuration/CMakeLists.txt index 0cfb3d67ac..19447fb28d 100644 --- a/sdk/src/configuration/CMakeLists.txt +++ b/sdk/src/configuration/CMakeLists.txt @@ -10,7 +10,12 @@ add_library( ryml_document_node.cc configured_sdk.cc sdk_builder.cc - registry.cc) + registry.cc + composable_always_off_sampler_configuration.cc + composable_always_on_sampler_configuration.cc + composable_probability_sampler_configuration.cc + composable_parent_threshold_sampler_configuration.cc + composable_rule_based_sampler_configuration.cc) set_target_properties(opentelemetry_configuration PROPERTIES EXPORT_NAME configuration) diff --git a/sdk/src/configuration/composable_always_off_sampler_configuration.cc b/sdk/src/configuration/composable_always_off_sampler_configuration.cc new file mode 100644 index 0000000000..f43d9b2605 --- /dev/null +++ b/sdk/src/configuration/composable_always_off_sampler_configuration.cc @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#include "opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +void ComposableAlwaysOffSamplerConfiguration::Accept(SamplerConfigurationVisitor *visitor) const +{ + visitor->VisitComposableAlwaysOff(this); +} +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/configuration/composable_always_on_sampler_configuration.cc b/sdk/src/configuration/composable_always_on_sampler_configuration.cc new file mode 100644 index 0000000000..5fa746356b --- /dev/null +++ b/sdk/src/configuration/composable_always_on_sampler_configuration.cc @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#include "opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +void ComposableAlwaysOnSamplerConfiguration::Accept(SamplerConfigurationVisitor *visitor) const +{ + visitor->VisitComposableAlwaysOn(this); +} +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/configuration/composable_parent_threshold_sampler_configuration.cc b/sdk/src/configuration/composable_parent_threshold_sampler_configuration.cc new file mode 100644 index 0000000000..2ffc0792d9 --- /dev/null +++ b/sdk/src/configuration/composable_parent_threshold_sampler_configuration.cc @@ -0,0 +1,17 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +void ComposableParentThresholdSamplerConfiguration::Accept( + SamplerConfigurationVisitor *visitor) const +{ + visitor->VisitComposableParentThreshold(this); +} +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/configuration/composable_probability_sampler_configuration.cc b/sdk/src/configuration/composable_probability_sampler_configuration.cc new file mode 100644 index 0000000000..91516d77a7 --- /dev/null +++ b/sdk/src/configuration/composable_probability_sampler_configuration.cc @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#include "opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +void ComposableProbabilitySamplerConfiguration::Accept(SamplerConfigurationVisitor *visitor) const +{ + visitor->VisitComposableProbability(this); +} +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/configuration/composable_rule_based_sampler_configuration.cc b/sdk/src/configuration/composable_rule_based_sampler_configuration.cc new file mode 100644 index 0000000000..fedba77de2 --- /dev/null +++ b/sdk/src/configuration/composable_rule_based_sampler_configuration.cc @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ +void ComposableRuleBasedSamplerConfiguration::Accept(SamplerConfigurationVisitor *visitor) const +{ + visitor->VisitComposableRuleBased(this); +} +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/configuration/configuration_parser.cc b/sdk/src/configuration/configuration_parser.cc index 044ab53eae..6cd1de4876 100644 --- a/sdk/src/configuration/configuration_parser.cc +++ b/sdk/src/configuration/configuration_parser.cc @@ -25,6 +25,14 @@ #include "opentelemetry/sdk/configuration/boolean_array_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/cardinality_limits_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_patterns_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_values_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_configuration.h" #include "opentelemetry/sdk/configuration/configuration.h" #include "opentelemetry/sdk/configuration/configuration_parser.h" #include "opentelemetry/sdk/configuration/console_log_record_exporter_configuration.h" @@ -1685,6 +1693,217 @@ ConfigurationParser::ParseTraceIdRatioBasedSamplerConfiguration( return model; } +std::unique_ptr +ConfigurationParser::ParseComposableAlwaysOffSamplerConfiguration( + const std::unique_ptr & /* node */, + size_t /* depth */) const +{ + return std::make_unique(); +} + +std::unique_ptr +ConfigurationParser::ParseComposableAlwaysOnSamplerConfiguration( + const std::unique_ptr & /* node */, + size_t /* depth */) const +{ + return std::make_unique(); +} + +std::unique_ptr +ConfigurationParser::ParseComposableProbabilitySamplerConfiguration( + const std::unique_ptr &node, + size_t /* depth */) const +{ + auto model = std::make_unique(); + model->ratio = node->GetDouble("ratio", 1.0); + return model; +} + +// NOLINTBEGIN(misc-no-recursion) +std::unique_ptr +ConfigurationParser::ParseComposableParentThresholdSamplerConfiguration( + const std::unique_ptr &node, + size_t depth) const +{ + auto model = std::make_unique(); + + std::unique_ptr child = node->GetRequiredChildNode("root"); + model->root = ParseComposableSamplerConfiguration(child, depth + 1); + + return model; +} + +std::unique_ptr +ConfigurationParser::ParseComposableRuleBasedSamplerRuleAttributeValuesConfiguration( + const std::unique_ptr &node) const +{ + auto model = std::make_unique(); + model->key = node->GetRequiredString("key"); + + auto vals = node->GetRequiredChildNode("values"); + for (auto vit = vals->begin(); vit != vals->end(); ++vit) + { + std::unique_ptr v(*vit); + model->values.push_back(v->AsString()); + } + + return model; +} + +std::unique_ptr +ConfigurationParser::ParseComposableRuleBasedSamplerRuleAttributePatternsConfiguration( + const std::unique_ptr &node) const +{ + auto model = std::make_unique(); + model->key = node->GetRequiredString("key"); + + auto included = node->GetChildNode("included"); + if (included) + { + for (auto iit = included->begin(); iit != included->end(); ++iit) + { + std::unique_ptr i(*iit); + model->included.push_back(i->AsString()); + } + } + + auto excluded = node->GetChildNode("excluded"); + if (excluded) + { + for (auto eit = excluded->begin(); eit != excluded->end(); ++eit) + { + std::unique_ptr e(*eit); + model->excluded.push_back(e->AsString()); + } + } + return model; +} + +std::unique_ptr +ConfigurationParser::ParseComposableRuleBasedSamplerRuleConfiguration( + const std::unique_ptr &node, + size_t depth) const +{ + auto rule = std::make_unique(); + + std::unique_ptr av = node->GetChildNode("attribute_values"); + if (av) + { + rule->attribute_values = ParseComposableRuleBasedSamplerRuleAttributeValuesConfiguration(av); + } + + std::unique_ptr ap = node->GetChildNode("attribute_patterns"); + if (ap) + { + rule->attribute_patterns = + ParseComposableRuleBasedSamplerRuleAttributePatternsConfiguration(ap); + } + + std::unique_ptr parent = node->GetChildNode("parent"); + if (parent) + { + for (auto pit = parent->begin(); pit != parent->end(); ++pit) + { + std::unique_ptr p(*pit); + std::string p_str = p->AsString(); + if (p_str == "none") + rule->match_parent_none = true; + else if (p_str == "remote") + rule->match_parent_remote = true; + else if (p_str == "local") + rule->match_parent_local = true; + else + throw InvalidSchemaException(p->Location(), "Illegal parent type: " + p_str); + } + } + + std::unique_ptr span_kinds = node->GetChildNode("span_kinds"); + if (span_kinds) + { + for (auto kit = span_kinds->begin(); kit != span_kinds->end(); ++kit) + { + std::unique_ptr k(*kit); + std::string k_str = k->AsString(); + if (k_str == "internal") + rule->match_span_kind_internal = true; + else if (k_str == "server") + rule->match_span_kind_server = true; + else if (k_str == "client") + rule->match_span_kind_client = true; + else if (k_str == "producer") + rule->match_span_kind_producer = true; + else if (k_str == "consumer") + rule->match_span_kind_consumer = true; + else + throw InvalidSchemaException(k->Location(), "Illegal span_kind type: " + k_str); + } + } + + std::unique_ptr sampler = node->GetRequiredChildNode("sampler"); + rule->sampler = ParseComposableSamplerConfiguration(sampler, depth + 1); + + return rule; +} + +std::unique_ptr +ConfigurationParser::ParseComposableRuleBasedSamplerConfiguration( + const std::unique_ptr &node, + size_t depth) const +{ + auto model = std::make_unique(); + + std::unique_ptr rules_node = node->GetChildNode("rules"); + if (rules_node) + { + for (auto it = rules_node->begin(); it != rules_node->end(); ++it) + { + std::unique_ptr rule_node(*it); + model->rules.push_back(ParseComposableRuleBasedSamplerRuleConfiguration(rule_node, depth)); + } + } + + return model; +} + +std::unique_ptr +ConfigurationParser::ParseComposableSamplerConfiguration(const std::unique_ptr &node, + size_t depth) const +{ + std::string inner_name; + std::unique_ptr inner_child; + size_t count = 0; + + for (auto it = node->begin_properties(); it != node->end_properties(); ++it) + { + inner_name = it.Name(); + inner_child = it.Value(); + count++; + } + + if (count != 1) + { + std::string message("Illegal composable sampler, properties count: "); + message.append(std::to_string(count)); + throw InvalidSchemaException(node->Location(), message); + } + + if (inner_name == "always_off") + return ParseComposableAlwaysOffSamplerConfiguration(inner_child, depth); + if (inner_name == "always_on") + return ParseComposableAlwaysOnSamplerConfiguration(inner_child, depth); + if (inner_name == "probability") + return ParseComposableProbabilitySamplerConfiguration(inner_child, depth); + if (inner_name == "parent_threshold") + return ParseComposableParentThresholdSamplerConfiguration(inner_child, depth); + if (inner_name == "rule_based") + return ParseComposableRuleBasedSamplerConfiguration(inner_child, depth); + + std::string message("Illegal composable sampler type: "); + message.append(inner_name); + throw InvalidSchemaException(node->Location(), message); +} +// NOLINTEND(misc-no-recursion) + std::unique_ptr ConfigurationParser::ParseSamplerExtensionConfiguration(const std::string &name, std::unique_ptr node, @@ -1755,6 +1974,10 @@ std::unique_ptr ConfigurationParser::ParseSamplerConfigura { model = ParseTraceIdRatioBasedSamplerConfiguration(child, depth); } + else if (name == "composite/development") + { + model = ParseComposableSamplerConfiguration(child, depth); + } else { model = ParseSamplerExtensionConfiguration(name, std::move(child), depth); diff --git a/sdk/src/configuration/sdk_builder.cc b/sdk/src/configuration/sdk_builder.cc index e1ce613cb0..6738725823 100644 --- a/sdk/src/configuration/sdk_builder.cc +++ b/sdk/src/configuration/sdk_builder.cc @@ -31,6 +31,12 @@ #include "opentelemetry/sdk/configuration/batch_span_processor_configuration.h" #include "opentelemetry/sdk/configuration/boolean_array_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" #include "opentelemetry/sdk/configuration/configuration.h" #include "opentelemetry/sdk/configuration/configured_sdk.h" #include "opentelemetry/sdk/configuration/console_log_record_exporter_builder.h" @@ -366,6 +372,43 @@ class SamplerBuilder : public opentelemetry::sdk::configuration::SamplerConfigur sampler = sdk_builder_->CreateExtensionSampler(model); } + void VisitComposableAlwaysOff( + const opentelemetry::sdk::configuration::ComposableAlwaysOffSamplerConfiguration + * /* model */) override + { + sampler = opentelemetry::sdk::trace::AlwaysOffSamplerFactory::Create(); + } + + void VisitComposableAlwaysOn( + const opentelemetry::sdk::configuration::ComposableAlwaysOnSamplerConfiguration * /* model */) + override + { + sampler = opentelemetry::sdk::trace::AlwaysOnSamplerFactory::Create(); + } + + void VisitComposableProbability( + const opentelemetry::sdk::configuration::ComposableProbabilitySamplerConfiguration *model) + override + { + sampler = opentelemetry::sdk::trace::TraceIdRatioBasedSamplerFactory::Create(model->ratio); + } + + void VisitComposableParentThreshold( + const opentelemetry::sdk::configuration::ComposableParentThresholdSamplerConfiguration + * /* model */) override + { + OTEL_INTERNAL_LOG_WARN("ComposableParentThresholdSampler not yet fully supported by SDK"); + sampler = opentelemetry::sdk::trace::AlwaysOnSamplerFactory::Create(); + } + + void VisitComposableRuleBased( + const opentelemetry::sdk::configuration::ComposableRuleBasedSamplerConfiguration + * /* model */) override + { + OTEL_INTERNAL_LOG_WARN("ComposableRuleBasedSampler not yet fully supported by SDK"); + sampler = opentelemetry::sdk::trace::AlwaysOnSamplerFactory::Create(); + } + std::unique_ptr sampler; private: diff --git a/sdk/test/configuration/yaml_trace_test.cc b/sdk/test/configuration/yaml_trace_test.cc index 98051ce65f..baeb94ede0 100644 --- a/sdk/test/configuration/yaml_trace_test.cc +++ b/sdk/test/configuration/yaml_trace_test.cc @@ -1,17 +1,26 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 - #include +#include #include #include #include #include +#include "opentelemetry/sdk/configuration/sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sampler_configuration_visitor.h" #include "opentelemetry/sdk/configuration/batch_span_processor_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" #include "opentelemetry/sdk/configuration/configuration.h" #include "opentelemetry/sdk/configuration/grpc_tls_configuration.h" #include "opentelemetry/sdk/configuration/headers_configuration.h" #include "opentelemetry/sdk/configuration/http_tls_configuration.h" +#include "opentelemetry/sdk/configuration/invalid_schema_exception.h" #include "opentelemetry/sdk/configuration/jaeger_remote_sampler_configuration.h" #include "opentelemetry/sdk/configuration/otlp_file_span_exporter_configuration.h" #include "opentelemetry/sdk/configuration/otlp_grpc_span_exporter_configuration.h" @@ -840,3 +849,311 @@ file_format: "1.0-trace" ASSERT_EQ(configurator->tracers[0].name, "noisy.library"); ASSERT_EQ(configurator->tracers[0].config.enabled, false); } + +namespace +{ +enum class SamplerType : std::uint8_t +{ + kUnmatched = 0, + kComposableAlwaysOn = 1, + kComposableProbability = 2, + kComposableRuleBased = 3, + kComposableAlwaysOff = 4, + kComposableParentThreshold = 5 +}; + +class TestSamplerVisitor : public opentelemetry::sdk::configuration::SamplerConfigurationVisitor +{ +public: + SamplerType type_matched = SamplerType::kUnmatched; + double ratio = -1.0; + + void VisitAlwaysOff( + const opentelemetry::sdk::configuration::AlwaysOffSamplerConfiguration *) override + {} + void VisitAlwaysOn( + const opentelemetry::sdk::configuration::AlwaysOnSamplerConfiguration *) override + {} + void VisitJaegerRemote( + const opentelemetry::sdk::configuration::JaegerRemoteSamplerConfiguration *) override + {} + void VisitParentBased( + const opentelemetry::sdk::configuration::ParentBasedSamplerConfiguration *) override + {} + void VisitTraceIdRatioBased( + const opentelemetry::sdk::configuration::TraceIdRatioBasedSamplerConfiguration *) override + {} + void VisitExtension( + const opentelemetry::sdk::configuration::ExtensionSamplerConfiguration *) override + {} + + void VisitComposableAlwaysOff( + const opentelemetry::sdk::configuration::ComposableAlwaysOffSamplerConfiguration *) override + { + type_matched = SamplerType::kComposableAlwaysOff; + } + void VisitComposableAlwaysOn( + const opentelemetry::sdk::configuration::ComposableAlwaysOnSamplerConfiguration *) override + { + type_matched = SamplerType::kComposableAlwaysOn; + } + void VisitComposableProbability( + const opentelemetry::sdk::configuration::ComposableProbabilitySamplerConfiguration *model) + override + { + type_matched = SamplerType::kComposableProbability; + ratio = model->ratio; + } + void VisitComposableParentThreshold( + const opentelemetry::sdk::configuration::ComposableParentThresholdSamplerConfiguration *) + override + { + type_matched = SamplerType::kComposableParentThreshold; + } + void VisitComposableRuleBased( + const opentelemetry::sdk::configuration::ComposableRuleBasedSamplerConfiguration *) override + { + type_matched = SamplerType::kComposableRuleBased; + } +}; +} // namespace + +TEST(YamlTrace, composable_always_on_sampler) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + sampler: + composite/development: + always_on: +)"; + + auto config = DoParse(yaml); + ASSERT_NE(config, nullptr); + ASSERT_NE(config->tracer_provider, nullptr); + ASSERT_NE(config->tracer_provider->sampler, nullptr); + + TestSamplerVisitor visitor; + config->tracer_provider->sampler->Accept(&visitor); + EXPECT_EQ(visitor.type_matched, SamplerType::kComposableAlwaysOn); +} + +TEST(YamlTrace, composable_probability_sampler) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + sampler: + composite/development: + probability: + ratio: 0.25 +)"; + + auto config = DoParse(yaml); + ASSERT_NE(config, nullptr); + ASSERT_NE(config->tracer_provider, nullptr); + ASSERT_NE(config->tracer_provider->sampler, nullptr); + + TestSamplerVisitor visitor; + config->tracer_provider->sampler->Accept(&visitor); + + EXPECT_EQ(visitor.type_matched, SamplerType::kComposableProbability); + EXPECT_DOUBLE_EQ(visitor.ratio, 0.25); +} + +TEST(YamlTrace, composable_rule_based_sampler) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + sampler: + composite/development: + rule_based: + rules: + - sampler: + always_on: +)"; + + auto config = DoParse(yaml); + ASSERT_NE(config, nullptr); + ASSERT_NE(config->tracer_provider, nullptr); + ASSERT_NE(config->tracer_provider->sampler, nullptr); + + TestSamplerVisitor visitor; + config->tracer_provider->sampler->Accept(&visitor); + EXPECT_EQ(visitor.type_matched, SamplerType::kComposableRuleBased); +} + +TEST(YamlTrace, composable_always_off_sampler) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + sampler: + composite/development: + always_off: +)"; + + auto config = DoParse(yaml); + ASSERT_NE(config, nullptr); + ASSERT_NE(config->tracer_provider, nullptr); + ASSERT_NE(config->tracer_provider->sampler, nullptr); + + TestSamplerVisitor visitor; + config->tracer_provider->sampler->Accept(&visitor); + EXPECT_EQ(visitor.type_matched, SamplerType::kComposableAlwaysOff); +} + +TEST(YamlTrace, composable_parent_threshold_sampler) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + sampler: + composite/development: + parent_threshold: + root: + always_on: +)"; + + auto config = DoParse(yaml); + ASSERT_NE(config, nullptr); + ASSERT_NE(config->tracer_provider, nullptr); + ASSERT_NE(config->tracer_provider->sampler, nullptr); + + TestSamplerVisitor visitor; + config->tracer_provider->sampler->Accept(&visitor); + EXPECT_EQ(visitor.type_matched, SamplerType::kComposableParentThreshold); +} + +TEST(YamlTrace, composable_rule_based_sampler_comprehensive) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + sampler: + composite/development: + rule_based: + rules: + - attribute_values: + key: "http.method" + values: + - "GET" + - "POST" + attribute_patterns: + key: "http.url" + included: + - ".*/api/.*" + excluded: + - ".*/healthz" + parent: + - remote + - local + span_kinds: + - server + - client + sampler: + always_on: +)"; + + auto config = DoParse(yaml); + ASSERT_NE(config, nullptr); + ASSERT_NE(config->tracer_provider, nullptr); + ASSERT_NE(config->tracer_provider->sampler, nullptr); + + // Cast down to our specific type to check the fields + auto *rule_based_sampler = + static_cast( + config->tracer_provider->sampler.get()); + ASSERT_NE(rule_based_sampler, nullptr); + ASSERT_EQ(rule_based_sampler->rules.size(), 1); + + const auto &rule = rule_based_sampler->rules[0]; + + // 1. Check attribute_values + ASSERT_NE(rule->attribute_values, nullptr); + EXPECT_EQ(rule->attribute_values->key, "http.method"); + ASSERT_EQ(rule->attribute_values->values.size(), 2); + EXPECT_EQ(rule->attribute_values->values[0], "GET"); + + // 2. Check attribute_patterns + ASSERT_NE(rule->attribute_patterns, nullptr); + EXPECT_EQ(rule->attribute_patterns->key, "http.url"); + ASSERT_EQ(rule->attribute_patterns->included.size(), 1); + EXPECT_EQ(rule->attribute_patterns->included[0], ".*/api/.*"); + ASSERT_EQ(rule->attribute_patterns->excluded.size(), 1); + EXPECT_EQ(rule->attribute_patterns->excluded[0], ".*/healthz"); + + // 3. Check parent flags + EXPECT_TRUE(rule->match_parent_remote); + EXPECT_TRUE(rule->match_parent_local); + EXPECT_FALSE(rule->match_parent_none); + + // 4. Check span kinds + EXPECT_TRUE(rule->match_span_kind_server); + EXPECT_TRUE(rule->match_span_kind_client); + EXPECT_FALSE(rule->match_span_kind_internal); + + // 5. Check inner sampler + ASSERT_NE(rule->sampler, nullptr); +} + +TEST(YamlTrace, composable_rule_based_sampler_illegal_parent) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + sampler: + composite/development: + rule_based: + rules: + - parent: + - illegal_parent_value + sampler: + always_on: +)"; + + EXPECT_THROW(DoParse(yaml), opentelemetry::sdk::configuration::InvalidSchemaException); +} + +TEST(YamlTrace, composable_rule_based_sampler_illegal_spankind) +{ + std::string yaml = R"( +file_format: "1.0-trace" +tracer_provider: + sampler: + composite/development: + rule_based: + rules: + - span_kinds: + - illegal_span_kind + sampler: + always_on: +)"; + + EXPECT_THROW(DoParse(yaml), opentelemetry::sdk::configuration::InvalidSchemaException); +} \ No newline at end of file