Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ Increment the:
* [CMAKE] Fix and test WITH_API_ONLY option
[#4201](https://github.com/open-telemetry/opentelemetry-cpp/pull/4201)

* [SDK] Span limits can now be configured through the TracerProvider interface
and declaritive configuration. When set, limits are enforced by the
SpanData and OtlpRecordables recordables used by the standard exporters
[#4232](https://github.com/open-telemetry/opentelemetry-cpp/pull/4232)

* [API] Fix `TraceState::IsValidKey()` to comply with the W3C Trace Context
Level 2, where keys containing `@` and keys with more than 241 characters
before `@` or more than 14 characters after `@` are now accepted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ struct OPENTELEMETRY_EXPORT OtlpGrpcExporterOptions : public OtlpGrpcClientOptio
OtlpGrpcExporterOptions &operator=(OtlpGrpcExporterOptions &&) = default;
~OtlpGrpcExporterOptions() override;

/** Collection Limits. No limit by default. */
/** Collection Limits. No limit by default.
* @deprecated Configure span limits via TracerProviderFactory::Create(), SpanLimitsConfiguration,
* or the YAML `tracer_provider.limits` node instead. These fields will be
* removed in a future release.
*/
std::uint32_t max_attributes = (std::numeric_limits<std::uint32_t>::max)();
std::uint32_t max_events = (std::numeric_limits<std::uint32_t>::max)();
std::uint32_t max_links = (std::numeric_limits<std::uint32_t>::max)();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ struct OPENTELEMETRY_EXPORT OtlpHttpExporterOptions
/** The backoff will be multiplied by this value after each retry attempt. */
float retry_policy_backoff_multiplier{};

/** Collection Limits. No limit by default. */
/** Collection Limits. No limit by default.
* @deprecated Configure span limits via TracerProviderFactory::Create(), SpanLimitsConfiguration,
* or the YAML `tracer_provider.limits` node instead. These fields will be
* removed in a future release.
*/
std::uint32_t max_attributes = (std::numeric_limits<std::uint32_t>::max)();
std::uint32_t max_events = (std::numeric_limits<std::uint32_t>::max)();
std::uint32_t max_links = (std::numeric_limits<std::uint32_t>::max)();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/trace/recordable.h"
#include "opentelemetry/sdk/trace/span_limits.h"
#include "opentelemetry/trace/span_context.h"
#include "opentelemetry/trace/span_id.h"
#include "opentelemetry/trace/span_metadata.h"
Expand All @@ -37,17 +38,25 @@ namespace otlp
class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable
{
public:
/**
* Construct with optional span limits parameters.
*
* @deprecated Configure span limits via TracerProviderFactory::Create(), SpanLimitsConfiguration,
* or the YAML `tracer_provider.limits` node instead. These optional span limit params will be
* removed in a future release.
*/
explicit OtlpRecordable(
std::uint32_t max_attributes = (std::numeric_limits<std::uint32_t>::max)(),
std::uint32_t max_events = (std::numeric_limits<std::uint32_t>::max)(),
std::uint32_t max_links = (std::numeric_limits<std::uint32_t>::max)(),
std::uint32_t max_attributes_per_event = (std::numeric_limits<std::uint32_t>::max)(),
std::uint32_t max_attributes_per_link = (std::numeric_limits<std::uint32_t>::max)())
: max_attributes_(max_attributes),
max_events_(max_events),
max_links_(max_links),
max_attributes_per_event_(max_attributes_per_event),
max_attributes_per_link_(max_attributes_per_link)
: span_limits_{max_attributes,
(std::numeric_limits<std::size_t>::max)(),
max_events,
max_links,
max_attributes_per_event,
max_attributes_per_link}
{}

proto::trace::v1::Span &span() noexcept { return span_; }
Expand Down Expand Up @@ -92,6 +101,17 @@ class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable

void SetDuration(std::chrono::nanoseconds duration) noexcept override;

/**
* Set span limits.
*
* Until the deprecated OTLP exporter option values are removed, the effective limit for each
* field is the minimum (most restrictive) of the deprecated otlp exporter option values (passed
* to the constructor) and the values set by calling this method.
*
* @param limits The span limits to set.
*/
void SetSpanLimits(const opentelemetry::sdk::trace::SpanLimits &limits) noexcept override;

void SetInstrumentationScope(const opentelemetry::sdk::instrumentationscope::InstrumentationScope
&instrumentation_scope) noexcept override;

Expand All @@ -100,11 +120,8 @@ class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable
const opentelemetry::sdk::resource::Resource *resource_ = nullptr;
const opentelemetry::sdk::instrumentationscope::InstrumentationScope *instrumentation_scope_ =
nullptr;
std::uint32_t max_attributes_;
std::uint32_t max_events_;
std::uint32_t max_links_;
std::uint32_t max_attributes_per_event_;
std::uint32_t max_attributes_per_link_;
opentelemetry::sdk::trace::SpanLimits span_limits_{
opentelemetry::sdk::trace::SpanLimits::NoLimits()};
};
} // namespace otlp
} // namespace exporter
Expand Down
40 changes: 31 additions & 9 deletions exporters/otlp/src/otlp_recordable.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include <stdint.h>
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <string>

