use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
impl LogLevel {
fn as_str(self) -> &'static str {
match self {
Self::Trace => "TRACE",
Self::Debug => "DEBUG",
Self::Info => "INFO",
Self::Warn => "WARN",
Self::Error => "ERROR",
}
}
}
#[derive(Debug, Clone)]
pub struct LogEntry {
level: LogLevel,
message: String,
timestamp: u64,
correlation_id: Option<String>,
fields: HashMap<String, String>,
}
impl LogEntry {
#[must_use]
pub fn new(level: LogLevel, message: &str) -> Self {
use std::time::{SystemTime, UNIX_EPOCH};
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
Self {
level,
message: message.to_string(),
timestamp,
correlation_id: None,
fields: HashMap::new(),
}
}
#[must_use]
pub fn with_correlation_id(mut self, id: &str) -> Self {
self.correlation_id = Some(id.to_string());
self
}
#[must_use]
pub fn with_field(mut self, key: &str, value: &str) -> Self {
self.fields.insert(key.to_string(), value.to_string());
self
}
#[must_use]
pub fn correlation_id(&self) -> Option<&str> {
self.correlation_id.as_deref()
}
#[must_use]
pub fn level(&self) -> LogLevel {
self.level
}
#[must_use]
pub fn timestamp(&self) -> u64 {
self.timestamp
}
#[must_use]
pub fn to_json(&self) -> String {
use std::fmt::Write;
let mut json = format!(
"{{\"level\":\"{}\",\"message\":\"{}\",\"timestamp\":{}",
self.level.as_str(),
self.message,
self.timestamp
);
if let Some(ref id) = self.correlation_id {
let _ = write!(json, ",\"correlation_id\":\"{}\"", id);
}
for (key, value) in &self.fields {
let _ = write!(json, ",\"{}\":\"{}\"", key, value);
}
json.push('}');
json
}
}
#[derive(Debug, Clone)]
pub struct LogConfig {
default_level: LogLevel,
json_format: bool,
module_levels: HashMap<String, LogLevel>,
}
impl LogConfig {
#[must_use]
pub fn new() -> Self {
Self {
default_level: LogLevel::Info,
json_format: false,
module_levels: HashMap::new(),
}
}
#[must_use]
pub fn with_level(mut self, level: LogLevel) -> Self {
self.default_level = level;
self
}
#[must_use]
pub fn with_json_format(mut self, enabled: bool) -> Self {
self.json_format = enabled;
self
}
#[must_use]
pub fn with_module_level(mut self, module: &str, level: LogLevel) -> Self {
self.module_levels.insert(module.to_string(), level);
self
}
}
impl Default for LogConfig {
fn default() -> Self {
Self::new()
}
}
pub struct Logger {
config: LogConfig,
}
impl Logger {
#[must_use]
pub fn new(config: LogConfig) -> Self {
Self { config }
}
#[must_use]
pub fn is_enabled(&self, level: LogLevel, module: &str) -> bool {
let min_level = self
.config
.module_levels
.get(module)
.copied()
.unwrap_or(self.config.default_level);
level >= min_level
}
}
pub struct PhaseTimer {
phases: std::sync::Mutex<HashMap<String, (Option<std::time::Instant>, u64)>>,
}
impl PhaseTimer {
#[must_use]
pub fn new() -> Self {
Self {
phases: std::sync::Mutex::new(HashMap::new()),
}
}
pub fn start_phase(&self, name: &str) {
let mut phases = self.phases.lock().expect("mutex poisoned");
phases.insert(name.to_string(), (Some(std::time::Instant::now()), 0));
}
pub fn end_phase(&self, name: &str) {
let mut phases = self.phases.lock().expect("mutex poisoned");
if let Some((Some(start_time), _)) = phases.get(name) {
let elapsed = start_time.elapsed().as_micros() as u64;
phases.insert(name.to_string(), (None, elapsed));
}
}
#[must_use]
pub fn breakdown(&self) -> HashMap<String, u64> {
let phases = self.phases.lock().expect("mutex poisoned");
phases.iter().map(|(k, (_, v))| (k.clone(), *v)).collect()
}
}
impl Default for PhaseTimer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct MemoryReport {
pub peak_bytes: u64,
pub current_bytes: u64,
pub allocation_count: u64,
}
pub struct MemoryTracker {
current: std::sync::atomic::AtomicU64,
peak: std::sync::atomic::AtomicU64,
allocation_count: std::sync::atomic::AtomicU64,
}
impl MemoryTracker {
#[must_use]
pub fn new() -> Self {
Self {
current: std::sync::atomic::AtomicU64::new(0),
peak: std::sync::atomic::AtomicU64::new(0),
allocation_count: std::sync::atomic::AtomicU64::new(0),
}
}
pub fn record_allocation(&self, _name: &str, bytes: u64) {
let new_current = self
.current
.fetch_add(bytes, std::sync::atomic::Ordering::SeqCst)
+ bytes;
self.allocation_count
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let mut peak = self.peak.load(std::sync::atomic::Ordering::SeqCst);
while new_current > peak {
match self.peak.compare_exchange_weak(
peak,
new_current,
std::sync::atomic::Ordering::SeqCst,
std::sync::atomic::Ordering::Relaxed,
) {
Ok(_) => break,
Err(current_peak) => peak = current_peak,
}
}
}
pub fn record_deallocation(&self, _name: &str, bytes: u64) {
self.current
.fetch_sub(bytes, std::sync::atomic::Ordering::SeqCst);
}
#[must_use]
pub fn report(&self) -> MemoryReport {
MemoryReport {
peak_bytes: self.peak.load(std::sync::atomic::Ordering::SeqCst),
current_bytes: self.current.load(std::sync::atomic::Ordering::SeqCst),
allocation_count: self
.allocation_count
.load(std::sync::atomic::Ordering::SeqCst),
}
}
}
impl Default for MemoryTracker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct DiagnosticsSummary {
pub request_count: u64,
}
pub struct DiagnosticsCollector {
pub(crate) request_count: std::sync::atomic::AtomicU64,
timings: std::sync::Mutex<Vec<HashMap<String, u64>>>,
memory_snapshots: std::sync::Mutex<Vec<MemoryReport>>,
}
impl DiagnosticsCollector {
#[must_use]
pub fn new() -> Self {
Self {
request_count: std::sync::atomic::AtomicU64::new(0),
timings: std::sync::Mutex::new(Vec::new()),
memory_snapshots: std::sync::Mutex::new(Vec::new()),
}
}
pub fn record_request_timing(&self, _request_id: &str, timing: HashMap<String, u64>) {
self.request_count
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
self.timings.lock().expect("mutex poisoned").push(timing);
}
pub fn record_memory_snapshot(&self, report: MemoryReport) {
self.memory_snapshots
.lock()
.expect("mutex poisoned")
.push(report);
}
#[must_use]
pub fn summary(&self) -> DiagnosticsSummary {
DiagnosticsSummary {
request_count: self.request_count.load(std::sync::atomic::Ordering::SeqCst),
}
}
}
impl Default for DiagnosticsCollector {
fn default() -> Self {
Self::new()
}
}
pub struct DebugMode {
enabled: std::sync::atomic::AtomicBool,
}
impl DebugMode {
#[must_use]
pub fn new() -> Self {
Self {
enabled: std::sync::atomic::AtomicBool::new(false),
}
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn enable(&self) {
self.enabled
.store(true, std::sync::atomic::Ordering::SeqCst);
}
pub fn disable(&self) {
self.enabled
.store(false, std::sync::atomic::Ordering::SeqCst);
}
}
impl Default for DebugMode {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct RequestCapture {
input: String,
params: HashMap<String, String>,
}
include!("request_capture.rs");
include!("diagnostics_request_capture.rs");