fix(telemetry): Name the key when a [telemetry] on/off value is invalid

The seven on/off keys were read as integers, so a value such as
enabled=true threw boost::bad_lexical_cast with no key in the message.
They now go through readFlag, which accepts 0, 1, true and false in any
case and otherwise throws a message naming the key. Startup still stops
on a bad value, so the operator has to fix it.
This commit is contained in:
Pratik Mankawde
2026-09-30 22:36:47 +01:00
parent 1a896804b8
commit 255db81e53
2 changed files with 42 additions and 7 deletions

View File

@@ -1698,6 +1698,8 @@ validators.txt
# enabled=0
#
# Enable or disable telemetry at runtime. Default: 0 (disabled).
# This key, use_tls and the trace_* keys take 0, 1, true or false, in
# any case. Any other value is a startup error that names the key.
#
# service_name=xrpld
#

View File

@@ -9,6 +9,7 @@
*/
#include <xrpl/basics/contract.h>
#include <xrpl/beast/core/LexicalCast.h>
#include <xrpl/config/BasicConfig.h>
#include <xrpl/telemetry/Telemetry.h>
@@ -18,6 +19,7 @@
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
namespace xrpl::telemetry {
@@ -129,6 +131,37 @@ readBounded(
return static_cast<std::uint32_t>(*parsed);
}
/**
* Read an on/off key. Accepts 0, 1, true or false, in any case.
*
* Reading it as a number would throw boost::bad_lexical_cast for "true",
* naming no key. This reads the text and names the key instead.
*
* @param section The [telemetry] section to read from.
* @param name Key to read, as documented in cfg/xrpld-example.cfg.
* @param absentValue Value returned when the key is absent.
* @return The configured value, or absentValue if the key is absent.
* @throws std::runtime_error For any other value, with a message naming the
* key.
*/
[[nodiscard]] bool
readFlag(Section const& section, std::string_view name, bool absentValue)
{
std::string const keyName{name};
auto const text = section.get(keyName);
if (!text)
return absentValue;
bool flag = false;
if (!beast::lexicalCastChecked(flag, *text))
{
Throw<std::runtime_error>(
"Invalid value '" + keyName + "' in " + kSectionLabel +
": must be 0, 1, true or false.");
}
return flag;
}
/**
* Derive a human-readable network type label from the numeric network ID.
* @param networkId The network identifier from [network_id] config.
@@ -161,14 +194,14 @@ makeTelemetrySetup(
{
Telemetry::Setup setup;
setup.enabled = section.valueOr<int>(key::enabled, 0) != 0;
setup.enabled = readFlag(section, key::enabled, false);
setup.serviceName = section.valueOr<std::string>(key::serviceName, dflt::serviceName);
setup.serviceVersion = version;
setup.serviceInstanceId = section.valueOr<std::string>(key::serviceInstanceId, nodePublicKey);
setup.tracesEndpoint = section.valueOr<std::string>(key::tracesEndpoint, dflt::tracesEndpoint);
setup.useTls = section.valueOr<int>(key::useTls, 0) != 0;
setup.useTls = readFlag(section, key::useTls, false);
setup.tlsCertPath = section.valueOr<std::string>(key::tlsCaCert, "");
// Head sampling is intentionally fixed at 1.0 (sample everything) and is
@@ -197,11 +230,11 @@ makeTelemetrySetup(
setup.networkId = networkId;
setup.networkType = networkTypeFromId(networkId);
setup.traceTransactions = section.valueOr<int>(key::traceTransactions, 1) != 0;
setup.traceConsensus = section.valueOr<int>(key::traceConsensus, 1) != 0;
setup.traceRpc = section.valueOr<int>(key::traceRpc, 1) != 0;
setup.tracePeer = section.valueOr<int>(key::tracePeer, 1) != 0;
setup.traceLedger = section.valueOr<int>(key::traceLedger, 1) != 0;
setup.traceTransactions = readFlag(section, key::traceTransactions, true);
setup.traceConsensus = readFlag(section, key::traceConsensus, true);
setup.traceRpc = readFlag(section, key::traceRpc, true);
setup.tracePeer = readFlag(section, key::tracePeer, true);
setup.traceLedger = readFlag(section, key::traceLedger, true);
return setup;
}