@@ -164,7 +164,7 @@ created.
164164` class Tracer ` is what users configure, and it is how ` Span ` s are extracted from
165165trace context or created as a trace's root. See [ tracer.h] [ 9 ] .
166166
167- ` Tracer ` has two methods :
167+ ` Tracer ` has two member functions :
168168
169169- ` create_span(...) `
170170- ` extract_span(...) `
@@ -224,10 +224,81 @@ uses the HTTP client to send a POST request to the Datadog Agent's
224224[ /v0.4/traces] [ 21 ] endpoint. It's all callback-based.
225225
226226### HTTPClient
227- TODO
227+ ` class HTTPClient ` is an interface for sending HTTP requests. It's defined in
228+ [ http_client.h] [ 15 ] .
229+
230+ The only kind of HTTP request that the library needs to make, currently, is a
231+ POST to the Datadog Agent's traces endpoint. ` HTTPClient ` has one member
232+ function for each HTTP method needed — so, currently just the one:
233+ ``` c++
234+ virtual Expected<void > post (const URL& url, HeadersSetter set_headers,
235+ std::string body, ResponseHandler on_response,
236+ ErrorHandler on_error) = 0;
237+ ```
238+ It's callback-based. `post` returns almost immediately. It invokes `set_headers`
239+ before returning, in order to get the HTTP request headers. The request `body`
240+ is moved elsewhere for later processing. One of `on_response` or `on_error` will
241+ eventually be called, depending on whether a response was received or if an
242+ error occurred before a response was received. If something goes wrong setting
243+ up the request, then `post` returns an error. If `post` returns an error, then
244+ neither of `on_response` nor `on_error` will be called.
245+
246+ `HTTPClient` also has another member function:
247+ ```c++
248+ virtual void drain(std::chrono::steady_clock::time_point deadline) = 0;
249+ ```
250+ ` drain ` waits for any in-flight requests to finish, blocking up until no later
251+ than ` deadline ` . It's used to ensure "clean shutdown." Without it, on average
252+ the last one second of traces would be lost on shutdown. Implementations of
253+ ` HTTPClient ` that don't have a dedicated thread need not support ` drain ` ; in
254+ those cases, ` drain ` returns immediately.
255+
256+ The default implementation of ` HTTPClient ` is [ class Curl : public
257+ HTTPClient] [ 18 ] , which uses libcurl's [ multi interface] [ 20 ] together with a
258+ dedicated thread as an event loop.
259+
260+ ` class Curl ` is also used within NGINX in Datadog's NGINX module,
261+ [ nginx-datadog] [ 22 ] . This is explicitly [ discouraged] [ 23 ] in NGINX's developer
262+ documentation, but libcurl-with-a-thread is widely used within NGINX modules
263+ regardless. One improvement that I am exploring is to use libcurl's
264+ "[ multi_socket] [ 24 ] " mode, which allows libcurl to utilize someone else's event
265+ loop, obviating the need for another thread. libcurl can then be made to use
266+ NGINX's event loop, as is done in [ an example library] [ 25 ] .
267+
268+ For now, though, nginx-datadog uses the threaded ` class Curl ` .
269+
270+ [ Envoy's Datadog tracing integration] [ 26 ] uses a different implementation,
271+ [ class AgentHTTPClient : public HTTPClient, ...] [ 27 ] , which uses Envoy's
272+ built-in HTTP facilities. libcurl is not involved at all.
228273
229274### EventScheduler
230- TODO
275+ As of this writing, ` class DatadogAgent ` flushes batches of finished trace
276+ segments to the Datadog Agent once every two second [ by default] [ 28 ] .
277+
278+ It does this by scheduling a recurring event with an ` EventScheduler ` , which is
279+ an interface defined in [ event_scheduler.h] [ 16 ] .
280+
281+ ` EventScheduler ` has one member function:
282+ ``` c++
283+ virtual Cancel schedule_recurring_event (
284+ std::chrono::steady_clock::duration interval,
285+ std::function<void()> callback) = 0;
286+ ```
287+ Every `interval`, the scheduler will invoke `callback`, starting an initial
288+ `interval` after `schedule_recurring_event` is called. The caller can invoke the
289+ returned `Cancel` to prevent subsequent invocations of `callback`.
290+
291+ The default implementation of `EventScheduler` is [class ThreadedEventScheduler
292+ : public EventScheduler][19], which uses a dedicated thread for executing
293+ scheduled events at the correct time. It was a fun piece of code to write.
294+
295+ Datadog's NGINX module, [nginx-datadog][22] uses a different implementation,
296+ [class NgxEventScheduler : public EventScheduler][29], which uses NGINX's own
297+ event loop instead of a dedicated thread.
298+
299+ [Envoy's Datadog tracing integration][26] also uses a different implementation,
300+ [class EventScheduler : public EventScheduler][30], which uses Envoy's built-in
301+ event dispatch facilities.
231302
232303### Configuration
233304TODO
@@ -281,3 +352,12 @@ TODO
281352[19]: ../src/datadog/threaded_event_scheduler.h
282353[20]: https://curl.se/libcurl/c/libcurl-multi.html
283354[21]: https://github.com/DataDog/datadog-agent/blob/9d57c10a9eeb3916e661d35dbd23c6e36395a99d/pkg/trace/api/version.go#L22
355+ [22]: https://github.com/DataDog/nginx-datadog
356+ [23]: https://nginx.org/en/docs/dev/development_guide.html#http_requests_to_ext
357+ [24]: https://curl.se/libcurl/c/curl_multi_socket_action.html
358+ [25]: https://github.com/dgoffredo/nginx-curl
359+ [26]: https://github.com/envoyproxy/envoy/tree/main/source/extensions/tracers/datadog#datadog-tracer
360+ [27]: https://github.com/envoyproxy/envoy/blob/main/source/extensions/tracers/datadog/agent_http_client.h
361+ [28]: https://github.com/DataDog/dd-trace-cpp/blob/ca155b3da65c2dc235cf64a28f8e0d8fdab3700c/src/datadog/datadog_agent_config.h#L50-L51
362+ [29]: https://github.com/DataDog/nginx-datadog/blob/master/src/ngx_event_scheduler.h
363+ [30]: https://github.com/envoyproxy/envoy/blob/main/source/extensions/tracers/datadog/event_scheduler.h
0 commit comments