Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
kcenon::logger::sampling::log_sampler Class Reference

Thread-safe log sampler with multiple strategy support. More...

#include <log_sampler.h>

Collaboration diagram for kcenon::logger::sampling::log_sampler:
Collaboration graph

Public Member Functions

 log_sampler (const sampling_config &config=sampling_config{})
 Construct a sampler with the given configuration.
 
 ~log_sampler ()=default
 Destructor.
 
 log_sampler (const log_sampler &)=delete
 Copy constructor (deleted)
 
log_sampleroperator= (const log_sampler &)=delete
 Copy assignment operator (deleted)
 
 log_sampler (log_sampler &&other) noexcept
 Move constructor.
 
log_sampleroperator= (log_sampler &&other) noexcept
 Move assignment operator.
 
bool should_sample (const log_entry &entry)
 Check if a log entry should be sampled (logged)
 
bool should_sample (log_level level, const std::string &message)
 Check if a log entry should be sampled (without category)
 
bool should_sample (log_level level, const std::string &message, const std::optional< std::string > &category)
 Check if a log entry should be sampled with category.
 
void set_config (const sampling_config &config)
 Update the sampling configuration.
 
sampling_config get_config () const
 Get the current configuration.
 
sampling_stats get_stats () const
 Get sampling statistics.
 
void reset_stats ()
 Reset sampling statistics.
 
bool is_enabled () const
 Check if sampling is enabled.
 
void set_enabled (bool enabled)
 Enable or disable sampling.
 
double get_effective_rate () const
 Get the current effective sampling rate.
 

Private Member Functions

bool should_bypass_level (log_level level) const
 Check if a level should bypass sampling.
 
bool should_bypass_field (const log_entry &entry) const
 Check if any field should bypass sampling.
 
double get_field_rate (const log_entry &entry) const
 Get the sampling rate based on structured fields.
 
double get_category_rate (const std::string &category) const
 Get the sampling rate for a category.
 
bool random_sample (double rate)
 Perform random sampling decision.
 
bool rate_limit_sample ()
 Perform rate limiting sampling decision.
 
bool adaptive_sample ()
 Perform adaptive sampling decision.
 
bool hash_sample (const std::string &message, double rate)
 Perform hash-based sampling decision.
 
void update_adaptive_rate ()
 Update adaptive sampling rate based on current throughput.
 
std::uint64_t xorshift64 ()
 Fast xorshift64 PRNG for random sampling.
 

Static Private Member Functions

static std::uint64_t fnv1a_hash (const std::string &str)
 FNV-1a hash for message hashing.
 

Private Attributes

sampling_config config_
 
std::shared_mutex config_mutex_
 
std::atomic< std::uint64_t > total_count_ {0}
 
std::atomic< std::uint64_t > sampled_count_ {0}
 
std::atomic< std::uint64_t > dropped_count_ {0}
 
std::atomic< std::uint64_t > bypassed_count_ {0}
 
std::atomic< std::uint64_t > rng_state_
 
std::atomic< std::uint64_t > rate_limit_count_ {0}
 
std::atomic< std::chrono::steady_clock::time_point::rep > rate_limit_window_start_ {0}
 
std::mutex rate_limit_mutex_
 
std::atomic< double > effective_rate_ {1.0}
 
std::atomic< std::uint64_t > adaptive_window_count_ {0}
 
std::atomic< std::chrono::steady_clock::time_point::rep > adaptive_window_start_ {0}
 
std::atomic< bool > is_throttling_ {false}
 

Detailed Description

Thread-safe log sampler with multiple strategy support.

The log_sampler class determines whether a log entry should be logged based on the configured sampling strategy. It maintains thread-safe statistics and supports runtime configuration updates.

Key features:

  • Thread-safe operation for high-concurrency scenarios
  • Multiple sampling strategies (random, rate limiting, adaptive, hash-based)
  • Level-based bypass for critical messages
  • Per-category sampling rates
  • Real-time statistics tracking
Note
The sampler uses fast PRNG (xorshift64) for random sampling to minimize overhead in the hot path.
Since
3.3.0

Definition at line 71 of file log_sampler.h.

Constructor & Destructor Documentation

◆ log_sampler() [1/3]

kcenon::logger::sampling::log_sampler::log_sampler ( const sampling_config & config = sampling_config{})
explicit

Construct a sampler with the given configuration.

Parameters
configSampling configuration

Definition at line 16 of file log_sampler.cpp.

