Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e244122
[SDK] LogRecord attribute limits enforcement
thc1006 Jun 14, 2026
1612edc
[SDK] LogRecord limits: address review (store by value, OTLP UTF-8-aw…
thc1006 Jun 17, 2026
c4532cd
[SDK] LogRecord limits: address dbarker review (extract utilities, de…
thc1006 Jun 19, 2026
3144991
[SDK] LogRecord limits: fix noexcept correctness + restore Bazel link
thc1006 Jun 19, 2026
ccdd5e9
Merge branch 'main' into feat/log-record-limits-4126
dbarker Jun 20, 2026
8c965e7
[SDK] LogRecord limits: fix MSVC variable shadow + drop dead includes…
thc1006 Jun 21, 2026
5588f2c
Merge branch 'main' into feat/log-record-limits-4126
dbarker Jun 23, 2026
cd413f8
[SDK] LogRecord limits: address dbarker v3 review (truncate during co…
thc1006 Jun 23, 2026
6c68257
Merge branch 'main' into feat/log-record-limits-4126
ThomsonTan Jun 25, 2026
3e32fd7
[SDK] LogRecord limits: gate SetLogRecordLimits on processor capability
thc1006 Jun 25, 2026
0977ddd
[SDK] LogRecord limits: UTF-8-safe truncation in AttributeConverter
thc1006 Jun 25, 2026
3be2379
[SDK] LogRecord limits: satisfy the clang-tidy warning limit
thc1006 Jun 26, 2026
622d670
[SDK] LogRecord limits: default recordable to no limits, inject via p…
thc1006 Jun 26, 2026
9627722
Merge branch 'main' into feat/log-record-limits-4126
dbarker Jun 26, 2026
980ef8e
[SDK] LogRecord limits: address dbarker review (comments, UTF-8 scan …
thc1006 Jun 27, 2026
0ac2cf3
Merge branch 'main' into feat/log-record-limits-4126
dbarker Jun 29, 2026
378442f
[SDK] LogRecord limits: drop vtable implementation-detail comment
thc1006 Jun 30, 2026
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ Increment the:
* [CI] iwyu and clang-tidy: use install_thirdparty.sh for third-party
[#4136](https://github.com/open-telemetry/opentelemetry-cpp/pull/4136)

* [SDK] LogRecord attribute limits enforcement
[#4157](https://github.com/open-telemetry/opentelemetry-cpp/pull/4157)

* [CONFIGURATION] File configuration: declarative resource detection types
[#4148](https://github.com/open-telemetry/opentelemetry-cpp/pull/4148)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class OStreamLogRecordExporter final : public opentelemetry::sdk::logs::LogRecor

std::unique_ptr<sdk::logs::Recordable> MakeRecordable() noexcept override;

/**
* The ostream exporter uses ReadWriteLogRecord, which applies the configured
* LogRecord attribute limits, so the SDK should push the limits onto each
* record it produces.
*/
bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; }

/**
* Exports a span of logs sent from the processor.
*/
Expand Down
17 changes: 17 additions & 0 deletions exporters/otlp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,23 @@ cc_test(
],
)

cc_test(
name = "otlp_populate_attribute_utils_test",
srcs = [
"test/otlp_populate_attribute_utils_test.cc",
],
tags = [
"otlp",
"test",
],
deps = [
":otlp_recordable",
"//sdk/src/metrics",
"@com_github_opentelemetry_proto//:common_proto_cc",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "otlp_file_client_test",
srcs = ["test/otlp_file_client_test.cc"],
Expand Down
10 changes: 10 additions & 0 deletions exporters/otlp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,16 @@ if(BUILD_TESTING)
TEST_PREFIX exporter.otlp.
TEST_LIST otlp_log_recordable_test)

add_executable(otlp_populate_attribute_utils_test
test/otlp_populate_attribute_utils_test.cc)
target_link_libraries(
otlp_populate_attribute_utils_test ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_otlp_recordable)
gtest_add_tests(
TARGET otlp_populate_attribute_utils_test
TEST_PREFIX exporter.otlp.
TEST_LIST otlp_populate_attribute_utils_test)

add_executable(otlp_metrics_serialization_test
test/otlp_metrics_serialization_test.cc)
target_link_libraries(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class OtlpFileLogRecordExporter final : public opentelemetry::sdk::logs::LogReco
*/
std::unique_ptr<opentelemetry::sdk::logs::Recordable> MakeRecordable() noexcept override;

/**
* The OTLP recordable applies the configured LogRecord attribute limits, so
* the SDK should push the limits onto each record it produces.
*/
bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; }

/**
* Exports a vector of log records to the Elasticsearch instance. Guaranteed to return after a
* timeout specified from the options passed from the constructor.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ class OtlpGrpcLogRecordExporter : public opentelemetry::sdk::logs::LogRecordExpo
*/
std::unique_ptr<opentelemetry::sdk::logs::Recordable> MakeRecordable() noexcept override;

/**
* The OTLP recordable applies the configured LogRecord attribute limits, so
* the SDK should push the limits onto each record it produces.
*/
bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; }

/**
* Exports a vector of log records to the configured gRPC endpoint. Guaranteed to return after a
* timeout specified from the options passed to the constructor.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ class OtlpHttpLogRecordExporter final : public opentelemetry::sdk::logs::LogReco
*/
std::unique_ptr<opentelemetry::sdk::logs::Recordable> MakeRecordable() noexcept override;

/**
* The OTLP recordable applies the configured LogRecord attribute limits, so
* the SDK should push the limits onto each record it produces.
*/
bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; }

/**
* Exports a vector of log records to the Elasticsearch instance. Guaranteed to return after a
* timeout specified from the options passed from the constructor.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
#include "opentelemetry/sdk/logs/log_record_limits.h"
#include "opentelemetry/sdk/logs/recordable.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/version.h"
Expand Down Expand Up @@ -96,6 +97,14 @@ class OtlpLogRecordable final : public opentelemetry::sdk::logs::Recordable
void SetAttribute(nostd::string_view key,
const opentelemetry::common::AttributeValue &value) noexcept override;

/**
* Apply attribute count and value length limits. Must be called before any
* SetAttribute call to take effect. The limits are copied into this
* recordable.
*/
void SetLogRecordLimits(
const opentelemetry::sdk::logs::LogRecordLimits &limits) noexcept override;

/**
* Set Resource of this log
* @param Resource the resource to set
Expand All @@ -114,6 +123,12 @@ class OtlpLogRecordable final : public opentelemetry::sdk::logs::Recordable
const opentelemetry::sdk::resource::Resource *resource_ = nullptr;
const opentelemetry::sdk::instrumentationscope::InstrumentationScope *instrumentation_scope_ =
nullptr;
// Stored by value so the recordable does not depend on the limits object
// outliving the LoggerContext that supplied it. Defaults to no limits; the
// LoggerProvider wiring injects the configured limits via SetLogRecordLimits,
// so a recordable used outside a provider does not cap attributes on its own.
opentelemetry::sdk::logs::LogRecordLimits limits_ =
opentelemetry::sdk::logs::LogRecordLimits::NoLimits();
};

} // namespace otlp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

#pragma once

#include <cstddef>
#include <limits>
#include <string>

#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
Expand Down Expand Up @@ -55,23 +59,61 @@ class OtlpPopulateAttributeUtils
const opentelemetry::sdk::instrumentationscope::InstrumentationScope
&instrumentation_scope) noexcept;

static void PopulateAnyValue(opentelemetry::proto::common::v1::AnyValue *proto_value,
const opentelemetry::common::AttributeValue &value,
bool allow_bytes) noexcept;
/**
* Populate a proto AnyValue from a non-owning AttributeValue.
* When `max_length` is less than `std::numeric_limits<std::size_t>::max()`,
* string alternatives are truncated to at most `max_length` bytes using
* UTF-8-safe truncation (Utf8SafePrefixLength) so the resulting proto
* `string_value` stays valid UTF-8 when the input was. Raw bytes
* (`span<const uint8_t>` when `allow_bytes` is true) are cut at the raw
* byte boundary since they are not UTF-8 text. Non-string alternatives
* are unaffected.
*/
static void PopulateAnyValue(
opentelemetry::proto::common::v1::AnyValue *proto_value,
const opentelemetry::common::AttributeValue &value,
bool allow_bytes,
std::size_t max_length = (std::numeric_limits<std::size_t>::max)()) noexcept;

static void PopulateAnyValue(
opentelemetry::proto::common::v1::AnyValue *proto_value,
const opentelemetry::sdk::common::OwnedAttributeValue &value,
bool allow_bytes,
std::size_t max_length = (std::numeric_limits<std::size_t>::max)()) noexcept;

static void PopulateAttribute(
opentelemetry::proto::common::v1::KeyValue *attribute,
nostd::string_view key,
const opentelemetry::common::AttributeValue &value,
bool allow_bytes,
std::size_t max_length = (std::numeric_limits<std::size_t>::max)()) noexcept;

static void PopulateAnyValue(opentelemetry::proto::common::v1::AnyValue *proto_value,
const opentelemetry::sdk::common::OwnedAttributeValue &value,
bool allow_bytes) noexcept;
static void PopulateAttribute(
opentelemetry::proto::common::v1::KeyValue *attribute,
nostd::string_view key,
const opentelemetry::sdk::common::OwnedAttributeValue &value,
bool allow_bytes,
std::size_t max_length = (std::numeric_limits<std::size_t>::max)()) noexcept;

static void PopulateAttribute(opentelemetry::proto::common::v1::KeyValue *attribute,
nostd::string_view key,
const opentelemetry::common::AttributeValue &value,
bool allow_bytes) noexcept;
/**
* Byte length of the longest prefix of `value` that fits within `max_bytes`
* without splitting a well-formed UTF-8 multi-byte sequence. A lead byte's
* declared length is only honored when its continuation bytes are present
* and in range (0x80-0xBF); otherwise the lead is treated as a one-byte
* unit, so malformed input degrades to plain byte truncation. The protobuf
* `string` field type requires valid UTF-8, so this utility lets callers
* truncate at a code-point boundary instead of cutting through a multi-byte
* sequence.
*/
static std::size_t Utf8SafePrefixLength(const char *data,
std::size_t size,
std::size_t max_bytes) noexcept;

static void PopulateAttribute(opentelemetry::proto::common::v1::KeyValue *attribute,
nostd::string_view key,
const opentelemetry::sdk::common::OwnedAttributeValue &value,
bool allow_bytes) noexcept;
/// Convenience overload that delegates to the pointer + size variant.
static std::size_t Utf8SafePrefixLength(const std::string &value, std::size_t max_bytes) noexcept
{
return Utf8SafePrefixLength(value.data(), value.size(), max_bytes);
}
};

} // namespace otlp
Expand Down
17 changes: 16 additions & 1 deletion exporters/otlp/src/otlp_log_recordable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
// SPDX-License-Identifier: Apache-2.0

#include "opentelemetry/exporters/otlp/otlp_log_recordable.h"
#include <cstddef>
#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/common/timestamp.h"
#include "opentelemetry/exporters/otlp/otlp_populate_attribute_utils.h"
#include "opentelemetry/logs/severity.h"
#include "opentelemetry/nostd/span.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
#include "opentelemetry/sdk/logs/log_record_limits.h"
#include "opentelemetry/sdk/logs/readable_log_record.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/trace/span_id.h"
Expand Down Expand Up @@ -236,7 +238,20 @@ void OtlpLogRecordable::SetTraceFlags(const opentelemetry::trace::TraceFlags &tr
void OtlpLogRecordable::SetAttribute(opentelemetry::nostd::string_view key,
const opentelemetry::common::AttributeValue &value) noexcept
{
OtlpPopulateAttributeUtils::PopulateAttribute(proto_record_.add_attributes(), key, value, true);
if (static_cast<std::size_t>(proto_record_.attributes_size()) >= limits_.attribute_count_limit)
{
proto_record_.set_dropped_attributes_count(proto_record_.dropped_attributes_count() + 1);
return;
}

OtlpPopulateAttributeUtils::PopulateAttribute(proto_record_.add_attributes(), key, value, true,
limits_.attribute_value_length_limit);
}

void OtlpLogRecordable::SetLogRecordLimits(
const opentelemetry::sdk::logs::LogRecordLimits &limits) noexcept
{
limits_ = limits;
}

void OtlpLogRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept
Expand Down
Loading
Loading