Skip to content
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

Feat/dynatrace sampler internal #10

Merged
merged 25 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
63a4be2
Allow specifying samplers for the OpenTelemetry tracer via a new conf…
samohte Oct 17, 2023
4f5930b
Merge remote-tracking branch 'upstream/main' into feat/sampler
samohte Oct 23, 2023
3bf0fb6
add integration test, remove redundant method, do not set empty trace…
samohte Oct 24, 2023
b47fd5b
Merge remote-tracking branch 'upstream/main' into feat/sampler
samohte Oct 24, 2023
2125d97
add dynatrace sampler
samohte Oct 19, 2023
40bff9b
add Dynatrace sampler to metadata yaml
samohte Oct 19, 2023
298a5c5
cleanup fix attribute
samohte Oct 19, 2023
8b21901
another cleanup
samohte Oct 19, 2023
3ec1421
add integration test for dynatrace sampler
samohte Oct 25, 2023
13afa60
add dynatrace_sampler_test
samohte Oct 25, 2023
16eb68a
Merge remote-tracking branch 'upstream/main' into feat/dynatrace-samp…
samohte Nov 2, 2023
8085c36
use 0 as hardcoded exponent
samohte Nov 3, 2023
2ba0b6a
first approach to get inital attributes
samohte Nov 7, 2023
e873324
get initial attributes.
samohte Nov 7, 2023
0c9bf5f
move span kind detection to startSpan(), adapt tests
samohte Nov 8, 2023
edfcb03
dummy commit
samohte Nov 17, 2023
c063f7e
Merge remote-tracking branch 'upstream/main' into feat/dynatrace-samp…
samohte Nov 17, 2023
8348303
fix partial merge
samohte Nov 17, 2023
258f4c4
* dummy usage of trace_context in dynatrace sampler
samohte Nov 20, 2023
b6e08dd
cleanup
samohte Nov 21, 2023
6319086
change FW4 tag creation (pathInfo was missing), seems it is now accec…
samohte Nov 22, 2023
a9ec6b0
do sampling decision: sample every second path
samohte Nov 22, 2023
34ee752
remove dead code, minor cleanup
samohte Nov 23, 2023
c19b070
make constructer param a reference
samohte Nov 24, 2023
b262510
review feedback
samohte Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
syntax = "proto3";

package envoy.extensions.tracers.opentelemetry.samplers.v3;

import "udpa/annotations/status.proto";

option java_package = "io.envoyproxy.envoy.extensions.tracers.opentelemetry.samplers.v3";
option java_outer_classname = "DynatraceSamplerProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/opentelemetry/samplers/v3;samplersv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Always On Sampler config]
samohte marked this conversation as resolved.
Show resolved Hide resolved
// [#extension: envoy.tracers.opentelemetry.samplers.dynatrace]

message DynatraceSamplerConfig {
string tenant_id = 1;

string cluster_id = 2;
}
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ EXTENSIONS = {
#

"envoy.tracers.opentelemetry.samplers.always_on": "//source/extensions/tracers/opentelemetry/samplers/always_on:config",
"envoy.tracers.opentelemetry.samplers.dynatrace": "//source/extensions/tracers/opentelemetry/samplers/dynatrace:config",

#
# Transport sockets
Expand Down
7 changes: 7 additions & 0 deletions source/extensions/extensions_metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,13 @@ envoy.tracers.opentelemetry.samplers.always_on:
status: wip
type_urls:
- envoy.extensions.tracers.opentelemetry.samplers.v3.AlwaysOnSamplerConfig
envoy.tracers.opentelemetry.samplers.dynatrace:
categories:
- envoy.tracers.opentelemetry.samplers
security_posture: unknown
status: wip
type_urls:
- envoy.extensions.tracers.opentelemetry.samplers.v3.DynatraceSamplerConfig
envoy.tracers.skywalking:
categories:
- envoy.tracers
Expand Down
42 changes: 42 additions & 0 deletions source/extensions/tracers/opentelemetry/samplers/dynatrace/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_extension",
"envoy_cc_library",
"envoy_extension_package",
)

licenses(["notice"]) # Apache 2

envoy_extension_package()

