Skip to content

Commit

Permalink
feat: add baggage API (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmehala authored Feb 26, 2025
1 parent 9f7d0aa commit 7fb5e59
Show file tree
Hide file tree
Showing 29 changed files with 1,277 additions and 64 deletions.
2 changes: 2 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ cc_library(
"src/datadog/telemetry/metrics.cpp",
"src/datadog/telemetry/log.h",
"src/datadog/telemetry/telemetry.cpp",
"src/datadog/baggage.cpp",
"src/datadog/base64.cpp",
"src/datadog/cerr_logger.cpp",
"src/datadog/clock.cpp",
Expand Down Expand Up @@ -78,6 +79,7 @@ cc_library(
"src/datadog/w3c_propagation.h",
],
hdrs = [
"include/datadog/baggage.h",
"include/datadog/cerr_logger.h",
"include/datadog/clock.h",
"include/datadog/collector.h",
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ target_sources(dd_trace_cpp-objects
src/datadog/telemetry/configuration.cpp
src/datadog/telemetry/metrics.cpp
src/datadog/telemetry/telemetry.cpp
src/datadog/baggage.cpp
src/datadog/base64.cpp
src/datadog/cerr_logger.cpp
src/datadog/clock.cpp
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ run chmod +x /tmp/install-cmake && /tmp/install-cmake && rm /tmp/install-cmake
copy bin/install-lcov /tmp/install-lcov
run chmod +x /tmp/install-lcov && /tmp/install-lcov && rm /tmp/install-lcov

run curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | bash

1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
add_subdirectory(baggage)
add_subdirectory(hasher)
add_subdirectory(http-server)
2 changes: 2 additions & 0 deletions examples/baggage/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_executable(baggage-example main.cpp)
target_link_libraries(baggage-example dd_trace_cpp-static)
95 changes: 95 additions & 0 deletions examples/baggage/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <datadog/dict_reader.h>
#include <datadog/tracer.h>

#include <iostream>

namespace dd = datadog::tracing;

struct CinReader : public dd::DictReader {
std::string input;

dd::Optional<dd::StringView> lookup(dd::StringView key) const override {
return input;
}

void visit(
const std::function<void(dd::StringView key, dd::StringView value)>&
visitor) const override{};
};

std::istream& operator>>(std::istream& is, CinReader& reader) {
is >> reader.input;
return is;
}

std::ostream& operator<<(std::ostream& os, dd::Baggage::Error error) {
using dd::Baggage;
switch (error.code) {
case Baggage::Error::MISSING_HEADER:
os << "missing `baggage` header";
break;
case Baggage::Error::MALFORMED_BAGGAGE_HEADER: {
os << "malformed `baggage` header";
if (error.pos) {
os << " at position " << *error.pos;
}
} break;
case Baggage::Error::MAXIMUM_CAPACITY_REACHED:
os << "maximum number of bagge items reached";
break;
case Baggage::Error::MAXIMUM_BYTES_REACHED:
os << "maximum amount of bytes written";
break;
default:
os << "unknown error code";
break;
}
return os;
}

int main() {
dd::TracerConfig cfg;
cfg.log_on_startup = false;
cfg.telemetry.enabled = false;
cfg.agent.remote_configuration_enabled = false;
const auto finalized_cfg = datadog::tracing::finalize_config(cfg);
if (auto error = finalized_cfg.if_error()) {
std::cerr << "Failed to initialize the tracer: " << error->message
<< std::endl;
return error->code;
}

dd::Tracer tracer(*finalized_cfg);

std::cout
<< "This program demonstrates how to use baggage, a feature that allows "
"metadata (key-value pairs) to be attached to a request and "
"propagated across services.\n"
"Baggage can be useful for passing contextual information, such as "
"user IDs, session tokens, or request attributes, between different "
"components of a distributed system.\n\n"
"This example lets you input baggage values, validate them and "
"displays the baggage content parsed.\n"
"You can enter baggage manually or provide it through a file, try:\n"
"- k1=v1,k2=v2\n"
"- ,invalid=input\n"
"or ./baggage-example < list-of-baggages.txt\n\n";

CinReader reader;
std::cout << "Enter baggage (or 'CTRL+C' to quit): ";
while (std::getline(std::cin, reader.input)) {
auto baggage = tracer.extract_baggage(reader);
if (!baggage) {
std::cout << "Error parsing \"" << reader.input
<< "\": " << baggage.error() << ".\n";
} else {
std::cout << "Baggage key-value parsed: \n";
baggage->visit([](dd::StringView key, dd::StringView value) {
std::cout << key << ": " << value << std::endl;
});
}

std::cout << "\nEnter baggage (or 'CTRL+C' to quit): ";
}
return 0;
}
1 change: 1 addition & 0 deletions fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
add_subdirectory(base64)
add_subdirectory(tracing)
add_subdirectory(w3c-propagation)

12 changes: 12 additions & 0 deletions fuzz/tracing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_executable(baggage-fuzz baggage.cpp)

add_dependencies(baggage-fuzz dd_trace_cpp-static)

target_include_directories(baggage-fuzz
PRIVATE
${CMAKE_SOURCE_DIR}/src
)

target_link_libraries(baggage-fuzz dd_trace_cpp-static)

add_target_to_group(baggage-fuzz dd_trace_cpp-fuzzers)
33 changes: 33 additions & 0 deletions fuzz/tracing/baggage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <datadog/baggage.h>
#include <datadog/dict_reader.h>
#include <datadog/string_view.h>

#include <cstdint>

namespace dd = datadog::tracing;

class MapReader : public dd::DictReader {
std::unordered_map<std::string, std::string> map_;

public:
~MapReader() override = default;

MapReader(std::unordered_map<std::string, std::string> map)
: map_(std::move(map)) {}

dd::Optional<dd::StringView> lookup(dd::StringView key) const override {
auto it = map_.find(std::string(key));
if (it == map_.cend()) return dd::nullopt;

return it->second;
}

void visit(const std::function<void(dd::StringView key,
dd::StringView value)>&) const override{};
};

extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, size_t size) {
MapReader reader({{"baggage", std::string((const char*)data, size)}});
dd::Baggage::extract(reader);
return 0;
}
148 changes: 148 additions & 0 deletions include/datadog/baggage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#pragma once

#include <datadog/dict_reader.h>
#include <datadog/dict_writer.h>
#include <datadog/expected.h>
#include <datadog/optional.h>
#include <datadog/string_view.h>

#include <string>
#include <unordered_map>

namespace datadog {
namespace tracing {

/// OpenTelemetry-like implementation of the Baggage concept.
/// Baggage is a key-value store meant to propagate data across services and
/// processes boundaries.
///
/// Baggage are extracted from any tracing context implementing the `DictReader`
/// interface using `Baggage::extract`.
///
/// Baggages are injected to any tracing context implementing the `DictWriter`
/// interface using the `inject` method.
class Baggage {
public:
struct Error final {
enum Code : char {
/// Baggage propagation is disabled. This may be due to one of the
/// following
/// reasons:
/// - `baggage` is not set as an extraction or injection propagation
/// style.
/// - The maximum number of items is less than 0.
/// - The number of bytes is less than 3.
DISABLED,
MISSING_HEADER,
MALFORMED_BAGGAGE_HEADER,
MAXIMUM_CAPACITY_REACHED,
MAXIMUM_BYTES_REACHED,
};
Code code;
Optional<size_t> pos;

Error(Code in_code) : code(in_code), pos(nullopt) {}
Error(Code in_code, size_t position) : code(in_code), pos(position) {}
};

struct Options final {
size_t max_bytes;
size_t max_items;
};

static constexpr size_t default_max_capacity = 64;
static constexpr Options default_options{2048, default_max_capacity};

/// Extracts a Baggage instance from a `DictReader` and creates a Baggage
/// instance if no errors are encounters .
///
/// @param `reader` The input `DictReader` from which to extract the data.
/// @return A `Baggage` instance or an `Error`.
static Expected<Baggage, Error> extract(const DictReader& reader);

/// Initializes an empty Baggage with the default maximum capacity.
Baggage() = default;

/// Initializes an empty Baggage instance with the given maximum capacity.
///
/// @param `max_capacity` The maximum capacity for this Baggage instance.
Baggage(size_t max_capacity);

/// Initializes a Baggage instance using the provided unordered_map of
/// key-value pairs. The maximum capacity can also be specified.
///
/// @param `baggage_map` The map containing key-value pairs to initialize the
/// Baggage.
/// @param `max_capacity` The maximum capacity for this Baggage instance.
Baggage(std::unordered_map<std::string, std::string>,
size_t max_capacity = default_max_capacity);

/// Checks if the Baggage contains a specified key.
///
/// @param `key` The key to check.
/// @return `true` if the key exists in the Baggage; otherwise, `false`.
bool contains(StringView key) const;

/// Retrieves the value associated with a specified key.
///
/// @param `key` The key to retrieve the value for.
/// @return An `Optional<StringView>` containing the value if the key exists,
/// or an empty Optional if the key is not found.
Optional<StringView> get(StringView key) const;

/// Adds a key-value pair to the Baggage.
///
/// This function will attempt to add the given key-value pair to the Baggage.
/// If the maximum capacity has been reached, the insertion will fail.
/// If a `key` already exists, its value will be overwritten with `value`.
///
/// @param `key` The key to insert.
/// @param `value` The value to associate with the key.
/// @return `true` if the key-value pair was successfully added; `false` if
/// the maximum capacity was reached.
bool set(std::string key, std::string value);

/// Removes the key-value pair corresponding to the specified key.
///
/// @param `key` The key to remove from the Baggage.
void remove(StringView key);

/// Removes all key-value pair.
void clear();

/// Retrieves the number of items stored.
size_t size() const;

/// Returns whether any items are stored.
bool empty() const;

/// Visits each key-value pair in the Baggage and invoke the provided
/// visitor function for each key-value pair in the Baggage.
///
/// @param `visitor` A function object that will be called for each
/// key-value pair.
void visit(std::function<void(StringView, StringView)>&& visitor);

/// Injects the Baggage data into a `DictWriter` with the constraint that
/// the amount of bytes written does not exceed the specified maximum byte
/// limit.
///
/// @param `writer` The DictWriter to inject the data into.
/// @param `opts` Injection options.
/// @return An `Expected<void>`, which may either succeed or contain an
/// error.
Expected<void> inject(DictWriter& writer,
const Options& opts = default_options) const;

/// Equality operator for comparing two Baggage instances.
inline bool operator==(const Baggage& rhs) const {
return baggage_ == rhs.baggage_;
}

private:
const size_t max_capacity_ = Baggage::default_max_capacity;
std::unordered_map<std::string, std::string> baggage_;
};

} // namespace tracing
} // namespace datadog
2 changes: 2 additions & 0 deletions include/datadog/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ enum class ConfigName : char {
TRACE_SAMPLING_LIMIT,
TRACE_SAMPLING_RULES,
SPAN_SAMPLING_RULES,
TRACE_BAGGAGE_MAX_BYTES,
TRACE_BAGGAGE_MAX_ITEMS,
};

// Represents metadata for configuration parameters
Expand Down
2 changes: 2 additions & 0 deletions include/datadog/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ namespace environment {
MACRO(DD_TELEMETRY_METRICS_ENABLED) \
MACRO(DD_TELEMETRY_METRICS_INTERVAL_SECONDS) \
MACRO(DD_TELEMETRY_DEBUG) \
MACRO(DD_TRACE_BAGGAGE_MAX_ITEMS) \
MACRO(DD_TRACE_BAGGAGE_MAX_BYTES) \
MACRO(DD_TELEMETRY_LOG_COLLECTION_ENABLED)

#define WITH_COMMA(ARG) ARG,
Expand Down
2 changes: 2 additions & 0 deletions include/datadog/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ struct Error {
DATADOG_AGENT_INVALID_REMOTE_CONFIG_POLL_INTERVAL = 51,
SAMPLING_DELEGATION_RESPONSE_INVALID_JSON = 52,
REMOTE_CONFIGURATION_INVALID_INPUT = 53,
BAGGAGE_MAXIMUM_BYTES_REACHED = 54,
BAGGAGE_MAXIMUM_ITEMS_REACHED = 55,
};

Code code;
Expand Down
Loading

0 comments on commit 7fb5e59

Please sign in to comment.