Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
622f8fb
Add bundle version of utf8_range to validate attribute values
owent Jul 1, 2025
b529903
Fixes styles and copyright
owent Jul 14, 2025
c924a76
Fixes type in `utf8_range_IsTrailByteOk`
owent Jul 14, 2025
d89343c
Fixes IWYU and clang-tidy warnings
owent Jul 14, 2025
d800b20
Add unit tests for resources and trace span
owent Jul 18, 2025
823fbfb
Add CHANGELOG
owent Jul 19, 2025
1702bc5
Fixes warnings
owent Jul 19, 2025
d0b4bfe
Fixes a ununsed warning
owent Jul 19, 2025
3c70972
Merge branch 'main' into validate_utf8_string_in_setters
owent Jul 26, 2025
1f75948
Merge branch 'main' into validate_utf8_string_in_setters
owent Aug 1, 2025
0de8b28
Merge branch 'main' into validate_utf8_string_in_setters
lalitb Aug 12, 2025
4c552f4
Merge branch 'main' into validate_utf8_string_in_setters
owent Aug 13, 2025
339c7b6
Fixes cppcheck warning
owent Aug 15, 2025
2ab31e8
Merge branch 'main' into validate_utf8_string_in_setters
ThomsonTan Aug 22, 2025
cd524a3
Merge branch 'main' into validate_utf8_string_in_setters
owent Aug 28, 2025
1fd6ef7
Merge branch 'main' into validate_utf8_string_in_setters
owent Aug 30, 2025
ff58708
Fixes inline
owent Aug 30, 2025
6625f78
Merge branch 'main' into validate_utf8_string_in_setters
owent Aug 30, 2025
52ef536
Merge branch 'main' into validate_utf8_string_in_setters
owent Sep 12, 2025
dbaa91d
Merge branch 'main' into validate_utf8_string_in_setters
owent Sep 26, 2025
007b26a
Merge branch 'main' into validate_utf8_string_in_setters
owent Sep 26, 2025
83cb1d3
Fixes markdownlint
owent Sep 26, 2025
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 @@ -15,6 +15,9 @@ Increment the:

## [Unreleased]

