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

[SDK] Add tracer scope configurator #3137

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Increment the:
* [SEMANTIC CONVENTIONS] Migration to weaver
[#3105](https://github.com/open-telemetry/opentelemetry-cpp/pull/3105)

* [SDK] Add tracer scope configurator
[#3137](https://github.com/open-telemetry/opentelemetry-cpp/pull/3137)

Important changes:

* [API] Jaeger Propagator should not be deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#pragma once

#include <functional>
#include <string>
#include <type_traits>

Expand Down Expand Up @@ -158,6 +159,8 @@ class InstrumentationScope
InstrumentationScopeAttributes attributes_;
};

template <class T>
using ScopeConfigurator = std::function<T(const InstrumentationScope &)>;
Comment on lines +162 to +163
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of comments.

(1)

ScopeConfigurator should be an abstract class, with a virtual configure() method.

This will help to define different kinds of configurators later, including the various builtins helpers.

(2)

One of the configurators is the always enabled one, so implement a AlwaysOnScopeConfigurator sub class.

Have a static AlwaysOnScopeConfigurator::Create() method, to return an instance.

(3)

Likewise, AlwaysOffScopeConfigurator::Create()

(4)

All this should be in dedicated headers, so instrumentation_scope.h should be unchanged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I had a class before, but removed since it was easily replaceable by a function. I was expecting to put the built-in helpers in a static utility class, but this approach might look better.

I will give this a try.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the mean time, the spec configuration repository is defining the yaml bits for this:

Instead of "AlwaysOn" / "AlwaysOff", the following is probably better:

class ScopeConfigurator;
class DefaultScopeConfigurator : public ScopeConfigurator;

and later

class NamedScopeConfigurator : public ScopeConfigurator; (for name matching)

} // namespace instrumentationscope
} // namespace sdk

Expand Down
4 changes: 4 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
#include "opentelemetry/sdk/trace/id_generator.h"
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
#include "opentelemetry/sdk/trace/tracer_context.h"
#include "opentelemetry/trace/noop.h"
#include "opentelemetry/trace/span.h"
#include "opentelemetry/trace/span_context_kv_iterable.h"
#include "opentelemetry/trace/span_startoptions.h"
Expand Down Expand Up @@ -103,8 +105,10 @@ class Tracer final : public opentelemetry::trace::Tracer,
private:
// order of declaration is important here - instrumentation scope should destroy after
// tracer-context.
std::unique_ptr<TracerConfig> tracer_config_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with:

TracerConfig tracer_config_;

There is no need to allocate memory and use a unique pointer, the tracer can own it's own configuration directly.

