Line data Source code
1 : #include "source/extensions/tracers/datadog/agent_http_client.h" 2 : 3 : #include <cstddef> 4 : #include <cstdint> 5 : #include <memory> 6 : #include <string> 7 : 8 : #include "source/common/common/assert.h" 9 : #include "source/common/http/message_impl.h" 10 : #include "source/common/http/utility.h" 11 : #include "source/extensions/tracers/datadog/dict_util.h" 12 : #include "source/extensions/tracers/datadog/tracer_stats.h" 13 : 14 : #include "absl/strings/str_format.h" 15 : #include "datadog/dict_reader.h" 16 : #include "datadog/dict_writer.h" 17 : #include "datadog/error.h" 18 : #include "datadog/json.hpp" 19 : 20 : namespace Envoy { 21 : namespace Extensions { 22 : namespace Tracers { 23 : namespace Datadog { 24 : 25 : AgentHTTPClient::AgentHTTPClient(Upstream::ClusterManager& cluster_manager, 26 : const std::string& cluster, const std::string& reference_host, 27 : TracerStats& stats) 28 : : collector_cluster_(cluster_manager, cluster), cluster_(cluster), 29 0 : reference_host_(reference_host), stats_(stats) {} 30 : 31 0 : AgentHTTPClient::~AgentHTTPClient() { 32 0 : for (const auto& [request_ptr, _] : handlers_) { 33 0 : RELEASE_ASSERT(request_ptr, 34 0 : "null Http::AsyncClient::Request* in handler map of Datadog::AgentHTTPClient"); 35 0 : request_ptr->cancel(); 36 0 : } 37 0 : } 38 : 39 : // datadog::tracing::HTTPClient 40 : 41 : datadog::tracing::Expected<void> AgentHTTPClient::post(const URL& url, HeadersSetter set_headers, 42 : std::string body, 43 : ResponseHandler on_response, 44 0 : ErrorHandler on_error) { 45 0 : ENVOY_LOG(debug, "flushing traces"); 46 : 47 0 : auto message = std::make_unique<Http::RequestMessageImpl>(); 48 0 : Http::RequestHeaderMap& headers = message->headers(); 49 0 : headers.setReferenceMethod(Http::Headers::get().MethodValues.Post); 50 0 : headers.setReferencePath(url.path); 51 0 : headers.setReferenceHost(reference_host_); 52 : 53 0 : RequestHeaderWriter writer{message->headers()}; 54 0 : set_headers(writer); 55 : 56 0 : message->body().add(body); 57 0 : ENVOY_LOG(debug, "submitting trace(s) to {} with payload size {}", url.path, body.size()); 58 : 59 0 : if (!collector_cluster_.threadLocalCluster().has_value()) { 60 0 : ENVOY_LOG(debug, "collector cluster '{}' does not exist", cluster_); 61 0 : stats_.reports_skipped_no_cluster_.inc(); 62 0 : return datadog::tracing::nullopt; 63 0 : } 64 : 65 0 : Http::AsyncClient::Request* request = 66 0 : collector_cluster_.threadLocalCluster()->get().httpAsyncClient().send( 67 0 : std::move(message), *this, 68 0 : Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(1000))); 69 0 : if (!request) { 70 0 : stats_.reports_failed_.inc(); 71 0 : return datadog::tracing::Error{datadog::tracing::Error::ENVOY_HTTP_CLIENT_FAILURE, 72 0 : "Failed to create request."}; 73 0 : } 74 : 75 0 : handlers_.emplace(request, Handlers{std::move(on_response), std::move(on_error)}); 76 0 : return datadog::tracing::nullopt; 77 0 : } 78 : 79 0 : void AgentHTTPClient::drain(std::chrono::steady_clock::time_point) {} 80 : 81 0 : nlohmann::json AgentHTTPClient::config_json() const { 82 0 : return nlohmann::json::object({ 83 0 : {"type", "Envoy::Extensions::Tracers::Datadog::AgentHTTPClient"}, 84 0 : }); 85 0 : } 86 : 87 : // Http::AsyncClient::Callbacks 88 : 89 : void AgentHTTPClient::onSuccess(const Http::AsyncClient::Request& request, 90 0 : Http::ResponseMessagePtr&& response) { 91 0 : const std::uint64_t status = Http::Utility::getResponseStatus(response->headers()); 92 0 : if (status != std::uint64_t(Http::Code::OK)) { 93 0 : stats_.reports_dropped_.inc(); 94 0 : } else { 95 0 : ENVOY_LOG(debug, "traces successfully submitted to datadog agent"); 96 0 : stats_.reports_sent_.inc(); 97 0 : } 98 : 99 0 : auto found = handlers_.find(const_cast<Http::AsyncClient::Request*>(&request)); 100 0 : if (found == handlers_.end()) { 101 0 : ENVOY_LOG(debug, "request at address 0x{} is not in the map of {} Handlers objects", 102 0 : absl::StrFormat("%p", &request), handlers_.size()); 103 0 : return; 104 0 : } 105 : 106 0 : Handlers& handlers = found->second; 107 0 : ResponseHeaderReader reader{response->headers()}; 108 0 : handlers.on_response(status, reader, response->bodyAsString()); 109 : 110 0 : handlers_.erase(found); 111 0 : } 112 : 113 : void AgentHTTPClient::onFailure(const Http::AsyncClient::Request& request, 114 0 : Http::AsyncClient::FailureReason reason) { 115 0 : auto found = handlers_.find(const_cast<Http::AsyncClient::Request*>(&request)); 116 : // If the request failed to start, then `post` never even registered the request. 117 0 : if (found == handlers_.end()) { 118 0 : return; 119 0 : } 120 : 121 0 : stats_.reports_failed_.inc(); 122 : 123 0 : Handlers& handlers = found->second; 124 0 : std::string message = "Failed to send request to Datadog Agent: "; 125 0 : switch (reason) { 126 0 : case Http::AsyncClient::FailureReason::Reset: 127 0 : message += "The stream has been reset."; 128 0 : break; 129 0 : default: 130 0 : message += "Unknown error."; 131 0 : } 132 0 : handlers.on_error(datadog::tracing::Error{datadog::tracing::Error::ENVOY_HTTP_CLIENT_FAILURE, 133 0 : std::move(message)}); 134 : 135 0 : handlers_.erase(found); 136 0 : } 137 : 138 0 : void AgentHTTPClient::onBeforeFinalizeUpstreamSpan(Tracing::Span&, const Http::ResponseHeaderMap*) { 139 0 : } 140 : 141 : } // namespace Datadog 142 : } // namespace Tracers 143 : } // namespace Extensions 144 : } // namespace Envoy