* [SDK] Add bundle version of utf8_range to validate attributes
[#3512](https://github.com/open-telemetry/opentelemetry-cpp/pull/3512)

* [TEST] Remove workaround for metrics cardinality limit test
[#3663](https://github.com/open-telemetry/opentelemetry-cpp/pull/3663)

Expand Down
47 changes: 42 additions & 5 deletions exporters/otlp/src/otlp_populate_attribute_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/sdk/common/attribute_validity.h"
#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/version.h"
Expand Down Expand Up @@ -85,8 +86,18 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
}
else if (nostd::holds_alternative<nostd::string_view>(value))
{
proto_value->set_string_value(nostd::get<nostd::string_view>(value).data(),
nostd::get<nostd::string_view>(value).size());
if (allow_bytes &&
!opentelemetry::sdk::common::AttributeIsValidString(nostd::get<nostd::string_view>(value)))
{
proto_value->set_bytes_value(
reinterpret_cast<const void *>(nostd::get<nostd::string_view>(value).data()),
nostd::get<nostd::string_view>(value).size());
}
else
{
proto_value->set_string_value(nostd::get<nostd::string_view>(value).data(),
nostd::get<nostd::string_view>(value).size());
}
}
else if (nostd::holds_alternative<nostd::span<const uint8_t>>(value))
{
Expand Down Expand Up @@ -159,7 +170,15 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
auto array_value = proto_value->mutable_array_value();
for (const auto &val : nostd::get<nostd::span<const nostd::string_view>>(value))
{
array_value->add_values()->set_string_value(val.data(), val.size());
if (allow_bytes && !opentelemetry::sdk::common::AttributeIsValidString(val))
{
array_value->add_values()->set_bytes_value(reinterpret_cast<const void *>(val.data()),
val.size());
}
else
{
array_value->add_values()->set_string_value(val.data(), val.size());
}
}
}
}
Expand Down Expand Up @@ -224,7 +243,17 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
}
else if (nostd::holds_alternative<std::string>(value))
{
proto_value->set_string_value(nostd::get<std::string>(value));
if (allow_bytes &&
!opentelemetry::sdk::common::AttributeIsValidString(nostd::get<std::string>(value)))
{
proto_value->set_bytes_value(
reinterpret_cast<const void *>(nostd::get<std::string>(value).data()),
nostd::get<std::string>(value).size());
}
else
{
proto_value->set_string_value(nostd::get<std::string>(value));
}
}
else if (nostd::holds_alternative<std::vector<bool>>(value))
{
Expand Down Expand Up @@ -281,7 +310,15 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
auto array_value = proto_value->mutable_array_value();
for (const auto &val : nostd::get<std::vector<std::string>>(value))
{
array_value->add_values()->set_string_value(val);
if (allow_bytes && !opentelemetry::sdk::common::AttributeIsValidString(val))
{
array_value->add_values()->set_bytes_value(reinterpret_cast<const void *>(val.data()),
val.size());
}
else
{
array_value->add_values()->set_string_value(val);
}
}
}
}
Expand Down
129 changes: 129 additions & 0 deletions sdk/include/opentelemetry/sdk/common/attribute_validity.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/nostd/function_ref.h"
#include "opentelemetry/nostd/span.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace common
{

OPENTELEMETRY_EXPORT bool AttributeIsValidString(nostd::string_view value) noexcept;

/**
* Validate if an attribute value is valid.
*/
struct AttributeValidator
{
bool operator()(bool /*v*/) noexcept { return true; }
bool operator()(int32_t /*v*/) noexcept { return true; }
bool operator()(uint32_t /*v*/) noexcept { return true; }
bool operator()(int64_t /*v*/) noexcept { return true; }
bool operator()(uint64_t /*v*/) noexcept { return true; }
bool operator()(double /*v*/) noexcept { return true; }
bool operator()(nostd::string_view v) noexcept { return AttributeIsValidString(v); }
bool operator()(const std::string &v) noexcept { return AttributeIsValidString(v); }
bool operator()(const char *v) noexcept { return AttributeIsValidString(v); }
bool operator()(nostd::span<const uint8_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const bool> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const int32_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const uint32_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const int64_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const uint64_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const double> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const nostd::string_view> v) noexcept
{
for (const auto &s : v)
{
if (!AttributeIsValidString(s))
{
return false;
}
}
return true;
}
bool operator()(const std::vector<bool> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<int32_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<uint32_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<int64_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<double> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<std::string> &v)
{
for (const auto &s : v)
{
if (!AttributeIsValidString(s))
{
return false;
}
}
return true;
}
bool operator()(const std::vector<uint64_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<uint8_t> & /*v*/) noexcept { return true; }

OPENTELEMETRY_EXPORT static bool IsValid(const std::string &value) noexcept;

OPENTELEMETRY_EXPORT static bool IsValid(nostd::string_view value) noexcept;

OPENTELEMETRY_EXPORT static bool IsValid(const OwnedAttributeValue &value) noexcept;

OPENTELEMETRY_EXPORT static bool IsValid(
const opentelemetry::common::AttributeValue &value) noexcept;

OPENTELEMETRY_EXPORT static bool IsAllValid(const AttributeMap &attributes) noexcept;

OPENTELEMETRY_EXPORT static bool IsAllValid(const OrderedAttributeMap &attributes) noexcept;

OPENTELEMETRY_EXPORT static void Filter(AttributeMap &attributes, nostd::string_view log_hint);

OPENTELEMETRY_EXPORT static void Filter(OrderedAttributeMap &attributes,
nostd::string_view log_hint);
};

/**
* Supports internal iteration over a collection of key-value pairs and filtering of invalid
* attributes.
*/
class OPENTELEMETRY_EXPORT KeyValueFilterIterable : public opentelemetry::common::KeyValueIterable
{
public:
KeyValueFilterIterable(const opentelemetry::common::KeyValueIterable &origin,
opentelemetry::nostd::string_view log_hint) noexcept;

~KeyValueFilterIterable() override;

bool ForEachKeyValue(
opentelemetry::nostd::function_ref<bool(opentelemetry::nostd::string_view,
opentelemetry::common::AttributeValue)> callback)
const noexcept override;

size_t size() const noexcept override;

private:
// Pointer to the original KeyValueIterable
const opentelemetry::common::KeyValueIterable *origin_;

// Size of valid attributes
mutable size_t size_;

// Log hint for invalid attributes
opentelemetry::nostd::string_view log_hint_;
};

} // namespace common
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "opentelemetry/nostd/unique_ptr.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/sdk/common/attribute_validity.h"
#include "opentelemetry/sdk/common/global_log_handler.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -42,6 +44,7 @@ class InstrumentationScope
nostd::string_view schema_url = "",
InstrumentationScopeAttributes &&attributes = {})
{
common::AttributeValidator::Filter(attributes, "[InstrumentationScope]");
return nostd::unique_ptr<InstrumentationScope>(
new InstrumentationScope{name, version, schema_url, std::move(attributes)});
}
Expand All @@ -60,8 +63,19 @@ class InstrumentationScope
nostd::string_view schema_url,
const InstrumentationScopeAttributes &attributes)
{
return nostd::unique_ptr<InstrumentationScope>(new InstrumentationScope{
name, version, schema_url, InstrumentationScopeAttributes(attributes)});
// Copy attributes only when we find some invalid attributes and try to remove them.
if (common::AttributeValidator::IsAllValid(attributes))
{
return nostd::unique_ptr<InstrumentationScope>(new InstrumentationScope{
name, version, schema_url, InstrumentationScopeAttributes(attributes)});
}
else
{
InstrumentationScopeAttributes copy_attributes = attributes;
common::AttributeValidator::Filter(copy_attributes, "[InstrumentationScope]");
return nostd::unique_ptr<InstrumentationScope>(new InstrumentationScope{
name, version, schema_url, InstrumentationScopeAttributes(copy_attributes)});
}
}

