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/utility.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
    std::shared_ptr<const Http::HttpServiceHeadersApplicator> headers_applicator)
22
10
    : cluster_manager_(cluster_manager), http_service_(http_service),
23
10
      headers_applicator_(std::move(headers_applicator)) {}
24

            
25
5
bool OpenTelemetryHttpTraceExporter::log(const ExportTraceServiceRequest& request) {
26
5
  std::string request_body;
27

            
28
5
  const auto ok = request.SerializeToString(&request_body);
29
5
  if (!ok) {
30
    ENVOY_LOG(warn, "Error while serializing the binary proto ExportTraceServiceRequest.");
31
    return false;
32
  }
33

            
34
5
  const auto thread_local_cluster =
35
5
      cluster_manager_.getThreadLocalCluster(http_service_.http_uri().cluster());
36
5
  if (thread_local_cluster == nullptr) {
37
1
    ENVOY_LOG(error, "OTLP HTTP exporter failed: [cluster = {}] is not configured",
38
1
              http_service_.http_uri().cluster());
39
1
    return false;
40
1
  }
41

            
42
4
  Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_service_.http_uri());
43

            
44
  // The request follows the OTLP HTTP specification:
45
  // https://github.com/open-telemetry/opentelemetry-proto/blob/v1.0.0/docs/specification.md#otlphttp.
46
4
  message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Post);
47
4
  message->headers().setReferenceContentType(Http::Headers::get().ContentTypeValues.Protobuf);
48

            
49
  // User-Agent header follows the OTLP specification:
50
  // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.30.0/specification/protocol/exporter.md#user-agent
51
4
  message->headers().setReferenceUserAgent(OtlpUtils::getOtlpUserAgentHeader());
52

            
53
  // Add all custom headers to the request (static values set once; formatted values
54
  // re-evaluated now so that runtime updates, e.g. SDS rotation, are reflected).
55
4
  headers_applicator_->apply(message->headers());
56

            
57
4
  message->body().add(request_body);
58

            
59
4
  const auto options =
60
4
      Http::AsyncClient::RequestOptions()
61
4
          .setTimeout(std::chrono::milliseconds(
62
4
              DurationUtil::durationToMilliseconds(http_service_.http_uri().timeout())))
63
4
          .setDiscardResponseBody(true);
64

            
65
4
  Http::AsyncClient::Request* in_flight_request =
66
4
      thread_local_cluster->httpAsyncClient().send(std::move(message), *this, options);
67

            
68
4
  OpenTelemetryTraceExporter::logExportedSpans(request);
69

            
70
4
  if (in_flight_request == nullptr) {
71
1
    return false;
72
1
  }
73

            
74
3
  active_requests_.add(*in_flight_request);
75
3
  return true;
76
4
}
77

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

            
90
void OpenTelemetryHttpTraceExporter::onFailure(const Http::AsyncClient::Request& request,
91
1
                                               Http::AsyncClient::FailureReason reason) {
92
1
  active_requests_.remove(request);
93
1
  ENVOY_LOG(debug, "The OTLP export request failed. Reason {}", enumToInt(reason));
94
1
}
95

            
96
} // namespace OpenTelemetry
97
} // namespace Tracers
98
} // namespace Extensions
99
} // namespace Envoy