std::shared_ptr<InstrumentationScope> instrumentation_scope_;
std::shared_ptr<TracerContext> context_;
static const std::shared_ptr<opentelemetry::trace::NoopTracer> kNoopTracer;
};
} // namespace trace
} // namespace sdk
Expand Down
71 changes: 71 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/tracer_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{
/**
* TracerConfig defines various configurable aspects of a Tracer's behavior.
* This class should not be used directly to configure a Tracer's behavior, instead a
* ScopeConfigurator should be used to compute the desired TracerConfig which can then be used to
* configure a Tracer.
*/
class TracerConfig
{
public:
bool operator==(const TracerConfig &other) const noexcept;

/**
* Returns if the Tracer is enabled or disabled. Tracers are enabled by default.
* @return a boolean indicating if the Tracer is enabled. Defaults to true.
*/
bool IsEnabled() const noexcept;

/**
* Returns a TracerConfig that represents a disabled Tracer. A disabled tracer behaves like a
* no-op tracer.
* @return a static constant TracerConfig that represents a disabled tracer.
*/
static TracerConfig Disabled();

/**
* Returns a TracerConfig that represents an enabled Tracer.
* @return a static constant TracerConfig that represents an enabled tracer.
*/
static TracerConfig Enabled();

/**
* Returns a TracerConfig that represents a Tracer configured with the default behavior.
* The default behavior is guided by the OpenTelemetry specification.
* @return a static constant TracerConfig that represents a tracer configured with default
* behavior.
*/
static TracerConfig Default();

/**
* Returns a ScopeConfigurator that can be used to get the default tracer configurator.
* A tracer configurator computes TracerConfig based on the InstrumentationScope. The default
* tracer configurator unconditionally computes the default TracerConfig for all scopes.
*
* @return a static constant TracerConfig that represents a tracer configurator with default
* behavior.
*/
static const instrumentationscope::ScopeConfigurator<TracerConfig> &DefaultConfigurator();
Comment on lines +52 to +60
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To move in a separate class.

A TracerConfig represents a configuration.

This class should not know how / when / by whom a configuration is created, and should not even know of ScopeConfigurator.


private:
explicit TracerConfig(const bool disabled = false) : disabled_(disabled) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use default values = false.

The choice of the default value should not be nested deep in the code, but bubble up in the call stack, up to where the decision is made to use a default.

This will be in the tracer provider factory, when no scope configurator is provided.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a private constructor, so it cannot be called externally and the value of the default is dictated by the spec.

bool disabled_;
static const TracerConfig kDefaultConfig;
static const TracerConfig kDisabledConfig;
static const instrumentationscope::ScopeConfigurator<TracerConfig> kDefaultTracerConfigurator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove kDefaultTracerConfigurator.

};
} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
11 changes: 10 additions & 1 deletion sdk/include/opentelemetry/sdk/trace/tracer_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "opentelemetry/sdk/trace/random_id_generator.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/samplers/always_on.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand All @@ -21,6 +22,8 @@ namespace sdk
namespace trace
{

using namespace opentelemetry::sdk::instrumentationscope;

/**
* A class which stores the TracerProvider context.
*
Expand All @@ -43,7 +46,10 @@ class TracerContext
opentelemetry::sdk::resource::Resource::Create({}),
std::unique_ptr<Sampler> sampler = std::unique_ptr<AlwaysOnSampler>(new AlwaysOnSampler),
std::unique_ptr<IdGenerator> id_generator =
std::unique_ptr<IdGenerator>(new RandomIdGenerator())) noexcept;
std::unique_ptr<IdGenerator>(new RandomIdGenerator()),
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator =
std::make_unique<instrumentationscope::ScopeConfigurator<TracerConfig>>(
TracerConfig::DefaultConfigurator())) noexcept;
Comment on lines +50 to +52
Copy link
Member

@marcalff marcalff Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with (assuming the default is enabled):

tracer_configurator = AlwaysOnScopeConfigurator::Create();

Likewise in other places.


virtual ~TracerContext() = default;

Expand Down Expand Up @@ -77,6 +83,8 @@ class TracerContext
*/
const opentelemetry::sdk::resource::Resource &GetResource() const noexcept;

instrumentationscope::ScopeConfigurator<TracerConfig> &GetTracerConfigurator() const noexcept;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type should probably be a const reference, not just a reference.


/**
* Obtain the Id Generator associated with this tracer context.
* @return The ID Generator for this tracer context.
Expand All @@ -100,6 +108,7 @@ class TracerContext
std::unique_ptr<Sampler> sampler_;
std::unique_ptr<IdGenerator> id_generator_;
std::unique_ptr<SpanProcessor> processor_;
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator_;
};

} // namespace trace
Expand Down
10 changes: 10 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/tracer_context_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ class OPENTELEMETRY_EXPORT TracerContextFactory
const opentelemetry::sdk::resource::Resource &resource,
std::unique_ptr<Sampler> sampler,
std::unique_ptr<IdGenerator> id_generator);

/**
* Create a TracerContext.
*/
static std::unique_ptr<TracerContext> Create(
std::vector<std::unique_ptr<SpanProcessor>> &&processors,
const opentelemetry::sdk::resource::Resource &resource,
std::unique_ptr<Sampler> sampler,
std::unique_ptr<IdGenerator> id_generator,
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator);
};

} // namespace trace
Expand Down
14 changes: 12 additions & 2 deletions sdk/include/opentelemetry/sdk/trace/tracer_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace sdk
namespace trace
{

using namespace opentelemetry::sdk::instrumentationscope;

class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::TracerProvider
{
public:
Expand All @@ -39,22 +41,30 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T
* not be a nullptr.
* @param id_generator The custom id generator for this tracer provider. This must
* not be a nullptr
* @param tracer_configurator Provides access to a function that computes the TracerConfig for
* Tracers provided by this TracerProvider.
*/
explicit TracerProvider(
std::unique_ptr<SpanProcessor> processor,
const opentelemetry::sdk::resource::Resource &resource =
opentelemetry::sdk::resource::Resource::Create({}),
std::unique_ptr<Sampler> sampler = std::unique_ptr<AlwaysOnSampler>(new AlwaysOnSampler),
std::unique_ptr<IdGenerator> id_generator =
std::unique_ptr<IdGenerator>(new RandomIdGenerator())) noexcept;
std::unique_ptr<IdGenerator>(new RandomIdGenerator()),
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator =
std::make_unique<instrumentationscope::ScopeConfigurator<TracerConfig>>(
TracerConfig::DefaultConfigurator())) noexcept;

explicit TracerProvider(
std::vector<std::unique_ptr<SpanProcessor>> &&processors,
const opentelemetry::sdk::resource::Resource &resource =
opentelemetry::sdk::resource::Resource::Create({}),
std::unique_ptr<Sampler> sampler = std::unique_ptr<AlwaysOnSampler>(new AlwaysOnSampler),
std::unique_ptr<IdGenerator> id_generator =
std::unique_ptr<IdGenerator>(new RandomIdGenerator())) noexcept;
std::unique_ptr<IdGenerator>(new RandomIdGenerator()),
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator =
std::make_unique<instrumentationscope::ScopeConfigurator<TracerConfig>>(
TracerConfig::DefaultConfigurator())) noexcept;

/**
* Initialize a new tracer provider with a specified context
Expand Down
18 changes: 16 additions & 2 deletions sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace trace
class OPENTELEMETRY_EXPORT TracerProviderFactory
{
public:
/* Serie of builders with a single processor. */
/* Series of creator methods with a single processor. */

