Skip to content

Commit

Permalink
stats: miscellaneous changes and housekeeping
Browse files Browse the repository at this point in the history
- `timing` cannot contain a negative value, change to `uint32_t`
- `sampling_rate` implies that the probes are sampled at that rate when
   in fact it's sampled every time and simply not broadcasted based on
   a dice roll, renamed to `frequency`
- remove `sendDouble`, use template specialization of `Send` instead,
  that can be reused as `send`
- rename `value` in `count()` to `delta`
- add more comments

`gaugeDouble` has been kept around because utilizing templates for it
would require us to either write template specializations for every kind
of `gauge` value (implicit conversions will not save us from this) or
move all logic to the header, neither option seems desirable.
  • Loading branch information
kwvg committed Sep 11, 2024
1 parent b4e1491 commit 20c419f
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4125,7 +4125,7 @@ void CConnman::RecordBytesRecv(uint64_t bytes)
{
nTotalBytesRecv += bytes;
::g_stats_client->count("bandwidth.bytesReceived", bytes, 0.1f);
::g_stats_client->gauge("bandwidth.totalBytesReceived", nTotalBytesRecv, 0.01f);
::g_stats_client->gauge("bandwidth.totalBytesReceived", nTotalBytesRecv.load(), 0.01f);
}

void CConnman::RecordBytesSent(uint64_t bytes)
Expand Down
46 changes: 29 additions & 17 deletions src/stats/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ static constexpr float EPSILON{0.0001f};
static constexpr char STATSD_MSG_DELIMITER{'\n'};
/** Delimiter segmenting namespaces in a Statsd key */
static constexpr char STATSD_NS_DELIMITER{'.'};
/** Character used to denote Statsd message type as count */
static constexpr char STATSD_METRIC_COUNT[]{"c"};
/** Character used to denote Statsd message type as gauge */
static constexpr char STATSD_METRIC_GAUGE[]{"g"};
/** Characters used to denote Statsd message type as timing */
static constexpr char STATSD_METRIC_TIMING[]{"ms"};
} // anonymous namespace

std::unique_ptr<StatsdClient> g_stats_client;
Expand Down Expand Up @@ -141,50 +147,50 @@ StatsdClient::StatsdClient(const std::string& host, uint16_t port, uint64_t batc
LogPrintf("StatsdClient initialized to transmit stats to %s:%d\n", host, port);
}

bool StatsdClient::dec(const std::string& key, float sample_rate) { return count(key, -1, sample_rate); }
bool StatsdClient::dec(const std::string& key, float frequency) { return count(key, -1, frequency); }

bool StatsdClient::inc(const std::string& key, float sample_rate) { return count(key, 1, sample_rate); }
bool StatsdClient::inc(const std::string& key, float frequency) { return count(key, 1, frequency); }

bool StatsdClient::count(const std::string& key, int64_t value, float sample_rate)
bool StatsdClient::count(const std::string& key, int64_t delta, float frequency)
{
return send(key, value, "c", sample_rate);
return send(key, delta, STATSD_METRIC_COUNT, frequency);
}

bool StatsdClient::gauge(const std::string& key, int64_t value, float sample_rate)
bool StatsdClient::gauge(const std::string& key, int64_t value, float frequency)
{
return send(key, value, "g", sample_rate);
return send(key, value, STATSD_METRIC_GAUGE, frequency);
}

bool StatsdClient::gaugeDouble(const std::string& key, double value, float sample_rate)
bool StatsdClient::gaugeDouble(const std::string& key, double value, float frequency)
{
return sendDouble(key, value, "g", sample_rate);
return send(key, value, STATSD_METRIC_GAUGE, frequency);
}

bool StatsdClient::timing(const std::string& key, int64_t ms, float sample_rate)
bool StatsdClient::timing(const std::string& key, uint64_t ms, float frequency)
{
return send(key, ms, "ms", sample_rate);
return send(key, ms, STATSD_METRIC_TIMING, frequency);
}

template <typename T1>
bool StatsdClient::Send(const std::string& key, T1 value, const std::string& type, float sample_rate)
bool StatsdClient::send(const std::string& key, T1 value, const std::string& type, float frequency)
{
if (!m_sender) {
return false;
}

// Determine if we should send the message at all but claim that we did even if we don't
sample_rate = std::clamp(sample_rate, 0.f, 1.f);
bool always_send = std::fabs(sample_rate - 1.f) < EPSILON;
bool never_send = std::fabs(sample_rate) < EPSILON;
frequency = std::clamp(frequency, 0.f, 1.f);
bool always_send = std::fabs(frequency - 1.f) < EPSILON;
bool never_send = std::fabs(frequency) < EPSILON;
if (never_send || (!always_send &&
WITH_LOCK(cs, return sample_rate < std::uniform_real_distribution<float>(0.f, 1.f)(insecure_rand)))) {
WITH_LOCK(cs, return frequency < std::uniform_real_distribution<float>(0.f, 1.f)(insecure_rand)))) {
return true;
}

