1
#include "source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h"
2

            
3
#include <chrono>
4

            
5
#include "source/common/common/enum_to_int.h"
6
#include "source/common/http/utility.h"
7

            
8
namespace Envoy {
9
namespace Extensions {
10
namespace Tracers {
11
namespace OpenTelemetry {
12

            
13
static constexpr std::chrono::seconds INITIAL_TIMER_DURATION{10};
14
static constexpr std::chrono::minutes TIMER_INTERVAL{5};
15

            
16
namespace {
17

            
18
9
bool reEnableTimer(Http::Code response_code) {
19
9
  switch (response_code) {
20
1
  case Http::Code::OK:
21
2
  case Http::Code::TooManyRequests:
22
3
  case Http::Code::InternalServerError:
23
4
  case Http::Code::BadGateway:
24
5
  case Http::Code::ServiceUnavailable:
25
6
  case Http::Code::GatewayTimeout:
26
6
    return true;
27
3
  default:
28
3
    return false;
29
9
  }
30
9
}
31

            
32
} // namespace
33

            
34
SamplerConfigProviderImpl::SamplerConfigProviderImpl(
35
    Server::Configuration::TracerFactoryContext& context,
36
    const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config)
37
17
    : cluster_manager_(context.serverFactoryContext().clusterManager()),
38
17
      http_uri_(config.http_service().http_uri()), sampler_config_(config.root_spans_per_minute()) {
39

            
40
17
  for (const auto& header_value_option : config.http_service().request_headers_to_add()) {
41
9
    parsed_headers_to_add_.push_back({Http::LowerCaseString(header_value_option.header().key()),
42
9
                                      header_value_option.header().value()});
43
9
  }
44

            
45
19
  timer_ = context.serverFactoryContext().mainThreadDispatcher().createTimer([this]() -> void {
46
14
    const auto thread_local_cluster = cluster_manager_.getThreadLocalCluster(http_uri_.cluster());
47
14
    if (thread_local_cluster == nullptr) {
48
1
      ENVOY_LOG(error, "SamplerConfigProviderImpl failed: [cluster = {}] is not configured",
49
1
                http_uri_.cluster());
50
13
    } else {
51
13
      Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_uri_);
52
13
      message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Get);
53
13
      for (const auto& header_pair : parsed_headers_to_add_) {
54
13
        message->headers().setReference(header_pair.first, header_pair.second);
55
13
      }
56
13
      active_request_ = thread_local_cluster->httpAsyncClient().send(
57
13
          std::move(message), *this,
58
13
          Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(
59
13
              DurationUtil::durationToMilliseconds(http_uri_.timeout()))));
60
13
    }
61
14
  });
62

            
63
17
  timer_->enableTimer(std::chrono::seconds(INITIAL_TIMER_DURATION));
64
17
}
65

            
66
17
SamplerConfigProviderImpl::~SamplerConfigProviderImpl() {
67
17
  if (active_request_) {
68
1
    active_request_->cancel();
69
1
  }
70
17
}
71

            
72
void SamplerConfigProviderImpl::onSuccess(const Http::AsyncClient::Request& /*request*/,
73
11
                                          Http::ResponseMessagePtr&& http_response) {
74
11
  active_request_ = nullptr;
75
11
  const auto response_code = Http::Utility::getResponseStatus(http_response->headers());
76
11
  bool json_valid = false;
77
11
  if (response_code == enumToInt(Http::Code::OK)) {
78
3
    ENVOY_LOG(debug, "Received sampling configuration from Dynatrace: {}",
79
3
              http_response->bodyAsString());
80
3
    json_valid = sampler_config_.parse(http_response->bodyAsString());
81
3
    if (!json_valid) {
82
1
      ENVOY_LOG(warn, "Failed to parse sampling configuration received from Dynatrace: {}",
83
1
                http_response->bodyAsString());
84
1
    }
85
8
  } else {
86
8
    ENVOY_LOG(warn, "Failed to get sampling configuration from Dynatrace: {}", response_code);
87
8
  }
88

            
89
11
  if (json_valid || reEnableTimer(static_cast<Http::Code>(response_code))) {
90
8
    timer_->enableTimer(std::chrono::seconds(TIMER_INTERVAL));
91
8
  } else {
92
3
    ENVOY_LOG(error, "Stopped to query sampling configuration from Dynatrace.");
93
3
  }
94
11
}
95

            
96
void SamplerConfigProviderImpl::onFailure(const Http::AsyncClient::Request& /*request*/,
97
1
                                          Http::AsyncClient::FailureReason reason) {
98
1
  active_request_ = nullptr;
99
1
  timer_->enableTimer(std::chrono::seconds(TIMER_INTERVAL));
100
1
  ENVOY_LOG(warn, "Failed to get sampling configuration from Dynatrace. Reason {}",
101
1
            enumToInt(reason));
102
1
}
103

            
104
10
const SamplerConfig& SamplerConfigProviderImpl::getSamplerConfig() const { return sampler_config_; }
105

            
106
} // namespace OpenTelemetry
107
} // namespace Tracers
108
} // namespace Extensions
109
} // namespace Envoy