mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-05 09:46:53 +00:00
feat(telemetry): add RPC trace filters and SpanGuard unit tests
- Grafana Tempo datasource: add rpc-command, rpc-status, rpc-role search filters for the Explore UI - Unit tests: TelemetryConfig (config parsing defaults and sections), SpanGuardFactory (null guard safety, move semantics, discard, all factory methods) - Test CMake registration with optional OTel linking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
# Search filters provide pre-configured dropdowns in the Explore UI.
|
||||
# Each phase adds filters for the span attributes it introduces.
|
||||
# Phase 1b (infra): Base filters — node identity, service, span name, status.
|
||||
# Phase 2 (RPC): RPC command, status, role filters.
|
||||
|
||||
apiVersion: 1
|
||||
|
||||
@@ -89,3 +90,19 @@ datasources:
|
||||
operator: ">"
|
||||
scope: intrinsic
|
||||
type: static
|
||||
# Phase 2: RPC tracing filters
|
||||
- id: rpc-command
|
||||
tag: xrpl.rpc.command
|
||||
operator: "="
|
||||
scope: span
|
||||
type: static
|
||||
- id: rpc-status
|
||||
tag: xrpl.rpc.status
|
||||
operator: "="
|
||||
scope: span
|
||||
type: dynamic
|
||||
- id: rpc-role
|
||||
tag: xrpl.rpc.role
|
||||
operator: "="
|
||||
scope: span
|
||||
type: dynamic
|
||||
|
||||
@@ -42,3 +42,14 @@ if(NOT WIN32)
|
||||
target_link_libraries(xrpl.test.net PRIVATE xrpl.imports.test)
|
||||
add_dependencies(xrpl.tests xrpl.test.net)
|
||||
endif()
|
||||
|
||||
xrpl_add_test(telemetry)
|
||||
target_link_libraries(xrpl.test.telemetry PRIVATE xrpl.imports.test)
|
||||
target_include_directories(xrpl.test.telemetry PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
if(telemetry)
|
||||
target_link_libraries(
|
||||
xrpl.test.telemetry
|
||||
PRIVATE opentelemetry-cpp::opentelemetry-cpp
|
||||
)
|
||||
endif()
|
||||
add_dependencies(xrpl.tests xrpl.test.telemetry)
|
||||
|
||||
77
src/tests/libxrpl/telemetry/SpanGuardFactory.cpp
Normal file
77
src/tests/libxrpl/telemetry/SpanGuardFactory.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <xrpl/telemetry/SpanGuard.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
using namespace xrpl::telemetry;
|
||||
|
||||
TEST(SpanGuardFactory, null_guard_methods_are_safe)
|
||||
{
|
||||
auto span = SpanGuard::span("nonexistent.span");
|
||||
EXPECT_FALSE(span);
|
||||
|
||||
span.setAttribute("key", "value");
|
||||
span.setAttribute("int_key", static_cast<int64_t>(42));
|
||||
span.setAttribute("bool_key", true);
|
||||
span.setOk();
|
||||
span.setError("test");
|
||||
span.addEvent("event");
|
||||
}
|
||||
|
||||
TEST(SpanGuardFactory, category_span_returns_null_when_disabled)
|
||||
{
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "test");
|
||||
EXPECT_FALSE(span);
|
||||
|
||||
span.setAttribute("xrpl.rpc.command", "test");
|
||||
span.setAttribute("xrpl.rpc.status", "success");
|
||||
}
|
||||
|
||||
TEST(SpanGuardFactory, child_span_null_when_no_parent)
|
||||
{
|
||||
auto span = SpanGuard::span("parent.test");
|
||||
auto child = span.childSpan("child.test");
|
||||
EXPECT_FALSE(child);
|
||||
}
|
||||
|
||||
TEST(SpanGuardFactory, linked_span_null_when_no_context)
|
||||
{
|
||||
auto span = SpanGuard::span("source.test");
|
||||
auto linked = span.linkedSpan("linked.test");
|
||||
EXPECT_FALSE(linked);
|
||||
}
|
||||
|
||||
TEST(SpanGuardFactory, capture_context_returns_invalid_on_null)
|
||||
{
|
||||
auto span = SpanGuard::span("ctx.test");
|
||||
auto ctx = span.captureContext();
|
||||
EXPECT_FALSE(ctx.isValid());
|
||||
}
|
||||
|
||||
TEST(SpanGuardFactory, move_construction_transfers_ownership)
|
||||
{
|
||||
auto span = SpanGuard::span("move.test");
|
||||
auto moved = std::move(span);
|
||||
EXPECT_FALSE(span);
|
||||
moved.setAttribute("key", "value");
|
||||
}
|
||||
|
||||
TEST(SpanGuardFactory, record_exception_safe_on_null)
|
||||
{
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc.command", "test");
|
||||
try
|
||||
{
|
||||
throw std::runtime_error("test error");
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
span.recordException(e);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SpanGuardFactory, discard_safe_on_null)
|
||||
{
|
||||
auto span = SpanGuard::span(TraceCategory::Transactions, "tx", "process");
|
||||
span.discard();
|
||||
EXPECT_FALSE(span);
|
||||
}
|
||||
111
src/tests/libxrpl/telemetry/TelemetryConfig.cpp
Normal file
111
src/tests/libxrpl/telemetry/TelemetryConfig.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include <xrpl/basics/BasicConfig.h>
|
||||
#include <xrpl/telemetry/Telemetry.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST(TelemetryConfig, setup_defaults)
|
||||
{
|
||||
telemetry::Telemetry::Setup s;
|
||||
EXPECT_FALSE(s.enabled);
|
||||
EXPECT_EQ(s.serviceName, "rippled");
|
||||
EXPECT_TRUE(s.serviceVersion.empty());
|
||||
EXPECT_TRUE(s.serviceInstanceId.empty());
|
||||
EXPECT_EQ(s.exporterType, "otlp_http");
|
||||
EXPECT_EQ(s.exporterEndpoint, "http://localhost:4318/v1/traces");
|
||||
EXPECT_FALSE(s.useTls);
|
||||
EXPECT_TRUE(s.tlsCertPath.empty());
|
||||
EXPECT_DOUBLE_EQ(s.samplingRatio, 1.0);
|
||||
EXPECT_EQ(s.batchSize, 512u);
|
||||
EXPECT_EQ(s.batchDelay, std::chrono::milliseconds{5000});
|
||||
EXPECT_EQ(s.maxQueueSize, 2048u);
|
||||
EXPECT_EQ(s.networkId, 0u);
|
||||
EXPECT_EQ(s.networkType, "mainnet");
|
||||
EXPECT_TRUE(s.traceTransactions);
|
||||
EXPECT_TRUE(s.traceConsensus);
|
||||
EXPECT_TRUE(s.traceRpc);
|
||||
EXPECT_FALSE(s.tracePeer);
|
||||
EXPECT_TRUE(s.traceLedger);
|
||||
}
|
||||
|
||||
TEST(TelemetryConfig, parse_empty_section)
|
||||
{
|
||||
Section section;
|
||||
auto setup = telemetry::setup_Telemetry(section, "nHUtest123", "2.0.0");
|
||||
|
||||
EXPECT_FALSE(setup.enabled);
|
||||
EXPECT_EQ(setup.serviceName, "rippled");
|
||||
EXPECT_EQ(setup.serviceVersion, "2.0.0");
|
||||
EXPECT_EQ(setup.serviceInstanceId, "nHUtest123");
|
||||
EXPECT_EQ(setup.exporterType, "otlp_http");
|
||||
EXPECT_DOUBLE_EQ(setup.samplingRatio, 1.0);
|
||||
EXPECT_TRUE(setup.traceRpc);
|
||||
EXPECT_TRUE(setup.traceTransactions);
|
||||
EXPECT_TRUE(setup.traceConsensus);
|
||||
EXPECT_FALSE(setup.tracePeer);
|
||||
EXPECT_TRUE(setup.traceLedger);
|
||||
}
|
||||
|
||||
TEST(TelemetryConfig, parse_full_section)
|
||||
{
|
||||
Section section;
|
||||
section.set("enabled", "1");
|
||||
section.set("service_name", "my-rippled");
|
||||
section.set("service_instance_id", "custom-id");
|
||||
section.set("exporter", "otlp_http");
|
||||
section.set("endpoint", "http://collector:4318/v1/traces");
|
||||
section.set("use_tls", "1");
|
||||
section.set("tls_ca_cert", "/etc/ssl/ca.pem");
|
||||
section.set("sampling_ratio", "0.5");
|
||||
section.set("batch_size", "256");
|
||||
section.set("batch_delay_ms", "3000");
|
||||
section.set("max_queue_size", "4096");
|
||||
section.set("trace_transactions", "0");
|
||||
section.set("trace_consensus", "0");
|
||||
section.set("trace_rpc", "1");
|
||||
section.set("trace_peer", "1");
|
||||
section.set("trace_ledger", "0");
|
||||
|
||||
auto setup = telemetry::setup_Telemetry(section, "nHUtest123", "2.0.0");
|
||||
|
||||
EXPECT_TRUE(setup.enabled);
|
||||
EXPECT_EQ(setup.serviceName, "my-rippled");
|
||||
EXPECT_EQ(setup.serviceInstanceId, "custom-id");
|
||||
EXPECT_EQ(setup.exporterType, "otlp_http");
|
||||
EXPECT_EQ(setup.exporterEndpoint, "http://collector:4318/v1/traces");
|
||||
EXPECT_TRUE(setup.useTls);
|
||||
EXPECT_EQ(setup.tlsCertPath, "/etc/ssl/ca.pem");
|
||||
EXPECT_DOUBLE_EQ(setup.samplingRatio, 0.5);
|
||||
EXPECT_EQ(setup.batchSize, 256u);
|
||||
EXPECT_EQ(setup.batchDelay, std::chrono::milliseconds{3000});
|
||||
EXPECT_EQ(setup.maxQueueSize, 4096u);
|
||||
EXPECT_FALSE(setup.traceTransactions);
|
||||
EXPECT_FALSE(setup.traceConsensus);
|
||||
EXPECT_TRUE(setup.traceRpc);
|
||||
EXPECT_TRUE(setup.tracePeer);
|
||||
EXPECT_FALSE(setup.traceLedger);
|
||||
}
|
||||
|
||||
TEST(TelemetryConfig, null_telemetry_factory)
|
||||
{
|
||||
telemetry::Telemetry::Setup setup;
|
||||
setup.enabled = false;
|
||||
|
||||
beast::Journal::Sink& sink = beast::Journal::getNullSink();
|
||||
beast::Journal j(sink);
|
||||
auto tel = telemetry::make_Telemetry(setup, j);
|
||||
EXPECT_TRUE(tel != nullptr);
|
||||
EXPECT_FALSE(tel->isEnabled());
|
||||
EXPECT_FALSE(tel->shouldTraceRpc());
|
||||
EXPECT_FALSE(tel->shouldTraceTransactions());
|
||||
EXPECT_FALSE(tel->shouldTraceConsensus());
|
||||
EXPECT_FALSE(tel->shouldTracePeer());
|
||||
EXPECT_FALSE(tel->shouldTraceLedger());
|
||||
|
||||
// start/stop should be no-ops without crashing
|
||||
tel->start();
|
||||
tel->stop();
|
||||
}
|
||||
8
src/tests/libxrpl/telemetry/main.cpp
Normal file
8
src/tests/libxrpl/telemetry/main.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in New Issue
Block a user