17 : config_(config),
18 rng_state_(static_cast<std::uint64_t>(
19 std::chrono::steady_clock::now().time_since_epoch().count()) | 1) {
20 // Initialize rate limiting window
21 auto now = std::chrono::steady_clock::now();
22 rate_limit_window_start_.store(now.time_since_epoch().count());
23 adaptive_window_start_.store(now.time_since_epoch().count());
25}
std::atomic< std::chrono::steady_clock::time_point::rep > rate_limit_window_start_
std::atomic< std::chrono::steady_clock::time_point::rep > adaptive_window_start_
std::atomic< std::uint64_t > rng_state_
std::atomic< double > effective_rate_
double rate
Base sampling rate (0.0 to 1.0)

References adaptive_window_start_, config_, effective_rate_, kcenon::logger::sampling::sampling_config::rate, and rate_limit_window_start_.

◆ ~log_sampler()

kcenon::logger::sampling::log_sampler::~log_sampler ( )
default

Destructor.

◆ log_sampler() [2/3]

kcenon::logger::sampling::log_sampler::log_sampler ( const log_sampler & )
delete

Copy constructor (deleted)

Samplers contain atomic state and should not be copied

◆ log_sampler() [3/3]

kcenon::logger::sampling::log_sampler::log_sampler ( log_sampler && other)
noexcept

Move constructor.

Definition at line 27 of file log_sampler.cpp.

28 : config_(std::move(other.config_)),
29 total_count_(other.total_count_.load()),
30 sampled_count_(other.sampled_count_.load()),
31 dropped_count_(other.dropped_count_.load()),
32 bypassed_count_(other.bypassed_count_.load()),
33 rng_state_(other.rng_state_.load()),
34 rate_limit_count_(other.rate_limit_count_.load()),
35 rate_limit_window_start_(other.rate_limit_window_start_.load()),
36 effective_rate_(other.effective_rate_.load()),
37 adaptive_window_count_(other.adaptive_window_count_.load()),
38 adaptive_window_start_(other.adaptive_window_start_.load()),
39 is_throttling_(other.is_throttling_.load()) {
40}
std::atomic< std::uint64_t > sampled_count_
std::atomic< std::uint64_t > rate_limit_count_
std::atomic< std::uint64_t > bypassed_count_
std::atomic< std::uint64_t > total_count_
std::atomic< std::uint64_t > adaptive_window_count_
std::atomic< std::uint64_t > dropped_count_

Member Function Documentation

◆ adaptive_sample()

bool kcenon::logger::sampling::log_sampler::adaptive_sample ( )
nodiscardprivate

Perform adaptive sampling decision.

Returns
true if the message should be sampled

Definition at line 364 of file log_sampler.cpp.

364 {
365 // Update adaptive rate if needed
367
368 // Use effective rate for sampling
369 double rate = effective_rate_.load(std::memory_order_relaxed);
370 return random_sample(rate);
371}
bool random_sample(double rate)
Perform random sampling decision.
void update_adaptive_rate()
Update adaptive sampling rate based on current throughput.

References effective_rate_, random_sample(), and update_adaptive_rate().

Referenced by should_sample(), and should_sample().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fnv1a_hash()

std::uint64_t kcenon::logger::sampling::log_sampler::fnv1a_hash ( const std::string & str)
staticnodiscardprivate

FNV-1a hash for message hashing.

Parameters
strString to hash
Returns
64-bit hash value

Definition at line 449 of file log_sampler.cpp.

449 {
450 constexpr std::uint64_t kFnvOffsetBasis = 14695981039346656037ULL;
451 constexpr std::uint64_t kFnvPrime = 1099511628211ULL;
452
453 std::uint64_t hash = kFnvOffsetBasis;
454 for (char c : str) {
455 hash ^= static_cast<std::uint64_t>(static_cast<unsigned char>(c));
456 hash *= kFnvPrime;
457 }
458 return hash;
459}

Referenced by hash_sample().

Here is the caller graph for this function:

◆ get_category_rate()

double kcenon::logger::sampling::log_sampler::get_category_rate ( const std::string & category) const
nodiscardprivate

Get the sampling rate for a category.

Parameters
categoryCategory name (empty for default rate)
Returns
Sampling rate for the category

Definition at line 296 of file log_sampler.cpp.

296 {
297 std::shared_lock<std::shared_mutex> lock(config_mutex_);
298 auto it = config_.category_rates.find(category);
299 if (it != config_.category_rates.end()) {
300 return it->second;
301 }
302 return config_.rate;
303}
std::unordered_map< std::string, double > category_rates