static std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> Create(
std::unique_ptr<SpanProcessor> processor);
Expand All @@ -48,7 +48,14 @@ class OPENTELEMETRY_EXPORT TracerProviderFactory
std::unique_ptr<Sampler> sampler,
std::unique_ptr<IdGenerator> id_generator);

/* Serie of builders with a vector of processor. */
static std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> Create(
std::unique_ptr<SpanProcessor> processor,
const opentelemetry::sdk::resource::Resource &resource,
std::unique_ptr<Sampler> sampler,
std::unique_ptr<IdGenerator> id_generator,
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator);

/* Series of creator methods with a single processor. */

static std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> Create(
std::vector<std::unique_ptr<SpanProcessor>> &&processors);
Expand All @@ -68,6 +75,13 @@ class OPENTELEMETRY_EXPORT TracerProviderFactory
std::unique_ptr<Sampler> sampler,
std::unique_ptr<IdGenerator> id_generator);

static std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> Create(
std::vector<std::unique_ptr<SpanProcessor>> &&processors,
const opentelemetry::sdk::resource::Resource &resource,
std::unique_ptr<Sampler> sampler,
std::unique_ptr<IdGenerator> id_generator,
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator);

/* Create with a tracer context. */

static std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> Create(
Expand Down
3 changes: 2 additions & 1 deletion sdk/src/trace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ add_library(
samplers/trace_id_ratio.cc
samplers/trace_id_ratio_factory.cc
random_id_generator.cc
random_id_generator_factory.cc)
random_id_generator_factory.cc
tracer_config.cc)

set_target_properties(opentelemetry_trace PROPERTIES EXPORT_NAME trace)
set_target_version(opentelemetry_trace)
Expand Down
13 changes: 12 additions & 1 deletion sdk/src/trace/tracer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <stdint.h>
#include <chrono>
#include <functional>
#include <map>
#include <new>
#include <utility>
Expand All @@ -16,6 +17,7 @@
#include "opentelemetry/sdk/trace/id_generator.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/tracer.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
#include "opentelemetry/sdk/trace/tracer_context.h"
#include "opentelemetry/trace/context.h"
#include "opentelemetry/trace/noop.h"
Expand All @@ -36,11 +38,16 @@ namespace sdk
{
namespace trace
{
const std::shared_ptr<opentelemetry::trace::NoopTracer> Tracer::kNoopTracer =
std::make_shared<opentelemetry::trace::NoopTracer>();
Comment on lines +41 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a shared_ptr ?

This memory will never be reclaimed.

This should be enough:

opentelemetry::trace::NoopTracer kNoopTracer();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to avoid making multiple copies of NoopTracer, but looking at it again, shared_ptr might actually have more overhead due to ref counts.


Tracer::Tracer(std::shared_ptr<TracerContext> context,
std::unique_ptr<InstrumentationScope> instrumentation_scope) noexcept
: instrumentation_scope_{std::move(instrumentation_scope)}, context_{std::move(context)}
{}
{
tracer_config_ =
std::make_unique<TracerConfig>(context_->GetTracerConfigurator()(*instrumentation_scope_));
}

nostd::shared_ptr<opentelemetry::trace::Span> Tracer::StartSpan(
nostd::string_view name,
Expand All @@ -51,6 +58,10 @@ nostd::shared_ptr<opentelemetry::trace::Span> Tracer::StartSpan(
opentelemetry::trace::SpanContext parent_context = GetCurrentSpan()->GetContext();
if (nostd::holds_alternative<opentelemetry::trace::SpanContext>(options.parent))
{
if (!tracer_config_->IsEnabled())
{
return kNoopTracer->StartSpan(name, attributes, links, options);
}
Comment on lines +61 to +64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my understanding, this should be moved earlier, not done only for the SpanContext alternative.

If the tracer is disabled, it is disabled for all types of spans.

auto span_context = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
if (span_context.IsValid())
{
Expand Down
52 changes: 52 additions & 0 deletions sdk/src/trace/tracer_config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include "opentelemetry/sdk/trace/tracer_config.h"
#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{

const TracerConfig TracerConfig::kDefaultConfig = TracerConfig();
const TracerConfig TracerConfig::kDisabledConfig = TracerConfig(true);

const instrumentationscope::ScopeConfigurator<TracerConfig>
TracerConfig::kDefaultTracerConfigurator =
[](const instrumentationscope::InstrumentationScope &) { return Default(); };

TracerConfig TracerConfig::Disabled()
{
return kDisabledConfig;
}

TracerConfig TracerConfig::Enabled()
{
return kDefaultConfig;
}

TracerConfig TracerConfig::Default()
{
return kDefaultConfig;
}

const instrumentationscope::ScopeConfigurator<TracerConfig> &TracerConfig::DefaultConfigurator()
{
return kDefaultTracerConfigurator;
}

bool TracerConfig::IsEnabled() const noexcept
{
return !disabled_;
}

bool TracerConfig::operator==(const TracerConfig &other) const noexcept
{
return disabled_ == other.disabled_;
}

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
Loading