mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-01 10:12:30 +00:00
Document known issues with inline TODO comments: - Thread safety of TelemetryImpl::stop() vs startSpan() in Telemetry.cpp and Application.cpp shutdown sequence - Unwired propagation utilities in TraceContextPropagator.h - Unused trace_state proto field in xrpl.proto Add "Known Issues / Future Work" sections to Phase 2 and Phase 3 task lists covering: thread safety, macro incompatibility between XRPL_TRACE_SPAN and XRPL_TRACE_SET_ATTR, unwired P2P propagation, and unused trace_state field. Add "reqps" to cspell dictionary and sort trailing entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
299 lines
8.9 KiB
C++
299 lines
8.9 KiB
C++
/** OpenTelemetry SDK implementation of the Telemetry interface.
|
|
|
|
Compiled only when XRPL_ENABLE_TELEMETRY is defined (via CMake
|
|
telemetry=ON). Contains:
|
|
|
|
- TelemetryImpl: configures the OTel SDK with an OTLP/HTTP exporter,
|
|
batch span processor, trace-ID-ratio sampler, and resource attributes.
|
|
- NullTelemetryOtel: no-op fallback used when telemetry is compiled in
|
|
but disabled at runtime (enabled=0 in config).
|
|
- make_Telemetry(): factory that selects the appropriate implementation.
|
|
*/
|
|
|
|
#ifdef XRPL_ENABLE_TELEMETRY
|
|
|
|
#include <xrpl/basics/Log.h>
|
|
#include <xrpl/telemetry/Telemetry.h>
|
|
|
|
#include <opentelemetry/exporters/otlp/otlp_http_exporter_factory.h>
|
|
#include <opentelemetry/exporters/otlp/otlp_http_exporter_options.h>
|
|
#include <opentelemetry/sdk/resource/semantic_conventions.h>
|
|
#include <opentelemetry/sdk/trace/batch_span_processor_factory.h>
|
|
#include <opentelemetry/sdk/trace/batch_span_processor_options.h>
|
|
#include <opentelemetry/sdk/trace/sampler.h>
|
|
#include <opentelemetry/sdk/trace/samplers/trace_id_ratio.h>
|
|
#include <opentelemetry/sdk/trace/tracer_provider.h>
|
|
#include <opentelemetry/sdk/trace/tracer_provider_factory.h>
|
|
#include <opentelemetry/trace/noop.h>
|
|
#include <opentelemetry/trace/provider.h>
|
|
|
|
namespace xrpl {
|
|
namespace telemetry {
|
|
|
|
namespace {
|
|
|
|
namespace trace_api = opentelemetry::trace;
|
|
namespace trace_sdk = opentelemetry::sdk::trace;
|
|
namespace otlp_http = opentelemetry::exporter::otlp;
|
|
namespace resource = opentelemetry::sdk::resource;
|
|
|
|
/** No-op implementation used when XRPL_ENABLE_TELEMETRY is defined but
|
|
setup.enabled is false at runtime.
|
|
|
|
Lives in the anonymous namespace so there is no ODR conflict with the
|
|
NullTelemetry in NullTelemetry.cpp.
|
|
*/
|
|
class NullTelemetryOtel : public Telemetry
|
|
{
|
|
/** Retained configuration (unused, kept for diagnostic access). */
|
|
Setup const setup_;
|
|
|
|
public:
|
|
explicit NullTelemetryOtel(Setup const& setup) : setup_(setup)
|
|
{
|
|
}
|
|
|
|
void
|
|
start() override
|
|
{
|
|
}
|
|
|
|
void
|
|
stop() override
|
|
{
|
|
}
|
|
|
|
bool
|
|
isEnabled() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
shouldTraceTransactions() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
shouldTraceConsensus() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
shouldTraceRpc() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
shouldTracePeer() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
shouldTraceLedger() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
opentelemetry::nostd::shared_ptr<trace_api::Tracer>
|
|
getTracer(std::string_view) override
|
|
{
|
|
static auto noopTracer =
|
|
opentelemetry::nostd::shared_ptr<trace_api::Tracer>(new trace_api::NoopTracer());
|
|
return noopTracer;
|
|
}
|
|
|
|
opentelemetry::nostd::shared_ptr<trace_api::Span>
|
|
startSpan(std::string_view, trace_api::SpanKind) override
|
|
{
|
|
return opentelemetry::nostd::shared_ptr<trace_api::Span>(new trace_api::NoopSpan(nullptr));
|
|
}
|
|
|
|
opentelemetry::nostd::shared_ptr<trace_api::Span>
|
|
startSpan(std::string_view, opentelemetry::context::Context const&, trace_api::SpanKind)
|
|
override
|
|
{
|
|
return opentelemetry::nostd::shared_ptr<trace_api::Span>(new trace_api::NoopSpan(nullptr));
|
|
}
|
|
};
|
|
|
|
/** Full OTel SDK implementation that exports trace spans via OTLP/HTTP.
|
|
|
|
Configures an OTLP/HTTP exporter, batch span processor,
|
|
TraceIdRatioBasedSampler, and resource attributes on start().
|
|
*/
|
|
class TelemetryImpl : public Telemetry
|
|
{
|
|
/** Configuration from the [telemetry] config section. */
|
|
Setup const setup_;
|
|
|
|
/** Journal used for log output during start/stop. */
|
|
beast::Journal const journal_;
|
|
|
|
/** The SDK TracerProvider that owns the export pipeline.
|
|
|
|
Held as std::shared_ptr so we can call ForceFlush() on shutdown.
|
|
Wrapped in a nostd::shared_ptr when registered as the global provider.
|
|
*/
|
|
std::shared_ptr<trace_sdk::TracerProvider> sdkProvider_;
|
|
|
|
public:
|
|
TelemetryImpl(Setup const& setup, beast::Journal journal) : setup_(setup), journal_(journal)
|
|
{
|
|
}
|
|
|
|
void
|
|
start() override
|
|
{
|
|
JLOG(journal_.info()) << "Telemetry starting: endpoint=" << setup_.exporterEndpoint
|
|
<< " sampling=" << setup_.samplingRatio;
|
|
|
|
// Configure OTLP HTTP exporter
|
|
otlp_http::OtlpHttpExporterOptions exporterOpts;
|
|
exporterOpts.url = setup_.exporterEndpoint;
|
|
if (setup_.useTls)
|
|
exporterOpts.ssl_ca_cert_path = setup_.tlsCertPath;
|
|
|
|
auto exporter = otlp_http::OtlpHttpExporterFactory::Create(exporterOpts);
|
|
|
|
// Configure batch processor
|
|
trace_sdk::BatchSpanProcessorOptions processorOpts;
|
|
processorOpts.max_queue_size = setup_.maxQueueSize;
|
|
processorOpts.schedule_delay_millis = std::chrono::milliseconds(setup_.batchDelay);
|
|
processorOpts.max_export_batch_size = setup_.batchSize;
|
|
|
|
auto processor =
|
|
trace_sdk::BatchSpanProcessorFactory::Create(std::move(exporter), processorOpts);
|
|
|
|
// Configure resource attributes
|
|
auto resourceAttrs = resource::Resource::Create({
|
|
{resource::SemanticConventions::kServiceName, setup_.serviceName},
|
|
{resource::SemanticConventions::kServiceVersion, setup_.serviceVersion},
|
|
{resource::SemanticConventions::kServiceInstanceId, setup_.serviceInstanceId},
|
|
{"xrpl.network.id", static_cast<int64_t>(setup_.networkId)},
|
|
{"xrpl.network.type", setup_.networkType},
|
|
});
|
|
|
|
// Configure sampler
|
|
auto sampler = std::make_unique<trace_sdk::TraceIdRatioBasedSampler>(setup_.samplingRatio);
|
|
|
|
// Create TracerProvider
|
|
sdkProvider_ = trace_sdk::TracerProviderFactory::Create(
|
|
std::move(processor), resourceAttrs, std::move(sampler));
|
|
|
|
// Set as global provider
|
|
trace_api::Provider::SetTracerProvider(
|
|
opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(sdkProvider_));
|
|
|
|
JLOG(journal_.info()) << "Telemetry started successfully";
|
|
}
|
|
|
|
void
|
|
stop() override
|
|
{
|
|
JLOG(journal_.info()) << "Telemetry stopping";
|
|
if (sdkProvider_)
|
|
{
|
|
// Force flush before shutdown
|
|
sdkProvider_->ForceFlush();
|
|
// TODO: sdkProvider_ is not thread-safe. This reset() races with
|
|
// getTracer() if any thread is still calling startSpan().
|
|
// Currently safe because Application::stop() shuts down
|
|
// serverHandler_, overlay_, and jobQueue_ before calling
|
|
// telemetry_->stop() — so no callers should remain. If the
|
|
// shutdown order ever changes, add an std::atomic<bool> stopped_
|
|
// flag checked in getTracer() to make this robust.
|
|
sdkProvider_.reset();
|
|
trace_api::Provider::SetTracerProvider(
|
|
opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(
|
|
new trace_api::NoopTracerProvider()));
|
|
}
|
|
JLOG(journal_.info()) << "Telemetry stopped";
|
|
}
|
|
|
|
bool
|
|
isEnabled() const override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
shouldTraceTransactions() const override
|
|
{
|
|
return setup_.traceTransactions;
|
|
}
|
|
|
|
bool
|
|
shouldTraceConsensus() const override
|
|
{
|
|
return setup_.traceConsensus;
|
|
}
|
|
|
|
bool
|
|
shouldTraceRpc() const override
|
|
{
|
|
return setup_.traceRpc;
|
|
}
|
|
|
|
bool
|
|
shouldTracePeer() const override
|
|
{
|
|
return setup_.tracePeer;
|
|
}
|
|
|
|
bool
|
|
shouldTraceLedger() const override
|
|
{
|
|
return setup_.traceLedger;
|
|
}
|
|
|
|
opentelemetry::nostd::shared_ptr<trace_api::Tracer>
|
|
getTracer(std::string_view name) override
|
|
{
|
|
if (!sdkProvider_)
|
|
return trace_api::Provider::GetTracerProvider()->GetTracer(std::string(name));
|
|
return sdkProvider_->GetTracer(std::string(name));
|
|
}
|
|
|
|
opentelemetry::nostd::shared_ptr<trace_api::Span>
|
|
startSpan(std::string_view name, trace_api::SpanKind kind) override
|
|
{
|
|
auto tracer = getTracer("rippled");
|
|
trace_api::StartSpanOptions opts;
|
|
opts.kind = kind;
|
|
return tracer->StartSpan(std::string(name), opts);
|
|
}
|
|
|
|
opentelemetry::nostd::shared_ptr<trace_api::Span>
|
|
startSpan(
|
|
std::string_view name,
|
|
opentelemetry::context::Context const& parentContext,
|
|
trace_api::SpanKind kind) override
|
|
{
|
|
auto tracer = getTracer("rippled");
|
|
trace_api::StartSpanOptions opts;
|
|
opts.kind = kind;
|
|
opts.parent = parentContext;
|
|
return tracer->StartSpan(std::string(name), opts);
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<Telemetry>
|
|
make_Telemetry(Telemetry::Setup const& setup, beast::Journal journal)
|
|
{
|
|
if (setup.enabled)
|
|
return std::make_unique<TelemetryImpl>(setup, journal);
|
|
return std::make_unique<NullTelemetryOtel>(setup);
|
|
}
|
|
|
|
} // namespace telemetry
|
|
} // namespace xrpl
|
|
|
|
#endif // XRPL_ENABLE_TELEMETRY
|