diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 164f912cc..ef6af873f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,16 +1,16 @@ -Contributing to dd-trace-cpp -============================ +# Contributing to dd-trace-cpp + Pull requests for bug fixes are welcome. Before submitting new features or changes to current functionality, [open an -issue](https://github.com/DataDog/dd-trace-cpp/issues/new) and discuss your -ideas or propose the changes you wish to make. After a resolution is reached, a -PR can be submitted for review. +issue](https://github.com/DataDog/dd-trace-cpp/issues/new) and discuss your ideas or propose the +changes you wish to make. After a resolution is reached, a PR can be submitted for review. + +## Code Style + +C++ code is formatted using `clang-format-14`. Before submitting code changes, run the following +command: -Code Style ----------- -C++ code is formatted using `clang-format-14`. Before submitting code changes, -run the following command: -```shell -$ bin/format +```shell +bin/format ``` diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/README.md b/README.md index dcf5adbb8..66cb32e5e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Datadog C++ Tracing Library -=========================== +# Datadog C++ Tracing Library ```c++ #include @@ -37,26 +36,30 @@ int main() { std::this_thread::sleep_for(std::chrono::seconds(2)); } ``` + See the [examples](examples) directory for more extensive usage examples. ## Platform Support -The library has been tested and is compatible on the following CPU architecture, OS and compiler combinations: -- x86_64 and arm64 Linux with GCC 11.4. -- x86_64 and arm64 Linux with Clang 14. -- x86_64 Windows with MSVC 2022. -- arm64 macOS with Apple Clang 15. +The library has been tested and is compatible on the following CPU architecture, OS and compiler +combinations: + +- x86_64 and arm64 Linux with GCC 11.4; +- x86_64 and arm64 Linux with Clang 14; +- x86_64 Windows with MSVC 2022; +- arm64 macOS with Apple Clang 15. ## Building and Installation ### Requirements + `dd-trace-cpp` requires a [supported](#platform-support) C++17 compiler. -A recent version of CMake is required (`3.28`), which might not be in your -system's package manager. [bin/install-cmake](bin/install-cmake) is an installer -for a recent CMake, on Linux. +A recent version of CMake is required (`3.28`), which might not be in your system's package manager. +[bin/install-cmake](bin/install-cmake) is an installer for a recent CMake, on Linux. ### Building + Build this library from source using [CMake](https://cmake.org). ```shell @@ -74,6 +77,7 @@ cmake -B build -DBUILD_SHARED_LIBS=1 . ``` ### Installation + Installation places a shared library and public headers into the appropriate system directories (`/usr/local/[…]`), or to a specified installation prefix. Example: @@ -83,12 +87,13 @@ cmake --install build --prefix=.install ``` ### Optional: Linking to the shared library + In case you decided to build the shared library: -When building an executable that uses `dd-trace-cpp`, specify the path to -the installed headers using an appropriate `-I` option. If the library was -installed into the default system directories, then the `-I` option is not -needed. +When building an executable that uses `dd-trace-cpp`, specify the path to the installed headers +using an appropriate `-I` option. If the library was installed into the default system directories, +then the `-I` option is not needed. + ```shell c++ -I/path/to/dd-trace-cpp/.install/include -c -o my_app.o my_app.cpp ``` @@ -97,15 +102,17 @@ When linking an executable that uses `dd-trace-cpp`, specify linkage to the built library using the `-ldd_trace_cpp` option and an appropriate `-L` option. If the library was installed into the default system directories, then the `-L` options is not needed. The `-ldd_trace_cpp` option is always needed. + ```shell c++ -o my_app my_app.o -L/path/to/dd-trace-cpp/.install/lib -ldd_trace_cpp ``` -Test ----- +## Test + Pass `-DDD_TRACE_BUILD_TESTING=1` to `cmake` to include the unit tests in the build. The resulting unit test executable is `test/tests` within the build directory. + ```shell cmake -B build -DDD_TRACE_BUILD_TESTING=1 . cmake --build build -j @@ -114,7 +121,7 @@ cmake --build build -j Alternatively, [bin/test](bin/test) is provided for convenience. -Contributing ------------- -See the [contributing guidelines](CONTRIBUTING.md) and the [maintainer docs](doc/maintainers.md) -for information on the overall structure of the repository. +## Contributing + +See the [contributing guidelines](CONTRIBUTING.md) and the [maintainer docs](doc/maintainers.md) for +information on the overall structure of the repository. diff --git a/SECURITY.md b/SECURITY.md index 6177cd67f..bdd299333 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,15 +1,19 @@ # Security Policy -This document outlines the security policy for the Datadog C++ client library (aka C++ tracer) and what to do if you discover a security vulnerability in the project. -Most notably, please do not share the details in a public forum (such as in a discussion, issue, or pull request) but instead reach out to us with the details. -This gives us an opportunity to release a fix for others to benefit from by the time details are made public. +This document outlines the security policy for the Datadog C++ client library (a.k.a. C++ tracer) +and what to do if you discover a security vulnerability in the project. Most notably, please do not +share the details in a public forum (such as in a discussion, issue, or pull request) but instead +reach out to us with the details. This gives us an opportunity to release a fix for others to +benefit from by the time details are made public. ## Supported Versions -We accept vulnerability submissions for the [currently maintained release](https://github.com/DataDog/dd-trace-cpp/releases). +We accept vulnerability submissions for the [currently maintained +release](https://github.com/DataDog/dd-trace-cpp/releases). ## Reporting a Vulnerability -If you discover a vulnerability in the Datadog C++ client library (or any Datadog product for that matter) please submit details to the following email address: +If you discover a vulnerability in the Datadog C++ client library (or any Datadog product for that +matter) please submit details to the following email address: * [security@datadoghq.com](mailto:security@datadoghq.com) diff --git a/benchmark/README.md b/benchmark/README.md index e26f95dd1..6b16cb5e2 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,19 +1,18 @@ -Microbenchmarks -=============== -This directory contains the definition of a program that measures the timing and -resource consumption of a test tracing scenario. +# Microbenchmarks -The benchmark uses [Google Benchmark][1], whose source is included as a git -submodule under `./google-benchmark`. +This directory contains the definition of a program that measures the timing and resource +consumption of a test tracing scenario. -The scenario that's measured is similar to the [../examples/hasher][3] setup. A trace -is created whose structure reflects that of a particular file directory -structure. The directory structure, in this case, is the source tree of the -[Tiny C Compiler][4], whose source is included as a git submodule under -`./tinycc`. +The benchmark uses [Google Benchmark](https://github.com/google/benchmark), whose source is included +as a git submodule under `./google-benchmark`. -The scenario does not use the network, spawn any threads, or read/write -any files. The operations that are implicitly covered by the scenario are: +The scenario that's measured is similar to the [../examples/hasher](../examples/hasher) setup. A +trace is created whose structure reflects that of a particular file directory structure. The +directory structure, in this case, is the source tree of the [Tiny C +Compiler](https://bellard.org/tcc), whose source is included as a git submodule under `./tinycc`. + +The scenario does not use the network, spawn any threads, or read/write any files. The operations +that are implicitly covered by the scenario are: - configuring and initializing a tracer, - creating a trace, @@ -23,14 +22,8 @@ any files. The operations that are implicitly covered by the scenario are: - finalizing a trace and making a sampling decision, - serializing a trace as MessagePack. -[../bin/benchmark][6] is a script that builds dd-trace-cpp, this benchmark, and +[../bin/benchmark](../bin/benchmark) is a script that builds `dd-trace-cpp`, this benchmark, and then runs the benchmark. -This benchmark is intended to be driven by Datadog's internal benchmarking -platform. See [../.gitlab/benchmarks.yml][7]. - -[1]: https://github.com/google/benchmark -[3]: ../examples/hasher -[4]: https://bellard.org/tcc/ -[6]: ../bin/benchmark -[7]: ../.gitlab/benchmarks.yml +This benchmark is intended to be driven by Datadog's internal benchmarking platform. See +[../.gitlab/benchmarks.yml](../.gitlab/benchmarks.yml). diff --git a/benchmark/hasher.cpp b/benchmark/hasher.cpp index 548b522c0..c0a143e74 100644 --- a/benchmark/hasher.cpp +++ b/benchmark/hasher.cpp @@ -3,11 +3,11 @@ // If the path does not exist, print an error. // // If the path exists and is a regular file, print the SHA256 digest of the -// file's contents. Produce a single tracing span indicating the calculation. +// file's contents. Produce a single tracing span indicating the calculation. // // If the path exists and is a directory, calculate the SHA256 digest of the // directory from the names and digests of its children, combined in some -// canonical format. Produce a trace whose structure reflects the directory +// canonical format. Produce a trace whose structure reflects the directory // structure. // // Files that are neither regular files nor directories are ignored. @@ -47,7 +47,7 @@ std::string hex(const Digest &digest) { } // Store into the specified `digest` the SHA256 digest of the contents of the -// specified `file`. Return zero on success, or a nonzero value if an error +// specified `file`. Return zero on success, or a nonzero value if an error // occurs. int sha256(Digest &digest, const fs::path &file) { std::ifstream in(file); diff --git a/bin/with-toolchain b/bin/with-toolchain index 21af87625..5a6727776 100755 --- a/bin/with-toolchain +++ b/bin/with-toolchain @@ -9,7 +9,7 @@ usage: with-toolchain llvm [COMMAND ...] Execute COMMAND in an environment that uses either GNU's compilers (gcc and g++) or LLVM's compilers (clang and clang++). - + with-toolchain --help with-toolchain -h Print this message. @@ -33,7 +33,7 @@ case "$1" in llvm) toolchain=llvm ;; *) - >&2 echo "Invalid toolchain value \"$1\". Expected \"gnu\" or \"llvm\"." + >&2 echo "Invalid toolchain value \"$1\". Expected \"gnu\" or \"llvm\"." exit 2 ;; esac diff --git a/doc/README.md b/doc/README.md index 2376261d9..41963ae91 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,29 +1,28 @@ -Logical Component Relationships -------------------------------- +# Logical Component Relationships + - Vertices are components. -- Edges are ownership relationships between components. Each edge is labeled - by the kind of "smart pointer" that could implement that kind of - relationship. +- Edges are ownership relationships between components. Each edge is labeled by the kind of "smart + pointer" that could implement that kind of relationship. - Components containing a padlock are protected by a mutex. ![diagram](ownership.svg) -Objects -------- -- _Span_ has a beginning, end, and tags. It is associated with a _TraceSegment_. -- _TraceSegment_ is part of a trace. It makes sampling decisions, detects when - it is finished, and sends itself to the _Collector_. -- _Collector_ receives trace segments. It provides a callback to deliver - sampler modifications, if applicable. -- _Tracer_ is responsible for creating trace segments. It contains the - instances of, and configuration for, the _Collector_, _TraceSampler_, and - _SpanSampler_. A tracer is created from a _TracerConfig_. -- _TraceSampler_ is used by trace segments to decide when to keep or drop - themselves. -- _SpanSampler_ is used by trace segments to decide which spans to keep when - the segment is dropped. -- _TracerConfig_ contains all of the information needed to configure the collector, - trace sampler, and span sampler, as well as defaults for span properties. +## Objects + +- `Span` has a beginning, end, and tags. It is associated with a `TraceSegment`. +- `TraceSegment` is part of a trace. It makes sampling decisions, detects when it is finished, and + sends itself to the `Collector`. +- `Collector` receives trace segments. It provides a callback to deliver sampler modifications, if + applicable. +- `Tracer` is responsible for creating trace segments. It contains the instances of, and + configuration for, the `Collector`, `TraceSampler`, and `SpanSampler`. A tracer is created from a + `TracerConfig`. +- `TraceSampler` is used by trace segments to decide when to keep or drop themselves. +- `SpanSampler` is used by trace segments to decide which spans to keep when the segment is dropped. +- `TracerConfig` contains all of the information needed to configure the collector, trace sampler, + and span sampler, as well as defaults for span properties. + +## Usage Intended usage is: @@ -32,11 +31,8 @@ Intended usage is: 3. Use the `Tracer` to create and/or extract local root `Span`s. 4. Use `Span` to create children and/or inject context. 5. Use a `Span`'s `TraceSegment` to perform trace-wide operations. -6. When all `Span`s in ` TraceSegment` are finished, the segment is sent to the +6. When all `Span`s in `TraceSegment` are finished, the segment is sent to the `Collector`. -Different instances of `Tracer` are independent of each other. If an -application wishes to reconfigure tracing at runtime, it can create another -`Tracer` using the new configuration. - -[1]: https://datadog.github.io/dd-trace-cpp/datadog +Different instances of `Tracer` are independent of each other. If an application wishes to +reconfigure tracing at runtime, it can create another `Tracer` using the new configuration. diff --git a/doc/decisions.md b/doc/decisions.md index 2a3ee0201..f9bea6fac 100644 --- a/doc/decisions.md +++ b/doc/decisions.md @@ -1,39 +1,39 @@ Here are some topics that were discussed during the design of this library. - Which version of C++ do we require? + - C++17 - Which C core libraries do we produce binaries for? - - glibc - - musl + - glibc + - musl - Which build systems do we support? - - CMake - - Bazel + - CMake + - Bazel - Which unit testing framework? - - Catch2 - - Google Test -- Integration tests? + - Catch2 + - Google Test - Error handling options: - - `std::variant` - - homebrew a `std::expected` - - `throw Error(...)` - - `RCode do_thing(T& output)` - - `struct Result { Error error; T value; }` + - `std::variant` + - homebrew a `std::expected` + - `throw Error(...)` + - `RCode do_thing(T& output)` + - `struct Result { Error error; T value; }` - Is a `Span` RAII with respect to start/finish? -- When we begin calculating trace metrics within the tracer, we'll need to hit - a `/stats` HTTP endpoint. - - Does it live on the same thread as the `Collector`? - - Do we invent a library-specific `HTTPClient` interface? +- When we begin calculating trace metrics within the tracer, we'll need to hit a `/stats` HTTP + endpoint. + - Does it live on the same thread as the `Collector`? + - Do we invent a library-specific `HTTPClient` interface? - Do we keep using cURL for the default `Collector`? - - In-tree C++ library instead? + - In-tree C++ library instead? - Naming conventions: - - `class TypeName;` - - `.member_function();` - - `free_function();` - - `f(int func_arg);` - - `int local_var;` - - `int private_member_;` - - `int public_member;` - - `enum Color { red, green, blue };` - - `which_one` + - `class TypeName;` + - `.member_function();` + - `free_function();` + - `f(int func_arg);` + - `int local_var;` + - `int private_member_;` + - `int public_member;` + - `enum Color { red, green, blue };` + - `which_one` - Can tracing be reconfigured at runtime? - Can multiple tracers share a collector? - Are rate limits per-tracer, per-process, or other? diff --git a/doc/maintainers.md b/doc/maintainers.md index 76cf4a6be..e8dd0e4e2 100644 --- a/doc/maintainers.md +++ b/doc/maintainers.md @@ -1,168 +1,159 @@ -Care and Feeding of Your New Tracing Library -============================================ +# Care and Feeding of Your New Tracing Library + Congratulations! You are now the proud owner of a distributed tracing library. -The primary purpose of this guide is to describe salient features of the -library's design. dd-trace-cpp differs considerably from its [older sibling][1] -and [peers][2]. +The primary purpose of this guide is to describe salient features of the library's design. +`dd-trace-cpp` differs considerably from its [older +sibling](https://github.com/DataDog/dd-opentracing-cpp) and +[peers](https://github.com/open-telemetry/opentelemetry-cpp). -This guide will also cover operations performed by maintainers of the library, -such as scooping the box, applying flea medication, and regular trips to the -vet. +This guide will also cover operations performed by maintainers of the library, such as scooping the +box, applying flea medication, and regular trips to the vet. -Design ------- +## Design ### Span -[class Span][3] is the component with which users will interact the most. + +[class Span](../include/datadog/span.h) is the component with which users will interact the most. Each span: - has an "ID," - is associated with a "trace ID," -- is associated with a "service," which has a "service type," a "version," and - an "environment," +- is associated with a "service," which has a "service type," a "version," and an "environment," - has a "name" (sometimes called the "operation name"), -- has a "resource name," which is a description of the thing that the span is - about, -- contains information about whether an error occurred during the represented - operation, including an error message, error type, and stack trace, +- has a "resource name," which is a description of the thing that the span is about, +- contains information about whether an error occurred during the represented operation, including + an error message, error type, and stack trace, - includes an arbitrary name/value mapping of strings, called "tags," - has a start time indicating when the represented operation began, - has a duration indicating how long the represented operation took to finish. -Aside from setting and retrieving its attributes, `Span` also has the following -operations: +Aside from setting and retrieving its attributes, `Span` also has the following operations: - `parent.create_child(...)` returns a new `Span` that is a child of `parent`. - `span.inject(writer)` writes trace propagation information to a - [DictWriter][11], which is an interface for setting a name/value mapping, e.g. - in HTTP request headers. + [DictWriter](../include/datadog/dict_writer.h), which is an interface for setting a name/value + mapping, e.g. in HTTP request headers. A `Span` does not own its data. `class Span` contains a raw pointer to a [class -SpanData][4], which contains the actual attributes of the span. The `SpanData` -is owned by a `TraceSegment`, which is described in the next section. The -`Span` holds a `shared_ptr` to its `TraceSegment`. - -By default, a span's start time is when it is created, and its end time (from -which its duration is calculated) is when it is destroyed. However, a span's -start time can be specified when it is created, via `SpanConfig::start` (see -[span_config.h][5]), and a span's end time can be overridden via -`Span::set_end_time`. - -When a span is destroyed, it is considered "finished" and notifies its -`TraceSegment`. There is no way to "finish" a span without destroying it. You -can override its end time throughout the lifetime of the `Span` object, but a -`TraceSegment` does not consider the span finished until the `Span` object is -destroyed. This allows us to avoid "finished" `Span` states. - -Along similar lines, `class Span` is move-only. Its copy constructor is deleted. -Functions that produce spans return them by value, but only one copy of a span -can exist at a time. In fact, `class Span` is even more strict than move-only: -its assignment operator is deleted, including the move-assignment operator. To -see why, consider the following (disallowed) example: +SpanData](../src/datadog/span_data.h), which contains the actual attributes of the span. The +`SpanData` is owned by a `TraceSegment`, which is described in the next section. The `Span` holds a +`shared_ptr` to its `TraceSegment`. + +By default, a span's start time is when it is created, and its end time (from which its duration is +calculated) is when it is destroyed. However, a span's start time can be specified when it is +created, via `SpanConfig::start` (see [span_config.h](../include/datadog/span_config.h)), and a +span's end time can be overridden via `Span::set_end_time`. + +When a span is destroyed, it is considered "finished" and notifies its `TraceSegment`. There is no +way to "finish" a span without destroying it. You can override its end time throughout the lifetime +of the `Span` object, but a `TraceSegment` does not consider the span finished until the `Span` +object is destroyed. This allows us to avoid "finished" `Span` states. + +Along similar lines, `class Span` is move-only. Its copy constructor is deleted. Functions that +produce spans return them by value, but only one copy of a span can exist at a time. In fact, `class +Span` is even more strict than move-only: its assignment operator is deleted, including the +move-assignment operator. To see why, consider the following (disallowed) example: + ```c++ Span span = tracer.create_span(); // ... // Let's reuse the variable `span`. span = tracer.create_span(); ``` -Move assignment begins with two objects and ends up with one object (and one -empty shell of an object). -Since destroying a `Span` has the side effect of finishing it, one sensible -definition of `Span::operator=(Span&& other)` would be equivalent to: +Move assignment begins with two objects and ends up with one object (and one empty shell of an +object). + +Since destroying a `Span` has the side effect of finishing it, one sensible definition of +`Span::operator=(Span&& other)` would be equivalent to: + ```c++ this->~Span(); new (this) Span(std::move(other)); return *this; ``` -This would have the potentially surprising feature of _finishing_ the first span -when you wish to replace it with another, i.e. there would always be two spans. - -This could be avoided if we could guarantee that the two `Span`s belong to the -same `TraceSegment`. Then move-assigning a `Span` could be defined as -move-assigning its `SpanData` and somehow annotating the moved-from `SpanData` -as being invalid. However, if the two `Span`s belong to different -`TraceSegment`s, then it could be that the moved-to `Span`'s `TraceSegment` -consists of only that one `Span`. Now we have to account for empty -`TraceSegment` states. This could all be dealt with, but no matter what we -decide, it would always be the case that `Span::operator=(Span&&)` has the -effect of making the original span (`this`) either finish implicitly or -_disappear entirely_, which is at odds with its otherwise [RAII][6] nature. + +This would have the potentially surprising feature of _finishing_ the first span when you wish to +replace it with another, i.e. there would always be two spans. + +This could be avoided if we could guarantee that the two `Span`s belong to the same `TraceSegment`. +Then move-assigning a `Span` could be defined as move-assigning its `SpanData` and somehow +annotating the moved-from `SpanData` as being invalid. However, if the two `Span`s belong to +different `TraceSegment`s, then it could be that the moved-to `Span`'s `TraceSegment` consists of +only that one `Span`. Now we have to account for empty `TraceSegment` states. This could all be +dealt with, but no matter what we decide, it would always be the case that `Span::operator=(Span&&)` +has the effect of making the original span (`this`) either finish implicitly or _disappear +entirely_, which is at odds with its otherwise +[RAII](https://en.cppreference.com/w/cpp/language/raii) nature. To avoid these issues, assignment to `Span` objects is disallowed. -Another opinionated property of `Span` is that it is not an interface, nor does -it implement an interface. Usually it is considered polite for a C++ library to -deal in handles (`unique_ptr` or `shared_ptr`) to interfaces, i.e. classes that -contain pure virtual functions. This way, a client of the library can substitute -an alternative implementation to the interface(s) for testing or for when the -behavior of the library is not desired. - -At the risk of being impolite, dd-trace-cpp takes a different approach. `Span` -is a concrete type whose behavior cannot be substituted. Instead, there are -other places in the library where dependency injection can be used to restrict -or alter the behavior of the library. The trade-off is that `Span` and related -components must always "go through the motions" of their definitions and cannot -be completely customized, but in exchange the indirection, pointer semantics, -and null states that accompany handle-to-interface are avoided. +Another opinionated property of `Span` is that it is not an interface, nor does it implement an +interface. Usually it is considered polite for a C++ library to deal in handles (`unique_ptr` or +`shared_ptr`) to interfaces, i.e. classes that contain pure virtual functions. This way, a client of +the library can substitute an alternative implementation to the interface(s) for testing or for when +the behavior of the library is not desired. + +At the risk of being impolite, dd-trace-cpp takes a different approach. `Span` is a concrete type +whose behavior cannot be substituted. Instead, there are other places in the library where +dependency injection can be used to restrict or alter the behavior of the library. The trade-off is +that `Span` and related components must always "go through the motions" of their definitions and +cannot be completely customized, but in exchange the indirection, pointer semantics, and null states +that accompany handle-to-interface are avoided. ### Trace Segment + A "trace" is the entire tree of spans having the same trace ID. -Within one process/worker/service, though, typically there is not an entire -trace but only part of the trace. Let's call the process/worker/service a -"tracer." +Within one process/worker/service, though, typically there is not an entire trace but only part of +the trace. Let's call the process/worker/service a "tracer." -One portion of a trace that's passing through the tracer is called a "trace -segment." A trace segment begins either at the trace's root span or at a span -extracted from trace context, e.g. a span created from the `X-Datadog-Trace-Id` -and `X-Datadog-Parent-Id` HTTP request headers. The trace segment includes all -local descendants of that span, and has as its "boundary" any descendant spans -without children or descendant spans that were used to inject trace context -out-of-tracer, e.g. in outgoing HTTP request headers. +One portion of a trace that's passing through the tracer is called a "trace segment." A trace +segment begins either at the trace's root span or at a span extracted from trace context, e.g. a +span created from the `X-Datadog-Trace-Id` and `X-Datadog-Parent-Id` HTTP request headers. The trace +segment includes all local descendants of that span, and has as its "boundary" any descendant spans +without children or descendant spans that were used to inject trace context out-of-tracer, e.g. in +outgoing HTTP request headers. -There might be more than one trace segment for the _same trace_ within a tracer -at the same time. Consider the diagram below. +There might be more than one trace segment for the _same trace_ within a tracer at the same time. +Consider the diagram below. flame graph -If our tracer is "service X," then this trace passes through the tracer twice. -We would have two concurrent trace segments for the same trace. +If our tracer is "service X," then this trace passes through the tracer twice. We would have two +concurrent trace segments for the same trace. -`class TraceSegment` is defined in [trace_segment.h][7]. `TraceSegment` objects -are managed internally by the library. That is to say, a user never creates a +`class TraceSegment` is defined in [trace_segment.h](../include/datadog/trace_segment.h). +`TraceSegment` objects are managed internally by the library. That is to say, a user never creates a `TraceSegment`. -The library creates a `TraceSegment` whenever a new trace is created or when -trace context is extracted. This is the job of `class Tracer`, described in the -next section. +The library creates a `TraceSegment` whenever a new trace is created or when trace context is +extracted. This is the job of `class Tracer`, described in the next section. -Primarily, `TraceSegment` is a bag of spans. It contains a -`vector>`. `Span` objects then refer to the `SpanData` -objects via raw pointers. Now that I think about it, `deque` would -work just as well. +Primarily, `TraceSegment` is a bag of spans. It contains a `vector>`. `Span` +objects then refer to the `SpanData` objects via raw pointers. Now that I think about it, +`deque` would work just as well. -When one of a trace segment's spans creates a child, the child is registered -with the trace segment. When a span is finished, the trace segment is notified. -The trace segment keeps track of how many spans it contains (the size of its -`vector`) and how many spans are finished. When the two numbers are equal, the -trace segment is finished. +When one of a trace segment's spans creates a child, the child is registered with the trace segment. +When a span is finished, the trace segment is notified. The trace segment keeps track of how many +spans it contains (the size of its `vector`) and how many spans are finished. When the two numbers +are equal, the trace segment is finished. -When a trace segment is finished, it performs some finalization logic in order -to prepare its spans for submission to the `Collector`. Then it moves its spans -into the collector via `Collector::send`, and a short time later the trace -segment is destroyed. See `TraceSegment::span_finished` in -[trace_segment.cpp][8]. `Collector` is described in a subsequent section. +When a trace segment is finished, it performs some finalization logic in order to prepare its spans +for submission to the `Collector`. Then it moves its spans into the collector via `Collector::send`, +and a short time later the trace segment is destroyed. See `TraceSegment::span_finished` in +[trace_segment.cpp](../src/datadog/trace_segment.cpp). `Collector` is described in a subsequent +section. -A `TraceSegment` contains `shared_ptr`s to everything that it needs in order to -do its job. Those objects are created by `class Tracer` when the tracer is -configured, and then shared with `TraceSegment` when the `TraceSegment` is -created. +A `TraceSegment` contains `shared_ptr`s to everything that it needs in order to do its job. Those +objects are created by `class Tracer` when the tracer is configured, and then shared with +`TraceSegment` when the `TraceSegment` is created. ### Tracer -`class Tracer` is what users configure, and it is how `Span`s are extracted from -trace context or created as a trace's root. See [tracer.h][9]. + +`class Tracer` is what users configure, and it is how `Span`s are extracted from trace context or +created as a trace's root. See [tracer.h](../include/datadog/tracer.h). `Tracer` has two member functions: @@ -173,196 +164,208 @@ and another that combines them: - `extract_or_create_span(...)`. -All of these result in the creation of a new `TraceSegment` (or otherwise return -an error). The `Tracer`'s data members, which were initialized based on the -tracer's configuration, are copied into the `TraceSegment` so that the -`TraceSegment` can operate independently. +All of these result in the creation of a new `TraceSegment` (or otherwise return an error). The +`Tracer`'s data members, which were initialized based on the tracer's configuration, are copied into +the `TraceSegment` so that the `TraceSegment` can operate independently. -Note how `create_span` never fails. This is a nice property. `extract_span` -_can_ fail. +Note how `create_span` never fails. This is a nice property. `extract_span` _can_ fail. -The bulk of `Tracer`'s implementation is `extract_span`. The other substantial -work is configuration, which is handled by `finalize_config(const -TracerConfig&)`, declared in [tracer_config.h][10]. Configuration will be -described in more depth in a subsequent section. +The bulk of `Tracer`'s implementation is `extract_span`. The other substantial work is +configuration, which is handled by `finalize_config(const TracerConfig&)`, declared in +[tracer_config.h](../include/datadog/tracer_config.h). Configuration will be described in more depth +in a subsequent section. ### Collector -`class Collector` is an interface for sending a `TraceSegment`'s spans somewhere -once they're all done. It's defined in [collector.h][12]. + +`class Collector` is an interface for sending a `TraceSegment`'s spans somewhere once they're all +done. It's defined in [collector.h](../include/datadog/collector.h). It's just one function: `send`. More of a callback than an interface. -A `Collector` is either created by `Tracer` or injected into its configuration. -The `Collector` instance is then shared with all `TraceSegment`s created by the -`Tracer`. The only thing that a `TraceSegment` does with the `Collector` is call -`send` once the segment is finished. +A `Collector` is either created by `Tracer` or injected into its configuration. The `Collector` +instance is then shared with all `TraceSegment`s created by the `Tracer`. The only thing that a +`TraceSegment` does with the `Collector` is call `send` once the segment is finished. -The default implementation is `DatadogAgent`, which is described in the next -section. +The default implementation is `DatadogAgent`, which is described in the next section. ### DatadogAgent -`class DatadogAgent` is the default implementation of `Collector`. It's defined -in [datadog_agent.h][13]. -`DatadogAgent` sends trace segments to the [Datadog Agent][14] in batches that -are flushed periodically. In order to do this, `DatadogAgent` needs a means to -make HTTP requests and a means to set a timer for the flush operation. So, -there are two interfaces: [HTTPClient][15] and [EventScheduler][16]. +`class DatadogAgent` is the default implementation of `Collector`. It's defined in +[datadog_agent.h](../src/datadog/datadog_agent.h). -The `HTTPClient` and `EventScheduler` can be injected as part of -`DatadogAgent`'s [configuration][17], which is usually specified via the `agent` -member of `Tracer`'s [configuration][10]. If they're not specified, then default -implementations are used: +`DatadogAgent` sends trace segments to the [Datadog Agent](../include/datadog/http_client.h) in +batches that are flushed periodically. In order to do this, `DatadogAgent` needs a means to make +HTTP requests and a means to set a timer for the flush operation. So, there are two interfaces: +[HTTPClient](../include/datadog/http_client.h) and +[EventScheduler](../include/datadog/event_scheduler.h). -- [class Curl : public HTTPClient][18], which uses libcurl's [multi - interface][20] together with a dedicated thread as an event loop. -- [class ThreadedEventScheduler : public EventScheduler][19], which uses a - dedicated thread for executing scheduled events at the correct time. +The `HTTPClient` and `EventScheduler` can be injected as part of `DatadogAgent`'s +[configuration](../include/datadog/datadog_agent_config.h), which is usually specified via the +`agent` member of `Tracer`'s [configuration](../include/datadog/tracer_config.h). If they're not +specified, then default implementations are used: -`DatadogAgent::flush` is periodically called by the event scheduler. `flush` -uses the HTTP client to send a POST request to the Datadog Agent's -[/v0.4/traces][21] endpoint. It's all callback-based. +- [class Curl : public HTTPClient](../src/datadog/curl.h), which uses libcurl's [multi + interface](https://curl.se/libcurl/c/libcurl-multi.html) together with a dedicated thread as an + event loop. +- [class ThreadedEventScheduler : public EventScheduler](../src/datadog/threaded_event_scheduler.h), +- which uses a dedicated thread for executing scheduled events at the correct time. + +`DatadogAgent::flush` is periodically called by the event scheduler. `flush` uses the HTTP client to +send a POST request to the Datadog Agent's +[/v0.4/traces](https://github.com/DataDog/datadog-agent/blob/9d57c10a9eeb3916e661d35dbd23c6e36395a99d/pkg/trace/api/version.go#L22) +endpoint. It's all callback-based. ### HTTPClient + `class HTTPClient` is an interface for sending HTTP requests. It's defined in -[http_client.h][15]. +[http_client.h](../include/datadog/http_client.h). + +The only kind of HTTP request that the library needs to make, currently, is a POST to the Datadog +Agent's traces endpoint. `HTTPClient` has one member function for each HTTP method needed — so, +currently just the one: -The only kind of HTTP request that the library needs to make, currently, is a -POST to the Datadog Agent's traces endpoint. `HTTPClient` has one member -function for each HTTP method needed — so, currently just the one: ```c++ virtual Expected post(const URL& url, HeadersSetter set_headers, std::string body, ResponseHandler on_response, ErrorHandler on_error) = 0; ``` -It's callback-based. `post` returns almost immediately. It invokes `set_headers` -before returning, in order to get the HTTP request headers. The request `body` -is moved elsewhere for later processing. One of `on_response` or `on_error` will -eventually be called, depending on whether a response was received or if an -error occurred before a response was received. If something goes wrong setting -up the request, then `post` returns an error. If `post` returns an error, then -neither of `on_response` nor `on_error` will be called. + +It's callback-based. `post` returns almost immediately. It invokes `set_headers` before returning, +in order to get the HTTP request headers. The request `body` is moved elsewhere for later +processing. One of `on_response` or `on_error` will eventually be called, depending on whether a +response was received or if an error occurred before a response was received. If something goes +wrong setting up the request, then `post` returns an error. If `post` returns an error, then neither +of `on_response` nor `on_error` will be called. `HTTPClient` also has another member function: + ```c++ virtual void drain(std::chrono::steady_clock::time_point deadline) = 0; ``` -`drain` waits for any in-flight requests to finish, blocking up until no later -than `deadline`. It's used to ensure "clean shutdown." Without it, on average -the last one second of traces would be lost on shutdown. Implementations of -`HTTPClient` that don't have a dedicated thread need not support `drain`; in -those cases, `drain` returns immediately. -The default implementation of `HTTPClient` is [class Curl : public -HTTPClient][18], which uses libcurl's [multi interface][20] together with a -dedicated thread as an event loop. +`drain` waits for any in-flight requests to finish, blocking up until no later than `deadline`. It's +used to ensure "clean shutdown." Without it, on average the last one second of traces would be lost +on shutdown. Implementations of `HTTPClient` that don't have a dedicated thread need not support +`drain`; in those cases, `drain` returns immediately. -`class Curl` is also used within NGINX in Datadog's NGINX module, -[nginx-datadog][22]. This is explicitly [discouraged][23] in NGINX's developer -documentation, but libcurl-with-a-thread is widely used within NGINX modules -regardless. One improvement that I am exploring is to use libcurl's -"[multi_socket][24]" mode, which allows libcurl to utilize someone else's event -loop, obviating the need for another thread. libcurl can then be made to use -NGINX's event loop, as is done in [an example library][25]. +The default implementation of `HTTPClient` is [class Curl : public +HTTPClient](../src/datadog/curl.h), which uses libcurl's [multi +interface](https://curl.se/libcurl/c/libcurl-multi.html) together with a dedicated thread as an +event loop. + +`class Curl` is also used within NGINX in Datadog's Nginx module, +[nginx-datadog](https://github.com/DataDog/nginx-datadog). This is explicitly +[discouraged](https://nginx.org/en/docs/dev/development_guide.html#http_requests_to_ext) in Nginx's +developer documentation, but libcurl-with-a-thread is widely used within NGINX modules regardless. +One improvement that I am exploring is to use libcurl's +"[multi_socket](https://curl.se/libcurl/c/curl_multi_socket_action.html)" mode, which allows libcurl +to utilize someone else's event loop, obviating the need for another thread. libcurl can then be +made to use NGINX's event loop, as is done in [an example +library](https://github.com/dgoffredo/nginx-curl). For now, though, nginx-datadog uses the threaded `class Curl`. -[Envoy's Datadog tracing integration][26] uses a different implementation, -[class AgentHTTPClient : public HTTPClient, ...][27], which uses Envoy's -built-in HTTP facilities. libcurl is not involved at all. +[Envoy's Datadog tracing +integration](https://github.com/envoyproxy/envoy/tree/main/source/extensions/tracers/datadog#datadog-tracer) +uses a different implementation, [class AgentHTTPClient : public HTTPClient, +...](https://github.com/envoyproxy/envoy/blob/main/source/extensions/tracers/datadog/agent_http_client.h), +which uses Envoy's built-in HTTP facilities. libcurl is not involved at all. ### EventScheduler -As of this writing, `class DatadogAgent` flushes batches of finished trace -segments to the Datadog Agent once every two second [by default][28]. -It does this by scheduling a recurring event with an `EventScheduler`, which is -an interface defined in [event_scheduler.h][16]. +As of this writing, `class DatadogAgent` flushes batches of finished trace segments to the Datadog +Agent once every two second [by +default](https://github.com/DataDog/dd-trace-cpp/blob/ca155b3da65c2dc235cf64a28f8e0d8fdab3700c/src/datadog/datadog_agent_config.h#L50-L51). + +It does this by scheduling a recurring event with an `EventScheduler`, which is an interface defined +in [event_scheduler.h](../include/datadog/event_scheduler.h). `EventScheduler` has one member function: + ```c++ virtual Cancel schedule_recurring_event( std::chrono::steady_clock::duration interval, std::function callback) = 0; ``` -Every `interval`, the scheduler will invoke `callback`, starting an initial -`interval` after `schedule_recurring_event` is called. The caller can invoke the -returned `Cancel` to prevent subsequent invocations of `callback`. -The default implementation of `EventScheduler` is [class ThreadedEventScheduler -: public EventScheduler][19], which uses a dedicated thread for executing -scheduled events at the correct time. It was a fun piece of code to write. +Every `interval`, the scheduler will invoke `callback`, starting an initial `interval` after +`schedule_recurring_event` is called. The caller can invoke the returned `Cancel` to prevent +subsequent invocations of `callback`. -Datadog's NGINX module, [nginx-datadog][22] uses a different implementation, -[class NgxEventScheduler : public EventScheduler][29], which uses NGINX's own -event loop instead of a dedicated thread. +The default implementation of `EventScheduler` is [class ThreadedEventScheduler : public +EventScheduler](../src/datadog/threaded_event_scheduler.h), which uses a dedicated thread for +executing scheduled events at the correct time. It was a fun piece of code to write. -[Envoy's Datadog tracing integration][26] also uses a different implementation, -[class EventScheduler : public EventScheduler][30], which uses Envoy's built-in -event dispatch facilities. +Datadog's Nginx module, [nginx-datadog](https://github.com/DataDog/nginx-datadog) uses a different +implementation, [class NgxEventScheduler : public +EventScheduler](https://github.com/DataDog/nginx-datadog/blob/master/src/ngx_event_scheduler.h), +which uses Nginx's own event loop instead of a dedicated thread. + +[Envoy's Datadog tracing +integration](https://github.com/envoyproxy/envoy/tree/main/source/extensions/tracers/datadog#datadog-tracer) +also uses a different implementation, [class EventScheduler : public +EventScheduler](https://github.com/envoyproxy/envoy/blob/main/source/extensions/tracers/datadog/event_scheduler.h), +which uses Envoy's built-in event dispatch facilities. ### Configuration -There's a good [blog post][32] by [Alexis King][31] where she makes the case for -encoding configuration validation into the type system. Forbid invalid states by -making configurable components accept a different type than that which is used -to specify configuration. - -This is not a new idea. It's been used, for example, to "taint" strings that -originate as program inputs. Then you can't accidentally pass user-influenced -inputs to, say, `std::system`, because `std::system` takes a `const char*`, not -a `class UserTaintedString`. There's still ample opportunity to cast away the -taint and sneak it into some string building operation, but at least `class -UserTaintedString` gives hope that a static analysis tool could be used to fill -in some gaps in human code review. - -This library adopts that approach for configuration. The configuration of `class -Tracer` is `class TracerConfig`, but in order to construct a `Tracer` you must -first convert the `TracerConfig` into a `FinalizedTracerConfig` by calling -`finalize_config`. If there is anything wrong with the `TracerConfig` or with -environment variables that would override it, `finalize_config` will return an -`Error` instead of a `FinalizedTracerConfig`. In that case, you can't create a -`Tracer` at all. + +There's a good [blog post](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) by +[Alexis King](https://lexi-lambda.github.io/about.html) where she makes the case for encoding +configuration validation into the type system. Forbid invalid states by making configurable +components accept a different type than that which is used to specify configuration. + +This is not a new idea. It's been used, for example, to "taint" strings that originate as program +inputs. Then you can't accidentally pass user-influenced inputs to, say, `std::system`, because +`std::system` takes a `const char*`, not a `class UserTaintedString`. There's still ample +opportunity to cast away the taint and sneak it into some string building operation, but at least +`class UserTaintedString` gives hope that a static analysis tool could be used to fill in some gaps +in human code review. + +This library adopts that approach for configuration. The configuration of `class Tracer` is `class +TracerConfig`, but in order to construct a `Tracer` you must first convert the `TracerConfig` into a +`FinalizedTracerConfig` by calling `finalize_config`. If there is anything wrong with the +`TracerConfig` or with environment variables that would override it, `finalize_config` will return +an `Error` instead of a `FinalizedTracerConfig`. In that case, you can't create a `Tracer` at all. This technique applies to multiple components: | Component | Unvalidated | Validated | Parser | | --------------- | ----------- | --------- | ----------------- | -| `Tracer` | `TracerConfig` | `FinalizedTracerConfig` | `finalize_config` in [tracer_config.h][10] | -| `DatadogAgent` | `DatadogAgentConfig` | `FinalizedDatadogAgentConfig` | `finalize_config` in [datadog_agent_config.h][17] | -| `TraceSampler` | `TraceSamplerConfig` | `FinalizedTraceSamplerConfig` | `finalize_config` in [trace_sampler_config.h][33] | -| `SpanSampler` | `SpanSamplerConfig` | `FinalizedSpanSamplerConfig` | `finalize_config` in [span_sampler_config.h][34] | -| multiple | `double` | `Rate` | `Rate::from` in [rate.h][35] | - -An alternative approach, that Caleb espouses, is to accept invalid configuration -quietly. When invalid configuration is detected, the library could substitute a -reasonable default and then send notice of the configuration issue to Datadog, -e.g. as a hidden span tag. That information would then be available to Support -should the customer raise an issue due to a difference in behavior between what -they see and what they think they configured. This approach is also resilient to -dynamic configuration changes. Rather than a "bad config update" causing tracing -to cease completely, instead tracing could continue to operate in the defaulted -mode. - -This library uses the stricter approach. The downside is that a user of the -library has to decide what to do when even the slightest part of the -configuration or environment is deemed invalid. - -One other convention of the library is that `FinalizedFooConfig` (for some -`Foo`) is never a data member of the configured component class. That is, -`FinalizedTracerConfig` is not stored in `Tracer`. Instead, a constructor might -individually copy the finalized config's data members. This is to prevent -eventual intermixing between the "configuration representation" and the "runtime -representation." In part, `finalize_config` already mitigates the problem. -Abstaining from storing the finalized config as a data member is a step further. +| `Tracer` | `TracerConfig` | `FinalizedTracerConfig` | `finalize_config` in [tracer_config.h](../include/datadog/tracer_config.h) | +| `DatadogAgent` | `DatadogAgentConfig` | `FinalizedDatadogAgentConfig` | `finalize_config` in [datadog_agent_config.h](../include/datadog/datadog_agent_config.h) | +| `TraceSampler` | `TraceSamplerConfig` | `FinalizedTraceSamplerConfig` | `finalize_config` in [trace_sampler_config.h](../include/datadog/trace_sampler_config.h) | +| `SpanSampler` | `SpanSamplerConfig` | `FinalizedSpanSamplerConfig` | `finalize_config` in [span_sampler_config.h](../include/datadog/span_sampler_config.h) | +| multiple | `double` | `Rate` | `Rate::from` in [rate.h](../include/datadog/rate.h) | + +An alternative approach, that Caleb espouses, is to accept invalid configuration quietly. When +invalid configuration is detected, the library could substitute a reasonable default and then send +notice of the configuration issue to Datadog, e.g. as a hidden span tag. That information would then +be available to Support should the customer raise an issue due to a difference in behavior between +what they see and what they think they configured. This approach is also resilient to dynamic +configuration changes. Rather than a "bad config update" causing tracing to cease completely, +instead tracing could continue to operate in the defaulted mode. + +This library uses the stricter approach. The downside is that a user of the library has to decide +what to do when even the slightest part of the configuration or environment is deemed invalid. + +One other convention of the library is that `FinalizedFooConfig` (for some `Foo`) is never a data +member of the configured component class. That is, `FinalizedTracerConfig` is not stored in +`Tracer`. Instead, a constructor might individually copy the finalized config's data members. This +is to prevent eventual intermixing between the "configuration representation" and the "runtime +representation." In part, `finalize_config` already mitigates the problem. Abstaining from storing +the finalized config as a data member is a step further. ### Error Handling -Most error scenarios within this library are individually enumerated by `enum -Error::Code`, defined in [error.h][36]. -As of this writing, some of the error codes are emitted in more than one place, -but most of them are emitted in only one place, as is illustrated by the -following incomprehensible command shell pipeline: -``` -david@ein:~/src/dd-trace-cpp/src/datadog$ sed -n 's/^\s*\(\w\+\)\s*=\s*[0-9]\+,\?\s*$/{Error::\1/p' error.h | paste -d '|' -s | xargs git grep -P | sed -n 's/.*Error::\(\w\+\),.*/\1/p' | sort | uniq -c | sort -rn +Most error scenarios within this library are individually enumerated by `enum Error::Code`, defined +in [error.h](../include/datadog/error.h). + +As of this writing, some of the error codes are emitted in more than one place, but most of them are +emitted in only one place, as is illustrated by the following incomprehensible command shell +pipeline: + +```bash +dd-trace-cpp/src/datadog$ sed -n 's/^\s*\(\w\+\)\s*=\s*[0-9]\+,\?\s*$/{Error::\1/p' error.h | paste -d '|' -s | xargs git grep -P | sed -n 's/.*Error::\(\w\+\),.*/\1/p' | sort | uniq -c | sort -rn 3 MESSAGEPACK_ENCODE_FAILURE 2 MAX_PER_SECOND_OUT_OF_RANGE 2 INVALID_INTEGER @@ -403,27 +406,26 @@ david@ein:~/src/dd-trace-cpp/src/datadog$ sed -n 's/^\s*\(\w\+\)\s*=\s*[0-9]\+,\ 1 CURL_HTTP_CLIENT_SETUP_FAILED 1 CURL_HTTP_CLIENT_NOT_RUNNING ``` -The integer values of the enumerated `Error::Code`s are intended to be -permanent. Given an error message from a log mentioning an error code 24, one -can look at the source code for the matching `Error::Code` -(`TAG_MISSING_SEPARATOR`) and then find all of the possible source origins for -that error. At first this seems like a poor substitute for including the source -file and line in the error message, but I suspect that this compromise is -better. - -In addition to an error code, each error condition is associated with a -contextual diagnostic message. The diagnostic is not only a description of the -error, but also contains runtime context that might help a user or a maintainer -identify the underlying issue. For example, a message for -`TAG_MISSING_SEPARATOR` might include the text that was being parsed, or mention -the name of the relevant environment variable. - -The `Error::Code code` and `std::string message` are combined in a `struct -Error`, which is the error type used by this library. - -`struct Error` has a convenience member function, `Error with_prefix(StringView) -const`, that allows context to be added to an error. It's analogous to -`catch`ing one exception and then `throw`ing another, e.g. + +The integer values of the enumerated `Error::Code`s are intended to be permanent. Given an error +message from a log mentioning an error code 24, one can look at the source code for the matching +`Error::Code` (`TAG_MISSING_SEPARATOR`) and then find all of the possible source origins for that +error. At first this seems like a poor substitute for including the source file and line in the +error message, but I suspect that this compromise is better. + +In addition to an error code, each error condition is associated with a contextual diagnostic +message. The diagnostic is not only a description of the error, but also contains runtime context +that might help a user or a maintainer identify the underlying issue. For example, a message for +`TAG_MISSING_SEPARATOR` might include the text that was being parsed, or mention the name of the +relevant environment variable. + +The `Error::Code code` and `std::string message` are combined in a `struct Error`, which is the +error type used by this library. + +`struct Error` has a convenience member function, `Error with_prefix(StringView) const`, that allows +context to be added to an error. It's analogous to `catch`ing one exception and then `throw`ing +another, e.g. + ```c++ Expected adopt_cat(AnimalShelter& shelter) { Expected result = shelter.adopt(Animals::CAT, 1); @@ -434,26 +436,27 @@ Expected adopt_cat(AnimalShelter& shelter) { } ``` -`struct Error` is most often used in conjunction with the `Expected` class -template, which is defined in [expected.h][37]. +`struct Error` is most often used in conjunction with the `Expected` class template, which is +defined in [expected.h](../include/datadog/expected.h). -`template class Expected` is either a `T` or an `Error`. It is a -wrapper around `std::variant`. It's inspired by C++23's -[std::expected][38], but the two are not compatible. +`template class Expected` is either a `T` or an `Error`. It is a wrapper around +`std::variant`. It's inspired by C++23's +[std::expected](https://en.cppreference.com/w/cpp/utility/expected), but the two are not compatible. -Functions in this library that intend to return a "`Value`," but that might -instead fail with an `Error`, return an `Expected`. +Functions in this library that intend to return a "`Value`," but that might instead fail with an +`Error`, return an `Expected`. -This library never reports errors by throwing an exception. However, the library -will allow exceptions, such as `std::bad_alloc` and `std::bad_variant_access`, -to pass through it, and does sometimes use exceptions internally. The intention -is that a client of this library does not need to write `catch`. An alternative -design would be to use exceptions for their intended purpose. We decided that, -for a tracing library embedded in proxies, the ergonomics of error values are a -better fit than exceptions. +This library never reports errors by throwing an exception. However, the library will allow +exceptions, such as `std::bad_alloc` and `std::bad_variant_access`, to pass through it, and does +sometimes use exceptions internally. The intention is that a client of this library does not need to +write `catch`. An alternative design would be to use exceptions for their intended purpose. We +decided that, for a tracing library embedded in proxies, the ergonomics of error values are a better +fit than exceptions. + +`Expected` supports two syntaxes of use. The first is +[std::get_if](https://en.cppreference.com/w/cpp/utility/variant/get_if)-style unpacking with the +`if_error` member function: -`Expected` supports two syntaxes of use. The first is [std::get_if][39]-style -unpacking with the `if_error` member function: ```c++ Expected lunch = go_buy_lunch(); if (Error *error = lunch.if_error()) { @@ -461,22 +464,21 @@ if (Error *error = lunch.if_error()) { } eat_salad(*lunch); // or, lunch.value() ``` -`if_error` returns a pointer to the `Error` if there is one, otherwise it -returns `nullptr`. The body of a conditional statement such as `if` or `while` -is allowed to contain a variable definition. If the conditional statement -contains a variable definition, then the truth value of the resulting variable -is used as the condition. So, if there is _not_ an error, then `if_error` -returns `nullptr`, and so the declared `Error*` is `nullptr`, which is false for -the purposes of conditionals, and so in that case the body (block) of the -conditional statement is skipped. - -`if_error` cannot be called on an [rvalue][40], because allowing this makes it -far too easy to obtain a pointer to a destroyed object. I wrote the component -and nonetheless made this mistake repeatedly. So, the rvalue-flavored overload -of `Expected::if_error` is `delete`d. - -The other syntax of use supported by `Expected` is the traditional -check-and-accessor pattern: + +`if_error` returns a pointer to the `Error` if there is one, otherwise it returns `nullptr`. The +body of a conditional statement such as `if` or `while` is allowed to contain a variable definition. +If the conditional statement contains a variable definition, then the truth value of the resulting +variable is used as the condition. So, if there is _not_ an error, then `if_error` returns +`nullptr`, and so the declared `Error*` is `nullptr`, which is false for the purposes of +conditionals, and so in that case the body (block) of the conditional statement is skipped. + +`if_error` cannot be called on an +[rvalue](https://en.cppreference.com/w/cpp/language/value_category), because allowing this makes it +far too easy to obtain a pointer to a destroyed object. I wrote the component and nonetheless made +this mistake repeatedly. So, the rvalue-flavored overload of `Expected::if_error` is `delete`d. + +The other syntax of use supported by `Expected` is the traditional check-and-accessor pattern: + ```c++ Expected lunch = go_buy_lunch(); if (!lunch) { @@ -485,195 +487,131 @@ if (!lunch) { eat_salad(*lunch); // or, lunch.value() ``` -`Expected` defines `explicit operator bool`, which is `true` if the `Expected` -contains a non-`Error`, and `false` if the `Expected` contains an `Error`. There -is also `has_value()`, which returns the same thing. +`Expected` defines `explicit operator bool`, which is `true` if the `Expected` contains a +non-`Error`, and `false` if the `Expected` contains an `Error`. There is also `has_value()`, which +returns the same thing. -Then the `Error` can be obtained via `error()`, or the non-`Error` can be -obtained via `value()` or `operator*()`. +Then the `Error` can be obtained via `error()`, or the non-`Error` can be obtained via `value()` or +`operator*()`. -Why bother with `if_error`? Because I like it! I'm a sucker for structured -bindings and pattern matching. +Why bother with `if_error`? Because I like it! I'm a sucker for structured bindings and pattern +matching. -`template class Expected` has one specialization: `Expected`. -`Expected` is "either an `Error` or nothing." It's used to convey the -result of an operation that might fail but that doesn't yield a value when it -succeeds. It behaves in the same way as `Expected`, except that `value()` and -`operator*()` are not defined. +`template class Expected` has one specialization: `Expected`. `Expected` is +"either an `Error` or nothing." It's used to convey the result of an operation that might fail but +that doesn't yield a value when it succeeds. It behaves in the same way as `Expected`, except +that `value()` and `operator*()` are not defined. -At first I instead used `std::optional` directly. The problem there was -that `explicit operator bool` has the opposite meaning as it does in -`Expected`. I wanted error handling code to be the same in the two cases, and -so I specialized `Expected`. `Expected` is implemented in terms of -`std::optional`, but inverts the value of `explicit operator bool`. +At first I instead used `std::optional` directly. The problem there was that `explicit +operator bool` has the opposite meaning as it does in `Expected`. I wanted error handling code to +be the same in the two cases, and so I specialized `Expected`. `Expected` is implemented +in terms of `std::optional`, but inverts the value of `explicit operator bool`. ### Logging -Can we write a tracing library that does not do any logging by itself? The -previous section describes how errors are reported by the library, and no -logging is involved there. Why not leave it up to the client to decide whether -to note error conditions in the log or to proceed silently? - -The problem is that the default implementations of [HTTPClient][15] ([Curl][18]) -and [EventScheduler][16] ([ThreadedEventScheduler][19]) do some of their work on -background threads, where error conditions may occur without having a "caller" -to notify, and where execution will proceed despite the error. In those cases, -should the library squelch the error? - -One option is for these async components to accept an `on_error(Error)` callback -that will be invoked whenever an error occurs on the background thread that -would not otherwise be reported to client code. What would a client library do -in `on_error`? I think that it would either do nothing or log the error. So, an -`on_error` callback for use in background threads is the same as a logging + +Can we write a tracing library that does not do any logging by itself? The previous section +describes how errors are reported by the library, and no logging is involved there. Why not leave it +up to the client to decide whether to note error conditions in the log or to proceed silently? + +The problem is that the default implementations of [HTTPClient](../include/datadog/http_client.h) +([Curl](../src/datadog/curl.h)) and [EventScheduler](../include/datadog/event_scheduler.h) +([ThreadedEventScheduler](../src/datadog/threaded_event_scheduler.h)) do some of their work on +background threads, where error conditions may occur without having a "caller" to notify, and where +execution will proceed despite the error. In those cases, should the library squelch the error? + +One option is for these async components to accept an `on_error(Error)` callback that will be +invoked whenever an error occurs on the background thread that would not otherwise be reported to +client code. What would a client library do in `on_error`? I think that it would either do nothing +or log the error. So, an `on_error` callback for use in background threads is the same as a logging interface. -It's tempting to omit a logging interface, leaving it to clients to decide -whether and how to log. Why have logging _and_ error reporting when you can have -just error reporting? - -We could do that. Here are three reasons why this library has a logging interface -anyway: - -1. Logging a `Tracer`'s configuration when it's initialized is a helpful - diagnostic tool. A logging interface allows `Tracer` to do this explicitly, - as opposed to counting on client code to log `Tracer::config_json()` itself. - A client library can still suppress the startup message in its implementation - of the logging interface, but this is more opt-out than opt-in. -2. Along the same lines, reporting errors that occur on a background thread by - invoking a logging interface allows for the library's default behavior to be - to print an error message to a log, as opposed to having an `on_error` - callback that a client library might choose to log within. -3. A logging interface allows warnings to be logged, notifying a user of a - potentially problematic, but valid, configuration. Sometimes these warnings - are a matter of taste, and sometimes they are required by the specification - of the feature being configured. Logging is not the only way to handle this — - imagine if the return value of `finalize_config` included a list of warnings. - But, as with the previous two points, a logging interface allows for the - default behavior to be a logged message. - -On the other hand, the primary clients of this library are NGINX and Envoy, -where Datadog engineers have a say in how the library is used. So, the -distinction is probably not so important. - -The logging interface is `class Logger`, defined in [logger.h][41]. +It's tempting to omit a logging interface, leaving it to clients to decide whether and how to log. +Why have logging _and_ error reporting when you can have just error reporting? + +We could do that. Here are three reasons why this library has a logging interface anyway: + +1. Logging a `Tracer`'s configuration when it's initialized is a helpful diagnostic tool. A logging + interface allows `Tracer` to do this explicitly, as opposed to counting on client code to log + `Tracer::config_json()` itself. A client library can still suppress the startup message in its + implementation of the logging interface, but this is more opt-out than opt-in. +2. Along the same lines, reporting errors that occur on a background thread by invoking a logging + interface allows for the library's default behavior to be to print an error message to a log, as + opposed to having an `on_error` callback that a client library might choose to log within. +3. A logging interface allows warnings to be logged, notifying a user of a potentially problematic, + but valid, configuration. Sometimes these warnings are a matter of taste, and sometimes they are + required by the specification of the feature being configured. Logging is not the only way to + handle this — imagine if the return value of `finalize_config` included a list of warnings. But, + as with the previous two points, a logging interface allows for the default behavior to be a + logged message. + +On the other hand, the primary clients of this library are Ngix and Envoy, where Datadog engineers +have a say in how the library is used. So, the distinction is probably not so important. + +The logging interface is `class Logger`, defined in [logger.h](../include/datadog/logger.h). The design of `class Logger` is informed by three constraints: -1. Most "warn," "info," "debug," and "trace" severity logging is noise. It is - more an artifact of feature development than it is a helpful event with which - issues can be diagnosed. The logger should have few severity levels, or - ideally only one. -2. Logging libraries that obscure a conditional branch via the use of - preprocessor macros are difficult to understand. Reverse engineering a - composition of preprocessor macros is hard, and is not justified by the - syntactic convenience that the macros provide. -3. When client code decides that a message will not be logged, the library - should minimize the amount of computation that is done — as close to nothing - as is feasible. +1. Most "warn," "info," "debug," and "trace" severity logging is noise. It is more an artifact of + feature development than it is a helpful event with which issues can be diagnosed. The logger + should have few severity levels, or ideally only one. +2. Logging libraries that obscure a conditional branch via the use of preprocessor macros are + difficult to understand. Reverse engineering a composition of preprocessor macros is hard, and is + not justified by the syntactic convenience that the macros provide. +3. When client code decides that a message will not be logged, the library should minimize the + amount of computation that is done — as close to nothing as is feasible. (1) and (3) work well together. -On account of (1), `Logger` has only two "severities": "error" and "startup." -`log_startup` is called once by a `Tracer` when it is initialized. `log_error` -is called in all other logging circumstances. +On account of (1), `Logger` has only two "severities": "error" and "startup." `log_startup` is +called once by a `Tracer` when it is initialized. `log_error` is called in all other logging +circumstances. -On account of (3), `Logger` must do as little work as possible when an -implementer decides that a logging function should not log. If this library were -to build diagnostic messages in all cases, and then pass them to the `Logger`, -then the cost of building the message would be paid even when the `Logger` -decides not to log. On account of (2), the library cannot define a `DD_LOG` -macro that hides an `if (logger.severity > ...)` branch. As a matter of taste, I -don't want the library to be littered with such branches explicitly. +On account of (3), `Logger` must do as little work as possible when an implementer decides that a +logging function should not log. If this library were to build diagnostic messages in all cases, and +then pass them to the `Logger`, then the cost of building the message would be paid even when the +`Logger` decides not to log. On account of (2), the library cannot define a `DD_LOG` macro that +hides an `if (logger.severity > ...)` branch. As a matter of taste, I don't want the library to be +littered with such branches explicitly. The compromise is to have `Logger::log_error` and `Logger::log_startup` accept a -[std::function][44] that, if invoked, does the work of building the diagnostic -message. When a `Logger` implementation decides not to log, the only cost paid -at the call site is the construction of the `std::function` and the underlying -capturing lambda expression with which it was likely initialized. The signature -of the `std::function`, aliased as `Logger::LogFunc`, is `void(std::ostream&)`. -I don't know whether this results in a real runtime savings in the not-logging -case, but it seems likely. - -`Logger` also has two convenience overloads of `log_error`: one that takes a -`const Error&` and one that takes a `StringView`. - -The `const Error&` overload reveals a contradiction in the design. If an `Error` -is produced in a context where its only destiny is to be passed to -`Logger::log_error(const Error&)`, then the cost of building `Error::message` -will always be paid, in violation of constraint (3). One way to avoid this would -be to have versions of the `Error`-returning operations that instead accept a -`Logger&` and use `Logger::log_error(Logger::LogFunc)` directly. I think that -the present state of things is acceptable and that such a change is not -warranted. +[std::function](https://en.cppreference.com/w/cpp/utility/functional/function) that, if invoked, +does the work of building the diagnostic message. When a `Logger` implementation decides not to log, +the only cost paid at the call site is the construction of the `std::function` and the underlying +capturing lambda expression with which it was likely initialized. The signature of the +`std::function`, aliased as `Logger::LogFunc`, is `void(std::ostream&)`. I don't know whether this +results in a real runtime savings in the not-logging case, but it seems likely. + +`Logger` also has two convenience overloads of `log_error`: one that takes a `const Error&` and one +that takes a `StringView`. + +The `const Error&` overload reveals a contradiction in the design. If an `Error` is produced in a +context where its only destiny is to be passed to `Logger::log_error(const Error&)`, then the cost +of building `Error::message` will always be paid, in violation of constraint (3). One way to avoid +this would be to have versions of the `Error`-returning operations that instead accept a `Logger&` +and use `Logger::log_error(Logger::LogFunc)` directly. I think that the present state of things is +acceptable and that such a change is not warranted. The default implementation of `Logger` is `NullLogger`, defined in -[null_logger.h][47]. `NullLogger` doesn't log anything. - -A client library might wish to install `CerrLogger` instead. `CerrLogger` is -defined in [cerr_logger.h][42] and logs to [std::cerr][43] in both `log_error` -and `log_startup`. - -The `Logger` used by a `Tracer` is configured via `std::shared_ptr -TracerConfig::logger`, defined in [tracer_config.h][10]. - -Finally, constraint (1) is still a matter of debate. Support representatives -have expressed frustration with this library's, and its predecessor's, lack of a -"debug mode" that logs the reason for every decision made throughout the -processing of a trace. This issue deserves revisiting. Here are some potential -approaches: - -- Introduce a "debug mode" that sends fine-grained internal information to - Datadog, rather than to a log, either as hidden tags on the trace or via a - different data channel, such as the [Telemetry API][45]. -- Introduce a "trace the tracer" mode that generates additional traces that - represent operations performed by the tracer. This technique was prototyped in - the [david.goffredo/traception][46] branch. -- Add a `Logger::log_debug` function that optionally prints fine-grained tracing - information to the log, in violation of constraint (1). - -[1]: https://github.com/DataDog/dd-opentracing-cpp -[2]: https://github.com/open-telemetry/opentelemetry-cpp -[3]: ../include/datadog/span.h -[4]: ../src/datadog/span_data.h -[5]: ../include/datadog/span_config.h -[6]: https://en.cppreference.com/w/cpp/language/raii -[7]: ../include/datadog/trace_segment.h -[8]: ../src/datadog/trace_segment.cpp -[9]: ../include/datadog/tracer.h -[10]: ../include/datadog/tracer_config.h -[11]: ../include/datadog/dict_writer.h -[12]: ../include/datadog/collector.h -[13]: ../src/datadog/datadog_agent.h -[14]: https://docs.datadoghq.com/agent/ -[15]: ../include/datadog/http_client.h -[16]: ../include/datadog/event_scheduler.h -[17]: ../include/datadog/datadog_agent_config.h -[18]: ../src/datadog/curl.h -[19]: ../src/datadog/threaded_event_scheduler.h -[20]: https://curl.se/libcurl/c/libcurl-multi.html -[21]: https://github.com/DataDog/datadog-agent/blob/9d57c10a9eeb3916e661d35dbd23c6e36395a99d/pkg/trace/api/version.go#L22 -[22]: https://github.com/DataDog/nginx-datadog -[23]: https://nginx.org/en/docs/dev/development_guide.html#http_requests_to_ext -[24]: https://curl.se/libcurl/c/curl_multi_socket_action.html -[25]: https://github.com/dgoffredo/nginx-curl -[26]: https://github.com/envoyproxy/envoy/tree/main/source/extensions/tracers/datadog#datadog-tracer -[27]: https://github.com/envoyproxy/envoy/blob/main/source/extensions/tracers/datadog/agent_http_client.h -[28]: https://github.com/DataDog/dd-trace-cpp/blob/ca155b3da65c2dc235cf64a28f8e0d8fdab3700c/src/datadog/datadog_agent_config.h#L50-L51 -[29]: https://github.com/DataDog/nginx-datadog/blob/master/src/ngx_event_scheduler.h -[30]: https://github.com/envoyproxy/envoy/blob/main/source/extensions/tracers/datadog/event_scheduler.h -[31]: https://lexi-lambda.github.io/about.html -[32]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ -[33]: ../include/datadog/trace_sampler_config.h -[34]: ../include/datadog/span_sampler_config.h -[35]: ../include/datadog/rate.h -[36]: ../include/datadog/error.h -[37]: ../include/datadog/expected.h -[38]: https://en.cppreference.com/w/cpp/utility/expected -[39]: https://en.cppreference.com/w/cpp/utility/variant/get_if -[40]: https://en.cppreference.com/w/cpp/language/value_category -[41]: ../include/datadog/logger.h -[42]: ../include/datadog/cerr_logger.h -[43]: https://en.cppreference.com/w/cpp/io/cerr -[44]: https://en.cppreference.com/w/cpp/utility/functional/function -[45]: https://github.com/DataDog/datadog-agent/blob/796ccb9e92326c85b51f519291e86eb5bc950180/pkg/trace/api/endpoints.go#L97 -[46]: https://github.com/DataDog/dd-trace-cpp/tree/david.goffredo/traception -[47]: ../src/datadog/null_logger.h +[null_logger.h](../src/datadog/null_logger.h). `NullLogger` doesn't log anything. + +A client library might wish to install `CerrLogger` instead. `CerrLogger` is defined in +[cerr_logger.h](../include/datadog/cerr_logger.h) and logs to +[std::cerr](https://en.cppreference.com/w/cpp/io/cerr) in both `log_error` and `log_startup`. + +The `Logger` used by a `Tracer` is configured via `std::shared_ptr TracerConfig::logger`, +defined in [tracer_config.h](../include/datadog/tracer_config.h). + +Finally, constraint (1) is still a matter of debate. Support representatives have expressed +frustration with this library's, and its predecessor's, lack of a "debug mode" that logs the reason +for every decision made throughout the processing of a trace. This issue deserves revisiting. Here +are some potential approaches: + +- Introduce a "debug mode" that sends fine-grained internal information to Datadog, rather than to a + log, either as hidden tags on the trace or via a different data channel, such as the [Telemetry + API](https://github.com/DataDog/datadog-agent/blob/796ccb9e92326c85b51f519291e86eb5bc950180/pkg/trace/api/endpoints.go#L97). +- Introduce a "trace the tracer" mode that generates additional traces that represent operations + performed by the tracer. This technique was prototyped in the + [david.goffredo/traception](https://github.com/DataDog/dd-trace-cpp/tree/david.goffredo/traception) + branch. +- Add a `Logger::log_debug` function that optionally prints fine-grained tracing information to the + log, in violation of constraint (1). diff --git a/examples/hasher/hasher.cpp b/examples/hasher/hasher.cpp index 4b71b7b87..efc3cb84e 100644 --- a/examples/hasher/hasher.cpp +++ b/examples/hasher/hasher.cpp @@ -3,11 +3,11 @@ // If the path does not exist, print an error. // // If the path exists and is a regular file, print the SHA256 digest of the -// file's contents. Produce a single tracing span indicating the calculation. +// file's contents. Produce a single tracing span indicating the calculation. // // If the path exists and is a directory, calculate the SHA256 digest of the // directory from the names and digests of its children, combined in some -// canonical format. Produce a trace whose structure reflects the directory +// canonical format. Produce a trace whose structure reflects the directory // structure. #include @@ -45,7 +45,7 @@ std::string hex(const Digest &digest) { } // Store into the specified `digest` the SHA256 digest of the contents of the -// specified `file`. Return zero on success, or a nonzero value if an error +// specified `file`. Return zero on success, or a nonzero value if an error // occurs. int sha256(Digest &digest, const fs::path &file) { std::ifstream in(file); diff --git a/examples/http-server/common/httplib.h b/examples/http-server/common/httplib.h index 88558dc4f..713613800 100644 --- a/examples/http-server/common/httplib.h +++ b/examples/http-server/common/httplib.h @@ -8713,7 +8713,7 @@ inline bool SSLClient::verify_host(X509 *server_cert) const { Certification Authorities are encouraged to use the dNSName instead. Matching is performed using the matching rules specified by - [RFC2459]. If more than one identity of a given type is present in + [RFC2459]. If more than one identity of a given type is present in the certificate (e.g., more than one dNSName name, a match in any one of the set is considered acceptable.) Names may contain the wildcard character * which is considered to match any single domain name diff --git a/examples/http-server/proxy/proxy.cpp b/examples/http-server/proxy/proxy.cpp index 3941e869c..cc7559fa1 100644 --- a/examples/http-server/proxy/proxy.cpp +++ b/examples/http-server/proxy/proxy.cpp @@ -24,7 +24,7 @@ namespace dd = datadog::tracing; void hard_stop(int /*signal*/) { std::exit(0); } int main() { - // Set up the Datadog tracer. See `src/datadog/tracer_config.h`. + // Set up the Datadog tracer. See `src/datadog/tracer_config.h`. dd::TracerConfig config; config.service = "dd-trace-cpp-http-server-example-proxy"; config.service_type = "proxy"; diff --git a/examples/http-server/server/server.cpp b/examples/http-server/server/server.cpp index 1b183ee2c..3b3f11a91 100644 --- a/examples/http-server/server/server.cpp +++ b/examples/http-server/server/server.cpp @@ -11,7 +11,7 @@ // array [created time, note], e.g. ["2023-05-12 12:38:25","here's a note"]. // // POST /notes -// Create a new note. The body of the request is the note content. +// Create a new note. The body of the request is the note content. // // GET /sleep?seconds= // @@ -107,7 +107,7 @@ void on_get_notes(const httplib::Request& request, httplib::Response& response); void on_post_notes(const httplib::Request& request, httplib::Response& response); int main() { - // Set up the Datadog tracer. See `src/datadog/tracer_config.h`. + // Set up the Datadog tracer. See `src/datadog/tracer_config.h`. dd::TracerConfig config; config.service = "dd-trace-cpp-http-server-example-server"; config.service_type = "server"; diff --git a/fuzz/README.md b/fuzz/README.md index 1929ebd7a..2264a5595 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -1,13 +1,15 @@ -Fuzzers -======= -Each subdirectory here contains the source of an executable that [fuzz tests][1] -some part of the library using [LLVM's libfuzzer][2]. +# Fuzzers -There is a toplevel CMake boolean option that adds all of the fuzzer -executables to the build: `BUILD_FUZZERS`. +Each subdirectory here contains the source of an executable that [fuzz +tests](https://en.wikipedia.org/wiki/Fuzzing) some part of the library using [LLVM's +libfuzzer](https://llvm.org/docs/LibFuzzer.html). -When building the fuzzers, the toolchain must be clang-based. For example: -```console +There is a toplevel CMake boolean option that adds all of the fuzzer executables to the build: +`BUILD_FUZZERS`. + +When building the fuzzers, the toolchain must be clang-based. For example: + +```shell $ rm -rf .build # if toolchain needs clearing $ bin/with-toolchain llvm bin/cmake-build -DDD_TRACE_BUILD_FUZZERS=1 $ .build/fuzz/w3c-propagation/w3c-propagation-fuzz @@ -16,6 +18,3 @@ $ .build/fuzz/w3c-propagation/w3c-propagation-fuzz ``` The fuzzer executables are named `.build/fuzz/*/*-fuzz` by convention. - -[1]: https://en.wikipedia.org/wiki/Fuzzing -[2]: https://llvm.org/docs/LibFuzzer.html diff --git a/fuzz/w3c-propagation/README.md b/fuzz/w3c-propagation/README.md index f803c278f..8aab9571d 100644 --- a/fuzz/w3c-propagation/README.md +++ b/fuzz/w3c-propagation/README.md @@ -1,11 +1,12 @@ -W3C Propagation Fuzzer -====================== -This directory defines an executable, `fuzz`, that fuzz tests extraction and -injection of the W3C tracing HTTP headers "traceparent" and "tracestate". - -Libfuzzer invokes the `LLVMFuzzerTestOneInput` function repeatedly with a binary -blob of varying size and contents. For each blob, [fuzz.cpp](./fuzz.cpp) runs -its test multiple times. The input blob is interpreted in the following way: +# W3C Propagation Fuzzer + +This directory defines an executable, `fuzz`, that fuzz tests extraction and injection of the W3C +tracing HTTP headers "traceparent" and "tracestate". + +Libfuzzer invokes the `LLVMFuzzerTestOneInput` function repeatedly with a binary blob of varying +size and contents. For each blob, [fuzz.cpp](./fuzz.cpp) runs its test multiple times. The input +blob is interpreted in the following way: + ```text blob: _ _ _ _ _ _ _ _ _ @@ -23,14 +24,15 @@ iteration 8: p p p p p p p p s iteration 9: p p p p p p p p p ``` - The `p`-labeled bytes are the "traceparent" header value, while the `s`-labeled - bytes are the "tracestate" header value. So, for an input blob of length `n`, - the `LLVMFuzzerTestOneInput` runs its test `n + 1` times, for the `n + 1` - possible divisions of the input between "traceparent" and "tracestate". - Each test uses a singleton[^1] `Tracer` to `extract_span` from a `DictReader` - containing the "traceparent" and "tracestate" headers. If that succeeds, then - the test `inject`s the resulting span into a no-op `DictWriter`. +The `p`-labeled bytes are the "traceparent" header value, while the `s`-labeled bytes are the +"tracestate" header value. So, for an input blob of length `n`, the `LLVMFuzzerTestOneInput` runs +its test `n + 1` times, for the `n + 1` possible divisions of the input between "traceparent" and +"tracestate". + +Each test uses a singleton[^1] `Tracer` to `extract_span` from a `DictReader` containing the +"traceparent" and "tracestate" headers. If that succeeds, then the test `inject`s the resulting span +into a no-op `DictWriter`. -[^1]: thread-local, actually, though it doesn't matter because even libfuzzer's - "worker" mode forks instead of threads +[^1]: thread-local, actually, though it doesn't matter because even libfuzzer's "worker" mode forks +instead of threads. diff --git a/include/datadog/cerr_logger.h b/include/datadog/cerr_logger.h index 63945ce0d..49497ad22 100644 --- a/include/datadog/cerr_logger.h +++ b/include/datadog/cerr_logger.h @@ -1,7 +1,7 @@ #pragma once // This component provides a class, `CerrLogger`, that implements the `Logger` -// interface from `logger.h`. `CerrLogger` prints to `std::cerr`, which is +// interface from `logger.h`. `CerrLogger` prints to `std::cerr`, which is // typically an unbuffered stream to the standard error file. #include diff --git a/include/datadog/clock.h b/include/datadog/clock.h index 84ef44f25..bc4bdcab9 100644 --- a/include/datadog/clock.h +++ b/include/datadog/clock.h @@ -5,7 +5,7 @@ // // Each `Span` has a start time and a duration. The start time ought to be // measured using a system clock, so that Network Time Protocol adjustments and -// other time settings are accurately reflected in the span start time. The +// other time settings are accurately reflected in the span start time. The // span's duration, however, is better measured using a steady (monotonic) clock // so that adjustments to the system clock made during the extent of the span do // not skew the span's measured duration. diff --git a/include/datadog/collector.h b/include/datadog/collector.h index 96f9e64d2..5198834ff 100644 --- a/include/datadog/collector.h +++ b/include/datadog/collector.h @@ -7,7 +7,7 @@ // serializing the spans and sending them to a Datadog Agent. // // As a result of `send`ing spans to a `Collector`, the `TraceSampler` might be -// adjusted to increase or decrease the rate at which traces are kept. See the +// adjusted to increase or decrease the rate at which traces are kept. See the // `response_handler` parameter to `Collector::send`. #include @@ -23,9 +23,9 @@ class TraceSampler; class Collector { public: - // Submit ownership of the specified `spans` to the collector. If the + // Submit ownership of the specified `spans` to the collector. If the // collector delivers a response relevant to trace sampling, reconfigure the - // sampler using the specified `response_handler`. Return an error if one + // sampler using the specified `response_handler`. Return an error if one // occurs. virtual Expected send( std::vector>&& spans, diff --git a/include/datadog/datadog_agent_config.h b/include/datadog/datadog_agent_config.h index a324f44f4..cb5334877 100644 --- a/include/datadog/datadog_agent_config.h +++ b/include/datadog/datadog_agent_config.h @@ -3,9 +3,9 @@ // This component provides facilities for configuring a `DatadogAgent`. // // `struct DatadogAgentConfig` contains fields that are used to configure -// `DatadogAgent`. The configuration must first be finalized before it can be -// used by `DatadogAgent`. The function `finalize_config` produces either an -// error or a `FinalizedDatadogAgentConfig`. The latter can be used by +// `DatadogAgent`. The configuration must first be finalized before it can be +// used by `DatadogAgent`. The function `finalize_config` produces either an +// error or a `FinalizedDatadogAgentConfig`. The latter can be used by // `DatadogAgent`. // // Typical usage of `DatadogAgentConfig` is implicit as part of `TracerConfig`. @@ -30,14 +30,14 @@ class EventScheduler; class Logger; struct DatadogAgentConfig { - // The `HTTPClient` used to submit traces to the Datadog Agent. If this + // The `HTTPClient` used to submit traces to the Datadog Agent. If this // library was built with libcurl (the default), then `http_client` is // optional: a `Curl` instance will be used if `http_client` is left null. // If this library was built without libcurl, then `http_client` is required // not to be null. std::shared_ptr http_client = nullptr; // The `EventScheduler` used to periodically submit batches of traces to the - // Datadog Agent. If `event_scheduler` is null, then a + // Datadog Agent. If `event_scheduler` is null, then a // `ThreadedEventScheduler` instance will be used instead. std::shared_ptr event_scheduler = nullptr; // A list of Remote Configuration listeners. diff --git a/include/datadog/dict_reader.h b/include/datadog/dict_reader.h index c6ee310f3..dfa6b1d19 100644 --- a/include/datadog/dict_reader.h +++ b/include/datadog/dict_reader.h @@ -1,7 +1,7 @@ #pragma once // This component provides an interface, `DictReader`, that represents a -// read-only key/value mapping of strings. It's used when extracting trace +// read-only key/value mapping of strings. It's used when extracting trace // context from externalized formats: HTTP headers, gRPC metadata, etc. #include diff --git a/include/datadog/dict_writer.h b/include/datadog/dict_writer.h index 89a4bfe30..34bb147d4 100644 --- a/include/datadog/dict_writer.h +++ b/include/datadog/dict_writer.h @@ -1,7 +1,7 @@ #pragma once // This component provides an interface, `DictWriter`, that represents a -// write-only key/value mapping of strings. It's used when injecting trace +// write-only key/value mapping of strings.It's used when injecting trace // context into externalized formats: HTTP headers, gRPC metadata, etc. // // Note that while the data structure modeled is a mapping, duplicate keys are @@ -17,7 +17,7 @@ class DictWriter { public: virtual ~DictWriter() {} - // Associate the specified `value` with the specified `key`. An + // Associate the specified `value` with the specified `key`. An // implementation may, but is not required to, overwrite any previous value at // `key`. virtual void set(StringView key, StringView value) = 0; diff --git a/include/datadog/environment.h b/include/datadog/environment.h index 824e4a3aa..6cfe231a1 100644 --- a/include/datadog/environment.h +++ b/include/datadog/environment.h @@ -3,7 +3,7 @@ // This component provides a registry of all environment variables that can be // used to configure this library. // -// Each `enum Variable` denotes an environment variable. The enum value names +// Each `enum Variable` denotes an environment variable. The enum value names // are the same as the names of the environment variables. // // `name` returns the name of a specified `Variable`. diff --git a/include/datadog/error.h b/include/datadog/error.h index af6825dc0..c7f9d7a94 100644 --- a/include/datadog/error.h +++ b/include/datadog/error.h @@ -7,7 +7,7 @@ // consistent across library versions, so integer values in error diagnostics // can always be looked up here. // -// `struct Error` is the error type used by the `Expected` class template. See +// `struct Error` is the error type used by the `Expected` class template. See // `expected.h`. #include diff --git a/include/datadog/event_scheduler.h b/include/datadog/event_scheduler.h index e8c7051a4..9a834dc8d 100644 --- a/include/datadog/event_scheduler.h +++ b/include/datadog/event_scheduler.h @@ -6,7 +6,7 @@ // `DatadogAgent` uses an `EventScheduler` to periodically send batches of // traces to the Datadog Agent. // -// The default implementation is `ThreadedEventScheduler`. See +// The default implementation is `ThreadedEventScheduler`. See // `threaded_event_scheduler.h`. #include @@ -21,8 +21,8 @@ class EventScheduler { using Cancel = std::function; // Invoke the specified `callback` repeatedly, with the specified `interval` - // elapsing between invocations. The first invocation is after an initial - // `interval`. Return a function-like object that can be invoked without + // elapsing between invocations. The first invocation is after an initial + // `interval`. Return a function-like object that can be invoked without // arguments to prevent subsequent invocations of `callback`. virtual Cancel schedule_recurring_event( std::chrono::steady_clock::duration interval, diff --git a/include/datadog/expected.h b/include/datadog/expected.h index 14cc5e0ed..84c582c03 100644 --- a/include/datadog/expected.h +++ b/include/datadog/expected.h @@ -1,7 +1,7 @@ #pragma once // This component provides a class template, `Expected`, that is either an -// instance of `T` or an instance of `Error`. `Expected` is either +// instance of `T` or an instance of `Error`. `Expected` is either // `nullopt` or an instance of `Error`. // // `Expected` is inspired by, but incompatible with, C++23's `std::expected`. @@ -66,7 +66,7 @@ class Expected { bool has_value() const noexcept; explicit operator bool() const noexcept; - // Return a reference to the `Value` held by this object. If this object is + // Return a reference to the `Value` held by this object. If this object is // an `Error`, throw a `std::bad_variant_access`. Value& value() &; const Value& value() const&; @@ -77,12 +77,12 @@ class Expected { Value&& operator*() &&; const Value&& operator*() const&&; - // Return a pointer to the `Value` held by this object. If this object is an + // Return a pointer to the `Value` held by this object. If this object is an // `Error`, throw a `std::bad_variant_access`. Value* operator->(); const Value* operator->() const; - // Return a reference to the `Error` held by this object. If this object is + // Return a reference to the `Error` held by this object. If this object is // not an `Error`, throw a `std::bad_variant_access`. Error& error() &; const Error& error() const&; diff --git a/include/datadog/http_client.h b/include/datadog/http_client.h index 7186acbe8..97e5abd9d 100644 --- a/include/datadog/http_client.h +++ b/include/datadog/http_client.h @@ -6,7 +6,7 @@ // `HTTPClient` is used by `DatadogAgent` to send traces to the Datadog Agent. // // If this library was built with support for libcurl, then `Curl` implements -// `HTTPClient` in terms of libcurl. See `curl.h`. +// `HTTPClient` in terms of libcurl. See `curl.h`. #include #include @@ -38,12 +38,12 @@ class HTTPClient { // error-indicating HTTP responses. using ErrorHandler = std::function; - // Send a POST request to the specified `url`. Set request headers by calling - // the specified `set_headers` callback. Include the specified `body` at the - // end of the request. Invoke the specified `on_response` callback if/when + // Send a POST request to the specified `url`. Set request headers by calling + // the specified `set_headers` callback. Include the specified `body` at the + // end of the request. Invoke the specified `on_response` callback if/when // a response is delivered (even if that response contains an error HTTP - // response status). Invoke the specified `on_error` if an error occurs - // outside of HTTP, such as a connection failure. If an error occurs while + // response status). Invoke the specified `on_error` if an error occurs + // outside of HTTP, such as a connection failure. If an error occurs while // preparing the request, return an `Error`. The behavior is undefined if // either of `on_response` or `on_error` throws an exception. virtual Expected post( diff --git a/include/datadog/optional.h b/include/datadog/optional.h index ae1deb7cc..e976203e8 100644 --- a/include/datadog/optional.h +++ b/include/datadog/optional.h @@ -3,7 +3,7 @@ // One of the clients of this library is Envoy, a service (HTTP) proxy. // // Envoy uses Abseil as its base C++ library, and additionally builds in C++17 -// mode. Abseil has a build option to forward its `std::string_view` and +// mode. Abseil has a build option to forward its `std::string_view` and // `std::optional` equivalents to the actual standard types when C++17 is // available. // @@ -17,9 +17,9 @@ // and `std::optional` in the exported interface, i.e. in header files. // // As a workaround, Bazel (the build tool used by Envoy) builds of this library -// will define the `DD_USE_ABSEIL_FOR_ENVOY` preprocessor macro. When this +// will define the `DD_USE_ABSEIL_FOR_ENVOY` preprocessor macro. When this // macro is defined, the library-specific `StringView` and `Optional` aliases -// will refer to the Abseil types. When the macro is not defined, the +// will refer to the Abseil types. When the macro is not defined, the // library-specific aliases will refer to the standard types. // // This file defines `datadog::tracing::Optional`, a type template that is an @@ -48,7 +48,7 @@ using Optional = std::optional; inline constexpr auto nullopt = std::nullopt; #endif // defined DD_USE_ABSEIL_FOR_ENVOY -// Return the first non-null argument value. The last argument must not be +// Return the first non-null argument value. The last argument must not be // `Optional`. template auto value_or(Value&& value) { diff --git a/include/datadog/propagation_style.h b/include/datadog/propagation_style.h index 075be1518..8b872c0a0 100644 --- a/include/datadog/propagation_style.h +++ b/include/datadog/propagation_style.h @@ -18,7 +18,7 @@ enum class PropagationStyle { B3, // W3C headers style, i.e. traceparent and tracestate W3C, - // The absence of propagation. If this is the only style set, then + // The absence of propagation. If this is the only style set, then // propagation is disabled in the relevant direction (extraction or // injection). NONE, diff --git a/include/datadog/sampling_mechanism.h b/include/datadog/sampling_mechanism.h index 07a65f1c7..b7376ec9d 100644 --- a/include/datadog/sampling_mechanism.h +++ b/include/datadog/sampling_mechanism.h @@ -1,7 +1,7 @@ #pragma once // This component provides an `enum class`, `SamplingMechanism`, describing a -// reason for a sampling decision. A sampler (or a user, with a manual +// reason for a sampling decision. A sampler (or a user, with a manual // override) decides whether to keep or to drop a trace, but it might do so for // various reasons. // @@ -9,9 +9,9 @@ // but `SamplingPriority` is inadequate for future expansion, for two reasons: // // - `SamplingPriority` conflates the keep/drop decision with the reason (e.g. -// `UserKeep` vs. `SamplerKeep`). Some engineers dislike this. +// `UserKeep` vs. `SamplerKeep`). Some engineers dislike this. // - Some tracer implementations do not decode `SamplingPriority` integer values -// outside of those enumerated in this library. This makes adding new values +// outside of those enumerated in this library. This makes adding new values // infeasible, as older versions of tracers propagating the `SamplingPriority` // along the trace will omit new integer values. // @@ -21,7 +21,7 @@ // Since `SamplingPriority` is already in use and has implications for sampling // behavior (both in its propagation along and trace and its interpretation by // the trace agent), the combination `{SamplingPriority, SamplingMechanism}` is -// used to completely describe a sampling decision. The `SamplingPriority` +// used to completely describe a sampling decision. The `SamplingPriority` // conveys the keep/drop decision, as well as the existing (and now redundant) // user vs. sampler distinction, while the `SamplingMechanism` conveys // precisely where the sampling decision came from, e.g. a user-specified @@ -30,7 +30,7 @@ // // To allow forward compatibility with future `SamplingMechanism` values, // sampling mechanism is treated as just an integer when being deserialized or -// serialized. `SamplingMechanism` enumerates integer values relevant to logic +// serialized. `SamplingMechanism` enumerates integer values relevant to logic // within the tracer. namespace datadog { diff --git a/include/datadog/sampling_priority.h b/include/datadog/sampling_priority.h index af6ae49f2..fb22d3e86 100644 --- a/include/datadog/sampling_priority.h +++ b/include/datadog/sampling_priority.h @@ -3,7 +3,7 @@ // This component defines an enumeration of "sampling priority" values. // // Sampling priority is a hybrid between a sampling decision ("keep" versus -// "drop") and a sampling reason ("user-specified rule"). Values less than or +// "drop") and a sampling reason ("user-specified rule"). Values less than or // equal to zero indicate a decision to "drop," while positive values indicate // a decision to "keep." // diff --git a/include/datadog/span.h b/include/datadog/span.h index 0018230f6..43be554c0 100644 --- a/include/datadog/span.h +++ b/include/datadog/span.h @@ -5,10 +5,10 @@ // query, calculation, etc. // // `Span` objects are created by calling member functions on `Tracer` or on -// another `Span` object. They are not instantiated directly. +// another `Span` object. They are not instantiated directly. // // A `Span` has a start time, an end time, and a name (sometimes called its -// "operation name"). A span is associated with a service, a resource (such as +// "operation name"). A span is associated with a service, a resource (such as // the URL endpoint in an HTTP request), and arbitrary key/value string pairs // known as tags. // @@ -19,25 +19,25 @@ // // For example, an HTTP server might create a `Span` for each request processed. // The `Span` begins when the server begins reading the request, and ends when -// the server has finished writing the response or reporting an error. The +// the server has finished writing the response or reporting an error. The // first child of the request span might represent the reading and parsing of -// the HTTP request's headers. The second child of the request span might +// the HTTP request's headers. The second child of the request span might // represent the dispatch of the request handling to an endpoint-specific -// handler. That child might itself have children, such as a database query or +// handler. That child might itself have children, such as a database query or // a request to an authentication service. // // The complete set of spans that are related to each other via the parent/child // relationship is called a trace. // // A trace can extend across processes and networks via trace context -// propagation. A `Span` can be _extracted_ from its external parent via +// propagation. A `Span` can be _extracted_ from its external parent via // `Tracer::extract_span`, and a `Span` can be _injected_ via `Span::inject` // into an outside context from which its external children might be extracted. // // If an error occurs during the operation that a span represents, the error can // be noted in the span via the `set_error` family of member functions. // -// A `Span` is finished when it is destroyed. The end time can be overridden +// A `Span` is finished when it is destroyed. The end time can be overridden // via the `set_end_time` member function prior to the span's destruction. #include @@ -81,18 +81,18 @@ class Span { Span& operator=(Span&&) = delete; Span& operator=(const Span&) = delete; - // Finish this span and submit it to the associated trace segment. If + // Finish this span and submit it to the associated trace segment. If // `set_end_time` has not been called on this span, then set this span's end // time to the current time. // If this span was moved-from, then the destructor has no effect aside from // destroying data members. ~Span(); - // Return a span that is a child of this span. Use the optionally specified - // `config` to determine the properties of the child span. If `config` is not + // Return a span that is a child of this span. Use the optionally specified + // `config` to determine the properties of the child span. If `config` is not // specified, then the child span's properties are determined by the // `SpanDefaults` that were used to configure the `Tracer` to which this span - // is related. The child span's start time is the current time unless + // is related. The child span's start time is the current time unless // overridden in `config`. Span create_child(const SpanConfig& config) const; Span create_child() const; @@ -148,19 +148,19 @@ class Span { // Set the name of the resource associated with the operation that this span // represents, e.g. "/api/v1/info" or "select count(*) from users". void set_resource_name(StringView); - // Set whether an error occurred during the extent of this span. If `false`, + // Set whether an error occurred during the extent of this span. If `false`, // then error-related tags will be removed from this span as well. void set_error(bool); // Associate a message with the error that occurred during the extent of this - // span. This also has the effect of calling `set_error(true)`. + // span. This also has the effect of calling `set_error(true)`. void set_error_message(StringView); // Associate an error type with the error that occurred during the extent of - // this span. This also has the effect of calling `set_error(true)`. + // this span. This also has the effect of calling `set_error(true)`. void set_error_type(StringView); // Associate a call stack with the error that occurred during the extent of - // this span. This also has the effect of calling `set_error(true)`. + // this span. This also has the effect of calling `set_error(true)`. void set_error_stack(StringView); - // Set end time of this span. Doing so will override the default behavior of + // Set end time of this span. Doing so will override the default behavior of // using the current time in the destructor. void set_end_time(std::chrono::steady_clock::time_point); // Specifies the product (AppSec, DBM) that created this span. @@ -171,7 +171,7 @@ class Span { void inject(DictWriter& writer) const; void inject(DictWriter& writer, const InjectionOptions& options) const; - // Return a reference to this span's trace segment. The trace segment has + // Return a reference to this span's trace segment. The trace segment has // member functions that affect the trace as a whole, such as // `TraceSegment::override_sampling_priority`. TraceSegment& trace_segment(); diff --git a/include/datadog/string_view.h b/include/datadog/string_view.h index 5e20c17ce..eb9cc5fa0 100644 --- a/include/datadog/string_view.h +++ b/include/datadog/string_view.h @@ -3,7 +3,7 @@ // One of the clients of this library is Envoy, a service (HTTP) proxy. // // Envoy uses Abseil as its base C++ library, and additionally builds in C++17 -// mode. Abseil has a build option to forward its `std::string_view` and +// mode. Abseil has a build option to forward its `std::string_view` and // `std::optional` equivalents to the actual standard types when C++17 is // available. // @@ -17,9 +17,9 @@ // and `std::optional` in the exported interface, i.e. in header files. // // As a workaround, Bazel (the build tool used by Envoy) builds of this library -// will define the `DD_USE_ABSEIL_FOR_ENVOY` preprocessor macro. When this +// will define the `DD_USE_ABSEIL_FOR_ENVOY` preprocessor macro. When this // macro is defined, the library-specific `StringView` and `Optional` aliases -// will refer to the Abseil types. When the macro is not defined, the +// will refer to the Abseil types. When the macro is not defined, the // library-specific aliases will refer to the standard types. // // This file defines `datadog::tracing::StringView`, a type that is an alias @@ -45,14 +45,14 @@ using StringView = std::string_view; #endif // defined DD_USE_ABSEIL_FOR_ENVOY // When `StringView` is not the same as `std::string_view`, -// `operator+=(string&, StringView)` isn't defined. To work around this, use +// `operator+=(string&, StringView)` isn't defined. To work around this, use // `append` everywhere. inline void append(std::string& destination, StringView text) { destination.append(text.data(), text.size()); } // When `StringView` is not the same as `std::string_view`, -// `operator=(string&, StringView)` isn't defined. To work around this, use +// `operator=(string&, StringView)` isn't defined. To work around this, use // `assign` everywhere. inline void assign(std::string& destination, StringView text) { destination.assign(text.data(), text.size()); diff --git a/include/datadog/trace_segment.h b/include/datadog/trace_segment.h index a9fcf171c..db32bf7ab 100644 --- a/include/datadog/trace_segment.h +++ b/include/datadog/trace_segment.h @@ -3,23 +3,23 @@ // This component provides a class, `TraceSegment`, that represents a portion of // a trace that is passing through this process. // -// `TraceSegment` is not instantiated directly. It is an implementation detail +// `TraceSegment` is not instantiated directly. It is an implementation detail // of this library. // // A trace might begin in this process, or it might have been propagated in from -// outside (see `Tracer::extract_span`). A trace might remain in this process, +// outside (see `Tracer::extract_span`). A trace might remain in this process, // or it might be propagated outward (see `Span::inject`) one or more times. // -// A trace might pass through this process twice or more. Consider an RPC +// A trace might pass through this process twice or more. Consider an RPC // server that receives a request, in handling that request makes a request to a // different service, and in the course of the other service handling its -// request, the original service is called again. Both "passes" through this +// request, the original service is called again. Both "passes" through this // process are part of the same trace, but each pass is a different _trace // segment_. // // `TraceSegment` stores context and configuration shared among all spans within -// the trace segment, and additionally owns the spans' data. When `Tracer` -// creates or extracts a span, it also creates a new `TraceSegment`. When a +// the trace segment, and additionally owns the spans' data. When `Tracer` +// creates or extracts a span, it also creates a new `TraceSegment`. When a // child `Span` is created from a `Span`, the child and the parent share the // same `TraceSegment`. // @@ -119,7 +119,7 @@ class TraceSegment { // Take ownership of the specified `span`. void register_span(std::unique_ptr span); - // Increment the number of finished spans. If that number is equal to the + // Increment the number of finished spans. If that number is equal to the // number of registered spans, send all of the spans to the `Collector`. void span_finished(); diff --git a/include/datadog/tracer.h b/include/datadog/tracer.h index 6a032a133..891b972fd 100644 --- a/include/datadog/tracer.h +++ b/include/datadog/tracer.h @@ -7,7 +7,7 @@ // from a provided key/value source (see `extract_span`). // // `Tracer` is instantiated with a `FinalizedTracerConfig`, which can be -// obtained from a `TracerConfig` via the `finalize_config` function. See +// obtained from a `TracerConfig` via the `finalize_config` function. See // `tracer_config.h`. #include @@ -65,15 +65,15 @@ class Tracer { Tracer(const FinalizedTracerConfig& config, const std::shared_ptr& generator); - // Create a new trace and return the root span of the trace. Optionally + // Create a new trace and return the root span of the trace. Optionally // specify a `config` indicating the attributes of the root span. Span create_span(); Span create_span(const SpanConfig& config); // Return a span whose parent and other context is parsed from the specified // `reader`, and whose attributes are determined by the optionally specified - // `config`. If there is no tracing information in `reader`, then return an - // error with code `Error::NO_SPAN_TO_EXTRACT`. If a failure occurs, then + // `config`. If there is no tracing information in `reader`, then return an + // error with code `Error::NO_SPAN_TO_EXTRACT`. If a failure occurs, then // return an error with some other code. Expected extract_span(const DictReader& reader); Expected extract_span(const DictReader& reader, diff --git a/include/datadog/tracer_config.h b/include/datadog/tracer_config.h index 4f9196dbd..5b464e64d 100644 --- a/include/datadog/tracer_config.h +++ b/include/datadog/tracer_config.h @@ -1,7 +1,7 @@ #pragma once // This component provides a struct, `TracerConfig`, used to configure a -// `Tracer`. `Tracer` is instantiated with a `FinalizedTracerConfig`, which +// `Tracer`. `Tracer` is instantiated with a `FinalizedTracerConfig`, which // must be obtained from the result of a call to `finalize_config`. #include @@ -59,19 +59,19 @@ struct TracerConfig { // Overriden by the `DD_TAGS` environment variable. Optional> tags; - // `agent` configures a `DatadogAgent` collector instance. See - // `datadog_agent_config.h`. Note that `agent` is ignored if `collector` is + // `agent` configures a `DatadogAgent` collector instance. See + // `datadog_agent_config.h`. Note that `agent` is ignored if `collector` is // set or if `report_traces` is `false`. DatadogAgentConfig agent; // `collector` is a `Collector` instance that the tracer will use to report - // traces to Datadog. If `collector` is null, then a `DatadogAgent` instance - // will be created using the `agent` configuration. Note that `collector` is + // traces to Datadog. If `collector` is null, then a `DatadogAgent` instance + // will be created using the `agent` configuration. Note that `collector` is // ignored if `report_traces` is `false`. std::shared_ptr collector; // `report_traces` indicates whether traces generated by the tracer will be - // sent to a collector (`true`) or discarded on completion (`false`). If + // sent to a collector (`true`) or discarded on completion (`false`). If // `report_traces` is `false`, then both `agent` and `collector` are ignored. // `report_traces` is overridden by the `DD_TRACE_ENABLED` environment // variable. @@ -81,13 +81,13 @@ struct TracerConfig { // `telemetry/configuration.h` By default, the telemetry module is enabled. telemetry::Configuration telemetry; - // `trace_sampler` configures trace sampling. Trace sampling determines which - // traces are sent to Datadog. See `trace_sampler_config.h`. + // `trace_sampler` configures trace sampling. Trace sampling determines which + // traces are sent to Datadog. See `trace_sampler_config.h`. TraceSamplerConfig trace_sampler; - // `span_sampler` configures span sampling. Span sampling allows specified + // `span_sampler` configures span sampling. Span sampling allows specified // spans to be sent to Datadog even when their enclosing trace is dropped by - // the trace sampler. See `span_sampler_config.h`. + // the trace sampler. See `span_sampler_config.h`. SpanSamplerConfig span_sampler; // `injection_styles` indicates with which tracing systems trace propagation @@ -113,12 +113,12 @@ struct TracerConfig { // `max_tags_header_size` is the maximum allowed size, in bytes, of the // serialized value of the "X-Datadog-Tags" header used when injecting trace - // context for propagation. If the serialized value of the header would + // context for propagation. If the serialized value of the header would // exceed `tags_header_size`, the header will be omitted instead. Optional max_tags_header_size; - // `logger` specifies how the tracer will issue diagnostic messages. If - // `logger` is null, then it defaults to no logging (`NullLogger`). See + // `logger` specifies how the tracer will issue diagnostic messages. If + // `logger` is null, then it defaults to no logging (`NullLogger`). See // `CerrLogger` for an alternative. std::shared_ptr logger; @@ -129,7 +129,7 @@ struct TracerConfig { Optional log_on_startup; // `trace_id_128_bit` indicates whether the tracer will generate 128-bit trace - // IDs. If true, the tracer will generate 128-bit trace IDs. If false, the + // IDs. If true, the tracer will generate 128-bit trace IDs. If false, the // tracer will generate 64-bit trace IDs. `trace_id_128_bit` is overridden by // the `DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED` environment variable. Optional generate_128bit_trace_ids; @@ -246,10 +246,10 @@ class FinalizedTracerConfig final { }; // Return a `FinalizedTracerConfig` from the specified `config` and from any -// relevant environment variables. If any configuration is invalid, return an +// relevant environment variables. If any configuration is invalid, return an // `Error`. // Optionally specify a `clock` used to calculate span start times, span -// durations, and timeouts. If `clock` is not specified, then `default_clock` +// durations, and timeouts. If `clock` is not specified, then `default_clock` // is used. Expected finalize_config(const TracerConfig& config); Expected finalize_config(const TracerConfig& config, diff --git a/src/datadog/common/hash.h b/src/datadog/common/hash.h index 3d2155d8c..4cf0bcbee 100644 --- a/src/datadog/common/hash.h +++ b/src/datadog/common/hash.h @@ -29,23 +29,23 @@ namespace details { // Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas // Feb 2 2012: production, same bits as beta // Feb 5 2012: adjusted definitions of uint* to be more portable -// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. +// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. // August 5 2012: SpookyV2 (different results) // -// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. +// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. // All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. // // This was developed for and tested on 64-bit x86-compatible processors. -// It assumes the processor is little-endian. There is a macro +// It assumes the processor is little-endian. There is a macro // controlling whether unaligned reads are allowed (by default they are). // This should be an equally good hash on big-endian machines, but it will // compute different results on them than on little-endian machines. // // Google's CityHash has similar specs to SpookyHash, and CityHash is faster -// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders -// of magnitude slower. CRCs are two or more times slower, but unlike +// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders +// of magnitude slower. CRCs are two or more times slower, but unlike // SpookyHash, they have nice math for combining the CRCs of pieces to form -// the CRCs of wholes. There are also cryptographic hashes, but those are even +// the CRCs of wholes. There are also cryptographic hashes, but those are even // slower than MD5. // // Source: @@ -377,7 +377,7 @@ class SpookyHash { // // Short is used for messages under 192 bytes in length // Short has a low startup cost, the normal mode is good for long - // keys, the cost crossover is at about 192 bytes. The two modes were + // keys, the cost crossover is at about 192 bytes. The two modes were // held to the same quality bar. // static void Short( diff --git a/src/datadog/curl.cpp b/src/datadog/curl.cpp index 10e5f8614..c4228d731 100644 --- a/src/datadog/curl.cpp +++ b/src/datadog/curl.cpp @@ -505,7 +505,7 @@ std::size_t CurlImpl::on_read_header(char *data, std::size_t, // " Foo-Bar : thingy, thingy, thing \r\n" // -> {"foo-bar", "thingy, thingy, thing"} // - // There isn't always a colon. Inputs without a colon can be ignored: + // There isn't always a colon. Inputs without a colon can be ignored: // // > For an HTTP transfer, the status line and the blank line preceding the // > response body are both included as headers and passed to this @@ -646,8 +646,8 @@ void CurlImpl::handle_message(const CURLMsg &message) { } auto &request = *reinterpret_cast(user_data); - // `request` is done. If we got a response, then call the response - // handler. If an error occurred, then call the error handler. + // `request` is done. If we got a response, then call the response + // handler. If an error occurred, then call the error handler. const auto result = message.data.result; if (result != CURLE_OK) { std::string error_message; diff --git a/src/datadog/curl.h b/src/datadog/curl.h index 3ff8e2398..a9d85e681 100644 --- a/src/datadog/curl.h +++ b/src/datadog/curl.h @@ -1,7 +1,7 @@ #pragma once // This component provides a `class`, `Curl`, that implements the `HTTPClient` -// interface in terms of [libcurl](https://curl.se/libcurl)]. `class Curl` +// interface in terms of [libcurl](https://curl.se/libcurl)]. `class Curl` // manages a thread that is used as the event loop for libcurl. // // If this library was built in a mode that does not include libcurl, then this @@ -25,7 +25,7 @@ namespace datadog::tracing { // corresponding member functions -- one for each `CURLINFO` value or // `CURLoption` value, respectively. // -// The default implementations forward to their libcurl counterparts. Unit +// The default implementations forward to their libcurl counterparts. Unit // tests override some of the member functions. class CurlLibrary { public: diff --git a/src/datadog/datadog_agent.cpp b/src/datadog/datadog_agent.cpp index e761d8feb..fe1db1626 100644 --- a/src/datadog/datadog_agent.cpp +++ b/src/datadog/datadog_agent.cpp @@ -63,7 +63,7 @@ std::variant parse_agent_traces_response( if (type != "object") { std::string message; message += - "Parsing the Datadog Agent's response to traces we sent it failed. " + "Parsing the Datadog Agent's response to traces we sent it failed. " "The response is expected to be a JSON object, but instead it's a JSON " "value with type \""; append(message, type); @@ -83,7 +83,7 @@ std::variant parse_agent_traces_response( if (type != "object") { std::string message; message += - "Parsing the Datadog Agent's response to traces we sent it failed. " + "Parsing the Datadog Agent's response to traces we sent it failed. " "The \""; append(message, sample_rates_property); message += @@ -289,7 +289,7 @@ void DatadogAgent::flush() { } }; - // This is the callback for the HTTP response. It's invoked + // This is the callback for the HTTP response. It's invoked // asynchronously. auto on_response = [samplers = std::move(response_handlers), logger = logger_](int response_status, @@ -344,7 +344,7 @@ void DatadogAgent::flush() { }; // This is the callback for if something goes wrong sending the - // request or retrieving the response. It's invoked + // request or retrieving the response. It's invoked // asynchronously. auto on_error = [logger = logger_](Error error) { telemetry::counter::increment(metrics::tracer::api::errors, diff --git a/src/datadog/datadog_agent.h b/src/datadog/datadog_agent.h index fe016c462..993c11ee7 100644 --- a/src/datadog/datadog_agent.h +++ b/src/datadog/datadog_agent.h @@ -3,7 +3,7 @@ // This component provides a `class`, `DatadogAgent`, that implements the // `Collector` interface in terms of periodic HTTP requests to a Datadog Agent. // -// `DatadogAgent` is configured by `DatadogAgentConfig`. See +// `DatadogAgent` is configured by `DatadogAgentConfig`. See // `datadog_agent_config.h`. #include diff --git a/src/datadog/datadog_agent_config.cpp b/src/datadog/datadog_agent_config.cpp index 05b3b065f..20967db7e 100644 --- a/src/datadog/datadog_agent_config.cpp +++ b/src/datadog/datadog_agent_config.cpp @@ -63,7 +63,7 @@ Expected finalize_config( if (!user_config.http_client) { result.http_client = default_http_client(logger, clock); // `default_http_client` might return a `Curl` instance depending on how - // this library was built. If it returns `nullptr`, then there's no + // this library was built. If it returns `nullptr`, then there's no // built-in default, and so the user must provide a value. if (!result.http_client) { return Error{Error::DATADOG_AGENT_NULL_HTTP_CLIENT, diff --git a/src/datadog/glob.cpp b/src/datadog/glob.cpp index eaac66171..6484793f5 100644 --- a/src/datadog/glob.cpp +++ b/src/datadog/glob.cpp @@ -26,7 +26,7 @@ bool glob_match(StringView pattern, StringView subject) { const char pattern_char = pattern[p]; switch (pattern_char) { case '*': - // Try to match at `s`. If that doesn't work out, restart at + // Try to match at `s`. If that doesn't work out, restart at // `s + 1` next. next_p = p; next_s = s + 1; @@ -47,7 +47,7 @@ bool glob_match(StringView pattern, StringView subject) { } } } - // Mismatch. Maybe restart. + // Mismatch. Maybe restart. if (0 < next_s && next_s <= s_size) { p = next_p; s = next_s; diff --git a/src/datadog/glob.h b/src/datadog/glob.h index dbb5f396b..60f458e8c 100644 --- a/src/datadog/glob.h +++ b/src/datadog/glob.h @@ -17,7 +17,7 @@ namespace datadog { namespace tracing { // Return whether the specified `subject` matches the specified glob `pattern`, -// i.e. whether `subject` is a member of the set of strings represented by the +// i.e.whether `subject` is a member of the set of strings represented by the // glob `pattern`. bool glob_match(StringView pattern, StringView subject); diff --git a/src/datadog/http_client.cpp b/src/datadog/http_client.cpp index c8b4a95f2..bd4a422c3 100644 --- a/src/datadog/http_client.cpp +++ b/src/datadog/http_client.cpp @@ -39,11 +39,11 @@ Expected HTTPClient::URL::parse(StringView input) { const StringView authority_and_path = input.substr(after_scheme + k_scheme_separator.size()); // If the scheme is for unix domain sockets, then there's no way to - // distinguish the path-to-socket from the path-to-resource. Some + // distinguish the path-to-socket from the path-to-resource. Some // implementations require that the forward slashes in the path-to-socket - // are URL-encoded. However, URLs that we will be parsing designate the + // are URL-encoded. However, URLs that we will be parsing designate the // location of the Datadog Agent service, and so do not have a resource - // location. Thus, if the scheme is for a unix domain socket, assume that + // location. Thus, if the scheme is for a unix domain socket, assume that // the entire part after the "://" is the path to the socket, and that // there is no resource path. if (scheme == "unix" || scheme == "http+unix" || scheme == "https+unix") { @@ -64,11 +64,11 @@ Expected HTTPClient::URL::parse(StringView input) { "", ""}; } - // The scheme is either "http" or "https". This means that the part after + // The scheme is either "http" or "https". This means that the part after // the "://" could be /, e.g. "localhost:8080/api/v1". // Again, though, we're only parsing URLs that designate the location of // the Datadog Agent service, and so they will not have a resource - // location. Still, let's parse it properly. + // location. Still, let's parse it properly. const auto after_authority = authority_and_path.find('/'); std::string path; diff --git a/src/datadog/json.hpp b/src/datadog/json.hpp index 82d69f7c5..c2cd0b198 100644 --- a/src/datadog/json.hpp +++ b/src/datadog/json.hpp @@ -1118,7 +1118,7 @@ NLOHMANN_JSON_NAMESPACE_END #endif /* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ #endif @@ -2082,7 +2082,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_REQUIRE_CONSTEXPR #endif /* JSON_HEDLEY_IS_CONSTEXPR_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ #if defined(JSON_HEDLEY_IS_CONSTEXPR_) #undef JSON_HEDLEY_IS_CONSTEXPR_ #endif @@ -6533,7 +6533,7 @@ Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at beginning of input. Does not support changing the underlying std::streambuf in mid-input. Maintains underlying std::istream and std::streambuf to support subsequent use of standard std::istream operations to process any input -characters following those used in parsing the JSON input. Clears the +characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ @@ -8355,7 +8355,7 @@ class lexer : public lexer_base This function provides the interface to the used input adapter. It does not throw in case the input reached EOF, but returns a - `char_traits::eof()` in that case. Stores the scanned characters + `char_traits::eof()` in that case. Stores the scanned characters for use in error messages. @return character read from the input @@ -8473,9 +8473,9 @@ class lexer : public lexer_base return position; } - /// return the last read token (for errors only). Will never contain EOF + /// return the last read token (for errors only). Will never contain EOF /// (an arbitrary value that is not a valid char value, often -1), because - /// 255 may legitimately occur. May contain NUL, which should be escaped. + /// 255 may legitimately occur. May contain NUL, which should be escaped. std::string get_token_string() const { // escape control characters diff --git a/src/datadog/json_serializer.h b/src/datadog/json_serializer.h index 014dd6809..9d23bf7a2 100644 --- a/src/datadog/json_serializer.h +++ b/src/datadog/json_serializer.h @@ -85,7 +85,7 @@ inline Expected from_json(const nlohmann::json& json) { } } else { // Unknown properties are OK. `SpanMatcher` is used as a base class for - // trace sampling rules and span sampling rules. Those derived types + // trace sampling rules and span sampling rules. Those derived types // will have additional properties in their JSON representations. } } diff --git a/src/datadog/msgpack.h b/src/datadog/msgpack.h index 4a8447cff..b8cb74300 100644 --- a/src/datadog/msgpack.h +++ b/src/datadog/msgpack.h @@ -1,15 +1,14 @@ #pragma once -// This component provides encoding routines for [MessagePack][1]. +// This component provides encoding routines for +// [MessagePack](https://msgpack.org/index.html). // // Each function is in `namespace msgpack` and appends a specified value to a -// `std::string`. For example, `msgpack::pack_integer(destination, -42)` +// `std::string`. For example, `msgpack::pack_integer(destination, -42)` // MessagePack encodes the number `-42` and appends the result to `destination`. // // Only encoding is provided, and only for the types required by `SpanData` and // `DatadogAgent`. -// -// [1]: https://msgpack.org/index.html #include #include @@ -37,11 +36,11 @@ Expected pack_array(std::string& buffer, std::size_t size); // Append to the specified `buffer` a MessagePack encoded array having the // specified `values`, where for each element of `values` the specified -// `pack_value` function appends the value. `pack_value` is invoked with two +// `pack_value` function appends the value. `pack_value` is invoked with two // arguments: the first is a reference to `buffer`, and the second is a -// reference to the current value. `pack_value` returns an `Expected`. If +// reference to the current value. `pack_value` returns an `Expected`. If // the return value is an error, then iteration is halted and the error is -// returned. If some other error occurs, then an error is returned. Otherwise, +// returned. If some other error occurs, then an error is returned. Otherwise, // the non-error value is returned. template Expected pack_array(std::string& buffer, Iterable&& values, @@ -54,23 +53,23 @@ Expected pack_map(std::string& buffer, std::size_t size); // map element, and the second element of each pair is some value that is // MessagePack encoded by the specified `pack_value` function. `pack_value` is // invoked with two arguments: the first is a reference to `buffer`, and the -// second is a reference to the current value. `pack_value` returns an +// second is a reference to the current value. `pack_value` returns an // `Expected`. If the return value is an error, then iteration is halted -// and the error is returned. If some other error occurs, then an error is -// returned. Otherwise, the non-error value is returned. +// and the error is returned. If some other error occurs, then an error is +// returned. Otherwise, the non-error value is returned. template Expected pack_map(std::string& buffer, const PairIterable& pairs, PackValue&& pack_value); // Append to the specified `buffer` a MessagePack encoded map consisting of the -// specified key value pairs. After the `buffer` argument, `pack_map` accepts -// an even number of arguments. First in each pair of arguments is `key`, the -// key name of the corresponding map item. Second in each pair of arguments is -// `pack_value`, a function that encodes the corresponding value. `pack_value` +// specified key value pairs. After the `buffer` argument, `pack_map` accepts +// an even number of arguments. First in each pair of arguments is `key`, the +// key name of the corresponding map item. Second in each pair of arguments is +// `pack_value`, a function that encodes the corresponding value. `pack_value` // is invoked with one argument: a reference to `buffer`. `pack_value` returns -// an `Expected`. If the return value is an error, then iteration is -// halted and the error is returned. If some other error occurs, then an error -// is returned. Otherwise, the non-error value is returned. +// an `Expected`. If the return value is an error, then iteration is +// halted and the error is returned. If some other error occurs, then an error +// is returned. Otherwise, the non-error value is returned. template Expected pack_map(std::string& buffer, StringView key, PackValue&& pack_value, Rest&&... rest); diff --git a/src/datadog/random.cpp b/src/datadog/random.cpp index 48138af2e..78f52e65f 100644 --- a/src/datadog/random.cpp +++ b/src/datadog/random.cpp @@ -44,7 +44,7 @@ std::uint64_t random_uint64() { return thread_local_generator(); } std::string uuid() { // clang-format off - // It's not all random. From most significant to least significant, the + // It's not all random. From most significant to least significant, the // bits look like this: // // xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 0100xxxx xxxxxxxx 10xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx diff --git a/src/datadog/sampling_util.h b/src/datadog/sampling_util.h index 1c74d8acc..19f424f45 100644 --- a/src/datadog/sampling_util.h +++ b/src/datadog/sampling_util.h @@ -1,6 +1,6 @@ #pragma once -// This component provides sampling-related miscellanea. It's used by both +// This component provides sampling-related miscellanea. It's used by both // `TraceSampler` and `SpanSampler`. #include @@ -11,7 +11,7 @@ namespace datadog { namespace tracing { -// Return a hash value for the specified `value`. `value` is one of the +// Return a hash value for the specified `value`. `value` is one of the // following: // // - a 64-bit span ID diff --git a/src/datadog/span_data.h b/src/datadog/span_data.h index 9413e173b..36950427d 100644 --- a/src/datadog/span_data.h +++ b/src/datadog/span_data.h @@ -38,7 +38,7 @@ struct SpanData { Optional version() const; // Modify the properties of this object to honor the specified `config` and - // `defaults`. The properties of `config`, if set, override the properties of + // `defaults`. The properties of `config`, if set, override the properties of // `defaults`. Use the specified `clock` to provide a start none of none is // specified in `config`. void apply_config(const SpanDefaults& defaults, const SpanConfig& config, @@ -50,7 +50,7 @@ struct SpanData { Expected msgpack_encode(std::string& destination, const SpanData& span); // Append to the specified `destination` the MessagePack representation of an -// array containing each of the specified `spans`. The behavior is undefined +// array containing each of the specified `spans`. The behavior is undefined // if any span is `nullptr`. Expected msgpack_encode( std::string& destination, diff --git a/src/datadog/span_sampler.h b/src/datadog/span_sampler.h index 0d4f09a2d..a6615afba 100644 --- a/src/datadog/span_sampler.h +++ b/src/datadog/span_sampler.h @@ -11,7 +11,7 @@ // is dropped. // // As with the `TraceSampler`, spans are matched by rules that indicate the -// sample rate at which the spans will be sent to Datadog. Each rule is +// sample rate at which the spans will be sent to Datadog. Each rule is // additionally associated with an optional limiter that prevents the sent // volume of spans from exceeding a specified number of spans per second. // diff --git a/src/datadog/span_sampler_config.cpp b/src/datadog/span_sampler_config.cpp index cf1b60fb8..28d5c4ef3 100644 --- a/src/datadog/span_sampler_config.cpp +++ b/src/datadog/span_sampler_config.cpp @@ -29,7 +29,7 @@ std::string to_string(const std::vector &rules) { } // `env_var` is the name of the environment variable from which `rules_raw` was -// obtained. It's used for error messages. +// obtained. It's used for error messages. Expected> parse_rules(StringView rules_raw, StringView env_var) { std::vector rules; @@ -86,7 +86,7 @@ Expected> parse_rules(StringView rules_raw, append(message, env_var); message += " JSON "; append(message, rules_raw); - message += ". The \"sample_rate\" property of the rule "; + message += ". The \"sample_rate\" property of the rule "; message += json_rule.dump(); message += " is not a number, but instead has type \""; message += type; @@ -106,7 +106,7 @@ Expected> parse_rules(StringView rules_raw, append(message, env_var); message += " JSON "; append(message, rules_raw); - message += ". The \"max_per_second\" property of the rule "; + message += ". The \"max_per_second\" property of the rule "; message += json_rule.dump(); message += " is not a number, but instead has type \""; message += type; @@ -129,7 +129,7 @@ Expected> parse_rules(StringView rules_raw, message += value.dump(); message += " in trace sampling rule "; message += json_rule.dump(); - message += ". Error occurred while parsing from "; + message += ". Error occurred while parsing from "; append(message, env_var); message += ": "; append(message, rules_raw); @@ -165,7 +165,7 @@ Expected load_span_sampler_env_config(Logger &logger) { append(message, rules_file_name); message += " is overridden by "; append(message, rules_name); - message += ". Since both are set, "; + message += ". Since both are set, "; append(message, rules_name); message += " takes precedence, and "; append(message, rules_file_name); diff --git a/src/datadog/tag_propagation.cpp b/src/datadog/tag_propagation.cpp index 2c790742d..46ead2d57 100644 --- a/src/datadog/tag_propagation.cpp +++ b/src/datadog/tag_propagation.cpp @@ -11,8 +11,11 @@ namespace datadog { namespace tracing { -// The following [eBNF][1] grammar describes the tag propagation encoding. -// The grammar was copied from [an internal design document][2]. +// The following +// [eBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) +// grammar describes the tag propagation encoding. The grammar was copied from +// [an internal design +// document](https://docs.google.com/document/d/1zeO6LGnvxk5XweObHAwJbK3SfK23z7jQzp7ozWJTa2A/edit#heading=h.yp07yuixga36). // // tagset = ( tag, { ",", tag } ) | ""; // tag = ( identifier - space or equal ), "=", identifier; @@ -24,14 +27,11 @@ namespace tracing { // // See `tag_propagation_test.cpp` for examples. // -// [1]: https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form -// [2]: -// https://docs.google.com/document/d/1zeO6LGnvxk5XweObHAwJbK3SfK23z7jQzp7ozWJTa2A/edit#heading=h.yp07yuixga36 namespace { // Insert into the specified `destination` a tag decoded from the specified -// `entry`. Return an `Error` if an error occurs. +// `entry`. Return an `Error` if an error occurs. Expected decode_tag( std::vector>& destination, StringView entry) { diff --git a/src/datadog/tag_propagation.h b/src/datadog/tag_propagation.h index 21169139f..7c9d0703c 100644 --- a/src/datadog/tag_propagation.h +++ b/src/datadog/tag_propagation.h @@ -1,7 +1,7 @@ #pragma once // Some span tags are associated with the entire local trace, rather than just -// a single span within the trace. These tags are added to the local root span +// a single span within the trace. These tags are added to the local root span // before the trace is flushed. // // Among these root span tags, some are also propagated as trace context. diff --git a/src/datadog/trace_id.cpp b/src/datadog/trace_id.cpp index 4eaff241c..ee61c481f 100644 --- a/src/datadog/trace_id.cpp +++ b/src/datadog/trace_id.cpp @@ -38,7 +38,7 @@ Expected TraceID::parse_hex(StringView input) { return result; }; - // A 64-bit integer is at most 16 hex characters. If the input is no + // A 64-bit integer is at most 16 hex characters. If the input is no // longer than that, then it will all fit in `TraceID::low`. if (input.size() <= 16) { auto result = parse_hex_piece(input); diff --git a/src/datadog/trace_sampler.cpp b/src/datadog/trace_sampler.cpp index 1831581f5..d455b9c42 100644 --- a/src/datadog/trace_sampler.cpp +++ b/src/datadog/trace_sampler.cpp @@ -71,7 +71,7 @@ SamplingDecision TraceSampler::decide(const SpanData& span) { return decision; } - // No sampling rule matched. Find the appropriate collector-controlled + // No sampling rule matched. Find the appropriate collector-controlled // sample rate. auto found_rate = collector_sample_rates_.find( CollectorResponse::key(span.service, span.environment().value_or(""))); @@ -83,7 +83,7 @@ SamplingDecision TraceSampler::decide(const SpanData& span) { decision.configured_rate = *collector_default_sample_rate_; decision.mechanism = int(SamplingMechanism::AGENT_RATE); } else { - // We have yet to receive a default rate from the collector. This + // We have yet to receive a default rate from the collector. This // corresponds to the `DEFAULT` sampling mechanism. decision.configured_rate = Rate::one(); decision.mechanism = int(SamplingMechanism::DEFAULT); diff --git a/src/datadog/trace_sampler.h b/src/datadog/trace_sampler.h index 78b27cd7b..640a4219b 100644 --- a/src/datadog/trace_sampler.h +++ b/src/datadog/trace_sampler.h @@ -8,11 +8,11 @@ // // When a span is extracted from an outside context (i.e. // `Tracer::extract_span`), then the trace sampling decision is included in the -// extracted information. In order to ensure that all parts of a trace are +// extracted information. In order to ensure that all parts of a trace are // sampled consistently, such sampling decisions are honored. // // However, when this process is the first service in a distributed trace (i.e. -// `Tracer::create_span`), it makes the trace sampling decision. The +// `Tracer::create_span`), it makes the trace sampling decision. The // `TraceSampler` determines how the decision is made. // // There are three levels of configuration, in order of increasing specificity, @@ -24,8 +24,8 @@ // sample rates. // // The Datadog Agent has a configured target number of traces per second to send -// to Datadog. It chases this target by adjusting the sample rates of services -// that send it traces. The target traces per second can be configured in the +// to Datadog. It chases this target by adjusting the sample rates of services +// that send it traces. The target traces per second can be configured in the // Datadog Agent via the environment variable `DD_APM_MAX_TPS` or the // corresponding YAML configuration option `max_traces_per_second`. // @@ -36,16 +36,16 @@ // --------------------- // If `TraceSamplerConfig::sample_rate` is given a value, or if the // `DD_TRACE_SAMPLE_RATE` environment variable has a value, then the rate at -// which traces are kept is overridden to be the configured value. The Datadog +// which traces are kept is overridden to be the configured value. The Datadog // Agent provided rate is no longer used. // // For example, if `TracerSamplerConfig::sample_rate` is `0.1`, then 10% of -// traces that originate with this tracer will be sent to Datadog. The +// traces that originate with this tracer will be sent to Datadog. The // remaining 90% will be sent to the Datadog Agent, but will not be sent to // Datadog's backend and will not be visible in the Datadog UI. // // The volume of traces kept on account of the global sample rate is limited by -// the same setting as for trace sampling rules. See the description of +// the same setting as for trace sampling rules. See the description of // `TraceSamplerConfig::max_per_second` and `DD_TRACE_RATE_LIMIT` at the end of // the following section. // @@ -57,7 +57,7 @@ // Trace sampling rules are configured via `TraceSamplerConfig::rules` or the // `DD_TRACE_SAMPLING_RULES` environment variable. // -// A trace sampling rule associates a span pattern with a sample rate. If the +// A trace sampling rule associates a span pattern with a sample rate. If the // root span of a new trace created by the tracer matches the span pattern, // then the associated sample rate is applied. // @@ -75,11 +75,11 @@ // matching rule is used. // // The global rate (section 2, above) is implemented as a sampling rule that -// matches any span and is appended to any configured sampling rules. Thus, +// matches any span and is appended to any configured sampling rules. Thus, // sampling rules override the global sample rate for matching root spans. // // The volume of traces kept by sampling rules (including the global sample -// rate) is limited by a configurable number of traces-per-second. The limit is +// rate) is limited by a configurable number of traces-per-second. The limit is // configured via `TraceSamplerConfig::max_per_second` or the // `DD_TRACE_RATE_LIMIT` environment variable. diff --git a/src/datadog/trace_sampler_config.cpp b/src/datadog/trace_sampler_config.cpp index 698731db3..b573bae8c 100644 --- a/src/datadog/trace_sampler_config.cpp +++ b/src/datadog/trace_sampler_config.cpp @@ -72,7 +72,7 @@ Expected load_trace_sampler_env_config() { append(message, name(environment::DD_TRACE_SAMPLING_RULES)); message += " value "; append(message, *rules_env); - message += ". The \"sample_rate\" property of the rule "; + message += ". The \"sample_rate\" property of the rule "; message += json_rule.dump(); message += " is not a number, but instead has type \""; message += type; @@ -95,7 +95,7 @@ Expected load_trace_sampler_env_config() { message += value.dump(); message += " in trace sampling rule "; message += json_rule.dump(); - message += ". Error occurred while parsing "; + message += ". Error occurred while parsing "; append(message, name(environment::DD_TRACE_SAMPLING_RULES)); message += ": "; append(message, *rules_env); diff --git a/src/datadog/trace_segment.cpp b/src/datadog/trace_segment.cpp index ea14aeb4c..4a7181b05 100644 --- a/src/datadog/trace_segment.cpp +++ b/src/datadog/trace_segment.cpp @@ -69,7 +69,7 @@ void inject_trace_tags( if (encoded_trace_tags.size() > tags_header_max_size) { std::string message; message += - "Serialized x-datadog-tags header value is too large. The configured " + "Serialized x-datadog-tags header value is too large. The configured " "maximum size is "; message += std::to_string(tags_header_max_size); message += " bytes, but the encoded value is "; @@ -227,7 +227,7 @@ void TraceSegment::span_finished() { telemetry::counter::increment(metrics::tracer::trace_chunks_enqueued); - // We don't need the lock anymore. There's nobody left to call our methods. + // We don't need the lock anymore. There's nobody left to call our methods. // On the other hand, there's nobody left to contend for the mutex, so it // doesn't make any difference. make_sampling_decision_if_null(); diff --git a/src/datadog/tracer.cpp b/src/datadog/tracer.cpp index 4209ac228..d1de11f89 100644 --- a/src/datadog/tracer.cpp +++ b/src/datadog/tracer.cpp @@ -172,7 +172,7 @@ void Tracer::store_config( // clang-format off msgpack::pack_map( - buffer, + buffer, "schema_version", [&](auto& buffer) { msgpack::pack_integer(buffer, std::uint64_t(2)); return Expected{}; }, "runtime_id", [&](auto& buffer) { return msgpack::pack_string(buffer, runtime_id_.string()); }, "tracer_version", [&](auto& buffer) { return msgpack::pack_string(buffer, signature_.library_version); }, @@ -343,7 +343,7 @@ Expected Tracer::extract_span(const DictReader& reader, } if (!merged_context.parent_id) { - // We have a trace ID, but not parent ID. We're meant to be the root, and + // We have a trace ID, but not parent ID. We're meant to be the root, and // whoever called us already created a trace ID for us (to correlate with // whatever they're doing). merged_context.parent_id = 0; @@ -359,7 +359,7 @@ Expected Tracer::extract_span(const DictReader& reader, merged_context.headers_examined)); } - // We're done extracting fields. Now create the span. + // We're done extracting fields. Now create the span. // This is similar to what we do in `create_span`. span_data->apply_config(*config_manager_->span_defaults(), config, clock_); span_data->span_id = generator_->span_id(); @@ -425,7 +425,7 @@ Expected Tracer::extract_span(const DictReader& reader, if (tracing_enabled_ && merged_context.sampling_priority) { SamplingDecision decision; decision.priority = *merged_context.sampling_priority; - // `decision.mechanism` is null. We might be able to infer it once we + // `decision.mechanism` is null. We might be able to infer it once we // extract `trace_tags`, but we would have no use for it, so we won't. decision.origin = SamplingDecision::Origin::EXTRACTED; diff --git a/src/datadog/tracer_config.cpp b/src/datadog/tracer_config.cpp index 1383ce478..7b2b1aca9 100644 --- a/src/datadog/tracer_config.cpp +++ b/src/datadog/tracer_config.cpp @@ -53,7 +53,7 @@ Expected> parse_propagation_styles( message += "\" in list \""; append(message, input); message += - "\". The following styles are supported: Datadog, B3, tracecontext."; + "\". The following styles are supported: Datadog, B3, tracecontext."; return Error{Error::UNKNOWN_PROPAGATION_STYLE, std::move(message)}; } diff --git a/src/datadog/w3c_propagation.cpp b/src/datadog/w3c_propagation.cpp index f6e1eb46e..b8174577b 100644 --- a/src/datadog/w3c_propagation.cpp +++ b/src/datadog/w3c_propagation.cpp @@ -251,7 +251,7 @@ void parse_datadog_tracestate(ExtractedData& result, StringView datadog_value) { const auto tag_suffix = key.substr(2); std::string tag_name = "_dd.p."; append(tag_name, tag_suffix); - // The tag value was encoded with all '=' replaced by '~'. Undo that + // The tag value was encoded with all '=' replaced by '~'. Undo that // transformation. std::string decoded_value{value}; std::replace(decoded_value.begin(), decoded_value.end(), '~', '='); diff --git a/test/README.md b/test/README.md index b866aa5c2..98a07a018 100644 --- a/test/README.md +++ b/test/README.md @@ -1,25 +1,21 @@ -Unit Tests -========== -This directory contains the unit tests for dd-trace-cpp. +# Unit Tests -The testing library used is [Catch2][1], vendored here as a single header, -[catch.hpp](catch.hpp) (see the [Makefile](Makefile)). +This directory contains the unit tests for `dd-trace-cpp`. + +The testing library used is [Catch2](https://github.com/catchorg/Catch2/tree/v2.x), vendored here as +a single header, [catch.hpp](catch.hpp) (see the [Makefile](Makefile)). [../bin/test](../bin/test) builds and runs the unit tests. -Code Layout ------------ +## Code Layout + Test-specific implementations of interfaces are defined in [mocks/](mocks). [test.h](test.h) is a wrapper around [catch.hpp](catch.hpp). -[matchers.h](matchers.h) defines extensions to Catch2 that are convenient in -test assertions. +[matchers.h](matchers.h) defines extensions to Catch2 that are convenient in test assertions. [main.cpp](main.cpp) is the test driver (executable). -All other translation units in this directory are the tests themselves. For -example, [test_span.cpp](test_span.cpp) contains the tests for the `Span` class -and associated behavior. - -[1]: https://github.com/catchorg/Catch2/tree/v2.x +All other translation units in this directory are the tests themselves. For example, +[test_span.cpp](test_span.cpp) contains the tests for the `Span` class and associated behavior. diff --git a/test/system-tests/utils.h b/test/system-tests/utils.h index dd4a5c84d..850e77fd6 100644 --- a/test/system-tests/utils.h +++ b/test/system-tests/utils.h @@ -61,7 +61,7 @@ class HeaderWriter final : public dd::DictWriter { HeaderWriter(nlohmann::json& headers) : j_(headers){}; ~HeaderWriter() = default; - // Associate the specified `value` with the specified `key`. An + // Associate the specified `value` with the specified `key`. An // implementation may, but is not required to, overwrite any previous value at // `key`. void set(dd::StringView key, dd::StringView value) override { diff --git a/test/test_cerr_logger.cpp b/test/test_cerr_logger.cpp index 472ae60bf..174455506 100644 --- a/test/test_cerr_logger.cpp +++ b/test/test_cerr_logger.cpp @@ -13,7 +13,7 @@ using namespace datadog::tracing; namespace { // Replace the `streambuf` associated with a specified `std::ios` for the -// lifetime of this object. Restore the previous `streambuf` afterward. +// lifetime of this object. Restore the previous `streambuf` afterward. class StreambufGuard { std::ios *stream_; std::streambuf *buffer_; diff --git a/test/test_curl.cpp b/test/test_curl.cpp index e48a9690c..9f2668bae 100644 --- a/test/test_curl.cpp +++ b/test/test_curl.cpp @@ -282,7 +282,7 @@ CURL_TEST("fail to allocate request handle") { CURL_TEST("setopt failures") { // Each call to `Curl::post` allocates a new "easy handle" and sets various - // options on it. Any of those setters can fail. When one does, `post` + // options on it. Any of those setters can fail. When one does, `post` // immediately returns an error. class MockCurlLibrary : public CurlLibrary { public: diff --git a/test/test_span.cpp b/test/test_span.cpp index 4af524ed7..237c9cf1e 100644 --- a/test/test_span.cpp +++ b/test/test_span.cpp @@ -1,5 +1,5 @@ -// These are tests for `Span`. `Span` is a container for labels associated with -// an extent in time. `Span` is also responsible for injecting trace context +// These are tests for `Span`. `Span` is a container for labels associated with +// an extent in time. `Span` is also responsible for injecting trace context // for propagation. #include diff --git a/test/test_trace_sampler.cpp b/test/test_trace_sampler.cpp index de10f3522..c1f889380 100644 --- a/test/test_trace_sampler.cpp +++ b/test/test_trace_sampler.cpp @@ -103,7 +103,7 @@ TEST_CASE("trace sampling rule sample rate") { // priority 2 ("user keep"), but no other values. REQUIRE(priority_counts.size() <= 2); // I assume that there have been enough trials that not _all_ traces are kept - // or dropped purely due to chance. That could happen only if the sample rate + // or dropped purely due to chance. That could happen only if the sample rate // were 0% or 100%, respectively. REQUIRE((test_case.sample_rate == 0.0 || priority_counts.count(int(SamplingPriority::USER_KEEP)))); @@ -162,7 +162,7 @@ TEST_CASE("trace sampling rate limiter") { test_case.expected_kept_count); // Now verify that there is a "cooldown period" of one second, after which - // the limiter will permit some more traces. How many it permits depends + // the limiter will permit some more traces. How many it permits depends // on how "over budget" it was, but it will allow at least one. collector->sampling_priority_count.clear(); current_time += std::chrono::seconds(1); diff --git a/test/test_trace_segment.cpp b/test/test_trace_segment.cpp index b1ac3a38e..37f8f94ad 100644 --- a/test/test_trace_segment.cpp +++ b/test/test_trace_segment.cpp @@ -376,7 +376,7 @@ TEST_CASE("TraceSegment finalization of spans") { SECTION("rules (implicit and explicit)") { // When sample rate is 100%, the sampler will consult the limiter. - // When sample rate is 0%, it won't. We test both cases. + // When sample rate is 0%, it won't.We test both cases. auto sample_rate = GENERATE(0.0, 1.0); SECTION("global sample rate") { diff --git a/test/test_tracer.cpp b/test/test_tracer.cpp index 72231115f..598901d50 100644 --- a/test/test_tracer.cpp +++ b/test/test_tracer.cpp @@ -1,4 +1,4 @@ -// These are tests for `Tracer`. `Tracer` is responsible for creating root +// These are tests for `Tracer`. `Tracer` is responsible for creating root // spans and for extracting spans from propagated trace context. #include @@ -748,7 +748,7 @@ TEST_TRACER("span extraction") { {__LINE__, "invalid: trailing characters when version is zero", "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-foo", // traceparent "malformed_traceparent"}, // expected_error_tag_value - + {__LINE__, "invalid: non hex trace ID", "00-abcdefghijklmnopqrstuvxyzabcdefg-00f067aa0ba902b7-00", // traceparent "malformed_traceid"}, // expected_error_tag_value @@ -792,7 +792,7 @@ TEST_TRACER("span extraction") { MockDictReader reader{headers}; // We can't `span->lookup(tags::internal::w3c_extraction_error)`, because - // that tag is internal and will not be returned by `lookup`. Instead, we + // that tag is internal and will not be returned by `lookup`. Instead, we // finish (destroy) the span to send it to a collector, and then inspect the // `SpanData` at the collector. Optional decision; diff --git a/test/test_tracer_config.cpp b/test/test_tracer_config.cpp index b6407f72d..5f6aad8ac 100644 --- a/test/test_tracer_config.cpp +++ b/test/test_tracer_config.cpp @@ -509,7 +509,7 @@ TRACER_CONFIG_TEST("TracerConfig::agent") { {"override port with default host", nullopt, "8080", nullopt, "http", "localhost:8080"}, // A bogus port number will cause an error in the TCPClient, not - // during configuration. For the purposes of configuration, any + // during configuration. For the purposes of configuration, any // value is accepted. {"we don't parse port", nullopt, "bogus", nullopt, "http", "localhost:bogus"}, @@ -1232,7 +1232,7 @@ TRACER_CONFIG_TEST("TracerConfig propagation styles") { } // It's the same as for injection styles, so let's omit most of the - // section. Keep only an example where parsing fails, so we cover the + // section. Keep only an example where parsing fails, so we cover the // error handling code in `TracerConfig`. SECTION("parsing failure") { const EnvGuard guard{"DD_PROPAGATION_STYLE_EXTRACT", "b3,,datadog"};