envoy_cc_extension(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
":dynatrace_sampler_lib",
"//envoy/registry",
"//source/common/config:utility_lib",
"@envoy_api//envoy/extensions/tracers/opentelemetry/samplers/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "dynatrace_sampler_lib",
srcs = [
"dynatrace_sampler.cc",
"dynatrace_tracestate.cc",
"tracestate.cc",
],
hdrs = [
"dynatrace_sampler.h",
"dynatrace_tracestate.h",
"tracestate.h",
],
deps = [
"//source/common/config:datasource_lib",
"//source/extensions/tracers/opentelemetry:opentelemetry_tracer_lib",
"//source/extensions/tracers/opentelemetry/samplers:sampler_lib",
"@envoy_api//envoy/extensions/tracers/opentelemetry/samplers/v3:pkg_cc_proto",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "config.h"

#include "envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.pb.validate.h"

#include "source/common/config/utility.h"
#include "source/common/protobuf/utility.h"

#include "dynatrace_sampler.h"
samohte marked this conversation as resolved.
Show resolved Hide resolved

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

SamplerSharedPtr
DynatraceSamplerFactory::createSampler(const Protobuf::Message& config,
Server::Configuration::TracerFactoryContext& context) {
auto mptr = Envoy::Config::Utility::translateAnyToFactoryConfig(
dynamic_cast<const ProtobufWkt::Any&>(config), context.messageValidationVisitor(), *this);
return std::make_shared<DynatraceSampler>(
MessageUtil::downcastAndValidate<
const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig&>(
*mptr, context.messageValidationVisitor()),
context);
}

/**
* Static registration for the Env sampler factory. @see RegisterFactory.
*/
REGISTER_FACTORY(DynatraceSamplerFactory, SamplerFactory);

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <string>

#include "envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.pb.h"
#include "envoy/registry/registry.h"

#include "source/extensions/tracers/opentelemetry/samplers/sampler.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

/**
* Config registration for the DynatraceSampler. @see SamplerFactory.
*/
class DynatraceSamplerFactory : public SamplerFactory {
public:
/**
* @brief Create a Sampler which samples every span
*
* @param context
* @return SamplerSharedPtr
*/
SamplerSharedPtr createSampler(const Protobuf::Message& config,
Server::Configuration::TracerFactoryContext& context) override;

ProtobufTypes::MessagePtr createEmptyConfigProto() override {
return std::make_unique<
envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig>();
}
std::string name() const override { return "envoy.tracers.opentelemetry.samplers.dynatrace"; }
};

DECLARE_FACTORY(DynatraceSamplerFactory);

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#include "source/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_sampler.h"

#include <memory>
#include <sstream>
#include <string>

#include "source/common/config/datasource.h"
#include "source/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_tracestate.h"
#include "source/extensions/tracers/opentelemetry/samplers/dynatrace/tracestate.h"
#include "source/extensions/tracers/opentelemetry/samplers/sampler.h"
#include "source/extensions/tracers/opentelemetry/span_context.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

static const char* SAMPLING_EXTRAPOLATION_SPAN_ATTRIBUTE_NAME =
"sampling_extrapolation_set_in_sampler";

FW4Tag DynatraceSampler::getFW4Tag(const Tracestate& tracestate) {
for (auto const& entry : tracestate.entries()) {
if (dt_tracestate_entry_.keyMatches(
entry.key)) { // found a tracestate entry with key matching our tenant/cluster
return FW4Tag::create(entry.value);
}
}
return FW4Tag::createInvalid();
}

DynatraceSampler::DynatraceSampler(
const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config,
Server::Configuration::TracerFactoryContext& /*context*/)
: tenant_id_(config.tenant_id()), cluster_id_(config.cluster_id()),
dt_tracestate_entry_(tenant_id_, cluster_id_), counter_(0) {}

SamplingResult DynatraceSampler::shouldSample(const absl::optional<SpanContext> parent_context,
const std::string& /*trace_id*/,
const std::string& /*name*/, OTelSpanKind /*kind*/,
OptRef<const Tracing::TraceContext> /*trace_context*/,
const std::vector<SpanContext>& /*links*/) {

SamplingResult result;
std::map<std::string, std::string> att;
// search for an existing forward tag in the tracestate
Tracestate tracestate;
tracestate.parse(parent_context.has_value() ? parent_context->tracestate() : "");

if (FW4Tag fw4_tag = getFW4Tag(tracestate);
fw4_tag.isValid()) { // we found a trace decision in tracestate header
result.decision = fw4_tag.isIgnored() ? Decision::DROP : Decision::RECORD_AND_SAMPLE;
att[SAMPLING_EXTRAPOLATION_SPAN_ATTRIBUTE_NAME] = std::to_string(fw4_tag.getSamplingExponent());
result.tracestate = parent_context->tracestate();

} else { // make a sampling decision
// this is just a demo, we sample every second request here
uint32_t current_counter = ++counter_;
bool sample;
int sampling_exponent;
if (current_counter % 2) {
sample = true;
sampling_exponent = 1;
} else {
sample = false;
sampling_exponent = 0;
}

att[SAMPLING_EXTRAPOLATION_SPAN_ATTRIBUTE_NAME] = std::to_string(sampling_exponent);

result.decision = sample ? Decision::RECORD_AND_SAMPLE : Decision::DROP;
// create new forward tag and add it to tracestate
FW4Tag new_tag = FW4Tag::create(!sample, sampling_exponent);
tracestate.add(dt_tracestate_entry_.getKey(), new_tag.asString());
result.tracestate = tracestate.asString();
}

if (!att.empty()) {
result.attributes = std::make_unique<const std::map<std::string, std::string>>(std::move(att));
}
return result;
}

std::string DynatraceSampler::getDescription() const { return "DynatraceSampler"; }

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include "envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.pb.h"
#include "envoy/server/factory_context.h"

#include "source/common/common/logger.h"
#include "source/common/config/datasource.h"
#include "source/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_tracestate.h"
#include "source/extensions/tracers/opentelemetry/samplers/dynatrace/tracestate.h"
#include "source/extensions/tracers/opentelemetry/samplers/sampler.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

/**
* @brief A Dynatrace specific sampler *
*/
class DynatraceSampler : public Sampler, Logger::Loggable<Logger::Id::tracing> {
public:
DynatraceSampler(
const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config,
Server::Configuration::TracerFactoryContext& context);

SamplingResult shouldSample(const absl::optional<SpanContext> parent_context,
const std::string& trace_id, const std::string& name,
OTelSpanKind spankind,
OptRef<const Tracing::TraceContext> trace_context,
const std::vector<SpanContext>& links) override;

std::string getDescription() const override;

private:
std::string tenant_id_;
std::string cluster_id_;
DtTracestateEntry dt_tracestate_entry_;
std::atomic<uint32_t> counter_; // request counter for dummy sampling
FW4Tag getFW4Tag(const Tracestate& tracestate);
};

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "source/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_tracestate.h"

#include <string>
#include <vector>

#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

FW4Tag FW4Tag::createInvalid() { return {false, false, 0}; }

FW4Tag FW4Tag::create(bool ignored, int sampling_exponent) {
return {true, ignored, sampling_exponent};
}

FW4Tag FW4Tag::create(const std::string& value) {
std::vector<absl::string_view> tracestate_components =
absl::StrSplit(value, ';', absl::AllowEmpty());
if (tracestate_components.size() < 7) {
return createInvalid();
}

if (tracestate_components[0] != "fw4") {
return createInvalid();
}
bool ignored = tracestate_components[5] == "1";
int sampling_exponent = std::stoi(std::string(tracestate_components[6]));
return {true, ignored, sampling_exponent};
}

std::string FW4Tag::asString() const {
std::string ret =
absl::StrCat("fw4;0;0;0;0;", ignored_ ? "1" : "0", ";", sampling_exponent_, ";0");
return ret;
}

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once

#include <string>

#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

class FW4Tag {
public:
static FW4Tag createInvalid();

static FW4Tag create(bool ignored, int sampling_exponent);

static FW4Tag create(const std::string& value);

std::string asString() const;

bool isValid() const { return valid_; };
bool isIgnored() const { return ignored_; };
int getSamplingExponent() const { return sampling_exponent_; };

private:
FW4Tag(bool valid, bool ignored, int sampling_exponent)
: valid_(valid), ignored_(ignored), sampling_exponent_(sampling_exponent) {}

bool valid_;
bool ignored_;
int sampling_exponent_;
};

// <tenantID>-<clusterID>@dt=fw4;0;0;0;0;<isIgnored>;<sampling_exponent>;<rootPathRandom>
class DtTracestateEntry {
public:
DtTracestateEntry(const std::string& tenant_id, const std::string& cluster_id) {
key_ = absl::StrCat(absl::string_view(tenant_id), "-", absl::string_view(cluster_id), "@dt");
}

std::string getKey() const { return key_; };

bool keyMatches(const std::string& key) { return (key_.compare(key) == 0); }

private:
std::string key_;
};
} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Loading
Loading