#![cfg_attr(
not(test),
deny(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing
)
)]
mod composite;
mod file;
#[cfg(feature = "mongodb")]
mod mongo;
#[cfg(feature = "mssql")]
mod mssql;
mod prefix;
#[cfg(feature = "redb")]
mod redb;
mod screen;
#[cfg(feature = "sql")]
mod sql;
mod tracing_log;
use std::path::PathBuf;
use thiserror::Error;
pub use composite::CompositeLog;
pub use file::{FileLog, FileLogOptions, RetentionPolicy};
#[cfg(feature = "mongodb")]
pub use mongo::{MongoLog, MongoLogConfig};
#[cfg(feature = "mssql")]
pub use mssql::{MssqlLog, MssqlLogConfig};
pub use prefix::SessionPrefixLog;
#[cfg(feature = "redb")]
pub use redb::{RedbLog, RedbLogConfig};
pub use screen::{ScreenLog, ScreenLogOptions};
#[cfg(feature = "sql")]
pub use sql::{SqlLog, SqlLogConfig, SqlLogPoolOptions};
pub use tracing_log::{TracingLog, TracingLogOptions};
pub(crate) fn is_heartbeat(message: &str) -> bool {
message.split('\u{1}').any(|field| field == "35=0")
}
#[derive(Debug, Error)]
pub enum LogError {
#[error("log I/O error: {0}")]
Io(String),
}
#[async_trait::async_trait]
pub trait Log: Send + Sync {
fn on_incoming(&self, message: &str);
fn on_outgoing(&self, message: &str);
fn on_event(&self, text: &str);
async fn shutdown(&self) {}
}
#[async_trait::async_trait]
impl Log for Box<dyn Log> {
fn on_incoming(&self, message: &str) {
(**self).on_incoming(message);
}
fn on_outgoing(&self, message: &str) {
(**self).on_outgoing(message);
}
fn on_event(&self, text: &str) {
(**self).on_event(text);
}
async fn shutdown(&self) {
(**self).shutdown().await;
}
}
#[derive(Debug, Clone)]
pub enum LogConfig {
Screen,
File {
dir: PathBuf,
options: FileLogOptions,
},
Tracing,
Composite(Vec<LogConfig>),
}
pub async fn build_log(config: &LogConfig) -> Result<Box<dyn Log>, LogError> {
Ok(match config {
LogConfig::Screen => Box::new(ScreenLog::new()),
LogConfig::File { dir, options } => {
Box::new(FileLog::open_with_options(dir, *options).await?)
}
LogConfig::Tracing => Box::new(TracingLog::new()),
LogConfig::Composite(parts) => {
let mut logs: Vec<Box<dyn Log>> = Vec::with_capacity(parts.len());
for part in parts {
logs.push(Box::pin(build_log(part)).await?);
}
Box::new(CompositeLog::new(logs))
}
})
}