Skip to content

Proposal: Include semantic convention attribute types in generated C++ SDK headers #3808

@dol

Description

@dol

Background

Currently, semantic convention attributes generated for OpenTelemetry C++ SDK only include string constants for attribute names, with type information (e.g., string, string[], int, bool) only present in source YAML or template files. This limits the ability of applications and SDK to perform type-based sanity checks for attributes, especially when setting resource or span attributes according to semantic conventions.

Reference: Java Implementation

The Java API provides type-safe semantic convention attributes using [AttributeKey<T>](https://github.com/open-telemetry/opentelemetry-java/blob/main/api/all/src/main/java/io/opentelemetry/api/common/AttributeKey. java).
This approach:

  • Exposes a generic, type-safe key with compile-time checks.
  • Supplies factory methods for common types and arrays.
  • Enables both compile-time and runtime attribute validation.

Example:

AttributeKey<String> kHostName = AttributeKey.stringKey("host.name");
AttributeKey<List<String>> kHostIp = AttributeKey.stringArrayKey("host.ip");

Type-Safe API with Backward Compatibility

Inspired by the Java AttributeKey<T> approach, this proposal introduces a type-safe API for semantic convention attributes, while fully preserving backward compatibility by keeping the existing API unchanged.

Solution: Dual API with Forward-Looking Migration Path

  1. Keep the current ResourceAttributes API as-is for legacy/backward-compatible usage (planned for eventual deprecation).
  2. Introduce a new type-safe API:
    • Add a TypedResourceAttributes class that provides a template method for setting attributes using typed keys.
    • Generate new AttributeKey<T> definitions for each semantic convention attribute, exposing both the attribute name and the expected type at compile time.
    • Generate the new keys in a typed sub-namespace for each semconv group, e.g., semconv::host::typed::kHostIp.
  3. Update SDK to accept TypedResourceAttributes directly:
    • Modify Resource::Create() to accept both ResourceAttributes (legacy) and TypedResourceAttributes (new).
    • No conversion methods — the new API is the forward path; the old API will eventually be deprecated and removed.
  4. Custom attributes are easy to define:
    • Users can define their own typed attribute keys using the same AttributeKey<T> pattern.

Example

// Legacy usage (unchanged, but deprecated in future)
ResourceAttributes legacy_attrs;
legacy_attrs.SetAttribute(semconv::host::kHostIp, std::vector<std::string>{"192.168.1.1"});
auto resource = Resource::Create(legacy_attrs); // Still works

// NEW:  Type-safe usage (forward-looking)
TypedResourceAttributes attrs;
attrs.Set(semconv::host::typed::kHostIp, std::vector<std::string>{"192.168.1.1"});
attrs.Set(semconv:: host::typed::kHostName, "my-server");

// Compile-time error for wrong type:
// attrs.Set(semconv::host::typed::kHostIp, "192.168.1.1");  // Error!

// Direct usage — no conversion needed
auto resource = Resource::Create(attrs); // NEW: Accepts TypedResourceAttributes

// Custom attributes
constexpr auto kMyCustomAttr = AttributeKey<int64_t>("my.custom.metric", AttributeType::kInt64);
attrs.Set(kMyCustomAttr, 12345);

Generated Semantic Convention Example

namespace semconv::host {
    // Legacy (eventually deprecated)
    static constexpr const char* kHostIp = "host.ip";
    
    // NEW: Type-safe keys
    namespace typed {
        constexpr auto kHostIp = AttributeKey<std::vector<std::string>>("host.ip", AttributeType::kStringArray);
        constexpr auto kHostName = AttributeKey<std::string>("host.name", AttributeType::kString);
        constexpr auto kHostCpuCacheL2Size = AttributeKey<int64_t>("host.cpu.cache.l2.size", AttributeType::kInt64);
    }
}

SDK API Update

namespace opentelemetry::sdk::resource {

class Resource {
public:
    // Legacy (keep for backward compatibility)
    static std::unique_ptr<Resource> Create(const ResourceAttributes& attributes);
    
    // NEW:  Forward-looking type-safe API
    static std::unique_ptr<Resource> Create(const TypedResourceAttributes& attributes);
};

} // namespace

Benefits

  • Backward compatible: All existing code works as-is.
  • Compile-time safety: New code cannot set incorrect types for semantic conventions.
  • Forward-looking: No conversion methods — TypedResourceAttributes is the future.
  • Clear migration path: Developers can incrementally migrate; old API will be deprecated later.
  • Easy custom attributes: Users define their own AttributeKey<T> for custom attributes.
  • Clear discoverability: New typed namespace for semantic-convention keys.

Migration Strategy

Phase 1 (v1.x): Introduce TypedResourceAttributes and AttributeKey<T>, update Resource::Create() to accept both.
Phase 2 (v1.x+n): Deprecate ResourceAttributes with warnings.
Phase 3 (v2.0): Remove ResourceAttributes entirely; TypedResourceAttributes becomes the standard.

Request

Is the community supportive of this forward-looking approach (aligning with Java's model)? If so, I am willing to contribute a PoC or PR for code generation, examples, and integration.


Previous alternatives considered:

  • Generate attribute type enums per constant (kHostIpType)
  • Generate a central registry map at runtime
  • Generate metadata structs per attribute
  • Conversion methods between old and new APIs (rejected — forward path only)

This proposal prefers a type-safe, Java-like API with a clear forward migration path and eventual removal of the legacy API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussTo discuss in SIG meetingtriage/acceptedIndicates an issue or PR is ready to be actively worked on.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions