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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ struct OPENTELEMETRY_EXPORT OtlpGrpcExporterOptions : public OtlpGrpcClientOptio
OtlpGrpcExporterOptions &operator=(const OtlpGrpcExporterOptions &) = default;
OtlpGrpcExporterOptions &operator=(OtlpGrpcExporterOptions &&) = default;
~OtlpGrpcExporterOptions() override;

/** Collection Limits. No limit by default. */
std::uint32_t max_attributes = UINT32_MAX;
std::uint32_t max_events = UINT32_MAX;
std::uint32_t max_links = UINT32_MAX;
std::uint32_t max_attributes_per_event = UINT32_MAX;
std::uint32_t max_attributes_per_link = UINT32_MAX;
};

} // namespace otlp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ 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. */
std::uint32_t max_attributes = UINT32_MAX;
std::uint32_t max_events = UINT32_MAX;
std::uint32_t max_links = UINT32_MAX;
std::uint32_t max_attributes_per_event = UINT32_MAX;
std::uint32_t max_attributes_per_link = UINT32_MAX;
};

} // namespace otlp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#pragma once

#include <chrono>
#include <cstdint> // For uint32_t
#include <limits> // For UINT32_MAX
#include <string>

// clang-format off
Expand Down Expand Up @@ -35,6 +37,18 @@ namespace otlp
class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable
{
public:
explicit OtlpRecordable(uint32_t max_attributes = UINT32_MAX,
uint32_t max_events = UINT32_MAX,
uint32_t max_links = UINT32_MAX,
uint32_t max_attributes_per_event = UINT32_MAX,
uint32_t max_attributes_per_link = UINT32_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)
{}

proto::trace::v1::Span &span() noexcept { return span_; }
const proto::trace::v1::Span &span() const noexcept { return span_; }

Expand Down Expand Up @@ -85,6 +99,11 @@ class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable
const opentelemetry::sdk::resource::Resource *resource_ = nullptr;
const opentelemetry::sdk::instrumentationscope::InstrumentationScope *instrumentation_scope_ =
nullptr;
uint32_t max_attributes_;
uint32_t max_events_;
uint32_t max_links_;
uint32_t max_attributes_per_event_;
uint32_t max_attributes_per_link_;
};
} // namespace otlp
} // namespace exporter
Expand Down
4 changes: 3 additions & 1 deletion exporters/otlp/src/otlp_grpc_exporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ OtlpGrpcExporter::~OtlpGrpcExporter()

std::unique_ptr<sdk::trace::Recordable> OtlpGrpcExporter::MakeRecordable() noexcept
{
return std::unique_ptr<sdk::trace::Recordable>(new OtlpRecordable);
return std::unique_ptr<sdk::trace::Recordable>(
new OtlpRecordable(options_.max_attributes, options_.max_events, options_.max_links,
options_.max_attributes_per_event, options_.max_attributes_per_link));
}

