-
Notifications
You must be signed in to change notification settings - Fork 5.4k
datadog: HTTP client portion of new tracing library #25417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
wbpcode
merged 23 commits into
envoyproxy:main
from
DataDog:david.goffredo/dd-trace-cpp-7/9-agent_http_client
Feb 22, 2023
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
12ca34d
dd-trace-cpp source/ dependency
dgoffredo 5408e3c
dd-trace-cpp test/ dependency
dgoffredo d0332e4
wrong year
dgoffredo d44b37d
TODO: AMEND
dgoffredo 68cb723
datadog: dict_util with tests
dgoffredo 63ca86f
datadog: add tracer_stats without tests
dgoffredo 63f5f68
datadog: add tracer_stats tests
dgoffredo 2555875
datadog: add comment describing dict_util
dgoffredo 9da7087
datadog: move private data members to bottom of class definition
dgoffredo efb9761
Merge branches 'david.goffredo/dd-trace-cpp-2/9-dict_util' and 'david…
dgoffredo 0c930aa
datadog: AgentHttpClient without tests
dgoffredo d9e989d
datadog: AgentHttpClient dummy test and some documentation
dgoffredo 5dee2fe
merge "main" and change some #include styles
dgoffredo 7c019cf
document AgentHTTPClient
dgoffredo e08c214
datadog: have one AgentHTTPClient test working
dgoffredo 1dee2ff
datadog: a few more AgentHTTPClient tests
dgoffredo 4cd97c9
datadog: AgentHTTPClient unit tests
dgoffredo d600c11
datadog: consolidate unit test boilerplate
dgoffredo 52cdeee
datadog: more unit tests
dgoffredo fd1620b
Merge branch 'main' into david.goffredo/dd-trace-cpp-7/9-agent_http_c…
dgoffredo 07d7d50
datadog: fix and comment and remove dead code
dgoffredo 9d8c148
datadog: address review comments for AgentHTTPClient
dgoffredo fe7826e
datadog: replicate request failure behavior in unit test
dgoffredo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| #include "source/extensions/tracers/datadog/agent_http_client.h" | ||
|
|
||
| #include <cstddef> | ||
| #include <cstdint> | ||
| #include <memory> | ||
| #include <string> | ||
|
|
||
| #include "source/common/common/assert.h" | ||
| #include "source/common/http/message_impl.h" | ||
| #include "source/common/http/utility.h" | ||
| #include "source/extensions/tracers/datadog/dict_util.h" | ||
| #include "source/extensions/tracers/datadog/tracer_stats.h" | ||
|
|
||
| #include "absl/strings/str_format.h" | ||
| #include "datadog/dict_reader.h" | ||
| #include "datadog/dict_writer.h" | ||
| #include "datadog/error.h" | ||
| #include "datadog/json.hpp" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace Tracers { | ||
| namespace Datadog { | ||
|
|
||
| AgentHTTPClient::AgentHTTPClient(Upstream::ClusterManager& cluster_manager, | ||
| const std::string& cluster, const std::string& reference_host, | ||
| TracerStats& stats) | ||
| : collector_cluster_(cluster_manager, cluster), cluster_(cluster), | ||
| reference_host_(reference_host), stats_(stats) {} | ||
|
|
||
| AgentHTTPClient::~AgentHTTPClient() { | ||
| for (const auto& [request_ptr, _] : handlers_) { | ||
| RELEASE_ASSERT(request_ptr, | ||
| "null Http::AsyncClient::Request* in handler map of Datadog::AgentHTTPClient"); | ||
| request_ptr->cancel(); | ||
| } | ||
| } | ||
|
|
||
| // datadog::tracing::HTTPClient | ||
|
|
||
| datadog::tracing::Expected<void> AgentHTTPClient::post(const URL& url, HeadersSetter set_headers, | ||
| std::string body, | ||
| ResponseHandler on_response, | ||
| ErrorHandler on_error) { | ||
| ENVOY_LOG(debug, "flushing traces"); | ||
|
|
||
| auto message = std::make_unique<Http::RequestMessageImpl>(); | ||
| Http::RequestHeaderMap& headers = message->headers(); | ||
| headers.setReferenceMethod(Http::Headers::get().MethodValues.Post); | ||
| headers.setReferencePath(url.path); | ||
| headers.setReferenceHost(reference_host_); | ||
|
|
||
| RequestHeaderWriter writer{message->headers()}; | ||
| set_headers(writer); | ||
|
|
||
| message->body().add(body); | ||
| ENVOY_LOG(debug, "submitting trace(s) to {} with payload size {}", url.path, body.size()); | ||
|
|
||
| if (!collector_cluster_.threadLocalCluster().has_value()) { | ||
| ENVOY_LOG(debug, "collector cluster '{}' does not exist", cluster_); | ||
| stats_.reports_skipped_no_cluster_.inc(); | ||
| return datadog::tracing::nullopt; | ||
| } | ||
|
|
||
| Http::AsyncClient::Request* request = | ||
| collector_cluster_.threadLocalCluster()->get().httpAsyncClient().send( | ||
| std::move(message), *this, | ||
| Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(1000))); | ||
| if (!request) { | ||
| stats_.reports_failed_.inc(); | ||
| return datadog::tracing::Error{datadog::tracing::Error::ENVOY_HTTP_CLIENT_FAILURE, | ||
| "Failed to create request."}; | ||
| } | ||
|
|
||
| handlers_.emplace(request, Handlers{std::move(on_response), std::move(on_error)}); | ||
| return datadog::tracing::nullopt; | ||
| } | ||
|
|
||
| void AgentHTTPClient::drain(std::chrono::steady_clock::time_point) {} | ||
|
|
||
| nlohmann::json AgentHTTPClient::config_json() const { | ||
| return nlohmann::json::object({ | ||
| {"type", "Envoy::Extensions::Tracers::Datadog::AgentHTTPClient"}, | ||
| }); | ||
| } | ||
|
|
||
| // Http::AsyncClient::Callbacks | ||
|
|
||
| void AgentHTTPClient::onSuccess(const Http::AsyncClient::Request& request, | ||
| Http::ResponseMessagePtr&& response) { | ||
| const std::uint64_t status = Http::Utility::getResponseStatus(response->headers()); | ||
| if (status != std::uint64_t(Http::Code::OK)) { | ||
| stats_.reports_dropped_.inc(); | ||
| } else { | ||
| ENVOY_LOG(debug, "traces successfully submitted to datadog agent"); | ||
| stats_.reports_sent_.inc(); | ||
| } | ||
|
|
||
| auto found = handlers_.find(const_cast<Http::AsyncClient::Request*>(&request)); | ||
| if (found == handlers_.end()) { | ||
| ENVOY_LOG(debug, "request at address 0x{} is not in the map of {} Handlers objects", | ||
| absl::StrFormat("%p", &request), handlers_.size()); | ||
| return; | ||
| } | ||
|
|
||
| Handlers& handlers = found->second; | ||
| ResponseHeaderReader reader{response->headers()}; | ||
| handlers.on_response(status, reader, response->bodyAsString()); | ||
|
|
||
| handlers_.erase(found); | ||
| } | ||
|
|
||
| void AgentHTTPClient::onFailure(const Http::AsyncClient::Request& request, | ||
| Http::AsyncClient::FailureReason reason) { | ||
| auto found = handlers_.find(const_cast<Http::AsyncClient::Request*>(&request)); | ||
| // If the request failed to start, then `post` never even registered the request. | ||
| if (found == handlers_.end()) { | ||
| return; | ||
| } | ||
|
|
||
| stats_.reports_failed_.inc(); | ||
|
|
||
| Handlers& handlers = found->second; | ||
| std::string message = "Failed to send request to Datadog Agent: "; | ||
| switch (reason) { | ||
| case Http::AsyncClient::FailureReason::Reset: | ||
| message += "The stream has been reset."; | ||
| break; | ||
| default: | ||
| message += "Unknown error."; | ||
| } | ||
| handlers.on_error(datadog::tracing::Error{datadog::tracing::Error::ENVOY_HTTP_CLIENT_FAILURE, | ||
| std::move(message)}); | ||
|
|
||
| handlers_.erase(found); | ||
| } | ||
|
|
||
| void AgentHTTPClient::onBeforeFinalizeUpstreamSpan(Tracing::Span&, const Http::ResponseHeaderMap*) { | ||
| } | ||
|
|
||
| } // namespace Datadog | ||
| } // namespace Tracers | ||
| } // namespace Extensions | ||
| } // namespace Envoy | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| #pragma once | ||
|
|
||
| #include <string> | ||
|
|
||
| #include "envoy/http/async_client.h" | ||
|
|
||
| #include "source/common/common/logger.h" | ||
| #include "source/common/upstream/cluster_update_tracker.h" | ||
|
|
||
| #include "absl/container/flat_hash_map.h" | ||
| #include "datadog/http_client.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace Tracers { | ||
| namespace Datadog { | ||
|
|
||
| struct TracerStats; | ||
|
|
||
| /** | ||
| * \c datadog::tracing::HTTPClient implementation that uses Envoy's | ||
| * \c Http::AsyncClient. The class is called \c AgentHTTPClient because it is | ||
| * not a general-purpose HTTP client. Instead, it sends requests to a specified | ||
| * cluster only. The idea is that the cluster is configured to point to a | ||
| * Datadog Agent instance. | ||
| */ | ||
| class AgentHTTPClient : public datadog::tracing::HTTPClient, | ||
| public Http::AsyncClient::Callbacks, | ||
| private Logger::Loggable<Logger::Id::tracing> { | ||
| public: | ||
| struct Handlers { | ||
| ResponseHandler on_response; | ||
| ErrorHandler on_error; | ||
| }; | ||
|
|
||
| public: | ||
| /** | ||
| * Create an \c AgentHTTPClient that uses the specified \p cluster_manager | ||
| * to make requests to the specified \p cluster, where requests include the | ||
| * specified \p reference_host as the "Host" header. Use the specified | ||
| * \p stats to keep track of usage statistics. | ||
| * @param cluster_manager cluster manager from which the thread local cluster | ||
| * is retrieved in order to make HTTP requests | ||
| * @param cluster the name of the cluster to which HTTP requests are made | ||
| * @param reference_host the value to use for the "Host" HTTP request header | ||
| * @param stats a collection of counters used to keep track of events, such as | ||
| * when a request fails | ||
| */ | ||
| AgentHTTPClient(Upstream::ClusterManager& cluster_manager, const std::string& cluster, | ||
| const std::string& reference_host, TracerStats& stats); | ||
| ~AgentHTTPClient() override; | ||
|
|
||
| // datadog::tracing::HTTPClient | ||
|
|
||
| /** | ||
| * Send an HTTP POST request to the cluster, where the requested resource is | ||
| * \p url.path. Invoke \p set_headers immediately to obtain the HTTP request | ||
| * headers. Send \p body as the request body. Return a | ||
| * \c datadog::tracing::Error if one occurs, otherwise return | ||
| * \c datadog::tracing::nullopt. When a complete response is received, | ||
| * invoke \p on_response with the response status, response headers, and | ||
| * response body. If an error occurs before a complete response is received, | ||
| * invoke \p on_error with a \c datadog::tracing::Error. | ||
| * @param url URL from which the request path is taken | ||
| * @param set_headers callback invoked immediately to obtain request headers | ||
| * @param body data to send as POST request body | ||
| * @param on_response callback to invoke when a complete response is received | ||
| * @param on_error callback to invoke when an error occurs before a complete | ||
| * response is received. | ||
| * @return \c datadog::tracing::Error if an error occurs, or | ||
| * \c datadog::tracing::nullopt otherwise. | ||
| */ | ||
| datadog::tracing::Expected<void> post(const URL& url, HeadersSetter set_headers, std::string body, | ||
| ResponseHandler on_response, | ||
| ErrorHandler on_error) override; | ||
|
|
||
| /** | ||
| * \c drain has no effect. It's a part of the \c datadog::tracing::HTTPClient | ||
| * that we do not need. | ||
| */ | ||
| void drain(std::chrono::steady_clock::time_point) override; | ||
|
|
||
| /** | ||
| * Return a JSON representation of this object's configuration. This function | ||
| * is used in the startup banner logged by \c dd-trace-cpp. | ||
| */ | ||
| nlohmann::json config_json() const override; | ||
|
|
||
| // Http::AsyncClient::Callbacks | ||
|
|
||
| void onSuccess(const Http::AsyncClient::Request&, Http::ResponseMessagePtr&&) override; | ||
| void onFailure(const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason) override; | ||
| void onBeforeFinalizeUpstreamSpan(Tracing::Span&, const Http::ResponseHeaderMap*) override; | ||
|
|
||
| private: | ||
| absl::flat_hash_map<Http::AsyncClient::Request*, Handlers> handlers_; | ||
| Upstream::ClusterUpdateTracker collector_cluster_; | ||
| const std::string cluster_; | ||
| const std::string reference_host_; | ||
| TracerStats& stats_; | ||
| }; | ||
|
|
||
| } // namespace Datadog | ||
| } // namespace Tracers | ||
| } // namespace Extensions | ||
| } // namespace Envoy |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.