Skip to content
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ Increment the:

## [Unreleased]

* [CONFIGURATION] Apply general `attribute_limits` per individual limit field.
If a model-specific limit is set it is used, otherwise the matching general
limit, otherwise the model-specific default. Limit fields on
`AttributeLimitsConfiguration`, `SpanLimitsConfiguration`, and
`LogRecordLimitsConfiguration` are now optional so omitted keys and YAML
`null` are distinct from explicit values. This is a breaking change to the
experimental configuration model.
[#4467](https://github.com/open-telemetry/opentelemetry-cpp/issues/4467)

* [CONFIGURATION] Add a configuration builder for the host resource detector
[#4451](https://github.com/open-telemetry/opentelemetry-cpp/issues/4451)
* [CONFIGURATION] Build the configured resource detectors in SdkBuilder, apply
Expand Down
1 change: 1 addition & 0 deletions install/test/src/test_sdk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
#include <opentelemetry/sdk/configuration/metric_reader_configuration.h>
#include <opentelemetry/sdk/configuration/metric_reader_configuration_visitor.h>
#include <opentelemetry/sdk/configuration/open_census_metric_producer_configuration.h>
#include <opentelemetry/sdk/configuration/optional_value.h>
#include <opentelemetry/sdk/configuration/otlp_file_log_record_exporter_configuration.h>
#include <opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h>
#include <opentelemetry/sdk/configuration/otlp_file_span_exporter_configuration.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cstddef>
#include <limits>

#include "opentelemetry/sdk/configuration/optional_value.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand All @@ -23,8 +24,8 @@ class AttributeLimitsConfiguration
(std::numeric_limits<std::size_t>::max)();
static constexpr std::size_t kDefaultAttributeCountLimit = 128;

std::size_t attribute_value_length_limit{kDefaultAttributeValueLengthLimit};
std::size_t attribute_count_limit{kDefaultAttributeCountLimit};
OptionalValue<std::size_t> attribute_value_length_limit;
OptionalValue<std::size_t> attribute_count_limit;
};

} // namespace configuration
Expand Down
2 changes: 2 additions & 0 deletions sdk/include/opentelemetry/sdk/configuration/document_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <memory>
#include <string>

#include "opentelemetry/sdk/configuration/optional_value.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -50,6 +51,7 @@ class DocumentNode

virtual std::size_t GetRequiredInteger(const std::string &name) const = 0;
virtual std::size_t GetInteger(const std::string &name, std::size_t default_value) const = 0;
virtual OptionalValue<std::size_t> GetOptionalInteger(const std::string &name) const = 0;

virtual std::int64_t GetSignedInteger(const std::string &name,
std::int64_t default_value) const = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cstddef>
#include <limits>

#include "opentelemetry/sdk/configuration/optional_value.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand All @@ -23,8 +24,8 @@ class LogRecordLimitsConfiguration
(std::numeric_limits<std::size_t>::max)();
static constexpr std::size_t kDefaultAttributeCountLimit = 128;

std::size_t attribute_value_length_limit{kDefaultAttributeValueLengthLimit};
std::size_t attribute_count_limit{kDefaultAttributeCountLimit};
OptionalValue<std::size_t> attribute_value_length_limit;
OptionalValue<std::size_t> attribute_count_limit;
};

} // namespace configuration
Expand Down
46 changes: 46 additions & 0 deletions sdk/include/opentelemetry/sdk/configuration/optional_value.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace configuration
{

/**
* C++14-friendly optional for configuration fields that may be set, omitted, or
* explicitly null in YAML.
*/
template <typename T>
class OptionalValue
{
public:
OptionalValue() = default;

explicit OptionalValue(T value) : has_value_(true), value_(value) {}

bool HasValue() const { return has_value_; }

const T &Value() const { return value_; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the Value method is called and there is no value then a runtime exception should be thrown. This more closely matches the behavior of std::optional.


T ValueOr(T fallback) const { return has_value_ ? value_ : fallback; }

OptionalValue &operator=(T value)
{
has_value_ = true;
value_ = value;
return *this;
}

private:
bool has_value_{false};
T value_{};
};

} // namespace configuration
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <string>

#include "opentelemetry/sdk/configuration/document_node.h"
#include "opentelemetry/sdk/configuration/optional_value.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -50,6 +51,7 @@ class RymlDocumentNode : public DocumentNode

std::size_t GetRequiredInteger(const std::string &name) const override;
std::size_t GetInteger(const std::string &name, std::size_t default_value) const override;
OptionalValue<std::size_t> GetOptionalInteger(const std::string &name) const override;

std::int64_t GetSignedInteger(const std::string &name, std::int64_t default_value) const override;

Expand Down
7 changes: 5 additions & 2 deletions sdk/include/opentelemetry/sdk/configuration/sdk_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <memory>

#include "opentelemetry/sdk/configuration/always_off_sampler_configuration.h"
#include "opentelemetry/sdk/configuration/attribute_limits_configuration.h"
#include "opentelemetry/sdk/configuration/base2_exponential_bucket_histogram_aggregation_configuration.h"
#include "opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h"
#include "opentelemetry/sdk/configuration/batch_span_processor_configuration.h"
Expand Down Expand Up @@ -161,7 +162,8 @@ class SdkBuilder

std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> CreateTracerProvider(
const std::unique_ptr<opentelemetry::sdk::configuration::TracerProviderConfiguration> &model,
const opentelemetry::sdk::resource::Resource &resource) const;
const opentelemetry::sdk::resource::Resource &resource,
const AttributeLimitsConfiguration *attribute_limits = nullptr) const;

