46namespace kcenon {
namespace monitoring {
58 "Max samples must be positive");
85 double min_value = (std::numeric_limits<double>::max)();
86 double max_value = (std::numeric_limits<double>::lowest)();
112 if (sorted_values.empty()) {
115 if (sorted_values.size() == 1) {
116 return sorted_values[0];
119 double rank = (percentile / 100.0) * (sorted_values.size() - 1);
120 size_t lower_idx =
static_cast<size_t>(rank);
121 size_t upper_idx = lower_idx + 1;
122 double fraction = rank - lower_idx;
124 if (upper_idx >= sorted_values.size()) {
125 return sorted_values[lower_idx];
128 return sorted_values[lower_idx] +
129 fraction * (sorted_values[upper_idx] - sorted_values[lower_idx]);
141 const std::vector<double>& values,
142 std::chrono::system_clock::time_point oldest_timestamp,
143 std::chrono::system_clock::time_point newest_timestamp) {
148 if (values.empty()) {
158 for (
double val : values) {
163 stats.
avg = sum / values.size();
165 double variance = 0.0;
166 for (
double val : values) {
167 double diff = val - stats.
avg;
168 variance += diff * diff;
170 stats.
stddev = std::sqrt(variance / values.size());
172 std::vector<double> sorted_values = values;
173 std::sort(sorted_values.begin(), sorted_values.end());
193template <
typename Sample>
204 return logical_index;
217 throw std::invalid_argument(
"Max samples must be positive");
228 std::lock_guard<std::mutex> lock(
mutex_);
238 template <
typename Duration>
240 auto cutoff = std::chrono::system_clock::now() - duration;
245 std::chrono::system_clock::time_point since)
const {
246 std::lock_guard<std::mutex> lock(
mutex_);
248 std::vector<Sample> result;
251 for (
size_t i = 0; i <
count_; ++i) {
253 if (sample.timestamp >= since) {
254 result.push_back(sample);
258 std::sort(result.begin(), result.end(),
259 [](
const Sample& a,
const Sample& b) {
260 return a.timestamp < b.timestamp;
267 std::lock_guard<std::mutex> lock(
mutex_);
269 std::vector<Sample> result;
272 for (
size_t i = 0; i <
count_; ++i) {
276 std::sort(result.begin(), result.end(),
277 [](
const Sample& a,
const Sample& b) {
278 return a.timestamp < b.timestamp;
285 std::lock_guard<std::mutex> lock(
mutex_);
292 return common::ok(
buffer_[latest_idx]);
296 std::lock_guard<std::mutex> lock(
mutex_);
301 std::lock_guard<std::mutex> lock(
mutex_);
308 std::lock_guard<std::mutex> lock(
mutex_);
332 static_assert(std::is_arithmetic_v<T>,
"T must be an arithmetic type");
344 :
buffer_(config.max_samples) {
345 auto validation = config.validate();
346 if (validation.is_err()) {
347 throw std::invalid_argument(
"Invalid time_series_buffer configuration: " +
348 validation.error().message);
363 std::chrono::system_clock::time_point timestamp =
364 std::chrono::system_clock::now()) {
373 template <
typename Duration>
374 std::vector<time_series_sample<T>>
get_samples(Duration duration)
const {
375 return buffer_.get_samples(duration);
384 std::chrono::system_clock::time_point since)
const {
385 return buffer_.get_samples_since(since);
393 return buffer_.get_all_samples();
401 template <
typename Duration>
421 auto sample_result =
buffer_.get_latest();
422 if (sample_result.is_err()) {
423 return common::Result<T>::err(sample_result.error());
425 return common::ok(sample_result.value().value);
468 return buffer_.memory_footprint();
474 if (samples.empty()) {
482 std::vector<double> values;
483 values.reserve(samples.size());
484 for (
const auto& sample : samples) {
485 values.push_back(
static_cast<double>(sample.value));
490 samples.front().timestamp,
491 samples.back().timestamp);
548 void add_sample(
double load_1m,
double load_5m,
double load_15m,
549 std::chrono::system_clock::time_point timestamp =
550 std::chrono::system_clock::now()) {
559 template <
typename Duration>
560 std::vector<load_average_sample>
get_samples(Duration duration)
const {
561 return buffer_.get_samples(duration);
570 std::chrono::system_clock::time_point since)
const {
571 return buffer_.get_samples_since(since);
579 return buffer_.get_all_samples();
587 template <
typename Duration>
642 return buffer_.memory_footprint();
647 const std::vector<load_average_sample>& samples) {
650 if (samples.empty()) {
659 std::vector<double> values_1m, values_5m, values_15m;
660 values_1m.reserve(samples.size());
661 values_5m.reserve(samples.size());
662 values_15m.reserve(samples.size());
664 for (
const auto& sample : samples) {
665 values_1m.push_back(sample.load_1m);
666 values_5m.push_back(sample.load_5m);
667 values_15m.push_back(sample.load_15m);
670 auto oldest = samples.front().timestamp;
671 auto newest = samples.back().timestamp;
Generic time-series ring buffer base template.
void add_sample(const Sample &sample)
std::vector< Sample > get_all_samples() const
time_series_ring_buffer & operator=(time_series_ring_buffer &&)=delete
time_series_ring_buffer(size_t max_samples)
Constructor with maximum sample count.
std::vector< Sample > buffer_
time_series_ring_buffer & operator=(const time_series_ring_buffer &)=delete
size_t memory_footprint() const noexcept
std::vector< Sample > get_samples(Duration duration) const
bool empty() const noexcept
size_t capacity() const noexcept
time_series_ring_buffer(const time_series_ring_buffer &)=delete
common::Result< Sample > get_latest() const
size_t get_actual_index(size_t logical_index) const noexcept
std::vector< Sample > get_samples_since(std::chrono::system_clock::time_point since) const
size_t size() const noexcept
time_series_ring_buffer(time_series_ring_buffer &&)=delete
Specialized buffer for tracking load average history.
detail::time_series_ring_buffer< load_average_sample > buffer_
std::vector< load_average_sample > get_samples_since(std::chrono::system_clock::time_point since) const
Get samples since a specific timestamp.
void clear() noexcept
Clear all samples.
std::vector< load_average_sample > get_all_samples() const
Get all samples in chronological order.
load_average_history & operator=(const load_average_history &)=delete
load_average_history(size_t max_samples=1000)
std::vector< load_average_sample > get_samples(Duration duration) const
Get samples within a duration from now.
size_t capacity() const noexcept
Get buffer capacity.
load_average_history & operator=(load_average_history &&)=delete
size_t memory_footprint() const noexcept
Get memory footprint in bytes.
load_average_statistics get_statistics(Duration duration) const
Get statistics for samples within a duration.
common::Result< load_average_sample > get_latest() const
Get the latest sample.
size_t size() const noexcept
Get current number of samples.
bool empty() const noexcept
Check if buffer is empty.
load_average_history(const load_average_history &)=delete
static load_average_statistics calculate_statistics(const std::vector< load_average_sample > &samples)
load_average_history(load_average_history &&)=delete
load_average_statistics get_statistics() const
Get statistics for all samples.
void add_sample(double load_1m, double load_5m, double load_15m, std::chrono::system_clock::time_point timestamp=std::chrono::system_clock::now())
Add a load average sample.
Thread-safe ring buffer for time-series data with statistics.
size_t capacity() const noexcept
Get buffer capacity.
std::vector< time_series_sample< T > > get_samples_since(std::chrono::system_clock::time_point since) const
Get samples since a specific timestamp.
time_series_statistics get_statistics() const
Get statistics for all samples.
common::Result< time_series_sample< T > > get_latest_sample() const
Get the latest sample with timestamp.
time_series_statistics get_statistics(Duration duration) const
Get statistics for samples within a duration.
size_t size() const noexcept
Get current number of samples.
bool empty() const noexcept
Check if buffer is empty.
size_t memory_footprint() const noexcept
Get memory footprint in bytes.
time_series_buffer(const time_series_buffer_config &config={})
Constructor with configuration.
void clear() noexcept
Clear all samples.
static time_series_statistics calculate_statistics(const std::vector< time_series_sample< T > > &samples)
std::vector< time_series_sample< T > > get_samples(Duration duration) const
Get samples within a duration from now.
common::Result< T > get_latest() const
Get the latest sample value.
time_series_buffer & operator=(const time_series_buffer &)=delete
time_series_buffer & operator=(time_series_buffer &&)=delete
std::vector< time_series_sample< T > > get_all_samples() const
Get all samples in chronological order.
detail::time_series_ring_buffer< time_series_sample< T > > buffer_
time_series_buffer(const time_series_buffer &)=delete
void add_sample(T value, std::chrono::system_clock::time_point timestamp=std::chrono::system_clock::now())
Add a sample to the buffer.
time_series_buffer(time_series_buffer &&)=delete
Monitoring system specific error codes.
Internal implementation details - not part of public API.
time_series_statistics calculate_basic_statistics(const std::vector< double > &values, std::chrono::system_clock::time_point oldest_timestamp, std::chrono::system_clock::time_point newest_timestamp)
Calculate basic statistics from a vector of double values.
double calculate_percentile(const std::vector< double > &sorted_values, double percentile)
Calculate percentile from sorted values.
Result pattern type definitions for monitoring system.
Extended error information with context.
common::error_info to_common_error() const
Convert to common_system error_info.
Sample containing all three load averages.
load_average_sample() noexcept
load_average_sample(std::chrono::system_clock::time_point ts, double l1, double l5, double l15) noexcept
std::chrono::system_clock::time_point timestamp
Statistics for load average history.
time_series_statistics load_15m_stats
time_series_statistics load_5m_stats
time_series_statistics load_1m_stats
Configuration for time series buffer.
common::VoidResult validate() const
Single sample in time series with timestamp.
time_series_sample(std::chrono::system_clock::time_point ts, T val) noexcept
std::chrono::system_clock::time_point timestamp
time_series_sample() noexcept
Statistics calculated from time series data.
std::chrono::system_clock::time_point newest_timestamp
std::chrono::system_clock::time_point oldest_timestamp