Skip to content

05. Component 3: OtelMetricSink (Counters & Queue Histogram)

Overview & Purpose

OtelMetricSink exports ns-3 metric data over OTLP. It defines monotonic counters for cumulative bytes (ns3.bytes_transferred) and packet drop counts (ns3.packet_drops), and a Histogram for non-monotonic queue occupancy samples (ns3.queue_size).


Detailed API Specification

Header: ns3/otlp-metric-sink.h
Base Class: ns3::Object
Namespace: ns3

Class Declaration

namespace ns3 {

class OtelMetricSink : public Object
{
public:
    static TypeId GetTypeId();

    OtelMetricSink();
    ~OtelMetricSink() override = default;

    /**
     * \brief Fetch active meter from global Provider and create instruments.
     */
    void InitMeter();

    /**
     * \brief Record throughput in bytes.
     * \param nodeId Node ID.
     * \param bytes Number of bytes transferred.
     */
    void RecordThroughput(uint32_t nodeId, double bytes);

    /**
     * \brief Record packet drop count.
     * \param nodeId Node ID.
     * \param count Number of dropped packets.
     */
    void RecordPacketDropCount(uint32_t nodeId, uint64_t count = 1);

    /**
     * \brief Record queue occupancy sample (Histogram).
     * \param nodeId Node ID.
     * \param packetsInQueue Current number of packets in queue.
     */
    void RecordQueueSize(uint32_t nodeId, uint64_t packetsInQueue);

private:
    opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Meter> m_meter;
    opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Counter<uint64_t>> m_dropCounter;
    opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Counter<double>> m_throughputCounter;
    opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Histogram<uint64_t>> m_queueHistogram;
};

} // namespace ns3

Metric Instruments Summary

Metric Name Instrument Type Units Description
ns3.bytes_transferred Counter<double> bytes Monotonic sum of transferred bytes per node
ns3.packet_drops Counter<uint64_t> packets Monotonic count of dropped packets per node
ns3.queue_size Histogram<uint64_t> packets Point-in-time queue occupancy samples per node

Implementation Code (model/otlp-metric-sink.cc)

// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026 Arun Santhosh R A <arunsanthosh.rashok@gmail.com>

#include "otlp-metric-sink.h"
#include "ns3/log.h"
#include <opentelemetry/context/context.h>

namespace ns3
{

NS_LOG_COMPONENT_DEFINE("OtelMetricSink");
NS_OBJECT_ENSURE_REGISTERED(OtelMetricSink);

TypeId
OtelMetricSink::GetTypeId()
{
    static TypeId tid = TypeId("ns3::OtelMetricSink")
                            .SetParent<Object>()
                            .SetGroupName("otlp")
                            .AddConstructor<OtelMetricSink>();
    return tid;
}

OtelMetricSink::OtelMetricSink()
    : m_meter(nullptr),
      m_dropCounter(nullptr),
      m_throughputCounter(nullptr),
      m_queueHistogram(nullptr)
{
}

void
OtelMetricSink::InitMeter()
{
    auto provider = opentelemetry::metrics::Provider::GetMeterProvider();
    m_meter = provider->GetMeter("ns3-otlp-meter", "1.0.0");

    if (m_meter)
    {
        m_dropCounter = m_meter->CreateUInt64Counter("ns3.packet_drops",
                                                     "Number of dropped packets",
                                                     "packets");
        m_throughputCounter = m_meter->CreateDoubleCounter("ns3.bytes_transferred",
                                                           "Total bytes transferred",
                                                           "bytes");
        m_queueHistogram =
            m_meter->CreateUInt64Histogram("ns3.queue_size", "Queue occupancy samples", "packets");
    }

    NS_LOG_INFO("OtelMetricSink: meter initialised");
}

void
OtelMetricSink::RecordThroughput(uint32_t nodeId, double bytes)
{
    if (m_throughputCounter)
    {
        m_throughputCounter->Add(bytes, {{"ns3.node_id", static_cast<int64_t>(nodeId)}});
    }
}

void
OtelMetricSink::RecordPacketDropCount(uint32_t nodeId, uint64_t count)
{
    if (m_dropCounter)
    {
        m_dropCounter->Add(count, {{"ns3.node_id", static_cast<int64_t>(nodeId)}});
    }
}

void
OtelMetricSink::RecordQueueSize(uint32_t nodeId, uint64_t packetsInQueue)
{
    if (m_queueHistogram)
    {
        m_queueHistogram->Record(packetsInQueue,
                                 {{"ns3.node_id", static_cast<int64_t>(nodeId)}},
                                 opentelemetry::context::Context{});
    }
}

} // namespace ns3