References kcenon::logger::sampling::sampling_config::category_rates, config_, config_mutex_, and kcenon::logger::sampling::sampling_config::rate.

Referenced by should_sample(), and should_sample().

Here is the caller graph for this function:

◆ get_config()

sampling_config kcenon::logger::sampling::log_sampler::get_config ( ) const
nodiscard

Get the current configuration.

Returns
Copy of the current configuration

Definition at line 200 of file log_sampler.cpp.

200 {
201 std::shared_lock<std::shared_mutex> lock(config_mutex_);
202 return config_;
203}

References config_, and config_mutex_.

◆ get_effective_rate()

double kcenon::logger::sampling::log_sampler::get_effective_rate ( ) const
nodiscard

Get the current effective sampling rate.

Returns
Current effective rate (may differ from configured rate in adaptive mode)

Definition at line 233 of file log_sampler.cpp.

233 {
234 return effective_rate_.load(std::memory_order_relaxed);
235}

References effective_rate_.

◆ get_field_rate()

double kcenon::logger::sampling::log_sampler::get_field_rate ( const log_entry & entry) const
nodiscardprivate

Get the sampling rate based on structured fields.

Parameters
entryLog entry with fields to check
Returns
Sampling rate for the matching field value, or -1 if no match
Since
3.4.0

Definition at line 259 of file log_sampler.cpp.

259 {
260 if (!entry.fields.has_value()) {
261 return -1.0; // No fields, use default rate
262 }
263
264 std::shared_lock<std::shared_mutex> lock(config_mutex_);
265 const auto& field_rates = config_.field_rates;
266
267 // Check each configured field
268 for (const auto& [field_name, value_rates] : field_rates) {
269 auto field_it = entry.fields->find(field_name);
270 if (field_it == entry.fields->end()) {
271 continue;
272 }
273
274 // Convert field value to string for lookup
275 std::string value_str;
276 if (std::holds_alternative<std::string>(field_it->second)) {
277 value_str = std::get<std::string>(field_it->second);
278 } else if (std::holds_alternative<int64_t>(field_it->second)) {
279 value_str = std::to_string(std::get<int64_t>(field_it->second));
280 } else if (std::holds_alternative<double>(field_it->second)) {
281 value_str = std::to_string(std::get<double>(field_it->second));
282 } else if (std::holds_alternative<bool>(field_it->second)) {
283 value_str = std::get<bool>(field_it->second) ? "true" : "false";
284 }
285
286 // Look up the rate for this value
287 auto rate_it = value_rates.find(value_str);
288 if (rate_it != value_rates.end()) {
289 return rate_it->second;
290 }
291 }
292
293 return -1.0; // No matching field/value rate found
294}
std::unordered_map< std::string, std::unordered_map< std::string, double > > field_rates

References config_, config_mutex_, kcenon::logger::sampling::sampling_config::field_rates, and kcenon::logger::log_entry::fields.

Referenced by should_sample().

Here is the caller graph for this function:

◆ get_stats()

sampling_stats kcenon::logger::sampling::log_sampler::get_stats ( ) const
nodiscard

Get sampling statistics.

Returns
Copy of current statistics
Note
Thread-safe. Returns a snapshot of current statistics.

Definition at line 205 of file log_sampler.cpp.

205 {
206 sampling_stats stats;
207 stats.total_count = total_count_.load(std::memory_order_relaxed);
208 stats.sampled_count = sampled_count_.load(std::memory_order_relaxed);
209 stats.dropped_count = dropped_count_.load(std::memory_order_relaxed);
210 stats.bypassed_count = bypassed_count_.load(std::memory_order_relaxed);
211 stats.effective_rate = effective_rate_.load(std::memory_order_relaxed);
212 stats.is_throttling = is_throttling_.load(std::memory_order_relaxed);
213 return stats;
214}

References kcenon::logger::sampling::sampling_stats::bypassed_count, bypassed_count_, kcenon::logger::sampling::sampling_stats::dropped_count, dropped_count_, kcenon::logger::sampling::sampling_stats::effective_rate, effective_rate_, kcenon::logger::sampling::sampling_stats::is_throttling, is_throttling_, kcenon::logger::sampling::sampling_stats::sampled_count, sampled_count_, kcenon::logger::sampling::sampling_stats::total_count, and total_count_.

◆ hash_sample()

bool kcenon::logger::sampling::log_sampler::hash_sample ( const std::string & message,
double rate )
nodiscardprivate

Perform hash-based sampling decision.

Parameters
messageMessage to hash
rateSampling rate to use
Returns
true if the message should be sampled

