Skip to content

Commit 7fb5e59

Browse files
authored
feat: add baggage API (#179)
1 parent 9f7d0aa commit 7fb5e59

29 files changed

+1277
-64
lines changed

BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ cc_library(
55
"src/datadog/telemetry/metrics.cpp",
66
"src/datadog/telemetry/log.h",
77
"src/datadog/telemetry/telemetry.cpp",
8+
"src/datadog/baggage.cpp",
89
"src/datadog/base64.cpp",
910
"src/datadog/cerr_logger.cpp",
1011
"src/datadog/clock.cpp",
@@ -78,6 +79,7 @@ cc_library(
7879
"src/datadog/w3c_propagation.h",
7980
],
8081
hdrs = [
82+
"include/datadog/baggage.h",
8183
"include/datadog/cerr_logger.h",
8284
"include/datadog/clock.h",
8385
"include/datadog/collector.h",

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ target_sources(dd_trace_cpp-objects
109109
src/datadog/telemetry/configuration.cpp
110110
src/datadog/telemetry/metrics.cpp
111111
src/datadog/telemetry/telemetry.cpp
112+
src/datadog/baggage.cpp
112113
src/datadog/base64.cpp
113114
src/datadog/cerr_logger.cpp
114115
src/datadog/clock.cpp

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ run chmod +x /tmp/install-cmake && /tmp/install-cmake && rm /tmp/install-cmake
3636
copy bin/install-lcov /tmp/install-lcov
3737
run chmod +x /tmp/install-lcov && /tmp/install-lcov && rm /tmp/install-lcov
3838

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

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
add_subdirectory(baggage)
12
add_subdirectory(hasher)
23
add_subdirectory(http-server)

examples/baggage/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
add_executable(baggage-example main.cpp)
2+
target_link_libraries(baggage-example dd_trace_cpp-static)

examples/baggage/main.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include <datadog/dict_reader.h>
2+
#include <datadog/tracer.h>
3+
4+
#include <iostream>
5+
6+
namespace dd = datadog::tracing;
7+
8+
struct CinReader : public dd::DictReader {
9+
std::string input;
10+
11+
dd::Optional<dd::StringView> lookup(dd::StringView key) const override {
12+
return input;
13+
}
14+
15+
void visit(
16+
const std::function<void(dd::StringView key, dd::StringView value)>&
17+
visitor) const override{};
18+
};
19+
20+
std::istream& operator>>(std::istream& is, CinReader& reader) {
21+
is >> reader.input;
22+
return is;
23+
}
24+
25+
std::ostream& operator<<(std::ostream& os, dd::Baggage::Error error) {
26+
using dd::Baggage;
27+
switch (error.code) {
28+
case Baggage::Error::MISSING_HEADER:
29+
os << "missing `baggage` header";
30+
break;
31+
case Baggage::Error::MALFORMED_BAGGAGE_HEADER: {
32+
os << "malformed `baggage` header";
33+
if (error.pos) {
34+
os << " at position " << *error.pos;
35+
}
36+
} break;
37+
case Baggage::Error::MAXIMUM_CAPACITY_REACHED:
38+
os << "maximum number of bagge items reached";
39+
break;
40+
case Baggage::Error::MAXIMUM_BYTES_REACHED:
41+
os << "maximum amount of bytes written";
42+
break;
43+
default:
44+
os << "unknown error code";
45+
break;
46+
}
47+
return os;
48+
}
49+
50+
int main() {
51+
dd::TracerConfig cfg;
52+
cfg.log_on_startup = false;
53+
cfg.telemetry.enabled = false;
54+
cfg.agent.remote_configuration_enabled = false;
55+
const auto finalized_cfg = datadog::tracing::finalize_config(cfg);
56+
if (auto error = finalized_cfg.if_error()) {
57+
std::cerr << "Failed to initialize the tracer: " << error->message
58+
<< std::endl;
59+
return error->code;
60+
}
61+
62+
dd::Tracer tracer(*finalized_cfg);
63+
64+
std::cout
65+
<< "This program demonstrates how to use baggage, a feature that allows "
66+
"metadata (key-value pairs) to be attached to a request and "
67+
"propagated across services.\n"
68+
"Baggage can be useful for passing contextual information, such as "
69+
"user IDs, session tokens, or request attributes, between different "
70+
"components of a distributed system.\n\n"
71+
"This example lets you input baggage values, validate them and "
72+
"displays the baggage content parsed.\n"
73+
"You can enter baggage manually or provide it through a file, try:\n"
74+
"- k1=v1,k2=v2\n"
75+
"- ,invalid=input\n"
76+
"or ./baggage-example < list-of-baggages.txt\n\n";
77+
78+
CinReader reader;
79+
std::cout << "Enter baggage (or 'CTRL+C' to quit): ";
80+
while (std::getline(std::cin, reader.input)) {
81+
auto baggage = tracer.extract_baggage(reader);
82+
if (!baggage) {
83+
std::cout << "Error parsing \"" << reader.input
84+
<< "\": " << baggage.error() << ".\n";
85+
} else {
86+
std::cout << "Baggage key-value parsed: \n";
87+
baggage->visit([](dd::StringView key, dd::StringView value) {
88+
std::cout << key << ": " << value << std::endl;
89+
});
90+
}
91+
92+
std::cout << "\nEnter baggage (or 'CTRL+C' to quit): ";
93+
}
94+
return 0;
95+
}

fuzz/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
add_subdirectory(base64)
2+
add_subdirectory(tracing)
23
add_subdirectory(w3c-propagation)
34

fuzz/tracing/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
add_executable(baggage-fuzz baggage.cpp)
2+
3+
add_dependencies(baggage-fuzz dd_trace_cpp-static)
4+
5+
target_include_directories(baggage-fuzz
6+
PRIVATE
7+
${CMAKE_SOURCE_DIR}/src
8+
)
9+
10+
target_link_libraries(baggage-fuzz dd_trace_cpp-static)
11+
12+
add_target_to_group(baggage-fuzz dd_trace_cpp-fuzzers)

fuzz/tracing/baggage.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <datadog/baggage.h>
2+
#include <datadog/dict_reader.h>
3+
#include <datadog/string_view.h>
4+
5+
#include <cstdint>
6+
7+
namespace dd = datadog::tracing;
8+
9+
class MapReader : public dd::DictReader {
10+
std::unordered_map<std::string, std::string> map_;
11+
12+
public:
13+
~MapReader() override = default;
14+
15+
MapReader(std::unordered_map<std::string, std::string> map)
16+
: map_(std::move(map)) {}
17+
18+
dd::Optional<dd::StringView> lookup(dd::StringView key) const override {
19+
auto it = map_.find(std::string(key));
20+
if (it == map_.cend()) return dd::nullopt;
21+
22+
return it->second;
23+
}
24+
25+
void visit(const std::function<void(dd::StringView key,
26+
dd::StringView value)>&) const override{};
27+
};
28+
29+
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, size_t size) {
30+
MapReader reader({{"baggage", std::string((const char*)data, size)}});
31+
dd::Baggage::extract(reader);
32+
return 0;
33+
}

include/datadog/baggage.h

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#pragma once
2+
3+
#include <datadog/dict_reader.h>
4+
#include <datadog/dict_writer.h>
5+
#include <datadog/expected.h>
6+
#include <datadog/optional.h>
7+
#include <datadog/string_view.h>
8+
9+
#include <string>
10+
#include <unordered_map>
11+
12+
namespace datadog {
13+
namespace tracing {
14+
15+
/// OpenTelemetry-like implementation of the Baggage concept.
16+
/// Baggage is a key-value store meant to propagate data across services and
17+
/// processes boundaries.
18+
///
19+
/// Baggage are extracted from any tracing context implementing the `DictReader`
20+
/// interface using `Baggage::extract`.
21+
///
22+
/// Baggages are injected to any tracing context implementing the `DictWriter`
23+
/// interface using the `inject` method.
24+
class Baggage {
25+
public:
26+
struct Error final {
27+
enum Code : char {
28+
/// Baggage propagation is disabled. This may be due to one of the
29+
/// following
30+
/// reasons:
31+
/// - `baggage` is not set as an extraction or injection propagation
32+
/// style.
33+
/// - The maximum number of items is less than 0.
34+
/// - The number of bytes is less than 3.
35+
DISABLED,
36+
MISSING_HEADER,
37+
MALFORMED_BAGGAGE_HEADER,
38+
MAXIMUM_CAPACITY_REACHED,
39+
MAXIMUM_BYTES_REACHED,
40+
};
41+
Code code;
42+
Optional<size_t> pos;
43+
44+
Error(Code in_code) : code(in_code), pos(nullopt) {}
45+
Error(Code in_code, size_t position) : code(in_code), pos(position) {}
46+
};
47+
48+
struct Options final {
49+
size_t max_bytes;
50+
size_t max_items;
51+
};
52+
53+
static constexpr size_t default_max_capacity = 64;
54+
static constexpr Options default_options{2048, default_max_capacity};
55+
56+
/// Extracts a Baggage instance from a `DictReader` and creates a Baggage
57+
/// instance if no errors are encounters .
58+
///
59+
/// @param `reader` The input `DictReader` from which to extract the data.
60+
/// @return A `Baggage` instance or an `Error`.
61+
static Expected<Baggage, Error> extract(const DictReader& reader);
62+
63+
/// Initializes an empty Baggage with the default maximum capacity.
64+
Baggage() = default;
65+
66+
/// Initializes an empty Baggage instance with the given maximum capacity.
67+
///
68+
/// @param `max_capacity` The maximum capacity for this Baggage instance.
69+
Baggage(size_t max_capacity);
70+
71+
/// Initializes a Baggage instance using the provided unordered_map of
72+
/// key-value pairs. The maximum capacity can also be specified.
73+
///
74+
/// @param `baggage_map` The map containing key-value pairs to initialize the
75+
/// Baggage.
76+
/// @param `max_capacity` The maximum capacity for this Baggage instance.
77+
Baggage(std::unordered_map<std::string, std::string>,
78+
size_t max_capacity = default_max_capacity);
79+
80+
/// Checks if the Baggage contains a specified key.
81+
///
82+
/// @param `key` The key to check.
83+
/// @return `true` if the key exists in the Baggage; otherwise, `false`.
84+
bool contains(StringView key) const;
85+
86+
/// Retrieves the value associated with a specified key.
87+
///
88+
/// @param `key` The key to retrieve the value for.
89+
/// @return An `Optional<StringView>` containing the value if the key exists,
90+
/// or an empty Optional if the key is not found.
91+
Optional<StringView> get(StringView key) const;
92+
93+
/// Adds a key-value pair to the Baggage.
94+
///
95+
/// This function will attempt to add the given key-value pair to the Baggage.
96+
/// If the maximum capacity has been reached, the insertion will fail.
97+
/// If a `key` already exists, its value will be overwritten with `value`.
98+
///
99+
/// @param `key` The key to insert.
100+
/// @param `value` The value to associate with the key.
101+
/// @return `true` if the key-value pair was successfully added; `false` if
102+
/// the maximum capacity was reached.
103+
bool set(std::string key, std::string value);
104+
105+
/// Removes the key-value pair corresponding to the specified key.
106+
///
107+
/// @param `key` The key to remove from the Baggage.
108+
void remove(StringView key);
109+
110+
/// Removes all key-value pair.
111+
void clear();
112+
113+
/// Retrieves the number of items stored.
114+
size_t size() const;
115+
116+
/// Returns whether any items are stored.
117+
bool empty() const;
118+
119+
/// Visits each key-value pair in the Baggage and invoke the provided
120+
/// visitor function for each key-value pair in the Baggage.
121+
///
122+
/// @param `visitor` A function object that will be called for each
123+
/// key-value pair.
124+
void visit(std::function<void(StringView, StringView)>&& visitor);
125+
126+
/// Injects the Baggage data into a `DictWriter` with the constraint that
127+
/// the amount of bytes written does not exceed the specified maximum byte
128+
/// limit.
129+
///
130+
/// @param `writer` The DictWriter to inject the data into.
131+
/// @param `opts` Injection options.
132+
/// @return An `Expected<void>`, which may either succeed or contain an
133+
/// error.
134+
Expected<void> inject(DictWriter& writer,
135+
const Options& opts = default_options) const;
136+
137+
/// Equality operator for comparing two Baggage instances.
138+
inline bool operator==(const Baggage& rhs) const {
139+
return baggage_ == rhs.baggage_;
140+
}
141+
142+
private:
143+
const size_t max_capacity_ = Baggage::default_max_capacity;
144+
std::unordered_map<std::string, std::string> baggage_;
145+
};
146+
147+
} // namespace tracing
148+
} // namespace datadog

0 commit comments

Comments
 (0)