#include "opentelemetry/common/attribute_value.h"
Expand All @@ -15,6 +16,7 @@
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/trace/span_limits.h"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OtlpRecordable benchmark performance is the same or slightly better compared to the baseline from #4203 here. This is expected since OtlpRecordable enforces span limits in the baseline also.


 ~/build/exporters/otlp/otlp_recordable_benchmark --benchmark_repetitions=5 --benchmark_display_aggregates_only=true
2026-07-09T22:48:17+00:00
Running /home/devuser/build/exporters/otlp/otlp_recordable_benchmark
Run on (32 X 5700 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x16)
  L1 Instruction 32 KiB (x16)
  L2 Unified 2048 KiB (x16)
  L3 Unified 36864 KiB (x1)
Load Average: 3.28, 3.82, 2.16
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
--------------------------------------------------------------------------------------------------------------------
Benchmark                                                                          Time             CPU   Iterations
--------------------------------------------------------------------------------------------------------------------
OtlpRecordableFixture/RecordMinimalSpan_mean                                     351 ns          352 ns            5
OtlpRecordableFixture/RecordMinimalSpan_median                                   351 ns          353 ns            5
OtlpRecordableFixture/RecordMinimalSpan_stddev                                  2.65 ns         2.72 ns            5
OtlpRecordableFixture/RecordMinimalSpan_cv                                      0.76 %          0.77 %             5
OtlpRecordableFixture/RecordNominalSpan_mean                                     615 ns          616 ns            5
OtlpRecordableFixture/RecordNominalSpan_median                                   610 ns          611 ns            5
OtlpRecordableFixture/RecordNominalSpan_stddev                                  14.3 ns         14.6 ns            5
OtlpRecordableFixture/RecordNominalSpan_cv                                      2.32 %          2.37 %             5
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_mean            390 ns          392 ns            5 items_per_second=2.55168M/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_median          391 ns          392 ns            5 items_per_second=2.55133M/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_stddev        0.703 ns        0.779 ns            5 items_per_second=5.07365k/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_cv             0.18 %          0.20 %             5 items_per_second=0.20%
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_mean           867 ns          868 ns            5 items_per_second=11.5241M/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_median         863 ns          863 ns            5 items_per_second=11.5835M/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_stddev        16.9 ns         17.3 ns            5 items_per_second=224.052k/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_cv            1.95 %          1.99 %             5 items_per_second=1.94%
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_mean         7492 ns         7496 ns            5 items_per_second=17.297M/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_median       7005 ns         7010 ns            5 items_per_second=18.2586M/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_stddev        965 ns          965 ns            5 items_per_second=2.13971M/s
OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_cv          12.89 %         12.88 %             5 items_per_second=12.37%
OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_mean                    474 ns          476 ns            5 items_per_second=2.10221M/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_median                  474 ns          476 ns            5 items_per_second=2.10201M/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_stddev                 1.25 ns         1.21 ns            5 items_per_second=5.33549k/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_cv                     0.26 %          0.25 %             5 items_per_second=0.25%
OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_mean                  1649 ns         1652 ns            5 items_per_second=6.05453M/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_median                1649 ns         1652 ns            5 items_per_second=6.05326M/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_stddev                3.75 ns         3.69 ns            5 items_per_second=13.5277k/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_cv                    0.23 %          0.22 %             5 items_per_second=0.22%
OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_mean                16709 ns        16711 ns            5 items_per_second=7.65969M/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_median              16658 ns        16662 ns            5 items_per_second=7.68232M/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_stddev               93.2 ns         92.5 ns            5 items_per_second=42.3267k/s
OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_cv                   0.56 %          0.55 %             5 items_per_second=0.55%
OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_mean                      491 ns          495 ns            5 items_per_second=2.02101M/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_median                    492 ns          495 ns            5 items_per_second=2.02057M/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_stddev                   1.83 ns         1.77 ns            5 items_per_second=7.24305k/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_cv                       0.37 %          0.36 %             5 items_per_second=0.36%
OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_mean                    1771 ns         1774 ns            5 items_per_second=5.63633M/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_median                  1771 ns         1774 ns            5 items_per_second=5.6372M/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_stddev                  11.1 ns         11.1 ns            5 items_per_second=35.1976k/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_cv                      0.63 %          0.63 %             5 items_per_second=0.62%
OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_mean                  18768 ns        18772 ns            5 items_per_second=6.821M/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_median                18776 ns        18779 ns            5 items_per_second=6.81616M/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_stddev                  400 ns          400 ns            5 items_per_second=143.963k/s
OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_cv                     2.13 %          2.13 %             5 items_per_second=2.11%
BM_OtlpPopulateRequest/span_count:1_mean                                       0.392 us        0.392 us            5 items_per_second=2.54888M/s
BM_OtlpPopulateRequest/span_count:1_median                                     0.391 us        0.391 us            5 items_per_second=2.55581M/s
BM_OtlpPopulateRequest/span_count:1_stddev                                     0.005 us        0.005 us            5 items_per_second=35.2498k/s
BM_OtlpPopulateRequest/span_count:1_cv                                          1.38 %          1.38 %             5 items_per_second=1.38%
BM_OtlpPopulateRequest/span_count:512_mean                                      24.0 us         24.0 us            5 items_per_second=21.3716M/s
BM_OtlpPopulateRequest/span_count:512_median                                    24.0 us         24.0 us            5 items_per_second=21.2952M/s
BM_OtlpPopulateRequest/span_count:512_stddev                                   0.321 us        0.321 us            5 items_per_second=288.031k/s
BM_OtlpPopulateRequest/span_count:512_cv                                        1.34 %          1.34 %             5 items_per_second=1.35%

