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

tracer: add CEL sampler to OpenTelemetry #38182

Merged
merged 21 commits into from
Feb 26, 2025
Merged
2 changes: 2 additions & 0 deletions api/envoy/extensions/tracers/opentelemetry/samplers/v3/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ api_proto_package(
deps = [
"//envoy/config/core/v3:pkg",
"@com_github_cncf_xds//udpa/annotations:pkg",
"@com_github_cncf_xds//xds/annotations/v3:pkg",
"@com_github_cncf_xds//xds/type/v3:pkg",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
syntax = "proto3";

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

import "xds/annotations/v3/status.proto";
import "xds/type/v3/cel.proto";

import "udpa/annotations/status.proto";

option java_package = "io.envoyproxy.envoy.extensions.tracers.opentelemetry.samplers.v3";
option java_outer_classname = "CelSamplerProto";
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]
// Configuration for the "CEL" Sampler extension.
//
// [#extension: envoy.tracers.opentelemetry.samplers.cel]

message CELSamplerConfig {
// Expression that, when evaluated, will be used to make sample decision.
xds.type.v3.CelExpression expression = 1
[(xds.annotations.v3.field_status).work_in_progress = true];
}
5 changes: 5 additions & 0 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ REPOSITORY_LOCATIONS_SPEC = dict(
"envoy.tracers.opentelemetry",
"envoy.tracers.opentelemetry.samplers.always_on",
"envoy.tracers.opentelemetry.samplers.dynatrace",
"envoy.tracers.opentelemetry.samplers.cel",
],
release_date = "2025-01-22",
cpe = "N/A",
Expand Down Expand Up @@ -1268,6 +1269,8 @@ REPOSITORY_LOCATIONS_SPEC = dict(
"envoy.formatter.cel",
"envoy.matching.inputs.cel_data_input",
"envoy.matching.matchers.cel_matcher",
"envoy.tracers.opentelemetry",
"envoy.tracers.opentelemetry.samplers.cel",
],
release_date = "2024-10-25",
cpe = "N/A",
Expand Down Expand Up @@ -1299,6 +1302,8 @@ REPOSITORY_LOCATIONS_SPEC = dict(
"envoy.rbac.matchers.upstream_ip_port",
"envoy.matching.inputs.cel_data_input",
"envoy.matching.matchers.cel_matcher",
"envoy.tracers.opentelemetry",
"envoy.tracers.opentelemetry.samplers.cel",
],
release_date = "2025-01-25",
cpe = "cpe:2.3:a:google:flatbuffers:*",
Expand Down
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,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",
"envoy.tracers.opentelemetry.samplers.cel": "//source/extensions/tracers/opentelemetry/samplers/cel: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 @@ -1264,6 +1264,13 @@ envoy.tracers.opentelemetry.samplers.dynatrace:
status: wip
type_urls:
- envoy.extensions.tracers.opentelemetry.samplers.v3.DynatraceSamplerConfig
envoy.tracers.opentelemetry.samplers.cel:
categories:
- envoy.tracers.opentelemetry.samplers
security_posture: unknown
status: wip
type_urls:
- envoy.extensions.tracers.opentelemetry.samplers.v3.CELSamplerConfig
envoy.tracers.skywalking:
categories:
- envoy.tracers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,16 @@ Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config,
const auto span_kind = getSpanKind(config);
if (!extractor.propagationHeaderPresent()) {
// No propagation header, so we can create a fresh span with the given decision.
Tracing::SpanPtr new_open_telemetry_span = tracer.startSpan(
operation_name, stream_info.startTime(), tracing_decision, trace_context, span_kind);
Tracing::SpanPtr new_open_telemetry_span =
tracer.startSpan(operation_name, stream_info, stream_info.startTime(), tracing_decision,
trace_context, span_kind);
return new_open_telemetry_span;
} else {
// Try to extract the span context. If we can't, just return a null span.
absl::StatusOr<SpanContext> span_context = extractor.extractSpanContext();
if (span_context.ok()) {
return tracer.startSpan(operation_name, stream_info.startTime(), span_context.value(),
trace_context, span_kind);
return tracer.startSpan(operation_name, stream_info, stream_info.startTime(),
span_context.value(), trace_context, span_kind);
} else {
ENVOY_LOG(trace, "Unable to extract span context: ", span_context.status());
return std::make_unique<Tracing::NullSpan>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

SamplingResult AlwaysOnSampler::shouldSample(const absl::optional<SpanContext> parent_context,
SamplingResult AlwaysOnSampler::shouldSample(const StreamInfo::StreamInfo&,
const absl::optional<SpanContext> parent_context,
const std::string& /*trace_id*/,
const std::string& /*name*/, OTelSpanKind /*kind*/,
OptRef<const Tracing::TraceContext> /*trace_context*/,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class AlwaysOnSampler : public Sampler, Logger::Loggable<Logger::Id::tracing> {
public:
explicit AlwaysOnSampler(const Protobuf::Message& /*config*/,
Server::Configuration::TracerFactoryContext& /*context*/) {}
SamplingResult shouldSample(const absl::optional<SpanContext> parent_context,
SamplingResult shouldSample(const StreamInfo::StreamInfo& stream_info,
const absl::optional<SpanContext> parent_context,
const std::string& trace_id, const std::string& name,
OTelSpanKind spankind,
OptRef<const Tracing::TraceContext> trace_context,
Expand Down
37 changes: 37 additions & 0 deletions source/extensions/tracers/opentelemetry/samplers/cel/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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"],
tags = ["skip_on_windows"],
deps = [
":cel_sampler_lib",
"//envoy/registry",
"//source/common/config:utility_lib",
"//source/extensions/filters/common/expr:evaluator_lib",
"@envoy_api//envoy/extensions/tracers/opentelemetry/samplers/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "cel_sampler_lib",
srcs = ["cel_sampler.cc"],
hdrs = ["cel_sampler.h"],
deps = [
"//source/common/config:datasource_lib",
"//source/extensions/filters/common/expr:evaluator_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,57 @@
#include "source/extensions/tracers/opentelemetry/samplers/cel/cel_sampler.h"

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

#include "source/common/config/datasource.h"
#include "source/extensions/tracers/opentelemetry/span_context.h"

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

CELSampler::CELSampler(const ::Envoy::LocalInfo::LocalInfo& local_info,
Expr::BuilderInstanceSharedPtr builder,
const google::api::expr::v1alpha1::Expr& expr)
: local_info_(local_info), builder_(builder), parsed_expr_(expr) {
compiled_expr_ = Expr::createExpression(builder_->builder(), parsed_expr_);
}

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

Protobuf::Arena arena;
auto eval_status = Expr::evaluate(*compiled_expr_, arena, &local_info_, stream_info,
nullptr /* request_headers */, nullptr /* response_headers */,
nullptr /* response_trailers */);
SamplingResult result;
if (!eval_status.has_value() || eval_status.value().IsError()) {
result.decision = Decision::Drop;
return result;
}
auto eval_result_val = eval_status.value();
auto eval_result = eval_result_val.IsBool() ? eval_result_val.BoolOrDie() : false;
if (!eval_result) {
result.decision = Decision::Drop;
return result;
}

result.decision = Decision::RecordAndSample;
if (parent_context.has_value()) {
result.tracestate = parent_context.value().tracestate();
}
return result;
}

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

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

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

#include "source/common/common/logger.h"
#include "source/common/config/datasource.h"
#include "source/extensions/filters/common/expr/evaluator.h"
#include "source/extensions/tracers/opentelemetry/samplers/sampler.h"

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

namespace Expr = Envoy::Extensions::Filters::Common::Expr;

/**
* @brief A sampler which samples on CEL expression.
*
* - Returns RecordAndSample always.
* - Description MUST be AlwaysOnSampler.
*
*/
class CELSampler : public Sampler, Logger::Loggable<Logger::Id::tracing> {
public:
CELSampler(const ::Envoy::LocalInfo::LocalInfo& local_info,
Expr::BuilderInstanceSharedPtr builder, const google::api::expr::v1alpha1::Expr& expr);
SamplingResult shouldSample(const StreamInfo::StreamInfo& stream_info,
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:
const ::Envoy::LocalInfo::LocalInfo& local_info_;
Extensions::Filters::Common::Expr::BuilderInstanceSharedPtr builder_;
const google::api::expr::v1alpha1::Expr parsed_expr_;
Extensions::Filters::Common::Expr::ExpressionPtr compiled_expr_;
};

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
55 changes: 55 additions & 0 deletions source/extensions/tracers/opentelemetry/samplers/cel/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "source/extensions/tracers/opentelemetry/samplers/cel/config.h"

#include <memory>

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

#include "source/common/config/utility.h"
#include "source/common/protobuf/utility.h"
#include "source/extensions/tracers/opentelemetry/samplers/cel/cel_sampler.h"

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

using ::xds::type::v3::CelExpression;

SamplerSharedPtr
CELSamplerFactory::createSampler(const Protobuf::Message& config,
Server::Configuration::TracerFactoryContext& context) {
auto mptr = Envoy::Config::Utility::translateAnyToFactoryConfig(
dynamic_cast<const ProtobufWkt::Any&>(config), context.messageValidationVisitor(), *this);

const auto& proto_config = MessageUtil::downcastAndValidate<
const envoy::extensions::tracers::opentelemetry::samplers::v3::CELSamplerConfig&>(
*mptr, context.messageValidationVisitor());

const CelExpression& input_expr = proto_config.expression();
google::api::expr::v1alpha1::Expr compiled_expr_;
switch (input_expr.expr_specifier_case()) {
case CelExpression::ExprSpecifierCase::kParsedExpr:
compiled_expr_ = input_expr.parsed_expr().expr();
break;
case CelExpression::ExprSpecifierCase::kCheckedExpr:
compiled_expr_ = input_expr.checked_expr().expr();
break;
case CelExpression::ExprSpecifierCase::EXPR_SPECIFIER_NOT_SET:
throw EnvoyException("CEL expression not set");
}

return std::make_unique<CELSampler>(
context.serverFactoryContext().localInfo(),
Extensions::Filters::Common::Expr::getBuilder(context.serverFactoryContext()),
compiled_expr_);
}

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

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
42 changes: 42 additions & 0 deletions source/extensions/tracers/opentelemetry/samplers/cel/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <string>

#include "envoy/extensions/tracers/opentelemetry/samplers/v3/cel_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 CELSampler. @see SamplerFactory.
*/
class CELSamplerFactory : public SamplerFactory {
public:
/**
* @brief Create a CEL Sampler.
*
* @param config Protobuf config for the sampler.
* @param context A reference to the TracerFactoryContext.
* @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::CELSamplerConfig>();
}
std::string name() const override { return "envoy.tracers.opentelemetry.samplers.cel"; }
};

DECLARE_FACTORY(AlwaysOnSamplerFactory);

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ DynatraceSampler::DynatraceSampler(
timer_->enableTimer(SAMPLING_UPDATE_TIMER_DURATION);
}

SamplingResult DynatraceSampler::shouldSample(const absl::optional<SpanContext> parent_context,
SamplingResult DynatraceSampler::shouldSample(const StreamInfo::StreamInfo&,
const absl::optional<SpanContext> parent_context,
const std::string& trace_id,
const std::string& /*name*/, OTelSpanKind /*kind*/,
OptRef<const Tracing::TraceContext> trace_context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class DynatraceSampler : public Sampler, Logger::Loggable<Logger::Id::tracing> {
SamplerConfigProviderPtr sampler_config_provider);

/** @see Sampler#shouldSample */
SamplingResult shouldSample(const absl::optional<SpanContext> parent_context,
SamplingResult shouldSample(const StreamInfo::StreamInfo& stream_info,
const absl::optional<SpanContext> parent_context,
const std::string& trace_id, const std::string& name,
OTelSpanKind spankind,
OptRef<const Tracing::TraceContext> trace_context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class Sampler {
* @param links Collection of links that will be associated with the Span to be created.
* @return SamplingResult @see SamplingResult
*/
virtual SamplingResult shouldSample(const absl::optional<SpanContext> parent_context,
virtual SamplingResult shouldSample(const StreamInfo::StreamInfo& stream_info,
const absl::optional<SpanContext> parent_context,
const std::string& trace_id, const std::string& name,
OTelSpanKind spankind,
OptRef<const Tracing::TraceContext> trace_context,
Expand Down
Loading
Loading