1
#include "source/extensions/tracers/opentelemetry/http_trace_exporter.h"
2

            
3
#include <chrono>
4
#include <memory>
5
#include <string>
6
#include <vector>
7

            
8
#include "source/common/common/enum_to_int.h"
9
#include "source/common/common/logger.h"
10
#include "source/common/protobuf/protobuf.h"
11
#include "source/extensions/tracers/opentelemetry/otlp_utils.h"
12

            
13
namespace Envoy {
14
namespace Extensions {
15
namespace Tracers {
16
namespace OpenTelemetry {
17

            
18
OpenTelemetryHttpTraceExporter::OpenTelemetryHttpTraceExporter(
19
    Upstream::ClusterManager& cluster_manager,
20
    const envoy::config::core::v3::HttpService& http_service)
21
9
    : cluster_manager_(cluster_manager), http_service_(http_service) {
22

            
23
  // Prepare and store headers to be used later on each export request
24
9
  for (const auto& header_value_option : http_service_.request_headers_to_add()) {
25
7
    parsed_headers_to_add_.push_back({Http::LowerCaseString(header_value_option.header().key()),
26
7
                                      header_value_option.header().value()});
27
7
  }
28
9
}
29

            
30
4
bool OpenTelemetryHttpTraceExporter::log(const ExportTraceServiceRequest& request) {
31
4
  std::string request_body;
32

            
33
4
  const auto ok = request.SerializeToString(&request_body);
34
4
  if (!ok) {
35
    ENVOY_LOG(warn, "Error while serializing the binary proto ExportTraceServiceRequest.");
36
    return false;
37
  }
38

            
39
4
  const auto thread_local_cluster =
40
4
      cluster_manager_.getThreadLocalCluster(http_service_.http_uri().cluster());
41
4
  if (thread_local_cluster == nullptr) {
42
1
    ENVOY_LOG(error, "OTLP HTTP exporter failed: [cluster = {}] is not configured",
43
1
              http_service_.http_uri().cluster());
44
1
    return false;
45
1
  }
46

            
47
3
  Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_service_.http_uri());
48

            
49
  // The request follows the OTLP HTTP specification:
50
  // https://github.com/open-telemetry/opentelemetry-proto/blob/v1.0.0/docs/specification.md#otlphttp.
51
3
  message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Post);
52
3
  message->headers().setReferenceContentType(Http::Headers::get().ContentTypeValues.Protobuf);
53

            
54
  // User-Agent header follows the OTLP specification:
55
  // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.30.0/specification/protocol/exporter.md#user-agent
56
3
  message->headers().setReferenceUserAgent(OtlpUtils::getOtlpUserAgentHeader());
57

            
58
  // Add all custom headers to the request.
59
4
  for (const auto& header_pair : parsed_headers_to_add_) {
60
3
    message->headers().setReference(header_pair.first, header_pair.second);
61
3
  }
62
3
  message->body().add(request_body);
63

            
64
3
  const auto options =
65
3
      Http::AsyncClient::RequestOptions()
66
3
          .setTimeout(std::chrono::milliseconds(
67
3
              DurationUtil::durationToMilliseconds(http_service_.http_uri().timeout())))
68
3
          .setDiscardResponseBody(true);
69

            
70
3
  Http::AsyncClient::Request* in_flight_request =
71
3
      thread_local_cluster->httpAsyncClient().send(std::move(message), *this, options);
72

            
73
3
  OpenTelemetryTraceExporter::logExportedSpans(request);
74

            
75
3
  if (in_flight_request == nullptr) {
76
1
    return false;
77
1
  }
78

            
79
2
  active_requests_.add(*in_flight_request);
80
2
  return true;
81
3
}
82

            
83
void OpenTelemetryHttpTraceExporter::onSuccess(const Http::AsyncClient::Request& request,
84
2
                                               Http::ResponseMessagePtr&& http_response) {
85
2
  active_requests_.remove(request);
86
2
  const auto response_code = Http::Utility::getResponseStatus(http_response->headers());
87
2
  if (response_code != enumToInt(Http::Code::OK)) {
88
1
    ENVOY_LOG(error,
89
1
              "OTLP HTTP exporter received a non-success status code: {} while exporting the OTLP "
90
1
              "message",
91
1
              response_code);
92
1
  }
93
2
}
94

            
95
void OpenTelemetryHttpTraceExporter::onFailure(const Http::AsyncClient::Request& request,
96
1
                                               Http::AsyncClient::FailureReason reason) {
97
1
  active_requests_.remove(request);
98
1
  ENVOY_LOG(debug, "The OTLP export request failed. Reason {}", enumToInt(reason));
99
1
}
100

            
101
} // namespace OpenTelemetry
102
} // namespace Tracers
103
} // namespace Extensions
104
} // namespace Envoy