Skip to content

Commit 00b14ac

Browse files
committed
feat: add baggage API
1 parent f590dce commit 00b14ac

28 files changed

+1059
-65
lines changed

BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ cc_library(
44
"src/datadog/telemetry/configuration.cpp",
55
"src/datadog/telemetry/metrics.cpp",
66
"src/datadog/telemetry/telemetry.cpp",
7+
"src/datadog/baggage.cpp",
78
"src/datadog/base64.cpp",
89
"src/datadog/cerr_logger.cpp",
910
"src/datadog/clock.cpp",
@@ -77,6 +78,7 @@ cc_library(
7778
"src/datadog/w3c_propagation.h",
7879
],
7980
hdrs = [
81+
"include/datadog/baggage.h",
8082
"include/datadog/clock.h",
8183
"include/datadog/collector.h",
8284
"include/datadog/config.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: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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) {
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+
break;
34+
case Baggage::Error::MAXIMUM_CAPACITY_REACHED:
35+
os << "maximum number of bagge items reached";
36+
break;
37+
case Baggage::Error::MAXIMUM_BYTES_REACHED:
38+
os << "maximum amount of bytes written";
39+
break;
40+
default:
41+
os << "unknown error code";
42+
break;
43+
}
44+
return os;
45+
}
46+
47+
int main() {
48+
dd::TracerConfig cfg;
49+
cfg.log_on_startup = false;
50+
const auto finalized_cfg = datadog::tracing::finalize_config(cfg);
51+
52+
dd::Tracer tracer(*finalized_cfg);
53+
54+
std::cout
55+
<< "This program demonstrates how to use baggage, a feature that allows "
56+
"metadata (key-value pairs) to be attached to a request and "
57+
"propagated across services.\n"
58+
"Baggage can be useful for passing contextual information, such as "
59+
"user IDs, session tokens, or request attributes, between different "
60+
"components of a distributed system.\n\n"
61+
"This example lets you input baggage values, validate them and "
62+
"displays the baggage content parsed.\n"
63+
"You can enter baggage manually or provide it through a file, try:\n"
64+
"- k1=v1,k2=v2\n"
65+
"- ,invalid=input\n"
66+
"or ./baggage-example < list-of-baggages.txt\n\n";
67+
68+
CinReader reader;
69+
std::cout << "Enter baggage (or 'CTRL+C' to quit): ";
70+
while (std::cin >> reader) {
71+
auto baggage = tracer.extract_baggage(reader);
72+
if (!baggage) {
73+
std::cout << "Error parsing \"" << reader.input
74+
<< "\": " << baggage.error() << ".\n";
75+
} else {
76+
std::cout << "Baggage key-value parsed: \n";
77+
baggage->visit([](dd::StringView key, dd::StringView value) {
78+
std::cout << key << ": " << value << std::endl;
79+
});
80+
}
81+
82+
std::cout << "\nEnter baggage (or 'CTRL+C' to quit): ";
83+
}
84+
return 0;
85+
}

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

0 commit comments

Comments
 (0)