debug: Display log in the background

This commit is contained in:
Victoria Brekenfeld 2022-02-05 00:40:17 +01:00
parent 9aa1d3ebc9
commit 62379bc74c
9 changed files with 498 additions and 69 deletions

145
src/logger/mod.rs Normal file
View file

@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-3.0-only
#[cfg(feature = "debug")]
use std::{
collections::VecDeque,
sync::{Arc, Mutex, atomic::{AtomicBool, Ordering}},
};
use anyhow::Result;
use slog::Drain;
#[cfg(feature = "debug")]
mod serializer;
#[cfg(feature = "debug")]
const MAX_RECORDS: usize = 1000;
#[cfg(feature = "debug")]
pub type LogBuffer = Arc<Mutex<VecDeque<OwnedRecord>>>;
#[cfg(feature = "debug")]
#[derive(Clone)]
struct DebugDrain {
buffer: LogBuffer,
dirty_flag:Arc<AtomicBool>,
}
pub struct LogState {
_guard: slog_scope::GlobalLoggerGuard,
#[cfg(feature = "debug")]
pub dirty_flag:Arc<AtomicBool>,
#[cfg(feature = "debug")]
pub debug_buffer: LogBuffer,
}
#[cfg(feature = "debug")]
pub struct OwnedRecord {
pub message: String,
pub level: slog::Level,
pub kv: serde_json::map::Map<String, serde_json::Value>,
}
#[cfg(feature = "debug")]
impl DebugDrain {
fn new() -> (DebugDrain, LogBuffer, Arc<AtomicBool>) {
let dirty_flag = Arc::new(AtomicBool::new(false));
let buffer = Arc::new(Mutex::new(VecDeque::new()));
(
DebugDrain {
buffer: buffer.clone(),
dirty_flag: dirty_flag.clone()
},
buffer,
dirty_flag,
)
}
}
#[cfg(feature = "debug")]
impl Drain for DebugDrain {
type Ok = ();
type Err = slog::Error;
fn log(
&self,
record: &slog::Record<'_>,
values: &slog::OwnedKVList
) -> Result<Self::Ok, Self::Err> {
use slog::KV;
use serializer::SerdeSerializer;
use serde_json::value::{
Serializer as ValueSerializer,
Value,
};
let mut serializer = SerdeSerializer::start(ValueSerializer, None)?;
values.serialize(record, &mut serializer)?;
record.kv().serialize(record, &mut serializer)?;
let value = match serializer.end().map_err(|_| slog::Error::Other)? {
Value::Object(map) => map,
_ => unreachable!(),
};
let mut buffer = self.buffer.lock().unwrap();
buffer.push_front(OwnedRecord {
message: format!("{}", record.msg()),
level: record.level(),
kv: value,
});
buffer.truncate(MAX_RECORDS);
self.dirty_flag.store(true, Ordering::SeqCst);
Ok(())
}
}
pub fn init_logger() -> Result<LogState> {
let decorator = slog_term::TermDecorator::new().stderr().build();
// usually we would not want to use a Mutex here, but this is usefull for a prototype,
// to make sure we do not miss any in-flight messages, when we crash.
#[cfg(not(feature = "debug"))]
let logger = slog::Logger::root(
std::sync::Mutex::new(
slog_term::CompactFormat::new(decorator)
.build()
.ignore_res(),
)
.fuse(),
slog::o!(),
);
#[cfg(feature = "debug")]
let (debug_drain, debug_buffer, dirty_flag) = DebugDrain::new();
#[cfg(feature = "debug")]
let logger = slog::Logger::root(
slog::Duplicate::new(
std::sync::Mutex::new(
slog_term::CompactFormat::new(decorator)
.build()
.ignore_res()
),
debug_drain,
)
.fuse(),
slog::o!(),
);
let _guard = slog_scope::set_global_logger(logger);
slog_stdlog::init().unwrap();
slog_scope::info!("Version: {}", std::env!("CARGO_PKG_VERSION"));
if cfg!(feature = "debug") {
slog_scope::debug!(
"Debug build ({})",
std::option_env!("GIT_HASH").unwrap_or("Unknown")
);
}
Ok(LogState {
_guard,
#[cfg(feature = "debug")]
debug_buffer,
#[cfg(feature = "debug")]
dirty_flag,
})
}

135
src/logger/serializer.rs Normal file
View file

@ -0,0 +1,135 @@
// SPDX-License-Identifier: MPL-2.0 OR MIT OR Apache-2.0
// Taken from slog_json:
// https://github.com/slog-rs/json/blob/c45d09422d7114222ee5b7f6b0123de2605c7864/src/lib.rs#L39-L160
use serde::ser::SerializeMap;
use serde::serde_if_integer128;
use slog::Key;
use std::{fmt, io, result};
use std::cell::RefCell;
use std::fmt::Write;
thread_local! {
static TL_BUF: RefCell<String> = RefCell::new(String::with_capacity(128))
}
/// `slog::Serializer` adapter for `serde::Serializer`
///
/// Newtype to wrap serde Serializer, so that `Serialize` can be implemented
/// for it
pub struct SerdeSerializer<S: serde::Serializer> {
/// Current state of map serializing: `serde::Serializer::MapState`
ser_map: S::SerializeMap,
}
impl<S: serde::Serializer> SerdeSerializer<S> {
/// Start serializing map of values
pub fn start(ser: S, len: Option<usize>) -> result::Result<Self, slog::Error> {
let ser_map = ser.serialize_map(len).map_err(|e| {
io::Error::new(
io::ErrorKind::Other,
format!("serde serialization error: {}", e),
)
})?;
Ok(SerdeSerializer { ser_map })
}
/// Finish serialization, and return the serializer
pub fn end(self) -> result::Result<S::Ok, S::Error> {
self.ser_map.end()
}
}
macro_rules! impl_m(
($s:expr, $key:expr, $val:expr) => ({
let k_s: &str = $key.as_ref();
$s.ser_map.serialize_entry(k_s, $val)
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("serde serialization error: {}", e)))?;
Ok(())
});
);
impl<S> slog::Serializer for SerdeSerializer<S>
where
S: serde::Serializer,
{
fn emit_bool(&mut self, key: Key, val: bool) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_unit(&mut self, key: Key) -> slog::Result {
impl_m!(self, key, &())
}
fn emit_char(&mut self, key: Key, val: char) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_none(&mut self, key: Key) -> slog::Result {
let val: Option<()> = None;
impl_m!(self, key, &val)
}
fn emit_u8(&mut self, key: Key, val: u8) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i8(&mut self, key: Key, val: i8) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u16(&mut self, key: Key, val: u16) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i16(&mut self, key: Key, val: i16) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_usize(&mut self, key: Key, val: usize) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_isize(&mut self, key: Key, val: isize) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u32(&mut self, key: Key, val: u32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i32(&mut self, key: Key, val: i32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_f32(&mut self, key: Key, val: f32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u64(&mut self, key: Key, val: u64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i64(&mut self, key: Key, val: i64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_f64(&mut self, key: Key, val: f64) -> slog::Result {
impl_m!(self, key, &val)
}
serde_if_integer128! {
fn emit_u128(&mut self, key: Key, val: u128) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i128(&mut self, key: Key, val: i128) -> slog::Result {
impl_m!(self, key, &val)
}
}
fn emit_str(&mut self, key: Key, val: &str) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_arguments(
&mut self,
key: Key,
val: &fmt::Arguments,
) -> slog::Result {
TL_BUF.with(|buf| {
let mut buf = buf.borrow_mut();
buf.write_fmt(*val).unwrap();
let res = { || impl_m!(self, key, &*buf) }();
buf.clear();
res
})
}
}