diff --git a/src/init.cpp b/src/init.cpp index f633ac533f..7c2dc4e766 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -289,6 +289,7 @@ void PrepareShutdown(NodeContext& node) node.banman.reset(); node.addrman.reset(); node.netgroupman.reset(); + ::g_stats_client.reset(); if (node.mempool && node.mempool->IsLoaded() && node.args->GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { DumpMempool(*node.mempool); @@ -839,12 +840,12 @@ static void PeriodicStats(ArgsManager& args, ChainstateManager& chainman, const CCoinsStats stats{CoinStatsHashType::NONE}; chainman.ActiveChainstate().ForceFlushStateToDisk(); if (WITH_LOCK(cs_main, return GetUTXOStats(&chainman.ActiveChainstate().CoinsDB(), std::ref(chainman.m_blockman), stats, RpcInterruptionPoint, chainman.ActiveChain().Tip()))) { - statsClient.gauge("utxoset.tx", stats.nTransactions, 1.0f); - statsClient.gauge("utxoset.txOutputs", stats.nTransactionOutputs, 1.0f); - statsClient.gauge("utxoset.dbSizeBytes", stats.nDiskSize, 1.0f); - statsClient.gauge("utxoset.blockHeight", stats.nHeight, 1.0f); + ::g_stats_client->gauge("utxoset.tx", stats.nTransactions, 1.0f); + ::g_stats_client->gauge("utxoset.txOutputs", stats.nTransactionOutputs, 1.0f); + ::g_stats_client->gauge("utxoset.dbSizeBytes", stats.nDiskSize, 1.0f); + ::g_stats_client->gauge("utxoset.blockHeight", stats.nHeight, 1.0f); if (stats.total_amount.has_value()) { - statsClient.gauge("utxoset.totalAmount", (double)stats.total_amount.value() / (double)COIN, 1.0f); + ::g_stats_client->gauge("utxoset.totalAmount", (double)stats.total_amount.value() / (double)COIN, 1.0f); } } else { // something went wrong @@ -866,22 +867,22 @@ static void PeriodicStats(ArgsManager& args, ChainstateManager& chainman, const int64_t timeDiff = maxTime - minTime; double nNetworkHashPS = workDiff.getdouble() / timeDiff; - statsClient.gaugeDouble("network.hashesPerSecond", nNetworkHashPS); - statsClient.gaugeDouble("network.terahashesPerSecond", nNetworkHashPS / 1e12); - statsClient.gaugeDouble("network.petahashesPerSecond", nNetworkHashPS / 1e15); - statsClient.gaugeDouble("network.exahashesPerSecond", nNetworkHashPS / 1e18); + ::g_stats_client->gaugeDouble("network.hashesPerSecond", nNetworkHashPS); + ::g_stats_client->gaugeDouble("network.terahashesPerSecond", nNetworkHashPS / 1e12); + ::g_stats_client->gaugeDouble("network.petahashesPerSecond", nNetworkHashPS / 1e15); + ::g_stats_client->gaugeDouble("network.exahashesPerSecond", nNetworkHashPS / 1e18); // No need for cs_main, we never use null tip here - statsClient.gaugeDouble("network.difficulty", (double)GetDifficulty(tip)); + ::g_stats_client->gaugeDouble("network.difficulty", (double)GetDifficulty(tip)); - statsClient.gauge("transactions.txCacheSize", WITH_LOCK(cs_main, return chainman.ActiveChainstate().CoinsTip().GetCacheSize()), 1.0f); - statsClient.gauge("transactions.totalTransactions", tip->nChainTx, 1.0f); + ::g_stats_client->gauge("transactions.txCacheSize", WITH_LOCK(cs_main, return chainman.ActiveChainstate().CoinsTip().GetCacheSize()), 1.0f); + ::g_stats_client->gauge("transactions.totalTransactions", tip->nChainTx, 1.0f); { LOCK(mempool.cs); - statsClient.gauge("transactions.mempool.totalTransactions", mempool.size(), 1.0f); - statsClient.gauge("transactions.mempool.totalTxBytes", (int64_t) mempool.GetTotalTxSize(), 1.0f); - statsClient.gauge("transactions.mempool.memoryUsageBytes", (int64_t) mempool.DynamicMemoryUsage(), 1.0f); - statsClient.gauge("transactions.mempool.minFeePerKb", mempool.GetMinFee(args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(), 1.0f); + ::g_stats_client->gauge("transactions.mempool.totalTransactions", mempool.size(), 1.0f); + ::g_stats_client->gauge("transactions.mempool.totalTxBytes", (int64_t) mempool.GetTotalTxSize(), 1.0f); + ::g_stats_client->gauge("transactions.mempool.memoryUsageBytes", (int64_t) mempool.DynamicMemoryUsage(), 1.0f); + ::g_stats_client->gauge("transactions.mempool.minFeePerKb", mempool.GetMinFee(args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(), 1.0f); } } @@ -1524,6 +1525,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) fDiscover = args.GetBoolArg("-discover", true); const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)}; + // We need to initialize g_stats_client early as currently, g_stats_client is called + // regardless of whether transmitting stats are desirable or not and if + // g_stats_client isn't present when that attempt is made, the client will crash. + ::g_stats_client = std::make_unique(args.GetArg("-statshost", DEFAULT_STATSD_HOST), + args.GetArg("-statshostname", DEFAULT_STATSD_HOSTNAME), + args.GetArg("-statsport", DEFAULT_STATSD_PORT), + args.GetArg("-statsns", DEFAULT_STATSD_NAMESPACE), + args.GetBoolArg("-statsenabled", DEFAULT_STATSD_ENABLE)); + { // Read asmap file if configured diff --git a/src/net.cpp b/src/net.cpp index a54b17b92a..4d16d43fab 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -623,7 +623,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo /*inbound_onion=*/false, std::move(i2p_transient_session)); pnode->AddRef(); - statsClient.inc("peers.connect", 1.0f); + ::g_stats_client->inc("peers.connect", 1.0f); // We're making a new connection, harvest entropy from the time (and our peer count) RandAddEvent((uint32_t)id); @@ -666,7 +666,7 @@ void CNode::CloseSocketDisconnect(CConnman* connman) m_sock.reset(); m_i2p_sam_session.reset(); - statsClient.inc("peers.disconnect", 1.0f); + ::g_stats_client->inc("peers.disconnect", 1.0f); } void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const { @@ -817,7 +817,7 @@ bool CNode::ReceiveMsgBytes(Span msg_bytes, bool& complete) } assert(i != mapRecvBytesPerMsgType.end()); i->second += msg.m_raw_message_size; - statsClient.count("bandwidth.message." + std::string(msg.m_type) + ".bytesReceived", msg.m_raw_message_size, 1.0f); + ::g_stats_client->count("bandwidth.message." + std::string(msg.m_type) + ".bytesReceived", msg.m_raw_message_size, 1.0f); // push the message to the process queue, vRecvMsg.push_back(std::move(msg)); @@ -1741,20 +1741,20 @@ void CConnman::CalculateNumConnectionsChangedStats() torNodes++; const auto last_ping_time = count_microseconds(pnode->m_last_ping_time); if (last_ping_time > 0) - statsClient.timing("peers.ping_us", last_ping_time, 1.0f); + ::g_stats_client->timing("peers.ping_us", last_ping_time, 1.0f); } for (const std::string &msg : getAllNetMessageTypes()) { - statsClient.gauge("bandwidth.message." + msg + ".totalBytesReceived", mapRecvBytesMsgStats[msg], 1.0f); - statsClient.gauge("bandwidth.message." + msg + ".totalBytesSent", mapSentBytesMsgStats[msg], 1.0f); + ::g_stats_client->gauge("bandwidth.message." + msg + ".totalBytesReceived", mapRecvBytesMsgStats[msg], 1.0f); + ::g_stats_client->gauge("bandwidth.message." + msg + ".totalBytesSent", mapSentBytesMsgStats[msg], 1.0f); } - statsClient.gauge("peers.totalConnections", nPrevNodeCount, 1.0f); - statsClient.gauge("peers.spvNodeConnections", spvNodes, 1.0f); - statsClient.gauge("peers.fullNodeConnections", fullNodes, 1.0f); - statsClient.gauge("peers.inboundConnections", inboundNodes, 1.0f); - statsClient.gauge("peers.outboundConnections", outboundNodes, 1.0f); - statsClient.gauge("peers.ipv4Connections", ipv4Nodes, 1.0f); - statsClient.gauge("peers.ipv6Connections", ipv6Nodes, 1.0f); - statsClient.gauge("peers.torConnections", torNodes, 1.0f); + ::g_stats_client->gauge("peers.totalConnections", nPrevNodeCount, 1.0f); + ::g_stats_client->gauge("peers.spvNodeConnections", spvNodes, 1.0f); + ::g_stats_client->gauge("peers.fullNodeConnections", fullNodes, 1.0f); + ::g_stats_client->gauge("peers.inboundConnections", inboundNodes, 1.0f); + ::g_stats_client->gauge("peers.outboundConnections", outboundNodes, 1.0f); + ::g_stats_client->gauge("peers.ipv4Connections", ipv4Nodes, 1.0f); + ::g_stats_client->gauge("peers.ipv6Connections", ipv6Nodes, 1.0f); + ::g_stats_client->gauge("peers.torConnections", torNodes, 1.0f); } bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const @@ -4126,8 +4126,8 @@ bool CConnman::DisconnectNode(NodeId id) void CConnman::RecordBytesRecv(uint64_t bytes) { nTotalBytesRecv += bytes; - statsClient.count("bandwidth.bytesReceived", bytes, 0.1f); - statsClient.gauge("bandwidth.totalBytesReceived", nTotalBytesRecv, 0.01f); + ::g_stats_client->count("bandwidth.bytesReceived", bytes, 0.1f); + ::g_stats_client->gauge("bandwidth.totalBytesReceived", nTotalBytesRecv, 0.01f); } void CConnman::RecordBytesSent(uint64_t bytes) @@ -4136,8 +4136,8 @@ void CConnman::RecordBytesSent(uint64_t bytes) LOCK(m_total_bytes_sent_mutex); nTotalBytesSent += bytes; - statsClient.count("bandwidth.bytesSent", bytes, 0.01f); - statsClient.gauge("bandwidth.totalBytesSent", nTotalBytesSent, 0.01f); + ::g_stats_client->count("bandwidth.bytesSent", bytes, 0.01f); + ::g_stats_client->gauge("bandwidth.totalBytesSent", nTotalBytesSent, 0.01f); const auto now = GetTime(); if (nMaxOutboundCycleStartTime + MAX_UPLOAD_TIMEFRAME < now) @@ -4293,8 +4293,8 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) msg.data.data() ); - statsClient.count(strprintf("bandwidth.message.%s.bytesSent", msg.m_type), nMessageSize, 1.0f); - statsClient.inc(strprintf("message.sent.%s", msg.m_type), 1.0f); + ::g_stats_client->count(strprintf("bandwidth.message.%s.bytesSent", msg.m_type), nMessageSize, 1.0f); + ::g_stats_client->inc(strprintf("message.sent.%s", msg.m_type), 1.0f); { LOCK(pnode->cs_vSend); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 43f151890b..1f9b466c30 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1699,9 +1699,9 @@ void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const s if (score_now >= DISCOURAGEMENT_THRESHOLD && score_before < DISCOURAGEMENT_THRESHOLD) { warning = " DISCOURAGE THRESHOLD EXCEEDED"; peer->m_should_discourage = true; - statsClient.inc("misbehavior.banned", 1.0f); + ::g_stats_client->inc("misbehavior.banned", 1.0f); } else { - statsClient.count("misbehavior.amount", howmuch, 1.0); + ::g_stats_client->count("misbehavior.amount", howmuch, 1.0); } LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s%s\n", @@ -3290,7 +3290,7 @@ void PeerManagerImpl::ProcessMessage( AssertLockHeld(g_msgproc_mutex); LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId()); - statsClient.inc("message.received." + SanitizeString(msg_type), 1.0f); + ::g_stats_client->inc("message.received." + SanitizeString(msg_type), 1.0f); const bool is_masternode = m_mn_activeman != nullptr; @@ -3789,7 +3789,7 @@ void PeerManagerImpl::ProcessMessage( if (inv.IsMsgBlk()) { const bool fAlreadyHave = AlreadyHaveBlock(inv.hash); LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId()); - statsClient.inc(strprintf("message.received.inv_%s", inv.GetCommand()), 1.0f); + ::g_stats_client->inc(strprintf("message.received.inv_%s", inv.GetCommand()), 1.0f); UpdateBlockAvailability(pfrom.GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !IsBlockRequested(inv.hash)) { @@ -3804,7 +3804,7 @@ void PeerManagerImpl::ProcessMessage( } else { const bool fAlreadyHave = AlreadyHave(inv); LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId()); - statsClient.inc(strprintf("message.received.inv_%s", inv.GetCommand()), 1.0f); + ::g_stats_client->inc(strprintf("message.received.inv_%s", inv.GetCommand()), 1.0f); static std::set allowWhileInIBDObjs = { MSG_SPORK diff --git a/src/random.h b/src/random.h index 71cdbf4a11..7f3b903622 100644 --- a/src/random.h +++ b/src/random.h @@ -207,10 +207,6 @@ public: return rand32() % nMax; } - uint32_t operator()(uint32_t nMax) { - return rand32(nMax); - } - /** Generate random bytes. */ template std::vector randbytes(size_t len); diff --git a/src/statsd_client.cpp b/src/statsd_client.cpp index d291510ac6..63e178302e 100644 --- a/src/statsd_client.cpp +++ b/src/statsd_client.cpp @@ -1,3 +1,8 @@ +// Copyright (c) 2014-2017 Statoshi Developers +// Copyright (c) 2020-2024 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + /** Copyright (c) 2014, Rex All rights reserved. @@ -32,217 +37,160 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include #include #include #include +#include -statsd::StatsdClient statsClient; +std::unique_ptr g_stats_client; namespace statsd { - -inline bool fequal(float a, float b) +bool StatsdClient::ShouldSend(float sample_rate) { - const float epsilon = 0.0001; - return ( fabs(a - b) < epsilon ); + sample_rate = std::clamp(sample_rate, 0.f, 1.f); + + constexpr float EPSILON{0.0001f}; + /* If sample rate is 1, we should always send */ + if (std::fabs(sample_rate - 1.f) < EPSILON) return true; + /* If sample rate is 0, we should never send */ + if (std::fabs(sample_rate) < EPSILON) return false; + + /* Sample rate is >0 and <1, roll the dice */ + LOCK(cs); + return sample_rate > std::uniform_real_distribution(0.f, 1.f)(insecure_rand); } -thread_local FastRandomContext insecure_rand; - -inline bool should_send(float sample_rate) +StatsdClient::StatsdClient(const std::string& host, const std::string& nodename, uint16_t port, const std::string& ns, + bool enabled) : + m_port{port}, + m_host{host}, + m_nodename{nodename}, + m_ns{ns} { - if ( fequal(sample_rate, 1.0) ) - { - return true; + if (!enabled) { + LogPrintf("Transmitting stats are disabled, will not init StatsdClient\n"); + return; } - float p = float(insecure_rand(std::numeric_limits::max())) / float(std::numeric_limits::max()); - return sample_rate > p; -} - -struct _StatsdClientData { - SOCKET sock; - struct sockaddr_in server; - - std::string ns; - std::string host; - std::string nodename; - short port; - bool init; - - char errmsg[1024]; -}; - -StatsdClient::StatsdClient(const std::string& host, int port, const std::string& ns) : - d(std::make_unique<_StatsdClientData>()) -{ - d->sock = INVALID_SOCKET; - config(host, port, ns); -} - -StatsdClient::~StatsdClient() -{ - // close socket - CloseSocket(d->sock); -} - -void StatsdClient::config(const std::string& host, int port, const std::string& ns) -{ - d->ns = ns; - d->host = host; - d->port = port; - d->init = false; - CloseSocket(d->sock); -} - -int StatsdClient::init() -{ - static bool fEnabled = gArgs.GetBoolArg("-statsenabled", DEFAULT_STATSD_ENABLE); - if (!fEnabled) return -3; - - if ( d->init ) return 0; - - config(gArgs.GetArg("-statshost", DEFAULT_STATSD_HOST), gArgs.GetArg("-statsport", DEFAULT_STATSD_PORT), gArgs.GetArg("-statsns", DEFAULT_STATSD_NAMESPACE)); - - d->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if ( d->sock == INVALID_SOCKET ) { - snprintf(d->errmsg, sizeof(d->errmsg), "could not create socket, err=%m"); - return -1; + CNetAddr netaddr; + if (!LookupHost(m_host, netaddr, /*fAllowLookup=*/true)) { + LogPrintf("ERROR: Unable to lookup host %s, cannot init StatsdClient\n", m_host); + return; + } + if (!netaddr.IsIPv4()) { + LogPrintf("ERROR: Host %s on unsupported network, cannot init StatsdClient\n", m_host); + return; + } + if (!CService(netaddr, port).GetSockAddr(reinterpret_cast(&m_server.first), &m_server.second)) { + LogPrintf("ERROR: Cannot get socket address for %s, cannot init StatsdClient\n", m_host); + return; } - memset(&d->server, 0, sizeof(d->server)); - d->server.sin_family = AF_INET; - d->server.sin_port = htons(d->port); - - CNetAddr netaddr(d->server.sin_addr); - if (!LookupHost(d->host, netaddr, true) || !netaddr.GetInAddr(&d->server.sin_addr)) { - snprintf(d->errmsg, sizeof(d->errmsg), "LookupHost or GetInAddr failed"); - return -2; + SOCKET hSocket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (hSocket == INVALID_SOCKET) { + LogPrintf("ERROR: Cannot create socket (socket() returned error %s), cannot init StatsdClient\n", + NetworkErrorString(WSAGetLastError())); + return; } + m_sock = std::make_unique(hSocket); - if (gArgs.IsArgSet("-statshostname")) { - d->nodename = gArgs.GetArg("-statshostname", DEFAULT_STATSD_HOSTNAME); - } - - d->init = true; - return 0; + LogPrintf("StatsdClient initialized to transmit stats to %s:%d\n", m_host, m_port); } /* will change the original string */ void StatsdClient::cleanup(std::string& key) { - size_t pos = key.find_first_of(":|@"); - while ( pos != std::string::npos ) - { + auto pos = key.find_first_of(":|@"); + while (pos != std::string::npos) { key[pos] = '_'; pos = key.find_first_of(":|@"); } } -int StatsdClient::dec(const std::string& key, float sample_rate) -{ - return count(key, -1, sample_rate); -} +bool StatsdClient::dec(const std::string& key, float sample_rate) { return count(key, -1, sample_rate); } -int StatsdClient::inc(const std::string& key, float sample_rate) -{ - return count(key, 1, sample_rate); -} +bool StatsdClient::inc(const std::string& key, float sample_rate) { return count(key, 1, sample_rate); } -int StatsdClient::count(const std::string& key, size_t value, float sample_rate) +bool StatsdClient::count(const std::string& key, int64_t value, float sample_rate) { return send(key, value, "c", sample_rate); } -int StatsdClient::gauge(const std::string& key, size_t value, float sample_rate) +bool StatsdClient::gauge(const std::string& key, int64_t value, float sample_rate) { return send(key, value, "g", sample_rate); } -int StatsdClient::gaugeDouble(const std::string& key, double value, float sample_rate) +bool StatsdClient::gaugeDouble(const std::string& key, double value, float sample_rate) { return sendDouble(key, value, "g", sample_rate); } -int StatsdClient::timing(const std::string& key, size_t ms, float sample_rate) +bool StatsdClient::timing(const std::string& key, int64_t ms, float sample_rate) { return send(key, ms, "ms", sample_rate); } -int StatsdClient::send(std::string key, size_t value, const std::string& type, float sample_rate) +bool StatsdClient::send(std::string key, int64_t value, const std::string& type, float sample_rate) { - if (!should_send(sample_rate)) { - return 0; + if (!m_sock) { + return false; + } + + if (!ShouldSend(sample_rate)) { + // Not our turn to send but report that we have + return true; } // partition stats by node name if set - if (!d->nodename.empty()) - key = key + "." + d->nodename; + if (!m_nodename.empty()) key = key + "." + m_nodename; cleanup(key); - char buf[256]; - if ( fequal( sample_rate, 1.0 ) ) - { - snprintf(buf, sizeof(buf), "%s%s:%zd|%s", - d->ns.c_str(), key.c_str(), (ssize_t) value, type.c_str()); - } - else - { - snprintf(buf, sizeof(buf), "%s%s:%zd|%s|@%.2f", - d->ns.c_str(), key.c_str(), (ssize_t) value, type.c_str(), sample_rate); + std::string buf{strprintf("%s%s:%d|%s", m_ns, key, value, type)}; + if (sample_rate < 1.f) { + buf += strprintf("|@%.2f", sample_rate); } return send(buf); } -int StatsdClient::sendDouble(std::string key, double value, const std::string& type, float sample_rate) +bool StatsdClient::sendDouble(std::string key, double value, const std::string& type, float sample_rate) { - if (!should_send(sample_rate)) { - return 0; + if (!m_sock) { + return false; + } + + if (!ShouldSend(sample_rate)) { + // Not our turn to send but report that we have + return true; } // partition stats by node name if set - if (!d->nodename.empty()) - key = key + "." + d->nodename; + if (!m_nodename.empty()) key = key + "." + m_nodename; cleanup(key); - char buf[256]; - if ( fequal( sample_rate, 1.0 ) ) - { - snprintf(buf, sizeof(buf), "%s%s:%f|%s", - d->ns.c_str(), key.c_str(), value, type.c_str()); - } - else - { - snprintf(buf, sizeof(buf), "%s%s:%f|%s|@%.2f", - d->ns.c_str(), key.c_str(), value, type.c_str(), sample_rate); + std::string buf{strprintf("%s%s:%f|%s", m_ns, key, value, type)}; + if (sample_rate < 1.f) { + buf += strprintf("|@%.2f", sample_rate); } return send(buf); } -int StatsdClient::send(const std::string& message) +bool StatsdClient::send(const std::string& message) { - int ret = init(); - if ( ret ) - { - return ret; - } - ret = sendto(d->sock, message.data(), message.size(), 0, reinterpret_cast(&d->server), sizeof(d->server)); - if ( ret == -1) { - snprintf(d->errmsg, sizeof(d->errmsg), - "sendto server fail, host=%s:%d, err=%m", d->host.c_str(), d->port); - return -1; - } - return 0; -} + assert(m_sock); -const char* StatsdClient::errmsg() -{ - return d->errmsg; -} + if (::sendto(m_sock->Get(), message.data(), message.size(), /*flags=*/0, + reinterpret_cast(&m_server.first), m_server.second) == SOCKET_ERROR) { + LogPrintf("ERROR: Unable to send message (sendto() returned error %s), host=%s:%d\n", + NetworkErrorString(WSAGetLastError()), m_host, m_port); + return false; + } + return true; +} } // namespace statsd diff --git a/src/statsd_client.h b/src/statsd_client.h index f06fd1b5c1..7568d8cd0e 100644 --- a/src/statsd_client.h +++ b/src/statsd_client.h @@ -1,3 +1,4 @@ +// Copyright (c) 2014-2017 Statoshi Developers // Copyright (c) 2020-2023 The Dash Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -5,67 +6,68 @@ #ifndef BITCOIN_STATSD_CLIENT_H #define BITCOIN_STATSD_CLIENT_H +#include +#include +#include + #include #include -static const bool DEFAULT_STATSD_ENABLE = false; -static const int DEFAULT_STATSD_PORT = 8125; -static const std::string DEFAULT_STATSD_HOST = "127.0.0.1"; -static const std::string DEFAULT_STATSD_HOSTNAME = ""; -static const std::string DEFAULT_STATSD_NAMESPACE = ""; +static constexpr bool DEFAULT_STATSD_ENABLE{false}; +static constexpr uint16_t DEFAULT_STATSD_PORT{8125}; +static const std::string DEFAULT_STATSD_HOST{"127.0.0.1"}; +static const std::string DEFAULT_STATSD_HOSTNAME{""}; +static const std::string DEFAULT_STATSD_NAMESPACE{""}; // schedule periodic measurements, in seconds: default - 1 minute, min - 5 sec, max - 1h. -static const int DEFAULT_STATSD_PERIOD = 60; -static const int MIN_STATSD_PERIOD = 5; -static const int MAX_STATSD_PERIOD = 60 * 60; +static constexpr int DEFAULT_STATSD_PERIOD{60}; +static constexpr int MIN_STATSD_PERIOD{5}; +static constexpr int MAX_STATSD_PERIOD{60 * 60}; namespace statsd { - -struct _StatsdClientData; - class StatsdClient { public: - explicit StatsdClient(const std::string& host = DEFAULT_STATSD_HOST, int port = DEFAULT_STATSD_PORT, const std::string& ns = DEFAULT_STATSD_NAMESPACE); - ~StatsdClient(); + explicit StatsdClient(const std::string& host, const std::string& nodename, uint16_t port, + const std::string& ns, bool enabled); public: - // you can config at anytime; client will use new address (useful for Singleton) - void config(const std::string& host, int port, const std::string& ns = DEFAULT_STATSD_NAMESPACE); - const char* errmsg(); - - public: - int inc(const std::string& key, float sample_rate = 1.0); - int dec(const std::string& key, float sample_rate = 1.0); - int count(const std::string& key, size_t value, float sample_rate = 1.0); - int gauge(const std::string& key, size_t value, float sample_rate = 1.0); - int gaugeDouble(const std::string& key, double value, float sample_rate = 1.0); - int timing(const std::string& key, size_t ms, float sample_rate = 1.0); - - public: - /** - * (Low Level Api) manually send a message - * which might be composed of several lines. - */ - int send(const std::string& message); + 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" */ - int send(std::string key, size_t value, - const std::string& type, float sample_rate); - int sendDouble(std::string key, double value, - const std::string& type, float sample_rate); + bool send(std::string key, int64_t value, const std::string& type, float sample_rate); + bool sendDouble(std::string key, double value, const std::string& type, float sample_rate); - protected: - int init(); - static void cleanup(std::string& key); + private: + /** + * (Low Level Api) manually send a message + * which might be composed of several lines. + */ + bool send(const std::string& message); - protected: - const std::unique_ptr d; + void cleanup(std::string& key); + bool ShouldSend(float sample_rate); + + private: + mutable Mutex cs; + mutable FastRandomContext insecure_rand GUARDED_BY(cs); + + std::unique_ptr m_sock{nullptr}; + std::pair m_server{{}, sizeof(struct sockaddr_storage)}; + + const uint16_t m_port; + const std::string m_host; + const std::string m_nodename; + const std::string m_ns; }; - } // namespace statsd -extern statsd::StatsdClient statsClient; +extern std::unique_ptr g_stats_client; #endif // BITCOIN_STATSD_CLIENT_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index f37b92d3c7..e3453a9a6e 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -39,6 +39,7 @@ #include #include