Definition at line 416 of file log_sampler.cpp.

416 {
417 if (rate >= 1.0) {
418 return true;
419 }
420 if (rate <= 0.0) {
421 return false;
422 }
423
424 std::uint64_t seed = 0;
425 {
426 std::shared_lock<std::shared_mutex> lock(config_mutex_);
427 seed = config_.hash_seed;
428 }
429
430 // Compute hash of message
431 std::uint64_t hash = fnv1a_hash(message);
432 hash ^= seed;
433
434 // Normalize hash to [0, 1) and compare with rate
435 double normalized = static_cast<double>(hash) /
436 static_cast<double>(std::numeric_limits<std::uint64_t>::max());
437 return normalized < rate;
438}
static std::uint64_t fnv1a_hash(const std::string &str)
FNV-1a hash for message hashing.
std::uint64_t hash_seed
Seed for hash-based sampling.

References config_, config_mutex_, fnv1a_hash(), and kcenon::logger::sampling::sampling_config::hash_seed.

Referenced by should_sample(), and should_sample().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_enabled()

bool kcenon::logger::sampling::log_sampler::is_enabled ( ) const
nodiscard

Check if sampling is enabled.

Returns
true if sampling is currently enabled

Definition at line 223 of file log_sampler.cpp.

223 {
224 std::shared_lock<std::shared_mutex> lock(config_mutex_);
225 return config_.enabled;
226}
bool enabled
Enable or disable sampling.

References config_, config_mutex_, and kcenon::logger::sampling::sampling_config::enabled.

◆ operator=() [1/2]

log_sampler & kcenon::logger::sampling::log_sampler::operator= ( const log_sampler & )
delete

Copy assignment operator (deleted)

◆ operator=() [2/2]

log_sampler & kcenon::logger::sampling::log_sampler::operator= ( log_sampler && other)
noexcept

Move assignment operator.

Definition at line 42 of file log_sampler.cpp.

42 {
43 if (this != &other) {
44 std::unique_lock<std::shared_mutex> lock(config_mutex_);
45 config_ = std::move(other.config_);
46 total_count_.store(other.total_count_.load());
47 sampled_count_.store(other.sampled_count_.load());
48 dropped_count_.store(other.dropped_count_.load());
49 bypassed_count_.store(other.bypassed_count_.load());
50 rng_state_.store(other.rng_state_.load());
51 rate_limit_count_.store(other.rate_limit_count_.load());
52 rate_limit_window_start_.store(other.rate_limit_window_start_.load());
53 effective_rate_.store(other.effective_rate_.load());
54 adaptive_window_count_.store(other.adaptive_window_count_.load());
55 adaptive_window_start_.store(other.adaptive_window_start_.load());
56 is_throttling_.store(other.is_throttling_.load());
57 }
58 return *this;
59}

◆ random_sample()

bool kcenon::logger::sampling::log_sampler::random_sample ( double rate)
nodiscardprivate

Perform random sampling decision.

Parameters
rateSampling rate to use
Returns
true if the message should be sampled (logged)

Definition at line 305 of file log_sampler.cpp.

305 {
306 if (rate >= 1.0) {
307 return true;
308 }
309 if (rate <= 0.0) {
310 return false;
311 }
312
313 // Generate random number and compare with threshold
314 std::uint64_t random = xorshift64();
315 double normalized = static_cast<double>(random) /
316 static_cast<double>(std::numeric_limits<std::uint64_t>::max());
317 return normalized < rate;
318}
std::uint64_t xorshift64()
Fast xorshift64 PRNG for random sampling.
@ random
Simple random sampling based on probability.

References kcenon::logger::sampling::random, and xorshift64().

Referenced by adaptive_sample(), should_sample(), and should_sample().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rate_limit_sample()

bool kcenon::logger::sampling::log_sampler::rate_limit_sample ( )
nodiscardprivate

Perform rate limiting sampling decision.

Returns
true if the message is within rate limits

Definition at line 320 of file log_sampler.cpp.

