///|
/// Rotating file handler with size-based log rotation.
pub(all) struct RotatingFileHandler {
level : Level
fmt : (LogEntry) -> String
mut lines : Array[String]
max_lines : Int
max_files : Int
base_path : String
mut file_count : Int
}
///|
/// Creates a new RotatingFileHandler with the given level, formatter, base path, and rotation limits.
pub fn RotatingFileHandler::new(
level : Level,
fmt : (LogEntry) -> String,
base_path : String,
max_lines : Int,
max_files : Int,
) -> RotatingFileHandler {
{ level, fmt, lines: [], max_lines, max_files, base_path, file_count: 0 }
}
///|
/// Logs an entry if its level is enabled, buffering until rotation threshold is met.
pub fn RotatingFileHandler::log(
self : RotatingFileHandler,
entry : LogEntry,
) -> Unit {
if entry.level.enabled(self.level) {
let line = (self.fmt)(entry)
self.lines.push(line)
if self.lines.length() >= self.max_lines {
self.rotate()
}
}
}
///|
/// Flushes buffered lines to disk and clears the buffer.
pub fn RotatingFileHandler::flush(self : RotatingFileHandler) -> Unit {
if self.lines.length() > 0 {
self.write_current()
self.lines = []
}
}
///|
/// Flushes and closes the handler.
pub fn RotatingFileHandler::close(self : RotatingFileHandler) -> Unit {
self.flush()
}
// Rotates the log file: writes current buffer and increments file counter.
///|
fn RotatingFileHandler::rotate(self : RotatingFileHandler) -> Unit {
self.write_current()
self.lines = []
self.file_count = self.file_count + 1
if self.file_count > self.max_files {
self.file_count = 0
}
}
// Writes the current buffered lines to the rotated file path.
///|
fn RotatingFileHandler::write_current(self : RotatingFileHandler) -> Unit {
let content = self.lines.join("\n")
let path = self.rotate_path()
write_to_file(path, content)
}
// Generates the file path for the current rotation index.
///|
fn RotatingFileHandler::rotate_path(self : RotatingFileHandler) -> String {
self.base_path + "." + self.file_count.to_string()
}
///|
/// Returns the current buffered lines.
pub fn RotatingFileHandler::lines(self : RotatingFileHandler) -> Array[String] {
self.lines
}
///|
/// Returns the current file rotation count.
pub fn RotatingFileHandler::file_count(self : RotatingFileHandler) -> Int {
self.file_count
}
// Placeholder for file I/O; actual implementation should write content to path.
///|
fn write_to_file(_path : String, _content : String) -> Unit {
}
///|
/// Handler that filters log entries by a minimum and maximum level range.
pub(all) struct LevelFilteredHandler {
inner : HandlerFn
min_level : Level
max_level : Level
}
///|
/// Creates a LevelFilteredHandler with the given inner handler and level bounds.
pub fn LevelFilteredHandler::new(
inner : HandlerFn,
min_level : Level,
max_level : Level,
) -> LevelFilteredHandler {
{ inner, min_level, max_level }
}
///|
/// Forwards the entry to the inner handler only if its level falls within [min_level, max_level].
pub fn LevelFilteredHandler::log(
self : LevelFilteredHandler,
entry : LogEntry,
) -> Unit {
if entry.level.enabled(self.min_level) && self.max_level.enabled(entry.level) {
self.inner.log(entry)
}
}
///|
/// Delegates flush to the inner handler.
pub fn LevelFilteredHandler::flush(self : LevelFilteredHandler) -> Unit {
self.inner.flush()
}
///|
/// Delegates close to the inner handler.
pub fn LevelFilteredHandler::close(self : LevelFilteredHandler) -> Unit {
self.inner.close()
}
///|
/// Handler that only logs every N-th entry (sampling).
pub(all) struct SamplingHandler {
inner : HandlerFn
rate : Int
mut count : Int
}
///|
/// Creates a SamplingHandler that logs one out of every `rate` entries.
pub fn SamplingHandler::new(inner : HandlerFn, rate : Int) -> SamplingHandler {
{ inner, rate, count: 0 }
}
///|
/// Logs the entry if the internal counter is divisible by the sampling rate.
pub fn SamplingHandler::log(self : SamplingHandler, entry : LogEntry) -> Unit {
self.count = self.count + 1
if self.count % self.rate == 0 {
self.inner.log(entry)
}
}
///|
/// Delegates flush to the inner handler.
pub fn SamplingHandler::flush(self : SamplingHandler) -> Unit {
self.inner.flush()
}
///|
/// Delegates close to the inner handler.
pub fn SamplingHandler::close(self : SamplingHandler) -> Unit {
self.inner.close()
}
///|
/// Resets the sampling counter to zero.
pub fn SamplingHandler::reset(self : SamplingHandler) -> Unit {
self.count = 0
}
///|
/// Handler that suppresses consecutive duplicate log messages.
pub(all) struct DedupHandler {
inner : HandlerFn
mut last_message : String
mut repeat_count : Int
}
///|
/// Creates a DedupHandler that deduplicates repeated log messages.
pub fn DedupHandler::new(inner : HandlerFn) -> DedupHandler {
{ inner, last_message: "", repeat_count: 0 }
}
///|
/// Logs the entry; if it repeats the previous message, increments a counter instead.
pub fn DedupHandler::log(self : DedupHandler, entry : LogEntry) -> Unit {
if entry.message == self.last_message {
self.repeat_count = self.repeat_count + 1
} else {
if self.repeat_count > 0 {
self.inner.log(
LogEntryBuilder::new(
Level::Info,
"[repeated " + self.repeat_count.to_string() + " times]",
).build(),
)
}
self.inner.log(entry)
self.last_message = entry.message
self.repeat_count = 0
}
}
///|
/// Flushes any pending repeat summary and delegates to the inner handler.
pub fn DedupHandler::flush(self : DedupHandler) -> Unit {
if self.repeat_count > 0 {
self.inner.log(
LogEntryBuilder::new(
Level::Info,
"[repeated " + self.repeat_count.to_string() + " times]",
).build(),
)
self.repeat_count = 0
}
self.inner.flush()
}
///|
/// Flushes pending repeats and closes the inner handler.
pub fn DedupHandler::close(self : DedupHandler) -> Unit {
self.flush()
self.inner.close()
}
///|
/// Ring buffer handler that keeps a fixed-capacity sliding window of recent log entries.
pub(all) struct RingBufferHandler {
level : Level
fmt : (LogEntry) -> String
mut buffer : Array[LogEntry]
capacity : Int
mut head : Int
mut count : Int
}
///|
/// Creates a RingBufferHandler with the given level, formatter, and capacity.
pub fn RingBufferHandler::new(
level : Level,
fmt : (LogEntry) -> String,
capacity : Int,
) -> RingBufferHandler {
{ level, fmt, buffer: [], capacity, head: 0, count: 0 }
}
///|
/// Adds an entry to the ring buffer, overwriting the oldest entry if full.
pub fn RingBufferHandler::log(
self : RingBufferHandler,
entry : LogEntry,
) -> Unit {
if entry.level.enabled(self.level) {
if self.count < self.capacity {
self.buffer.push(entry)
} else {
self.buffer[self.head] = entry
}
self.head = (self.head + 1) % self.capacity
if self.count < self.capacity {
self.count = self.count + 1
}
}
}
///|
/// No-op for ring buffer; entries are managed in memory.
pub fn RingBufferHandler::flush(self : RingBufferHandler) -> Unit {
let _ = self
}
///|
/// No-op for ring buffer; entries are managed in memory.
pub fn RingBufferHandler::close(self : RingBufferHandler) -> Unit {
let _ = self
}
///|
/// Returns all entries in the ring buffer in insertion order.
pub fn RingBufferHandler::entries(self : RingBufferHandler) -> Array[LogEntry] {
let n = self.count
if n == 0 {
return []
}
let result : Array[LogEntry] = []
let start = if n < self.capacity { 0 } else { self.head }
let mut i = 0
while i < n {
let idx = (start + i) % n
result.push(self.buffer[idx])
i = i + 1
}
result
}
///|
/// Returns and clears all entries from the ring buffer.
pub fn RingBufferHandler::drain(self : RingBufferHandler) -> Array[LogEntry] {
let result = self.entries()
self.buffer = []
self.head = 0
self.count = 0
result
}
///|
/// Returns true if the ring buffer has reached its capacity.
pub fn RingBufferHandler::is_full(self : RingBufferHandler) -> Bool {
self.count >= self.capacity
}
///|
/// Returns the fraction of capacity currently used, as a value between 0.0 and 1.0.
pub fn RingBufferHandler::fill_ratio(self : RingBufferHandler) -> Double {
self.count.to_double() / self.capacity.to_double()
}
///|
/// Handler that rate-limits log entries to a maximum per second.
pub(all) struct RateLimitedHandler {
inner : HandlerFn
max_per_second : Int
mut window_count : Int
mut window_start : Int64
window_duration : Int64
}
///|
/// Creates a RateLimitedHandler that limits to `max_per_second` entries per second.
pub fn RateLimitedHandler::new(
inner : HandlerFn,
max_per_second : Int,
) -> RateLimitedHandler {
{
inner,
max_per_second,
window_count: 0,
window_start: 0L,
window_duration: 1000000L,
}
}
///|
/// Forwards the entry to the inner handler if the rate limit has not been exceeded.
pub fn RateLimitedHandler::log(
self : RateLimitedHandler,
entry : LogEntry,
) -> Unit {
let now = entry.timestamp
if now - self.window_start > self.window_duration {
self.window_start = now
self.window_count = 0
}
if self.window_count < self.max_per_second {
self.inner.log(entry)
self.window_count = self.window_count + 1
}
}
///|
/// Delegates flush to the inner handler.
pub fn RateLimitedHandler::flush(self : RateLimitedHandler) -> Unit {
self.inner.flush()
}
///|
/// Delegates close to the inner handler.
pub fn RateLimitedHandler::close(self : RateLimitedHandler) -> Unit {
self.inner.close()
}