/**
Expand All @@ -88,6 +102,19 @@ class InstrumentationScope
result->attributes_.reserve(opentelemetry::nostd::size(arg));
for (auto &argv : arg)
{
if (!common::AttributeValidator::IsValid(argv.first))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute key "
<< std::string{argv.first} << ". This attribute will be ignored.");
continue;
}

if (!common::AttributeValidator::IsValid(argv.second))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute value for "
<< std::string{argv.first} << ". This attribute will be ignored.");
continue;
}
result->SetAttribute(argv.first, argv.second);
}

Expand Down Expand Up @@ -148,6 +175,19 @@ class InstrumentationScope
void SetAttribute(nostd::string_view key,
const opentelemetry::common::AttributeValue &value) noexcept
{
if (!common::AttributeValidator::IsValid(key))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute key "
<< std::string{key} << ". This attribute will be ignored.");
return;
}

if (!common::AttributeValidator::IsValid(value))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute value for "
<< std::string{key} << ". This attribute will be ignored.");
return;
}
attributes_[std::string(key)] =
nostd::visit(opentelemetry::sdk::common::AttributeConverter(), value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/common/attribute_validity.h"
#include "opentelemetry/sdk/common/custom_hash_equality.h"
#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h"
#include "opentelemetry/version.h"
Expand Down Expand Up @@ -59,7 +60,8 @@ class DefaultAttributesProcessor : public AttributesProcessor
MetricAttributes process(
const opentelemetry::common::KeyValueIterable &attributes) const noexcept override
{
MetricAttributes result(attributes);
MetricAttributes result(
opentelemetry::sdk::common::KeyValueFilterIterable(attributes, "[Metrics] "));
return result;
}

Expand All @@ -86,7 +88,9 @@ class FilteringAttributesProcessor : public AttributesProcessor
const opentelemetry::common::KeyValueIterable &attributes) const noexcept override
{
MetricAttributes result;
attributes.ForEachKeyValue(
opentelemetry::sdk::common::KeyValueFilterIterable validate_attributes{attributes,
"[Metrics] "};
validate_attributes.ForEachKeyValue(
[&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept {
if (opentelemetry::sdk::common::find_heterogeneous(allowed_attribute_keys_, key) !=
allowed_attribute_keys_.end())
Expand Down
30 changes: 30 additions & 0 deletions sdk/src/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,36 @@

package(default_visibility = ["//visibility:public"])

cc_library(
name = "utf8_range",
srcs = [
"internal/utf8_range/uft8_range.cc",
],
hdrs = [
"internal/utf8_range/utf8_range.h",
"internal/utf8_range/utf8_range_neon.inc",
"internal/utf8_range/utf8_range_sse.inc",
],
include_prefix = "src/common",
deps = [
"//api",
],
)

cc_library(
name = "attribute_validity",
srcs = [
"attribute_validity.cc",
],
include_prefix = "src/common",
deps = [
"//api",
"//sdk:headers",
"//sdk/src/common:global_log_handler",
"//sdk/src/common:utf8_range",
],
)

cc_library(
name = "random",
srcs = [
Expand Down
10 changes: 8 additions & 2 deletions sdk/src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

set(COMMON_SRCS random.cc global_log_handler.cc env_variables.cc base64.cc
disabled.cc)
set(COMMON_SRCS
random.cc
global_log_handler.cc
env_variables.cc
base64.cc
disabled.cc
attribute_validity.cc
internal/utf8_range/uft8_range.cc)
if(WIN32)
list(APPEND COMMON_SRCS platform/fork_windows.cc)
else()
Expand Down
Loading
Loading