Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
694884f
first version
denisichh Jun 19, 2026
2b7be26
added metric builder + write_serialized_metric to k2-api
denisichh Jun 19, 2026
7bb602d
v1.0
denisichh Jun 19, 2026
f6af9d2
v1.1
denisichh Jun 21, 2026
0608cf2
rename
denisichh Jun 21, 2026
12b21ac
mask size 32bits to 8bits
denisichh Jun 21, 2026
5bb69d9
fixes
denisichh Jun 22, 2026
f8f635f
fixes
denisichh Jun 22, 2026
064d869
add comments
denisichh Jun 22, 2026
de690e2
added noexcept
denisichh Jun 22, 2026
127f856
fixes
denisichh Jun 22, 2026
537b967
minor fix
denisichh Jun 22, 2026
22a6227
added metric type
denisichh Jun 22, 2026
86f1de8
fixes
denisichh Jun 22, 2026
22cdf85
timestamp u32 to u64 + fixes
denisichh Jun 23, 2026
21e5bdb
fix
denisichh Jun 23, 2026
88709e2
fix
denisichh Jun 23, 2026
4590662
fix concept
denisichh Jun 23, 2026
6f3052d
new metrics ctors
denisichh Jun 23, 2026
57d6b2e
rename static method
denisichh Jun 23, 2026
a5f3057
funny fixes
denisichh Jun 23, 2026
6c9137b
minor fix
denisichh Jun 23, 2026
9c63912
minor fix
denisichh Jun 23, 2026
713b47a
returned buffer.clear()
denisichh Jun 23, 2026
a7e392f
fix
denisichh Jun 23, 2026
b1d7add
added include
denisichh Jun 23, 2026
dfd5c0c
chrono to k2::system_time
denisichh Jun 23, 2026
9c5b54e
format changed: my own to tl
denisichh Jun 25, 2026
476e9cf
fixes
denisichh Jun 26, 2026
9e23659
fixes
denisichh Jun 26, 2026
3366cb6
minor rename
denisichh Jun 26, 2026
0ca6ecd
minor fix
denisichh Jun 26, 2026
3fc576a
added tl scheme fot metric type
denisichh Jun 26, 2026
6c6e917
fix .tl
denisichh Jun 26, 2026
67a07ff
added tl::span
denisichh Jun 29, 2026
202accd
rule of 5 for tl::span
denisichh Jun 29, 2026
292da38
added from_bytes for tl::span
denisichh Jun 29, 2026
bd72a56
fix tl scheme
denisichh Jun 29, 2026
1fef69e
removed tl::span, moved metrics tl classes
denisichh Jun 29, 2026
954d130
removed unused include
denisichh Jun 29, 2026
b3b2335
removed unused space
denisichh Jun 29, 2026
8308d75
removed monitoring system enum
denisichh Jun 29, 2026
787afac
fix metric tl classes
denisichh Jun 29, 2026
54003fb
added {}
denisichh Jun 29, 2026
0a1c7a4
added requires
denisichh Jun 29, 2026
6b6b303
send && fixed
denisichh Jun 29, 2026
e4bd9e8
fixes
denisichh Jun 30, 2026
28a2773
fixes
denisichh Jun 30, 2026
2310872
minor update
denisichh Jul 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions runtime-light/k2-platform/k2-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ using StreamStatus = StreamStatus;

using UpdateStatus = UpdateStatus;

using MonitoringSystem = MonitoringSystem;

using MetricValueKind = MetricValueKind;

using TimePoint = TimePoint;

using SystemTime = SystemTime;
Expand Down Expand Up @@ -245,6 +249,13 @@ inline std::expected<void, int32_t> madvise(void* addr, size_t length, int32_t a
return {};
}

inline std::expected<void, int32_t> write_metric(const std::span<const std::byte> serialized_metric, k2::MonitoringSystem ms) noexcept {
Comment thread
apolyakov marked this conversation as resolved.
Outdated
if (auto error_code{k2_write_metric(serialized_metric.data(), serialized_metric.size(), ms)}; error_code != k2::errno_ok) [[unlikely]] {
return std::unexpected{error_code};
}
return {};
}

inline void please_shutdown(k2::descriptor descriptor) noexcept {
k2_please_shutdown(descriptor);
}
Expand Down
31 changes: 31 additions & 0 deletions runtime-light/k2-platform/k2-header.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ enum UpdateStatus {
NewDescriptor = 2,
};

enum MonitoringSystem { StatsHouse };

/*
* Serialized metric format:
* <timestamp_u32><value_format><msg_size><metric_name_len><metric_name><tag1_name_len><tag1_name><tag1_value_len><tag1_value>...
Comment thread
apolyakov marked this conversation as resolved.
Outdated
Comment thread
apolyakov marked this conversation as resolved.
Outdated
*
* value_format:
* <VALUE><f64> - single float value
* <VALUES_ARRAY><array_len><f64_1><f64_2>... - array of float values
* <COUNT><u32> - count value
* <INC> - counter increment
*
* msg_size: sizeof(metric_name_len) + sizeof(metric_name) + sizeof(tag1_name_len) + sizeof(tag1_name) + sizeof(tag1_value_len) + sizeof(tag1_value) + ...
*
* All numeric fields are stored in native byte order
* All string length fields are stored as size_t
*/
enum MetricValueKind : uint8_t { VALUE, VALUES_ARRAY, COUNT, INC };

