Program Listing for File prometheus_writer.cpp

Return to documentation for file (monitoring/prometheus_writer.cpp)

#include "prometheus_writer.hpp"

PrometheusWriter::PrometheusWriter(int port)
    : exposer{std::make_unique<prometheus::Exposer>(std::format("0.0.0.0:{}", port))},
      registry{std::make_shared<prometheus::Registry>()} {
    exposer->RegisterCollectable(registry);
}

void PrometheusWriter::write_message(const nlohmann::json& message) {
    try {
        const std::string hostname = message.at("hostname").get<std::string>();

        for (const auto& device : message.at("devices")) {
            const std::string app = device.at("app").get<std::string>();
            const std::string device_id = std::to_string(device.at("device_id").get<int>());

            for (const auto& buffer : device.at("buffers")) {
                const std::string dmaid = std::to_string(buffer.at("dmaid").get<int>());

                for (const auto& thread : buffer.at("threads")) {
                    if (app == "fromhost") {
                        const std::string fromhost_thread_id = std::to_string(thread.at("thread_id").get<int>());
                        metric_to_prometheus(app, "dma_free_MB", buffer.at("dma_free_MB").get<double>(), hostname, device_id, dmaid);
                        metric_to_prometheus(app, "msg_rate_Hz", buffer.at("msg_rate_Hz").get<double>(), hostname, device_id, dmaid);
                        metric_to_prometheus(app, "msg_rate_Mbps", buffer.at("msg_rate_Mbps").get<double>(), hostname, device_id, dmaid);

                        metric_to_prometheus(app, "number_of_connections", thread.at("number_of_connections").get<double>(), hostname, device_id, dmaid, fromhost_thread_id);

                        for (const auto& elink : thread.at("elinks")) {
                            const std::string fid = std::format("{:#x}", elink.at("fid").get<int>());

                            metric_to_prometheus(app, "msgs", elink.at("msgs").get<double>(), hostname, device_id, dmaid, fromhost_thread_id, fid);
                            metric_to_prometheus(app, "bytes", elink.at("bytes").get<double>(), hostname, device_id, dmaid, fromhost_thread_id, fid);
                            metric_to_prometheus(app, "max_msg_size", elink.at("max_msg_size").get<double>(), hostname, device_id, dmaid, fromhost_thread_id, fid);
                            metric_to_prometheus(app, "dropped_msgs", elink.at("dropped_msgs").get<double>(), hostname, device_id, dmaid, fromhost_thread_id, fid);
                            metric_to_prometheus(app, "rate_msg_Hz", elink.at("rate_msg_Hz").get<double>(), hostname, device_id, dmaid, fromhost_thread_id, fid);
                            metric_to_prometheus(app, "rate_msg_Mbps", elink.at("rate_msg_Mbps").get<double>(), hostname, device_id, dmaid, fromhost_thread_id, fid);
                        }
                    } else if (app == "tohost") {
                        const std::string tohost_thread = std::to_string(thread.at("thread_id").get<int>());
                        const std::string tohost_elink_type = thread.at("type");
                        const std::string tohost_thread_id = std::format("{}_{}", tohost_elink_type, tohost_thread);
                        metric_to_prometheus(app, "dma_free_MB", buffer.at("dma_free_MB").get<double>(), hostname, device_id, dmaid);
                        metric_to_prometheus(app, "irqs", buffer.at("irqs").get<double>(), hostname, device_id, dmaid);

                        metric_to_prometheus(app, "net_calls", thread.at("net_calls").get<double>(), hostname, device_id, dmaid, tohost_thread_id);
                        metric_to_prometheus(app, "net_resources", thread.at("net_resources").get<double>(), hostname, device_id, dmaid,  tohost_thread_id);
                        metric_to_prometheus(app, "subscriptions", thread.at("subscriptions").get<double>(), hostname, device_id, dmaid, tohost_thread_id);

                        for (const auto& elink : thread.at("elinks")) {
                            const std::string fid = std::format("{:#x}", elink.at("fid").get<int>());

                            metric_to_prometheus(app, "avg_chunk_size", elink.at("avg_chunk_size").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "blocks", elink.at("blocks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "chunk_kHz", elink.at("chunk_kHz").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "chunks", elink.at("chunks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "dropped_empty_chunks", elink.at("dropped_empty_chunks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "dropped_blocks", elink.at("dropped_blocks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "fw_crc_chunks", elink.at("fw_crc_chunks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "fw_error_chunks", elink.at("fw_error_chunks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "fw_trunc_chunks", elink.at("fw_trunc_chunks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "max_chunk_size", elink.at("max_chunk_size").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "oosequence_l0id", elink.at("oosequence_l0id").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "sw_error_chunks", elink.at("sw_error_chunks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                            metric_to_prometheus(app, "sw_trunc_chunks", elink.at("sw_trunc_chunks").get<double>(), hostname, device_id, dmaid, tohost_thread_id, fid);
                        }
                    }
                }
            }
        }
    } catch (const nlohmann::json::out_of_range& e) {
        ers::error(monitoring_log::prometheus_issue( std::format("JSON out of range error: {}", e.what())));
    } catch (const std::invalid_argument& e) {
        ers::error(monitoring_log::prometheus_issue( std::format("Invalid argument error: {}", e.what())));
    }
}

void PrometheusWriter::metric_to_prometheus(const std::string& app, const std::string& key, double value,
                                            const std::string& hostname, const std::string& device,
                                            const std::string& dma, const std::string& thread,
                                            const std::string& fid) {

    const std::string metric_name = std::format("felix_{}_{}", app, key);

    prometheus::Labels labels_map;
    if (not hostname.empty()) { labels_map["hostname"] = hostname; }
    if (not device.empty()) { labels_map["device"] = device; }
    if (not dma.empty()) { labels_map["dma"] = dma; }
    if (not thread.empty()) { labels_map["thread"] = thread; }
    if (not fid.empty()) { labels_map["fid"] = fid; }

    std::string pkey = metric_name;
    for (const auto& label : labels_map) {
        pkey = std::format("{}:{}={}", pkey, label.first, label.second);
    }

    if (not prometheus_metrics.contains(pkey)) {
        auto& gauge_family = prometheus::BuildGauge()
                                 .Name(metric_name)
                                 .Help("")
                                 .Register(*registry);
        prometheus_metrics[pkey] = &gauge_family.Add(labels_map);
    }

    auto& gauge = *prometheus_metrics[pkey];
    gauge.Set(value);
}