std::unique_ptr<opentelemetry::context::propagation::TextMapPropagator> CreateTextMapPropagator(
const std::string &name) const;
Expand Down Expand Up @@ -286,7 +288,8 @@ class SdkBuilder

std::unique_ptr<opentelemetry::sdk::logs::LoggerProvider> CreateLoggerProvider(
const std::unique_ptr<opentelemetry::sdk::configuration::LoggerProviderConfiguration> &model,
const opentelemetry::sdk::resource::Resource &resource) const;
const opentelemetry::sdk::resource::Resource &resource,
const AttributeLimitsConfiguration *attribute_limits = nullptr) const;

std::unique_ptr<opentelemetry::sdk::resource::ResourceDetector> CreateContainerResourceDetector(
const opentelemetry::sdk::configuration::ContainerResourceDetectorConfiguration *model) const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <limits>

#include "opentelemetry/sdk/configuration/optional_value.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand All @@ -27,12 +29,12 @@ class SpanLimitsConfiguration
static constexpr std::uint32_t kDefaultEventAttributeCountLimit = 128;
static constexpr std::uint32_t kDefaultLinkAttributeCountLimit = 128;

std::size_t attribute_value_length_limit{kDefaultAttributeValueLengthLimit};
std::uint32_t attribute_count_limit{kDefaultAttributeCountLimit};
std::uint32_t event_count_limit{kDefaultEventCountLimit};
std::uint32_t link_count_limit{kDefaultLinkCountLimit};
std::uint32_t event_attribute_count_limit{kDefaultEventAttributeCountLimit};
std::uint32_t link_attribute_count_limit{kDefaultLinkAttributeCountLimit};
OptionalValue<std::size_t> attribute_value_length_limit;
OptionalValue<std::uint32_t> attribute_count_limit;
OptionalValue<std::uint32_t> event_count_limit;
OptionalValue<std::uint32_t> link_count_limit;
OptionalValue<std::uint32_t> event_attribute_count_limit;
OptionalValue<std::uint32_t> link_attribute_count_limit;
};

} // namespace configuration
Expand Down
52 changes: 23 additions & 29 deletions sdk/src/configuration/configuration_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
#include "opentelemetry/sdk/configuration/metric_producer_configuration.h"
#include "opentelemetry/sdk/configuration/metric_reader_configuration.h"
#include "opentelemetry/sdk/configuration/open_census_metric_producer_configuration.h"
#include "opentelemetry/sdk/configuration/optional_value.h"
#include "opentelemetry/sdk/configuration/otlp_file_log_record_exporter_configuration.h"
#include "opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h"
#include "opentelemetry/sdk/configuration/otlp_file_span_exporter_configuration.h"
Expand Down Expand Up @@ -360,13 +361,10 @@ std::unique_ptr<AttributeLimitsConfiguration>
ConfigurationParser::ParseAttributeLimitsConfiguration(
const std::unique_ptr<DocumentNode> &node) const
{
using Config = AttributeLimitsConfiguration;
auto model = std::make_unique<AttributeLimitsConfiguration>();
auto model = std::make_unique<AttributeLimitsConfiguration>();

model->attribute_value_length_limit =
node->GetInteger("attribute_value_length_limit", Config::kDefaultAttributeValueLengthLimit);
model->attribute_count_limit =
node->GetInteger("attribute_count_limit", Config::kDefaultAttributeCountLimit);
model->attribute_value_length_limit = node->GetOptionalInteger("attribute_value_length_limit");
model->attribute_count_limit = node->GetOptionalInteger("attribute_count_limit");

return model;
}
Expand Down Expand Up @@ -640,13 +638,10 @@ std::unique_ptr<LogRecordLimitsConfiguration>
ConfigurationParser::ParseLogRecordLimitsConfiguration(
const std::unique_ptr<DocumentNode> &node) const
{
using Config = LogRecordLimitsConfiguration;
auto model = std::make_unique<LogRecordLimitsConfiguration>();
auto model = std::make_unique<LogRecordLimitsConfiguration>();

model->attribute_value_length_limit =
node->GetInteger("attribute_value_length_limit", Config::kDefaultAttributeValueLengthLimit);
model->attribute_count_limit =
node->GetInteger("attribute_count_limit", Config::kDefaultAttributeCountLimit);
model->attribute_value_length_limit = node->GetOptionalInteger("attribute_value_length_limit");
model->attribute_count_limit = node->GetOptionalInteger("attribute_count_limit");

return model;
}
Expand Down Expand Up @@ -1673,29 +1668,28 @@ std::unique_ptr<PropagatorConfiguration> ConfigurationParser::ParsePropagatorCon
std::unique_ptr<SpanLimitsConfiguration> ConfigurationParser::ParseSpanLimitsConfiguration(
const std::unique_ptr<DocumentNode> &node) const
{
using Config = SpanLimitsConfiguration;
auto model = std::make_unique<SpanLimitsConfiguration>();
auto model = std::make_unique<SpanLimitsConfiguration>();

const auto get_valid_uint32 = [&node](const std::string &name, std::size_t default_value) {
std::size_t value = node->GetInteger(name, default_value);
if (value > std::numeric_limits<std::uint32_t>::max())
const auto get_optional_uint32 = [&node](const std::string &name) {
OptionalValue<std::size_t> value = node->GetOptionalInteger(name);
if (!value.HasValue())
{
std::string message = "Invalid value for " + name + ": " + std::to_string(value);
return OptionalValue<std::uint32_t>{};
}
if (value.Value() > std::numeric_limits<std::uint32_t>::max())
{
std::string message = "Invalid value for " + name + ": " + std::to_string(value.Value());
throw InvalidSchemaException(node->Location(), message);
}
return static_cast<uint32_t>(value);
return OptionalValue<std::uint32_t>{static_cast<std::uint32_t>(value.Value())};
};

model->attribute_value_length_limit =
node->GetInteger("attribute_value_length_limit", Config::kDefaultAttributeValueLengthLimit);
model->attribute_count_limit =
get_valid_uint32("attribute_count_limit", Config::kDefaultAttributeCountLimit);
model->event_count_limit = get_valid_uint32("event_count_limit", Config::kDefaultEventCountLimit);
model->link_count_limit = get_valid_uint32("link_count_limit", Config::kDefaultLinkCountLimit);
model->event_attribute_count_limit =
get_valid_uint32("event_attribute_count_limit", Config::kDefaultEventAttributeCountLimit);
model->link_attribute_count_limit =
get_valid_uint32("link_attribute_count_limit", Config::kDefaultLinkAttributeCountLimit);
model->attribute_value_length_limit = node->GetOptionalInteger("attribute_value_length_limit");
model->attribute_count_limit = get_optional_uint32("attribute_count_limit");
model->event_count_limit = get_optional_uint32("event_count_limit");
model->link_count_limit = get_optional_uint32("link_count_limit");
model->event_attribute_count_limit = get_optional_uint32("event_attribute_count_limit");
model->link_attribute_count_limit = get_optional_uint32("link_attribute_count_limit");

return model;
}
Expand Down
25 changes: 25 additions & 0 deletions sdk/src/configuration/ryml_document_node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "opentelemetry/sdk/common/global_log_handler.h"
#include "opentelemetry/sdk/configuration/document_node.h"
#include "opentelemetry/sdk/configuration/invalid_schema_exception.h"
#include "opentelemetry/sdk/configuration/optional_value.h"
#include "opentelemetry/sdk/configuration/ryml_document.h"
#include "opentelemetry/sdk/configuration/ryml_document_node.h"
#include "opentelemetry/version.h"
Expand Down Expand Up @@ -297,6 +298,30 @@ size_t RymlDocumentNode::GetInteger(const std::string &name, size_t default_valu
return IntegerFromString(value);
}

OptionalValue<std::size_t> RymlDocumentNode::GetOptionalInteger(const std::string &name) const
{
OTEL_INTERNAL_LOG_DEBUG("RymlDocumentNode::GetOptionalInteger(" << name << ")");

auto ryml_child = GetRymlChildNode(name);

if (ryml_child.invalid() || !ryml_child.has_val())
{
return OptionalValue<std::size_t>{};
}

ryml::csubstr view = ryml_child.val();
std::string value(view.str, view.len);

value = DoSubstitution(value);

if (value.empty() || value == "~" || value == "null" || value == "Null" || value == "NULL")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DocumentNode and RymlDocumentNode now have an IsNull() method. Please use it where appropriate.

{
return OptionalValue<std::size_t>{};
}

return OptionalValue<std::size_t>{IntegerFromString(value)};
}

std::int64_t RymlDocumentNode::GetSignedInteger(const std::string &name,
std::int64_t default_value) const
{
Expand Down
Loading
Loading