320 {
321 auto now = std::chrono::steady_clock::now();
322 auto now_count = now.time_since_epoch().count();
323
324 std::size_t limit_per_second = 0;
325 std::size_t window_ms = 0;
326 {
327 std::shared_lock<std::shared_mutex> lock(config_mutex_);
328 limit_per_second = config_.rate_limit_per_second;
329 window_ms = config_.rate_limit_window_ms;
330 }
331
332 // Calculate max messages per window
333 std::size_t max_per_window = (limit_per_second * window_ms) / 1000;
334 if (max_per_window == 0) {
335 max_per_window = 1;
336 }
337
338 // Check if we need to reset the window
339 auto window_start = std::chrono::steady_clock::time_point(
340 std::chrono::steady_clock::duration(rate_limit_window_start_.load()));
341 auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
342 now - window_start).count();
343
344 if (static_cast<std::size_t>(elapsed_ms) >= window_ms) {
345 // Reset window
346 std::lock_guard<std::mutex> lock(rate_limit_mutex_);
347 // Double-check after acquiring lock
348 window_start = std::chrono::steady_clock::time_point(
349 std::chrono::steady_clock::duration(rate_limit_window_start_.load()));
350 elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
351 now - window_start).count();
352
353 if (static_cast<std::size_t>(elapsed_ms) >= window_ms) {
354 rate_limit_window_start_.store(now_count);
355 rate_limit_count_.store(0);
356 }
357 }
358
359 // Check if within limit
360 std::uint64_t current = rate_limit_count_.fetch_add(1, std::memory_order_relaxed);
361 return current < max_per_window;
362}
std::size_t rate_limit_per_second
Maximum logs per second for rate limiting strategy.
std::size_t rate_limit_window_ms
Time window for rate limiting (milliseconds)

References config_, config_mutex_, rate_limit_count_, rate_limit_mutex_, kcenon::logger::sampling::sampling_config::rate_limit_per_second, kcenon::logger::sampling::sampling_config::rate_limit_window_ms, and rate_limit_window_start_.

Referenced by should_sample(), and should_sample().

Here is the caller graph for this function:

◆ reset_stats()

void kcenon::logger::sampling::log_sampler::reset_stats ( )

Reset sampling statistics.

Note
Thread-safe. Does not affect the configuration.

Definition at line 216 of file log_sampler.cpp.

216 {
217 total_count_.store(0, std::memory_order_relaxed);
218 sampled_count_.store(0, std::memory_order_relaxed);
219 dropped_count_.store(0, std::memory_order_relaxed);
220 bypassed_count_.store(0, std::memory_order_relaxed);
221}

References bypassed_count_, dropped_count_, sampled_count_, and total_count_.

◆ set_config()

void kcenon::logger::sampling::log_sampler::set_config ( const sampling_config & config)

Update the sampling configuration.

Parameters
configNew configuration to apply
Note
Thread-safe. Takes effect for subsequent sampling decisions.

Definition at line 194 of file log_sampler.cpp.

194 {
195 std::unique_lock<std::shared_mutex> lock(config_mutex_);
196 config_ = config;
197 effective_rate_.store(config.rate);
198}

References config_, config_mutex_, effective_rate_, and kcenon::logger::sampling::sampling_config::rate.

◆ set_enabled()

void kcenon::logger::sampling::log_sampler::set_enabled ( bool enabled)

Enable or disable sampling.

Parameters
enabledtrue to enable, false to disable

Definition at line 228 of file log_sampler.cpp.

228 {
229 std::unique_lock<std::shared_mutex> lock(config_mutex_);
230 config_.enabled = enabled;
231}

References config_, config_mutex_, and kcenon::logger::sampling::sampling_config::enabled.

◆ should_bypass_field()

bool kcenon::logger::sampling::log_sampler::should_bypass_field ( const log_entry & entry) const
nodiscardprivate

Check if any field should bypass sampling.

Parameters
entryLog entry to check
Returns
true if any configured bypass field is present
Since
3.4.0

Definition at line 243 of file log_sampler.cpp.

243 {
244 if (!entry.fields.has_value()) {
245 return false;
246 }
247
248 std::shared_lock<std::shared_mutex> lock(config_mutex_);
249 const auto& bypass_fields = config_.always_log_fields;
250
251 for (const auto& field_name : bypass_fields) {
252 if (entry.fields->find(field_name) != entry.fields->end()) {
253 return true; // Found a bypass field
254 }
255 }
256 return false;
257}
std::vector< std::string > always_log_fields

References kcenon::logger::sampling::sampling_config::always_log_fields, config_, config_mutex_, and kcenon::logger::log_entry::fields.

Referenced by should_sample().

Here is the caller graph for this function:

◆ should_bypass_level()

bool kcenon::logger::sampling::log_sampler::should_bypass_level ( log_level level) const
nodiscardprivate

Check if a level should bypass sampling.

Parameters
levelLog level to check
Returns
true if the level should always be logged

Definition at line 237 of file log_sampler.cpp.

