@@ -3656,91 +3656,105 @@ void DatabaseAPI::taskPurge(uint32_t a_age_sec, LogContext log_context) {
36563656 }
36573657 */
36583658
3659+ /* *
3660+ * @brief Converts client metrics into a structured JSON string.
3661+ *
3662+ * This method takes a timestamp, a total count, and a nested map of client
3663+ * metrics, and produces a JSON string with the following structure:
3664+ *
3665+ * - `timestamp`: the timestamp provided.
3666+ * - `total`: the total count provided.
3667+ * - `uids` (or clients): a JSON object where each key is a client ID and the
3668+ * value is an object containing:
3669+ * - `tot`: subtotal for the client (from message type 0, if present)
3670+ * - `msg`: an object mapping message types (as strings) to counts.
3671+ *
3672+ * @param a_timestamp The timestamp to include in the JSON payload.
3673+ * @param a_total The total count across all clients (sum of all subtotals).
3674+ * @param a_metrics A map where:
3675+ * - The first key (`std::string`) is the client ID.
3676+ * - The nested map (`std::map<uint16_t, uint32_t>`) maps:
3677+ * - Key: message type (uint16_t), where `0` is reserved for subtotal.
3678+ * - Value: count of messages for that type, at key `0` it is the
3679+ * subtotal.
3680+ * - If a client has no metrics, the nested map will be empty.
3681+ * - Message type `0` is used as a subtotal and is never a valid message type.
3682+ *
3683+ * @return A formatted JSON string representing the clients and their message
3684+ * counts.
3685+ *
3686+ * @note The JSON structure will always include the `"uids"` object, even if
3687+ * empty.
3688+ * @note Message type keys in `"msg"` are converted to strings to ensure valid
3689+ * JSON objects.
3690+ *
3691+ * In the below example total is equivalent to subtotal because there is only
3692+ * one client.
3693+ *
3694+ * @example
3695+ * Input:
3696+ * @code
3697+ * timestamp = 111
3698+ * total = 15
3699+ * metrics = {
3700+ * {"client1", {{0, 15}, {1, 10}, {2, 5}}}
3701+ * }
3702+ * @endcode
3703+ *
3704+ * Output JSON:
3705+ * @code
3706+ * {
3707+ * "timestamp": 111,
3708+ * "total": 15,
3709+ * "uids": {
3710+ * "client1": {
3711+ * "tot": 15,
3712+ * "msg": {
3713+ * "1": 10,
3714+ * "2": 5
3715+ * }
3716+ * }
3717+ * }
3718+ * }
3719+ * @endcode
3720+ */
36593721std::string DatabaseAPI::newJsonMetricParse (
36603722 uint32_t a_timestamp, uint32_t a_total,
36613723 const std::map<std::string, std::map<uint16_t , uint32_t >> &a_metrics) {
3662- map<string, std::map<uint16_t , uint32_t >>::const_iterator u;
3663- map<uint16_t , uint32_t >::const_iterator m;
3724+
36643725 nlohmann::json payload;
3665- payload[" timestamp" ] = to_string (a_timestamp);
3666- payload[" total" ] = to_string (a_total);
3667-
3668- nlohmann::json uids;
3669- for (u = a_metrics.begin (); u != a_metrics.end (); ++u) {
3670- nlohmann::json uid_body;
3671- uid_body[" tot" ] = to_string (u->second .at (0 ));
3672- nlohmann::json uid_msg;
3673- for (m = u->second .begin (); m != u->second .end (); ++m) {
3674- if (m->first != 0 ) {
3675- uid_msg[to_string (m->first )] = to_string (m->second );
3676- }
3677- }
3678- uid_body[" msg" ] = uid_msg;
3726+ payload[" timestamp" ] = a_timestamp;
3727+ payload[" total" ] = a_total;
36793728
3680- uids[u->first ] = uid_body;
3681- }
3729+ nlohmann::json clients_json = nlohmann::json::object ();
36823730
3683- payload[" uids" ] = uids;
3684- string body = payload.dump (-1 , ' ' , true );
3685- return body;
3686- }
3731+ for (const auto &[client_id, client_metrics] : a_metrics) {
3732+ nlohmann::json client_json;
36873733
3688- // TODO: verify and remove
3689- std::string DatabaseAPI::oldJsonMetricParse (
3690- uint32_t a_timestamp, uint32_t a_total,
3691- const std::map<std::string, std::map<uint16_t , uint32_t >> &a_metrics) {
3692- map<string, std::map<uint16_t , uint32_t >>::const_iterator u;
3693- map<uint16_t , uint32_t >::const_iterator m;
3694- string body = " {\" timestamp\" :" + to_string (a_timestamp) +
3695- " ,\" total\" :" + to_string (a_total) + " ,\" uids\" :{" ;
3696- bool c = false , cc;
3697-
3698- for (u = a_metrics.begin (); u != a_metrics.end (); ++u) {
3699- if (c)
3700- body += " ," ;
3701- else
3702- c = true ;
3703-
3704- body += " \" " + u->first + " \" :{\" tot\" :" + to_string (u->second .at (0 )) +
3705- " ,\" msg\" :{" ;
3706-
3707- for (cc = false , m = u->second .begin (); m != u->second .end (); ++m) {
3708- if (m->first != 0 ) {
3709- if (cc)
3710- body += " ," ;
3711- else
3712- cc = true ;
3713-
3714- body += " \" " + to_string (m->first ) + " \" :" + to_string (m->second );
3715- }
3734+ // Use safe access for total
3735+ auto it = client_metrics.find (0 );
3736+ client_json[" tot" ] = (it != client_metrics.end ()) ? it->second : 0 ;
3737+
3738+ nlohmann::json msg_json;
3739+ for (const auto &[msg_id, msg_count] : client_metrics) {
3740+ if (msg_id != 0 )
3741+ msg_json[std::to_string (msg_id)] = msg_count;
37163742 }
3717- body += " }}" ;
3743+
3744+ client_json[" msg" ] = msg_json;
3745+ clients_json[client_id] = client_json;
37183746 }
37193747
3720- body += " }} " ;
3721- return body ;
3748+ payload[ " uids " ] = clients_json ;
3749+ return payload. dump (- 1 , ' ' , true ) ;
37223750}
37233751
37243752void DatabaseAPI::metricsUpdateMsgCounts (
37253753 uint32_t a_timestamp, uint32_t a_total,
37263754 const std::map<std::string, std::map<uint16_t , uint32_t >> &a_metrics,
37273755 LogContext log_context) {
37283756
3729- string body;
3730- string new_body = newJsonMetricParse (a_timestamp, a_total, a_metrics);
3731- string old_body = oldJsonMetricParse (a_timestamp, a_total, a_metrics);
3732-
3733- if (new_body == old_body) {
3734- // on match use safer serialization
3735- body = new_body;
3736- } else {
3737- body = old_body;
3738- DL_WARNING (
3739- log_context,
3740- " Serialized metric bodies did not match, new serialization yielded:\n "
3741- << new_body << " \n old serialization yielded:\n "
3742- << old_body);
3743- }
3757+ std::string body = newJsonMetricParse (a_timestamp, a_total, a_metrics);
37443758
37453759 libjson::Value result;
37463760
0 commit comments