Files
rippled/include/xrpl/beast/insight/Event.h
Pratik Mankawde 76c9051203 feat(insight): let an Event declare what it measures
beast::insight::Event documents itself as carrying "a millisecond time, or
other integral value", but both backends assumed the first case: the OTel
bridge declared every instrument with unit `ms` and StatsD tagged every
sample `|ms`. One Event does not measure time -- ServerHandler's "size"
records the serialized RPC response length -- so it exported as
rpc_size_milliseconds and inherited the millisecond bucket ladder. A quarter
of its samples landed above that ladder's top edge, and since Prometheus
returns the second-highest edge for a quantile in the `+Inf` bucket, its p95
panel showed a flat 5.00 kB rather than a measurement.

Adds beast::insight::Unit (Millis, Bytes) plus otelUnitCode(), carried on
EventImpl and selectable at makeEvent(). Naming the unit at creation is what
lets a backend pick the export unit and, through it, the bucket ladder.

- Collector gains a virtual makeEvent(name, Unit) whose default delegates to
  the millisecond overload, so a collector that cannot act on a unit keeps
  working unchanged. NullCollector and the Groups wrapper override it.
- The Groups override matters most: call sites reach a collector through a
  Group, so forwarding only the prefixed name would silently drop the unit.
  A test covers that hop specifically.
- Event gains notify(std::uint64_t) for non-duration samples, replacing
  ServerHandler's `Event::value_type{response.size()}` -- wrapping a byte
  count in a std::chrono::milliseconds compiles but reads as a duration to
  everything downstream.
- EventImpl::value_type stays std::chrono::milliseconds. Widening it would
  change the wire value of every existing StatsD timer, and metrics needing
  finer resolution use the OTel-native microsecond instruments.

The StatsD collector deliberately keeps emitting `|ms`: that path is retired
here (its UDP port is commented out of the compose file and the integration
test fails if anything listens on 8125), so changing its wire format would
alter a legacy contract with no consumer and no way to verify it.

The exported name does not change yet -- OTelEventImpl still hardcodes its
unit. That follows with the unit-keyed histogram views.
2026-08-21 12:11:32 +01:00

83 lines
2.1 KiB
C++

#pragma once
#include <xrpl/beast/insight/EventImpl.h>
#include <chrono>
#include <memory>
#include <utility>
namespace beast::insight {
/**
* A metric for reporting event timing.
*
* An event is an operation that has an associated millisecond time, or
* other integral value. Because events happen at a specific moment, the
* metric only supports a push-style interface.
*
* This is a lightweight reference wrapper which is cheap to copy and assign.
* When the last reference goes away, the metric is no longer collected.
*/
class Event final
{
public:
using value_type = EventImpl::value_type;
/**
* Create a null metric.
* A null metric reports no information.
*/
Event() = default;
/**
* Create the metric reference the specified implementation.
* Normally this won't be called directly. Instead, call the appropriate
* factory function in the Collector interface.
* @see Collector.
*/
explicit Event(std::shared_ptr<EventImpl> impl) : impl_(std::move(impl))
{
}
/**
* Push an event notification.
*/
template <class Rep, class Period>
void
notify(std::chrono::duration<Rep, Period> const& value) const
{
using namespace std::chrono;
if (impl_)
impl_->notify(ceil<value_type>(value));
}
/**
* Push a raw integral sample.
*
* For Events whose unit is not a duration, such as a byte count. The
* value is stored in the same integral field the duration overload uses
* and is interpreted per the Event's unit by the backend.
*
* Prefer this over constructing an `Event::value_type` at the call site:
* wrapping a byte count in a `std::chrono::milliseconds` compiles, but
* reads as a duration to everything downstream.
*/
void
notify(std::uint64_t value) const
{
if (impl_)
impl_->notify(value_type{value});
}
[[nodiscard]] std::shared_ptr<EventImpl> const&
impl() const
{
return impl_;
}
private:
std::shared_ptr<EventImpl> impl_;
};
} // namespace beast::insight