237 {
238 std::shared_lock<std::shared_mutex> lock(config_mutex_);
239 const auto& levels = config_.always_log_levels;
240 return std::find(levels.begin(), levels.end(), level) != levels.end();
241}
std::vector< log_level > always_log_levels
Log levels that are never sampled (always logged)

References kcenon::logger::sampling::sampling_config::always_log_levels, config_, and config_mutex_.

Referenced by should_sample(), and should_sample().

Here is the caller graph for this function:

◆ should_sample() [1/3]

bool kcenon::logger::sampling::log_sampler::should_sample ( const log_entry & entry)
nodiscard

Check if a log entry should be sampled (logged)

Parameters
entryThe log entry to check
Returns
true if the entry should be logged, false if it should be dropped

This is the main entry point for sampling decisions. It considers:

  1. Whether sampling is enabled
  2. Whether the log level bypasses sampling
  3. Category-specific sampling rates
  4. The configured sampling strategy
Note
Thread-safe. Can be called from multiple threads concurrently.

Definition at line 61 of file log_sampler.cpp.

61 {
62 // Increment total count
63 total_count_.fetch_add(1, std::memory_order_relaxed);
64
65 // Check if sampling is enabled
66 bool enabled = false;
68 double rate = 1.0;
69 {
70 std::shared_lock<std::shared_mutex> lock(config_mutex_);
71 enabled = config_.enabled;
72 strategy = config_.strategy;
73 rate = config_.rate;
74 }
75
76 if (!enabled) {
77 sampled_count_.fetch_add(1, std::memory_order_relaxed);
78 return true; // Pass through when disabled
79 }
80
81 // Check if level bypasses sampling
82 if (should_bypass_level(entry.level)) {
83 bypassed_count_.fetch_add(1, std::memory_order_relaxed);
84 return true;
85 }
86
87 // Check if any field bypasses sampling (Phase 3.4)
88 if (should_bypass_field(entry)) {
89 bypassed_count_.fetch_add(1, std::memory_order_relaxed);
90 return true;
91 }
92
93 // Try to get field-specific rate first (Phase 3.4)
94 double effective = get_field_rate(entry);
95 if (effective < 0) {
96 // No field-specific rate found, try category
97 effective = rate;
98 if (entry.category.has_value()) {
99 effective = get_category_rate(entry.category->to_string());
100 }
101 }
102
103 // Apply the configured strategy
104 bool sampled = false;
105 switch (strategy) {
107 sampled = random_sample(effective);
108 break;
110 sampled = rate_limit_sample();
111 break;
113 sampled = adaptive_sample();
114 break;
116 sampled = hash_sample(entry.message.to_string(), effective);
117 break;
118 }
119
120 if (sampled) {
121 sampled_count_.fetch_add(1, std::memory_order_relaxed);
122 } else {
123 dropped_count_.fetch_add(1, std::memory_order_relaxed);
124 }
125
126 return sampled;
127}
bool adaptive_sample()
Perform adaptive sampling decision.
bool rate_limit_sample()
Perform rate limiting sampling decision.
double get_field_rate(const log_entry &entry) const
Get the sampling rate based on structured fields.
bool should_bypass_level(log_level level) const
Check if a level should bypass sampling.
bool should_bypass_field(const log_entry &entry) const
Check if any field should bypass sampling.
double get_category_rate(const std::string &category) const
Get the sampling rate for a category.
bool hash_sample(const std::string &message, double rate)
Perform hash-based sampling decision.
sampling_strategy
Defines the sampling algorithm to use.
@ hash_based
Deterministic sampling based on message hash.
@ rate_limiting
Rate-based sampling (N logs per time window)
@ adaptive
Adaptive sampling that adjusts based on volume.
sampling_strategy strategy
Sampling strategy to use.

References kcenon::logger::sampling::adaptive, adaptive_sample(), bypassed_count_, kcenon::logger::log_entry::category, config_, config_mutex_, dropped_count_, kcenon::logger::sampling::sampling_config::enabled, get_category_rate(), get_field_rate(), kcenon::logger::sampling::hash_based, hash_sample(), kcenon::logger::log_entry::level, kcenon::logger::log_entry::message, kcenon::logger::sampling::random, random_sample(), kcenon::logger::sampling::sampling_config::rate, rate_limit_sample(), kcenon::logger::sampling::rate_limiting, sampled_count_, should_bypass_field(), should_bypass_level(), kcenon::logger::sampling::sampling_config::strategy, kcenon::logger::small_string< SSO_SIZE >::to_string(), and total_count_.

Referenced by should_sample().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ should_sample() [2/3]