// Construct the message and if our message isn't always-send, report the sample rate
std::string buf{strprintf("%s%s%s:%s|%s", m_prefix, key, m_suffix, ToString(value), type)};
if (sample_rate < 1.f) {
buf += strprintf("|@%.2f", sample_rate);
if (frequency < 1.f) {
buf += strprintf("|@%.2f", frequency);
}

// Send it and report an error if we encounter one
Expand All @@ -195,3 +201,9 @@ bool StatsdClient::Send(const std::string& key, T1 value, const std::string& typ

return true;
}

template bool StatsdClient::send(const std::string& key, const double value, const std::string& type, float frequency);
template bool StatsdClient::send(const std::string& key, const int32_t value, const std::string& type, float frequency);
template bool StatsdClient::send(const std::string& key, const int64_t value, const std::string& type, float frequency);
template bool StatsdClient::send(const std::string& key, const uint32_t value, const std::string& type, float frequency);
template bool StatsdClient::send(const std::string& key, const uint64_t value, const std::string& type, float frequency);
45 changes: 23 additions & 22 deletions src/stats/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@

class ArgsManager;

/** Default port used to connect to a Statsd server */
static constexpr uint16_t DEFAULT_STATSD_PORT{8125};
/** Default host assumed to be running a Statsd server */
static const std::string DEFAULT_STATSD_HOST{"127.0.0.1"};
/** Default prefix prepended to Statsd message keys */
static const std::string DEFAULT_STATSD_PREFIX{""};
/** Default prefix appended to Statsd message keys */
static const std::string DEFAULT_STATSD_SUFFIX{""};

/** Default number of milliseconds between flushing a queue of messages */
Expand All @@ -39,43 +43,40 @@ class StatsdClient {
bool enabled);

public:
bool inc(const std::string& key, float sample_rate = 1.f);
bool dec(const std::string& key, float sample_rate = 1.f);
bool count(const std::string& key, int64_t value, float sample_rate = 1.f);
bool gauge(const std::string& key, int64_t value, float sample_rate = 1.f);
bool gaugeDouble(const std::string& key, double value, float sample_rate = 1.f);
bool timing(const std::string& key, int64_t ms, float sample_rate = 1.f);

/* (Low Level Api) manually send a message
* type = "c", "g" or "ms"
*/
bool send(const std::string& key, int64_t value, const std::string& type, float sample_rate = 1.f)
{
return Send(key, value, type, sample_rate);
}
bool sendDouble(const std::string& key, double value, const std::string& type, float sample_rate = 1.f)
{
return Send(key, value, type, sample_rate);
}
/* Statsd-defined APIs */
bool dec(const std::string& key, float frequency = 1.f);
bool inc(const std::string& key, float frequency = 1.f);
bool count(const std::string& key, int64_t delta, float frequency = 1.f);
bool gauge(const std::string& key, int64_t value, float frequency = 1.f);
bool gaugeDouble(const std::string& key, double value, float frequency = 1.f);
bool timing(const std::string& key, uint64_t ms, float frequency = 1.f);

/* Statsd-compatible APIs */
template <typename T1>
bool send(const std::string& key, T1 value, const std::string& type, float frequency = 1.f);

/* Check if a StatsdClient instance is ready to send messages */
bool active() const { return m_sender != nullptr; }

private:
template <typename T1>
bool Send(const std::string& key, T1 value, const std::string& type, float sample_rate);

private:
/* Mutex to protect PRNG */
mutable Mutex cs;
/* PRNG used to dice-roll messages that are 0 < f < 1 */
mutable FastRandomContext insecure_rand GUARDED_BY(cs);

/* Broadcasts messages crafted by StatsdClient */
std::unique_ptr<RawSender> m_sender{nullptr};

/* Phrase prepended to keys */
const std::string m_prefix{""};
/* Phrase appended to keys */
const std::string m_suffix{""};
};

/** Parses arguments and constructs a StatsdClient instance */
std::unique_ptr<StatsdClient> InitStatsClient(const ArgsManager& args);

/** Global smart pointer containing StatsdClient instance */
extern std::unique_ptr<StatsdClient> g_stats_client;

#endif // BITCOIN_STATS_CLIENT_H

0 comments on commit 20c419f

Please sign in to comment.