sdk::common::ExportResult OtlpGrpcExporter::Export(
Expand Down
5 changes: 3 additions & 2 deletions exporters/otlp/src/otlp_http_exporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,9 @@ OtlpHttpExporter::OtlpHttpExporter(std::unique_ptr<OtlpHttpClient> http_client)

std::unique_ptr<opentelemetry::sdk::trace::Recordable> OtlpHttpExporter::MakeRecordable() noexcept
{
return std::unique_ptr<opentelemetry::sdk::trace::Recordable>(
new exporter::otlp::OtlpRecordable());
return std::unique_ptr<opentelemetry::sdk::trace::Recordable>(new exporter::otlp::OtlpRecordable(
options_.max_attributes, options_.max_events, options_.max_links,
options_.max_attributes_per_event, options_.max_attributes_per_link));
}

opentelemetry::sdk::common::ExportResult OtlpHttpExporter::Export(
Expand Down
27 changes: 27 additions & 0 deletions exporters/otlp/src/otlp_recordable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ void OtlpRecordable::SetResource(const sdk::resource::Resource &resource) noexce
void OtlpRecordable::SetAttribute(nostd::string_view key,
const common::AttributeValue &value) noexcept
{
if (static_cast<uint32_t>(span_.attributes_size()) >= max_attributes_)
{
span_.set_dropped_attributes_count(span_.dropped_attributes_count() + 1);
return;
}

auto *attribute = span_.add_attributes();
OtlpPopulateAttributeUtils::PopulateAttribute(attribute, key, value, false);
}
Expand All @@ -128,11 +134,22 @@ 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_)
{
span_.set_dropped_events_count(span_.dropped_events_count() + 1);
return;
}

auto *event = span_.add_events();
event->set_name(name.data(), name.size());
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_)
{
event->set_dropped_attributes_count(event->dropped_attributes_count() + 1);
return true;
}
OtlpPopulateAttributeUtils::PopulateAttribute(event->add_attributes(), key, value, false);
return true;
});
Expand All @@ -141,13 +158,23 @@ void OtlpRecordable::AddEvent(nostd::string_view name,
void OtlpRecordable::AddLink(const trace::SpanContext &span_context,
const common::KeyValueIterable &attributes) noexcept
{
if (static_cast<uint32_t>(span_.links_size()) >= max_links_)
{
span_.set_dropped_links_count(span_.dropped_links_count() + 1);
return;
}
auto *link = span_.add_links();
link->set_trace_id(reinterpret_cast<const char *>(span_context.trace_id().Id().data()),
trace::TraceId::kSize);
link->set_span_id(reinterpret_cast<const char *>(span_context.span_id().Id().data()),
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_)
{
link->set_dropped_attributes_count(link->dropped_attributes_count() + 1);
return true;
}
OtlpPopulateAttributeUtils::PopulateAttribute(link->add_attributes(), key, value, false);
return true;
});
Expand Down
41 changes: 41 additions & 0 deletions exporters/otlp/test/otlp_recordable_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,47 @@ TYPED_TEST(IntAttributeTest, SetIntArrayAttribute)
EXPECT_EQ(rec.span().attributes(0).value().array_value().values(i).int_value(), int_span[i]);
}
}

TEST(OtlpRecordableTest, TestCollectionLimits)
{
// Initialize recordable with strict limits:
// Max Attributes: 2, Max Events: 2, Max Links: 2
// Max Attributes Per Event: 1, Max Attributes Per Link: 1
OtlpRecordable recordable(2, 2, 2, 1, 1);

// Test Attribute Limits
recordable.SetAttribute("attr1", 1);
recordable.SetAttribute("attr2", 2);
recordable.SetAttribute("attr3", 3); // Should be dropped

EXPECT_EQ(recordable.span().attributes_size(), 2);
EXPECT_EQ(recordable.span().dropped_attributes_count(), 1);

// Test Event Limits
std::map<std::string, int> empty_map;
common::KeyValueIterableView<std::map<std::string, int>> empty_attrs(empty_map);

recordable.AddEvent("event1", std::chrono::system_clock::now(), empty_attrs);
recordable.AddEvent("event2", std::chrono::system_clock::now(), empty_attrs);
recordable.AddEvent("event3", std::chrono::system_clock::now(),
empty_attrs); // Should be dropped

EXPECT_EQ(recordable.span().events_size(), 2);
EXPECT_EQ(recordable.span().dropped_events_count(), 1);

// Test Per-Event Attribute Limits
std::map<std::string, int> attr_map = {{"e_attr1", 1}, {"e_attr2", 2}};
common::KeyValueIterableView<std::map<std::string, int>> event_attrs(attr_map);

OtlpRecordable event_recordable(10, 10, 10, 1, 1);
event_recordable.AddEvent("event_with_attrs", std::chrono::system_clock::now(), event_attrs);

auto &event_list = event_recordable.span().events();
ASSERT_EQ(event_list.size(), 1);
// The event itself should have 1 attribute, and 1 dropped
EXPECT_EQ(event_list[0].attributes_size(), 1);
EXPECT_EQ(event_list[0].dropped_attributes_count(), 1);
}
} // namespace otlp
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
Loading