bool kcenon::logger::sampling::log_sampler::should_sample ( log_level level,
const std::string & message )
nodiscard

Check if a log entry should be sampled (without category)

Parameters
levelLog level
messageLog message (used for hash-based sampling)
Returns
true if the entry should be logged

Definition at line 129 of file log_sampler.cpp.

130 {
131 return should_sample(level, message, std::nullopt);
132}
bool should_sample(const log_entry &entry)
Check if a log entry should be sampled (logged)

References should_sample().

Here is the call graph for this function:

◆ should_sample() [3/3]

bool kcenon::logger::sampling::log_sampler::should_sample ( log_level level,
const std::string & message,
const std::optional< std::string > & category )
nodiscard

Check if a log entry should be sampled with category.

Parameters
levelLog level
messageLog message
categoryOptional category for category-specific rates
Returns
true if the entry should be logged

Definition at line 134 of file log_sampler.cpp.

136 {
137 // Increment total count
138 total_count_.fetch_add(1, std::memory_order_relaxed);
139
140 // Check if sampling is enabled
141 bool enabled = false;
143 double rate = 1.0;
144 {
145 std::shared_lock<std::shared_mutex> lock(config_mutex_);
146 enabled = config_.enabled;
147 strategy = config_.strategy;
148 rate = config_.rate;
149 }
150
151 if (!enabled) {
152 sampled_count_.fetch_add(1, std::memory_order_relaxed);
153 return true; // Pass through when disabled
154 }
155
156 // Check if level bypasses sampling
157 if (should_bypass_level(level)) {
158 bypassed_count_.fetch_add(1, std::memory_order_relaxed);
159 return true;
160 }
161
162 // Get category-specific rate if applicable
163 double effective = rate;
164 if (category.has_value()) {
165 effective = get_category_rate(category.value());
166 }
167
168 // Apply the configured strategy
169 bool sampled = false;
170 switch (strategy) {
172 sampled = random_sample(effective);
173 break;
175 sampled = rate_limit_sample();
176 break;
178 sampled = adaptive_sample();
179 break;
181 sampled = hash_sample(message, effective);
182 break;
183 }
184
185 if (sampled) {
186 sampled_count_.fetch_add(1, std::memory_order_relaxed);
187 } else {
188 dropped_count_.fetch_add(1, std::memory_order_relaxed);
189 }
190
191 return sampled;
192}

References kcenon::logger::sampling::adaptive, adaptive_sample(), bypassed_count_, config_, config_mutex_, dropped_count_, kcenon::logger::sampling::sampling_config::enabled, get_category_rate(), kcenon::logger::sampling::hash_based, hash_sample(), kcenon::logger::sampling::random, random_sample(), kcenon::logger::sampling::sampling_config::rate, rate_limit_sample(), kcenon::logger::sampling::rate_limiting, sampled_count_, should_bypass_level(), kcenon::logger::sampling::sampling_config::strategy, and total_count_.

Here is the call graph for this function:

◆ update_adaptive_rate()

void kcenon::logger::sampling::log_sampler::update_adaptive_rate ( )
private

Update adaptive sampling rate based on current throughput.

Definition at line 373 of file log_sampler.cpp.

373 {
374 auto now = std::chrono::steady_clock::now();
375 auto now_count = now.time_since_epoch().count();
376
377 std::size_t threshold = 0;
378 double min_rate = 0.01;
379 double base_rate = 1.0;
380 {
381 std::shared_lock<std::shared_mutex> lock(config_mutex_);
382 threshold = config_.adaptive_threshold;
383 min_rate = config_.adaptive_min_rate;
384 base_rate = config_.rate;
385 }
386
387 // Increment window count
388 adaptive_window_count_.fetch_add(1, std::memory_order_relaxed);
389
390 // Check if we need to recalculate rate (every second)
391 auto window_start = std::chrono::steady_clock::time_point(
392 std::chrono::steady_clock::duration(adaptive_window_start_.load()));
393 auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
394 now - window_start).count();
395
396 if (elapsed_ms >= 1000) {
397 // Calculate messages per second
398 std::uint64_t count = adaptive_window_count_.exchange(0);
399 adaptive_window_start_.store(now_count);
400
401 if (count > threshold) {
402 // Calculate new rate based on how much we're over threshold
403 double ratio = static_cast<double>(threshold) / static_cast<double>(count);
404 double new_rate = base_rate * ratio;
405 new_rate = std::max(new_rate, min_rate);
406 effective_rate_.store(new_rate);
407 is_throttling_.store(true);
408 } else {
409 // Reset to base rate
410 effective_rate_.store(base_rate);
411 is_throttling_.store(false);
412 }
413 }
414}
std::size_t adaptive_threshold
Threshold (messages/second) to trigger adaptive sampling.
double adaptive_min_rate
Minimum sampling rate when adaptive sampling is active.