struct ImageInfo {
// Base
const char* image_name;
Expand Down Expand Up @@ -383,6 +402,18 @@ void* k2_mmap(uint64_t* md, void* addr, size_t length, int32_t prot, int32_t fla
*/
int32_t k2_madvise(void* addr, size_t length, int32_t advise);

/**
* Writes a pre-serialized metric to the specified monitoring system.
* The buffer must contain a metric serialized according to the format described above
* (see `MetricValueKind` and the serialized metric format comment).
*
* @param `buf` A pointer to the serialized metric data.
* @param `buf_len` The length of the serialized metric data in bytes.
* @param `ms` The target monitoring system.
* @return returns 0 if everything is fine, otherwise error code
*/
int32_t k2_write_metric(const void* buf, size_t buf_len, enum MonitoringSystem ms);

/**
* Sets `StreamStatus.please_whutdown_write=true` for the component on the
* opposite side (does not affect `StreamStatus` on your side).
Expand Down
205 changes: 205 additions & 0 deletions runtime-light/stdlib/diagnostics/metrics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2026 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt

#pragma once

#include <chrono>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <memory>
#include <ranges>
#include <span>
#include <string_view>
#include <utility>

#include "runtime-common/core/allocator/script-allocator.h"
#include "runtime-common/core/std/containers.h"
#include "runtime-light/k2-platform/k2-api.h"

namespace kphp::diagnostics {

namespace details {
inline size_t string_sizeof(const std::string_view& string) noexcept {
return sizeof(size_t) + string.size();
}

template<typename T>
concept tag_range = std::ranges::range<T> && requires(const std::ranges::range_value_t<T>& tag) {
Comment thread
apolyakov marked this conversation as resolved.
Outdated
std::string_view{tag.first};
std::string_view{tag.second};
};
} // namespace details

struct metric final {
private:
using bytes_vector = kphp::stl::vector<std::byte, kphp::memory::script_allocator>;

const bytes_vector buffer;
k2::MonitoringSystem ms;

explicit metric(bytes_vector buffer, k2::MonitoringSystem ms) noexcept
Comment thread
apolyakov marked this conversation as resolved.
Outdated
: buffer{std::move(buffer)},
ms{ms} {}

template<typename T>
requires std::is_arithmetic_v<T>
static void store_number(bytes_vector& buf, const T& number) noexcept {
const auto* src{static_cast<const std::byte*>(static_cast<const void*>(std::addressof(number)))};
buf.insert(buf.end(), src, src + sizeof(T));
}

static void store_string(bytes_vector& buf, const std::string_view& string) noexcept {
metric::store_number(buf, string.size());
buf.append_range(std::as_bytes(std::span{string.data(), string.size()}));
}

static void store_tag(bytes_vector& buf, std::string_view tag_name, std::string_view tag_value) noexcept {
metric::store_string(buf, tag_name);
metric::store_string(buf, tag_value);
}

template<details::tag_range TagRange>
static void store_msg(bytes_vector& buf, std::string_view metric_name, size_t msg_size, TagRange&& tags) noexcept {
metric::store_number(buf, msg_size);
metric::store_string(buf, metric_name);
for (const auto& [tag_name, tag_value] : tags) {
metric::store_tag(buf, std::string_view{tag_name}, std::string_view{tag_value});
}
}

static uint32_t s_timestamp_now() noexcept {
Comment thread
apolyakov marked this conversation as resolved.
Outdated
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}

template<details::tag_range TagRange>
static size_t calc_msg_size(std::string_view metric_name, TagRange&& tags) noexcept {
size_t result{kphp::diagnostics::details::string_sizeof(metric_name)};
for (const auto& [tag_name, tag_value] : tags) {
result += kphp::diagnostics::details::string_sizeof(std::string_view{tag_name}) + kphp::diagnostics::details::string_sizeof(std::string_view{tag_value});
}
return result;
}

public:
template<details::tag_range TagRange>
static metric from_value(k2::MonitoringSystem ms, std::string_view metric_name, TagRange&& tags, double value,
std::optional<uint32_t> timestamp = std::nullopt) noexcept {
size_t msg_size{metric::calc_msg_size(metric_name, tags)};

bytes_vector buf{};
buf.reserve(sizeof(uint32_t) + sizeof(uint8_t) + sizeof(double) + sizeof(size_t) +
msg_size); // timestamp_u32 + value_kind_u8 + value_f64 + msg_size_usize + msg_len

uint32_t s_timestamp{timestamp.value_or(metric::s_timestamp_now())};

metric::store_number(buf, s_timestamp);
metric::store_number(buf, static_cast<uint8_t>(k2::MetricValueKind::VALUE));
metric::store_number(buf, value);
metric::store_msg(buf, metric_name, msg_size, tags);
return metric{std::move(buf), ms};
}

template<details::tag_range TagRange>
static metric from_values_array(k2::MonitoringSystem ms, std::string_view metric_name, TagRange&& tags, std::span<const double> values,
std::optional<uint32_t> timestamp = std::nullopt) noexcept {
size_t msg_size{metric::calc_msg_size(metric_name, tags)};

bytes_vector buf{};
buf.reserve(sizeof(uint32_t) + sizeof(uint8_t) + sizeof(size_t) + sizeof(double) * values.size() + sizeof(size_t) +
msg_size); // timestamp_u32 + value_kind_u8 + array_len_usize + value_f64*array_len + msg_size_usize + msg_len

uint32_t s_timestamp{timestamp.value_or(metric::s_timestamp_now())};

metric::store_number(buf, s_timestamp);
metric::store_number(buf, static_cast<uint8_t>(k2::MetricValueKind::VALUES_ARRAY));
metric::store_number(buf, values.size());
for (const auto& value : values) {
metric::store_number(buf, value);
}
metric::store_msg(buf, metric_name, msg_size, tags);
return metric{std::move(buf), ms};
}

template<details::tag_range TagRange>
static metric from_count(k2::MonitoringSystem ms, std::string_view metric_name, TagRange&& tags, uint32_t count,
std::optional<uint32_t> timestamp = std::nullopt) noexcept {
size_t msg_size{metric::calc_msg_size(metric_name, tags)};

bytes_vector buf{};
buf.reserve(sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(size_t) +
msg_size); // timestamp_u32 + value_kind_u8 + count_u32 + msg_size_usize + msg_len

uint32_t s_timestamp{timestamp.value_or(metric::s_timestamp_now())};

metric::store_number(buf, s_timestamp);
metric::store_number(buf, static_cast<uint8_t>(k2::MetricValueKind::COUNT));
metric::store_number(buf, count);
metric::store_msg(buf, metric_name, msg_size, tags);

return metric{std::move(buf), ms};
}

template<details::tag_range TagRange>
static metric from_increment(k2::MonitoringSystem ms, std::string_view metric_name, TagRange&& tags,
std::optional<uint32_t> timestamp = std::nullopt) noexcept {
size_t msg_size{metric::calc_msg_size(metric_name, tags)};

bytes_vector buf{};
buf.reserve(sizeof(uint32_t) + sizeof(uint8_t) + sizeof(size_t) + msg_size); // timestamp_u32 + value_kind_u8 + msg_size_usize + msg_len

uint32_t s_timestamp{timestamp.value_or(metric::s_timestamp_now())};

metric::store_number(buf, s_timestamp);
metric::store_number(buf, static_cast<uint8_t>(k2::MetricValueKind::INC));
metric::store_msg(buf, metric_name, msg_size, tags);
return metric{std::move(buf), ms};
}

std::expected<void, int32_t> send() noexcept {
Comment thread
apolyakov marked this conversation as resolved.
Outdated
return k2::write_metric(this->buffer, this->ms);
}
};

// ---------------------------------------------------------------------------------------------------------

struct metric_builder final {
private:
kphp::stl::string<kphp::memory::script_allocator> metric_name;
kphp::stl::vector<std::pair<kphp::stl::string<kphp::memory::script_allocator>, kphp::stl::string<kphp::memory::script_allocator>>,
kphp::memory::script_allocator>
tags;
k2::MonitoringSystem ms;

explicit metric_builder(std::string_view metric_name, k2::MonitoringSystem ms) noexcept
Comment thread
apolyakov marked this conversation as resolved.
Outdated
: metric_name{metric_name},
ms{ms} {}

public:
static metric_builder metric(std::string_view metric_name, k2::MonitoringSystem ms) noexcept {
return metric_builder{metric_name, ms};
}

metric_builder& tag(std::string_view tag_name, std::string_view tag_value) noexcept {
this->tags.emplace_back(tag_name, tag_value);
return *this;
}

kphp::diagnostics::metric build_value(double value, std::optional<uint32_t> timestamp = std::nullopt) const noexcept {
return metric::from_value(this->ms, this->metric_name, this->tags, value, timestamp);
}

kphp::diagnostics::metric build_values_array(std::span<const double> values, std::optional<uint32_t> timestamp = std::nullopt) const noexcept {
return metric::from_values_array(this->ms, this->metric_name, this->tags, values, timestamp);
}

kphp::diagnostics::metric build_count(uint32_t count, std::optional<uint32_t> timestamp = std::nullopt) const noexcept {
return metric::from_count(this->ms, this->metric_name, this->tags, count, timestamp);
}

kphp::diagnostics::metric build_increment(std::optional<uint32_t> timestamp = std::nullopt) const noexcept {
return metric::from_increment(this->ms, this->metric_name, this->tags, timestamp);
}
};
} // namespace kphp::diagnostics
Loading