use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use tracing::Level;
use tracing::{Event, Subscriber};
use tracing_subscriber::layer::Context;
use tracing_subscriber::Layer;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogEntry {
pub timestamp: String,
pub level: String,
pub target: String,
pub message: String,
}
impl LogEntry {
pub fn new(level: Level, target: &str, message: String) -> Self {
let timestamp = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
Self {
timestamp,
level: level.to_string(),
target: target.to_string(),
message,
}
}
}
#[derive(Clone, Default)]
pub struct LogBuffer {
entries: Arc<Mutex<Vec<LogEntry>>>,
}
impl LogBuffer {
pub fn new() -> Self {
Self {
entries: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn push(&self, entry: LogEntry) {
if let Ok(mut entries) = self.entries.lock() {
entries.push(entry);
if entries.len() > 10000 {
entries.drain(0..1000);
}
}
}
pub fn get_all(&self) -> Vec<LogEntry> {
self.entries
.lock()
.map(|entries| entries.clone())
.unwrap_or_default()
}
#[allow(dead_code)]
pub fn clear(&self) {
if let Ok(mut entries) = self.entries.lock() {
entries.clear();
}
}
}
pub struct LogBufferLayer {
buffer: LogBuffer,
}
impl LogBufferLayer {
pub fn new(buffer: LogBuffer) -> Self {
Self { buffer }
}
}
impl<S> Layer<S> for LogBufferLayer
where
S: Subscriber,
{
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
let metadata = event.metadata();
let level = *metadata.level();
let target = metadata.target();
let mut visitor = MessageVisitor::default();
event.record(&mut visitor);
let entry = LogEntry::new(level, target, visitor.message);
self.buffer.push(entry);
}
}
#[derive(Default)]
struct MessageVisitor {
message: String,
}
impl tracing::field::Visit for MessageVisitor {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.message = format!("{:?}", value);
if self.message.starts_with('"') && self.message.ends_with('"') {
self.message = self.message[1..self.message.len() - 1].to_string();
}
}
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
if field.name() == "message" {
self.message = value.to_string();
}
}
}