///|
fn sampling_seed() -> Bytes {
let t = @env.now()
let constants : Array[UInt64] = [
0x243f6a8885a308d3UL, 0x13198a2e03707344UL, 0xa4093822299f31d0UL, 0x082efa98ec4e6c89UL,
]
let arr : Array[Byte] = Array::make(32, b'\x00')
for seg in 0..<4 {
let v = t ^ constants[seg]
for i in 0..<8 {
arr[seg * 8 + i] = (v >> ((7 - i) * 8)).to_byte()
}
}
Bytes::from_array(arr[:])
}
///|
let sampling_rng : @random.Rand = @random.Rand::chacha8(seed=sampling_seed())
///|
fn default_sampling_rand() -> Double {
sampling_rng.double()
}
///|
fn clamp_sampling_ratio(ratio : Double) -> Double {
match (ratio <= 0.0, ratio >= 1.0) {
(true, _) => 0.0
(_, true) => 1.0
_ => ratio
}
}
///|
fn keep_by_ratio(ratio : Double, rand : () -> Double) -> Bool {
let ratio = clamp_sampling_ratio(ratio)
match (ratio <= 0.0, ratio >= 1.0) {
(true, _) => false
(_, true) => true
_ => rand() < ratio
}
}
///|
pub(all) struct RatioSampler {
inner : (Event) -> Unit
ratio : Double
rand : () -> Double
mut kept : Int
mut dropped : Int
}
///|
pub fn ratio_sampler(
inner : (Event) -> Unit,
ratio : Double,
rand? : () -> Double = default_sampling_rand,
) -> RatioSampler {
{ inner, ratio: clamp_sampling_ratio(ratio), rand, kept: 0, dropped: 0 }
}
///|
pub fn RatioSampler::subscriber(self : RatioSampler) -> (Event) -> Unit {
fn(event) {
match keep_by_ratio(self.ratio, self.rand) {
true => {
self.kept = self.kept + 1
(self.inner)(event)
}
false => self.dropped = self.dropped + 1
}
}
}
///|
pub fn RatioSampler::kept(self : RatioSampler) -> Int {
self.kept
}
///|
pub fn RatioSampler::dropped(self : RatioSampler) -> Int {
self.dropped
}
///|
pub(all) struct RateLimiter {
inner : (Event) -> Unit
limit : Int
window_ns : UInt64
clock : () -> UInt64
mut window_start : UInt64
mut count : Int
mut kept : Int
mut dropped : Int
}
///|
pub fn rate_limiter(
inner : (Event) -> Unit,
limit : Int,
window_ns : UInt64,
clock? : () -> UInt64 = @env.now,
) -> RateLimiter {
{
inner,
limit: match limit < 0 {
true => 0
false => limit
},
window_ns: match window_ns == 0UL {
true => 1UL
false => window_ns
},
clock,
window_start: clock(),
count: 0,
kept: 0,
dropped: 0,
}
}
///|
fn should_reset_window(
now : UInt64,
window_start : UInt64,
window_ns : UInt64,
) -> Bool {
match now >= window_start {
true => now - window_start >= window_ns
false => false
}
}
///|
pub fn RateLimiter::subscriber(self : RateLimiter) -> (Event) -> Unit {
fn(event) {
let now = (self.clock)()
match should_reset_window(now, self.window_start, self.window_ns) {
true => {
self.window_start = now
self.count = 0
}
false => ()
}
match self.count < self.limit {
true => {
self.count = self.count + 1
self.kept = self.kept + 1
(self.inner)(event)
}
false => self.dropped = self.dropped + 1
}
}
}
///|
pub fn RateLimiter::kept(self : RateLimiter) -> Int {
self.kept
}
///|
pub fn RateLimiter::dropped(self : RateLimiter) -> Int {
self.dropped
}
///|
fn trace_id_hash(trace_id : String) -> UInt64 {
let mut hash = 14695981039346656037UL
for i in 0.. Double {
let hash = trace_id_hash(trace_id)
Double::convert_uint64(hash << 11 >> 11) / Double::convert_uint64(1UL << 53)
}
///|
pub fn should_sample_trace(trace_id : String, ratio : Double) -> Bool {
let ratio = clamp_sampling_ratio(ratio)
match (ratio <= 0.0, ratio >= 1.0) {
(true, _) => false
(_, true) => true
_ => trace_id_unit(trace_id) < ratio
}
}
///|
pub fn sample_trace_decision(
parent_flags : TraceFlags?,
trace_id : String,
ratio : Double,
) -> Bool {
match parent_flags {
Some(flags) => flags.is_sampled()
None => should_sample_trace(trace_id, ratio)
}
}
///|
fn trace_id_field(event : Event) -> String? {
for field in event.fields {
if field.key == "trace_id" {
match field.value {
String(trace_id) => return Some(trace_id)
_ => ()
}
}
}
None
}
///|
pub(all) struct TraceSampler {
inner : (Event) -> Unit
ratio : Double
mut kept : Int
mut dropped : Int
}
///|
pub fn trace_sampler(inner : (Event) -> Unit, ratio : Double) -> TraceSampler {
{ inner, ratio: clamp_sampling_ratio(ratio), kept: 0, dropped: 0 }
}
///|
pub fn TraceSampler::subscriber(self : TraceSampler) -> (Event) -> Unit {
fn(event) {
match trace_id_field(event) {
Some(trace_id) =>
match should_sample_trace(trace_id, self.ratio) {
true => {
self.kept = self.kept + 1
(self.inner)(event)
}
false => self.dropped = self.dropped + 1
}
None => {
self.kept = self.kept + 1
(self.inner)(event)
}
}
}
}
///|
pub fn TraceSampler::kept(self : TraceSampler) -> Int {
self.kept
}
///|
pub fn TraceSampler::dropped(self : TraceSampler) -> Int {
self.dropped
}