pub trait Logger:
Debug
+ Send
+ Sync {
// Required methods
fn finish_log_record(&self, record: LogRecord);
fn finish_log_record_async<'s>(
&'s self,
record: LogRecord,
) -> Pin<Box<dyn Future<Output = ()> + Send + 's>>;
fn prepare_to_die(&self);
}Expand description
Core trait for implementing logging backends in logwise.
This trait defines the interface that all loggers must implement to receive and process log records from the logwise logging system. Implementations can output logs to any destination: stderr, files, network services, or in-memory buffers.
§Requirements
All implementations must be:
- Thread-safe: The logger will be called from multiple threads simultaneously
- Send + Sync: Required for use in multi-threaded environments
- Debug: For diagnostic purposes and error reporting
§Example Implementation
use logwise::{Logger, LogRecord};
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug)]
struct CountingLogger {
count: AtomicUsize,
}
impl CountingLogger {
fn new() -> Self {
Self {
count: AtomicUsize::new(0),
}
}
fn get_count(&self) -> usize {
self.count.load(Ordering::Relaxed)
}
}
impl Logger for CountingLogger {
fn finish_log_record(&self, _record: LogRecord) {
self.count.fetch_add(1, Ordering::Relaxed);
// In a real implementation, you would process the record here
}
fn finish_log_record_async<'s>(
&'s self,
record: LogRecord,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 's>> {
Box::pin(async move {
self.finish_log_record(record);
})
}
fn prepare_to_die(&self) {
// No cleanup needed for this simple logger
}
}
// Usage
let logger = CountingLogger::new();
let mut record = LogRecord::new(logwise::Level::Info);
record.log("Test message");
logger.finish_log_record(record);
assert_eq!(logger.get_count(), 1);Required Methods§
Sourcefn finish_log_record(&self, record: LogRecord)
fn finish_log_record(&self, record: LogRecord)
Processes a log record synchronously.
This method receives a complete LogRecord and is responsible for outputting it
to the logger’s destination. The implementation should be as fast as possible since
it may be called frequently and from performance-critical code paths.
§Parameters
record- The log record containing the formatted message and metadata
§Implementation Notes
- This method may be called from multiple threads simultaneously
- The implementation should not panic; errors should be handled gracefully
- Consider buffering writes for better performance
- The record is passed by value to avoid lifetime issues
§Examples
use logwise::{Logger, LogRecord, Level};
impl Logger for ConsoleLogger {
fn finish_log_record(&self, record: LogRecord) {
// Simple console output
println!("{}", record);
}
}Sourcefn finish_log_record_async<'s>(
&'s self,
record: LogRecord,
) -> Pin<Box<dyn Future<Output = ()> + Send + 's>>
fn finish_log_record_async<'s>( &'s self, record: LogRecord, ) -> Pin<Box<dyn Future<Output = ()> + Send + 's>>
Processes a log record asynchronously.
This method provides an async interface for processing log records, allowing loggers to leverage existing async contexts and avoid blocking async executors. This is particularly useful for loggers that perform I/O operations or need to integrate with async-first systems.
§Parameters
record- The log record containing the formatted message and metadata
§Returns
A pinned future that completes when the log record has been processed.
The future must be Send to work across thread boundaries.
§Implementation Options
Loggers have two common implementation strategies:
-
Simple wrapper: For loggers that don’t benefit from async, wrap the sync method:
fn finish_log_record_async<'s>( &'s self, record: LogRecord, ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 's>> { Box::pin(async move { self.finish_log_record(record) }) } -
True async: For loggers that benefit from async I/O:
fn finish_log_record_async<'s>( &'s self, record: LogRecord, ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 's>> { Box::pin(async move { // Perform actual async I/O here // e.g., async_write_to_file(record).await todo!("Implement async logging") }) }
§Lifetime Notes
The lifetime parameter 's ties the returned future to the logger’s lifetime,
ensuring the logger remains valid while the future is being polled.
Sourcefn prepare_to_die(&self)
fn prepare_to_die(&self)
Prepares the logger for application shutdown.
This method is called when the application is about to exit, giving the logger a chance to flush any buffered data, close file handles, send final network packets, or perform other cleanup operations. This ensures no log data is lost during shutdown.
§When This Is Called
- Before normal application exit
- During panic handling (if possible)
- When explicitly requested by the application
§Implementation Guidelines
- Flush all buffered data to persistent storage
- Close network connections gracefully
- Release any system resources
- This method may take longer than
finish_log_recordsince it’s called rarely - Should not panic; handle errors gracefully
§Examples
use logwise::{Logger, LogRecord};
use std::sync::Mutex;
#[derive(Debug)]
struct BufferedLogger {
buffer: Mutex<Vec<String>>,
}
impl BufferedLogger {
fn new() -> Self {
Self {
buffer: Mutex::new(Vec::new()),
}
}
}
impl Logger for BufferedLogger {
fn finish_log_record(&self, record: LogRecord) {
let mut buffer = self.buffer.lock().unwrap();
buffer.push(record.to_string());
}
fn finish_log_record_async<'s>(
&'s self,
record: LogRecord,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 's>> {
Box::pin(async move {
self.finish_log_record(record);
})
}
fn prepare_to_die(&self) {
// Flush buffer to stdout before exit
let buffer = self.buffer.lock().unwrap();
for message in buffer.iter() {
println!("{}", message);
}
}
}Implementors§
impl Logger for InMemoryLogger
Implementation of the Logger trait for InMemoryLogger.
This implementation captures all log records in memory by converting them to strings and storing them in the internal vector. Both synchronous and asynchronous logging are supported, with the async version being a simple wrapper around the synchronous implementation.