#include "opentelemetry/trace/span_context.h"
#include "opentelemetry/trace/span_id.h"
#include "opentelemetry/trace/span_metadata.h"
Expand Down Expand Up @@ -114,24 +116,40 @@ void OtlpRecordable::SetResource(const sdk::resource::Resource &resource) noexce
resource_ = &resource;
}

void OtlpRecordable::SetSpanLimits(const opentelemetry::sdk::trace::SpanLimits &limits) noexcept
{
// Update the span limits to be the minimum of the current limits and the new limits
// The initial limits are set on construction from the deprecated span limits otlp options.
// TODO: replace with a simple copy once the deprecated options are removed.
span_limits_ = {
(std::min)(span_limits_.attribute_count_limit, limits.attribute_count_limit),
(std::min)(span_limits_.attribute_value_length_limit, limits.attribute_value_length_limit),
(std::min)(span_limits_.event_count_limit, limits.event_count_limit),
(std::min)(span_limits_.link_count_limit, limits.link_count_limit),
(std::min)(span_limits_.event_attribute_count_limit, limits.event_attribute_count_limit),
(std::min)(span_limits_.link_attribute_count_limit, limits.link_attribute_count_limit),
};
}

void OtlpRecordable::SetAttribute(nostd::string_view key,
const common::AttributeValue &value) noexcept
{
if (static_cast<uint32_t>(span_.attributes_size()) >= max_attributes_)
if (static_cast<std::uint32_t>(span_.attributes_size()) >= span_limits_.attribute_count_limit)
{
span_.set_dropped_attributes_count(span_.dropped_attributes_count() + 1);
return;
}

auto *attribute = span_.add_attributes();
OtlpPopulateAttributeUtils::PopulateAttribute(attribute, key, value, false);
OtlpPopulateAttributeUtils::PopulateAttribute(attribute, key, value, false,
span_limits_.attribute_value_length_limit);
}

void OtlpRecordable::AddEvent(nostd::string_view name,
common::SystemTimestamp timestamp,
const common::KeyValueIterable &attributes) noexcept
{
if (static_cast<uint32_t>(span_.events_size()) >= max_events_)
if (static_cast<std::uint32_t>(span_.events_size()) >= span_limits_.event_count_limit)
{
span_.set_dropped_events_count(span_.dropped_events_count() + 1);
return;
Expand All @@ -142,20 +160,22 @@ void OtlpRecordable::AddEvent(nostd::string_view name,
event->set_time_unix_nano(timestamp.time_since_epoch().count());

attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
if (static_cast<uint32_t>(event->attributes_size()) >= max_attributes_per_event_)
if (static_cast<std::uint32_t>(event->attributes_size()) >=
span_limits_.event_attribute_count_limit)
{
event->set_dropped_attributes_count(event->dropped_attributes_count() + 1);
return true;
}
OtlpPopulateAttributeUtils::PopulateAttribute(event->add_attributes(), key, value, false);
OtlpPopulateAttributeUtils::PopulateAttribute(event->add_attributes(), key, value, false,
span_limits_.attribute_value_length_limit);
return true;
});
}

void OtlpRecordable::AddLink(const trace::SpanContext &span_context,
const common::KeyValueIterable &attributes) noexcept
{
if (static_cast<uint32_t>(span_.links_size()) >= max_links_)
if (static_cast<std::uint32_t>(span_.links_size()) >= span_limits_.link_count_limit)
{
span_.set_dropped_links_count(span_.dropped_links_count() + 1);
return;
Expand All @@ -167,12 +187,14 @@ void OtlpRecordable::AddLink(const trace::SpanContext &span_context,
trace::SpanId::kSize);
link->set_trace_state(span_context.trace_state()->ToHeader());
attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
if (static_cast<uint32_t>(link->attributes_size()) >= max_attributes_per_link_)
if (static_cast<std::uint32_t>(link->attributes_size()) >=
span_limits_.link_attribute_count_limit)
{
link->set_dropped_attributes_count(link->dropped_attributes_count() + 1);
return true;
}
OtlpPopulateAttributeUtils::PopulateAttribute(link->add_attributes(), key, value, false);
OtlpPopulateAttributeUtils::PopulateAttribute(link->add_attributes(), key, value, false,
span_limits_.attribute_value_length_limit);
return true;
});
}
Expand Down
Loading
Loading