References kcenon::logger::sampling::sampling_config::adaptive_min_rate, kcenon::logger::sampling::sampling_config::adaptive_threshold, adaptive_window_count_, adaptive_window_start_, config_, config_mutex_, effective_rate_, is_throttling_, and kcenon::logger::sampling::sampling_config::rate.

Referenced by adaptive_sample().

Here is the caller graph for this function:

◆ xorshift64()

std::uint64_t kcenon::logger::sampling::log_sampler::xorshift64 ( )
nodiscardprivate

Fast xorshift64 PRNG for random sampling.

Returns
Random number in range [0, UINT64_MAX]

Definition at line 440 of file log_sampler.cpp.

440 {
441 std::uint64_t x = rng_state_.load(std::memory_order_relaxed);
442 x ^= x << 13;
443 x ^= x >> 7;
444 x ^= x << 17;
445 rng_state_.store(x, std::memory_order_relaxed);
446 return x;
447}

References rng_state_.

Referenced by random_sample().

Here is the caller graph for this function:

Member Data Documentation

◆ adaptive_window_count_

std::atomic<std::uint64_t> kcenon::logger::sampling::log_sampler::adaptive_window_count_ {0}
private

Definition at line 283 of file log_sampler.h.

283{0};

Referenced by update_adaptive_rate().

◆ adaptive_window_start_

std::atomic<std::chrono::steady_clock::time_point::rep> kcenon::logger::sampling::log_sampler::adaptive_window_start_ {0}
private

Definition at line 284 of file log_sampler.h.

284{0};

Referenced by log_sampler(), and update_adaptive_rate().

◆ bypassed_count_

std::atomic<std::uint64_t> kcenon::logger::sampling::log_sampler::bypassed_count_ {0}
private

Definition at line 271 of file log_sampler.h.

271{0};

Referenced by get_stats(), reset_stats(), should_sample(), and should_sample().

◆ config_

◆ config_mutex_

std::shared_mutex kcenon::logger::sampling::log_sampler::config_mutex_
mutableprivate

◆ dropped_count_

std::atomic<std::uint64_t> kcenon::logger::sampling::log_sampler::dropped_count_ {0}
private

Definition at line 270 of file log_sampler.h.

270{0};

Referenced by get_stats(), reset_stats(), should_sample(), and should_sample().

◆ effective_rate_

std::atomic<double> kcenon::logger::sampling::log_sampler::effective_rate_ {1.0}
private

◆ is_throttling_

std::atomic<bool> kcenon::logger::sampling::log_sampler::is_throttling_ {false}
private

Definition at line 285 of file log_sampler.h.

285{false};

Referenced by get_stats(), and update_adaptive_rate().

◆ rate_limit_count_

std::atomic<std::uint64_t> kcenon::logger::sampling::log_sampler::rate_limit_count_ {0}
private

Definition at line 277 of file log_sampler.h.

277{0};

Referenced by rate_limit_sample().

◆ rate_limit_mutex_

std::mutex kcenon::logger::sampling::log_sampler::rate_limit_mutex_
private

Definition at line 279 of file log_sampler.h.

Referenced by rate_limit_sample().

◆ rate_limit_window_start_

std::atomic<std::chrono::steady_clock::time_point::rep> kcenon::logger::sampling::log_sampler::rate_limit_window_start_ {0}
private

Definition at line 278 of file log_sampler.h.

278{0};

Referenced by log_sampler(), and rate_limit_sample().

◆ rng_state_

std::atomic<std::uint64_t> kcenon::logger::sampling::log_sampler::rng_state_
private

Definition at line 274 of file log_sampler.h.

Referenced by xorshift64().

◆ sampled_count_

std::atomic<std::uint64_t> kcenon::logger::sampling::log_sampler::sampled_count_ {0}
private

Definition at line 269 of file log_sampler.h.

269{0};

Referenced by get_stats(), reset_stats(), should_sample(), and should_sample().

◆ total_count_

std::atomic<std::uint64_t> kcenon::logger::sampling::log_sampler::total_count_ {0}
private

Definition at line 268 of file log_sampler.h.

268{0};

Referenced by get_stats(), reset_stats(), should_sample(), and should_sample().


The documentation for this class was generated from the following files: