Capture `tracing` logs; stub/placeholder tui
This commit is contained in:
parent
e214d9dce3
commit
76d8d9e907
|
@ -97,6 +97,12 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.7"
|
||||
|
@ -931,6 +937,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
|
@ -1252,6 +1264,15 @@ dependencies = [
|
|||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
|
@ -1551,16 +1572,19 @@ dependencies = [
|
|||
"futures",
|
||||
"lazy_static",
|
||||
"notify",
|
||||
"parking_lot",
|
||||
"preserves-schema",
|
||||
"structopt",
|
||||
"syndicate",
|
||||
"syndicate-macros",
|
||||
"termion",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
"tracing-subscriber",
|
||||
"tui",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
|
@ -1578,6 +1602,18 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
|
@ -1793,6 +1829,19 @@ dependencies = [
|
|||
"tracing-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tui"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"termion",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.13.0"
|
||||
|
|
|
@ -22,6 +22,7 @@ chrono = "0.4"
|
|||
futures = "0.3"
|
||||
lazy_static = "1.4"
|
||||
notify = "4.0"
|
||||
parking_lot = "0.11"
|
||||
structopt = "0.3"
|
||||
|
||||
tungstenite = "0.13"
|
||||
|
@ -33,3 +34,6 @@ tokio-util = "0.6"
|
|||
tracing = "0.1"
|
||||
tracing-subscriber = "0.2"
|
||||
tracing-futures = "0.2"
|
||||
|
||||
tui = "0.16"
|
||||
termion = "1.5"
|
||||
|
|
|
@ -21,6 +21,7 @@ mod language;
|
|||
mod lifecycle;
|
||||
mod protocol;
|
||||
mod services;
|
||||
mod ui;
|
||||
|
||||
mod schemas {
|
||||
include!(concat!(env!("OUT_DIR"), "/src/schemas/mod.rs"));
|
||||
|
@ -46,6 +47,9 @@ struct ServerConfig {
|
|||
#[structopt(short = "c", long)]
|
||||
config: Vec<PathBuf>,
|
||||
|
||||
#[structopt(short = "u", long)]
|
||||
ui: bool,
|
||||
|
||||
#[structopt(long)]
|
||||
no_banner: bool,
|
||||
}
|
||||
|
@ -54,7 +58,11 @@ struct ServerConfig {
|
|||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = Arc::new(ServerConfig::from_args());
|
||||
|
||||
if config.ui {
|
||||
ui::start()?;
|
||||
} else {
|
||||
syndicate::convenient_logging()?;
|
||||
}
|
||||
|
||||
if !config.no_banner && !config.inferior {
|
||||
const BRIGHT_GREEN: &str = "\x1b[92m";
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
use parking_lot::RwLock;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use syndicate::value::IOValue;
|
||||
use syndicate::value::Map;
|
||||
use syndicate::value::NestedValue;
|
||||
use syndicate::value::Value;
|
||||
|
||||
use termion::raw::IntoRawMode;
|
||||
|
||||
use tracing::Event;
|
||||
use tracing::Level;
|
||||
use tracing::Metadata;
|
||||
use tracing::field::Field;
|
||||
use tracing::span::Attributes;
|
||||
use tracing::span::Id;
|
||||
use tracing::span::Record;
|
||||
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing_subscriber::layer::Context;
|
||||
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct FieldsSnapshot(Map<&'static str, IOValue>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct EventSnapshot {
|
||||
spans: Vec<(&'static Metadata<'static>, FieldsSnapshot)>,
|
||||
event_metadata: &'static Metadata<'static>,
|
||||
event_fields: FieldsSnapshot,
|
||||
}
|
||||
|
||||
struct LogCollector {
|
||||
dirty: AtomicBool,
|
||||
current_level: Level,
|
||||
minimum_level: Level,
|
||||
history: RwLock<VecDeque<EventSnapshot>>,
|
||||
history_limit: usize,
|
||||
}
|
||||
|
||||
fn write_fields(f: &mut std::fmt::Formatter, fs: &Map<&'static str, IOValue>) -> std::fmt::Result {
|
||||
for (k, v) in fs.iter() {
|
||||
if let Some(s) = v.value().as_string() {
|
||||
write!(f, "{}={} ", k, s)?;
|
||||
} else {
|
||||
write!(f, "{}={:?} ", k, v)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EventSnapshot {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let mut fs = self.event_fields.0.clone();
|
||||
if let Some(m) = fs.remove("message") {
|
||||
match m.value().as_string() {
|
||||
Some(s) => write!(f, "{} ", s)?,
|
||||
None => { fs.insert("message", m); }
|
||||
}
|
||||
}
|
||||
write_fields(f, &fs)?;
|
||||
write!(f, "at {}:{}\n",
|
||||
self.event_metadata.file().unwrap_or("-"),
|
||||
self.event_metadata.line().unwrap_or(0))?;
|
||||
for s in self.spans.iter() {
|
||||
let (m, fs) = s;
|
||||
write!(f, " - in {} ", m.name())?;
|
||||
write_fields(f, &fs.0)?;
|
||||
write!(f, "at {}:{}\n", m.file().unwrap_or("-"), m.line().unwrap_or(0))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldsSnapshot {
|
||||
fn push(&mut self, field: &Field, v: IOValue) {
|
||||
let name = field.name();
|
||||
match self.0.remove(name) {
|
||||
None => {
|
||||
self.0.insert(name, v);
|
||||
}
|
||||
Some(o) => match o.value().as_sequence() {
|
||||
Some(vs) => {
|
||||
let mut vs = vs.clone();
|
||||
vs.push(v);
|
||||
self.0.insert(name, IOValue::new(vs));
|
||||
},
|
||||
None => {
|
||||
self.0.insert(name, IOValue::new(vec![o, v]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl tracing::field::Visit for FieldsSnapshot {
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
|
||||
self.push(field, IOValue::new(format!("{:?}", value)));
|
||||
}
|
||||
fn record_f64(&mut self, field: &Field, value: f64) {
|
||||
self.push(field, IOValue::new(value));
|
||||
}
|
||||
fn record_i64(&mut self, field: &Field, value: i64) {
|
||||
self.push(field, IOValue::new(value));
|
||||
}
|
||||
fn record_u64(&mut self, field: &Field, value: u64) {
|
||||
self.push(field, IOValue::new(value));
|
||||
}
|
||||
fn record_bool(&mut self, field: &Field, value: bool) {
|
||||
self.push(field, IOValue::new(value));
|
||||
}
|
||||
fn record_str(&mut self, field: &Field, value: &str) {
|
||||
self.push(field, IOValue::new(value));
|
||||
}
|
||||
fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
|
||||
let mut r = Value::simple_record("error", 1);
|
||||
r.fields_vec_mut().push(IOValue::new(format!("{}", value)));
|
||||
let r = r.finish();
|
||||
self.push(field, r.wrap());
|
||||
}
|
||||
}
|
||||
|
||||
impl LogCollector {
|
||||
fn new() -> Self {
|
||||
LogCollector {
|
||||
dirty: AtomicBool::new(false),
|
||||
current_level: Level::INFO,
|
||||
minimum_level: Level::TRACE,
|
||||
history: Default::default(),
|
||||
history_limit: 10000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a> + std::fmt::Debug>
|
||||
tracing_subscriber::Layer<S> for LogCollector
|
||||
where for<'a> <S as tracing_subscriber::registry::LookupSpan<'a>>::Data: std::fmt::Debug
|
||||
{
|
||||
fn enabled(&self, metadata: &Metadata, _ctx: Context<S>) -> bool {
|
||||
metadata.level() <= &self.minimum_level
|
||||
}
|
||||
|
||||
fn new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
|
||||
if let Some(s) = ctx.span(id) {
|
||||
let mut snap = FieldsSnapshot::default();
|
||||
attrs.record(&mut snap);
|
||||
s.extensions_mut().insert(snap);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_record(&self, span: &Id, values: &Record, ctx: Context<S>) {
|
||||
if let Some(s) = ctx.span(span) {
|
||||
if let Some(snap) = s.extensions_mut().get_mut::<FieldsSnapshot>() {
|
||||
values.record(snap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_event(&self, event: &Event, ctx: Context<S>) {
|
||||
if event.metadata().level() > &self.current_level {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut snap = EventSnapshot {
|
||||
spans: Vec::new(),
|
||||
event_metadata: event.metadata(),
|
||||
event_fields: Default::default(),
|
||||
};
|
||||
event.record(&mut snap.event_fields);
|
||||
|
||||
if let Some(scope) = ctx.event_scope(event) {
|
||||
for s in scope.from_root() {
|
||||
snap.spans.push((
|
||||
s.metadata(),
|
||||
s.extensions().get::<FieldsSnapshot>().cloned().unwrap_or_else(FieldsSnapshot::default)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
println!("{}", &snap);
|
||||
|
||||
let mut history = self.history.write();
|
||||
history.push_front(snap);
|
||||
history.truncate(self.history_limit);
|
||||
self.dirty.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start() -> io::Result<()> {
|
||||
let stdout = io::stdout().into_raw_mode()?;
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
let subscriber = tracing_subscriber::Registry::default()
|
||||
.with(LogCollector::new());
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("Could not set UI global subscriber");
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue