use anyhow::Result;
use crossterm::event::PopKeyboardEnhancementFlags;
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, LeaveAlternateScreen};
use std::io::{self, Write};
use tracing::Level;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
use super::Args;
use crate::logging::RotatingFileWriter;
struct ColoredLogWriter {
inner: RotatingFileWriter,
}
impl ColoredLogWriter {
fn new(file: RotatingFileWriter) -> Self {
Self { inner: file }
}
}
impl Write for ColoredLogWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if let Ok(s) = std::str::from_utf8(buf) {
let mut modified = String::with_capacity(s.len());
let mut chars = s.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '\x1b' {
let mut seq = String::from("\x1b");
while let Some(&next_ch) = chars.peek() {
seq.push(next_ch);
chars.next();
if next_ch == 'm' {
break;
}
}
let remaining: String = chars.clone().collect();
if remaining.starts_with(" TRACE") {
modified.push_str("\x1b[96m");
} else {
modified.push_str(&seq);
}
} else {
modified.push(ch);
}
}
self.inner.write_all(modified.as_bytes())?;
Ok(buf.len())
} else {
self.inner.write(buf)
}
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl Clone for ColoredLogWriter {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
struct ColoredLogWriterMaker {
writer: ColoredLogWriter,
}
impl ColoredLogWriterMaker {
fn new(file: RotatingFileWriter) -> Self {
Self {
writer: ColoredLogWriter::new(file),
}
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for ColoredLogWriterMaker {
type Writer = ColoredLogWriter;
fn make_writer(&'a self) -> Self::Writer {
self.writer.clone()
}
}
pub fn init_logging(args: &Args, is_interactive: bool) -> Result<()> {
if args.logging_disabled() {
tracing_subscriber::registry()
.with(EnvFilter::new("off"))
.init();
} else if is_interactive {
let log_file = RotatingFileWriter::new("netget.log")?;
let colored_writer = ColoredLogWriterMaker::new(log_file);
let default_level = if cfg!(debug_assertions) {
Level::TRACE
} else {
Level::INFO
};
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(format!("netget={}", default_level)));
tracing_subscriber::registry()
.with(
fmt::layer()
.with_writer(colored_writer)
.with_ansi(true)
.with_target(true)
.with_thread_ids(false)
.with_line_number(true),
)
.with(filter)
.init();
} else {
let log_level = args.effective_log_level();
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(format!("netget={log_level}")));
tracing_subscriber::registry()
.with(
fmt::layer()
.with_writer(io::stderr)
.with_ansi(true)
.with_target(false)
.with_thread_ids(false)
.with_line_number(false),
)
.with(filter)
.init();
}
crate::panic_log::install();
Ok(())
}
pub struct TerminalGuard {
enhanced_supported: bool,
}
impl TerminalGuard {
#[allow(dead_code)]
pub fn new(enhanced_supported: bool) -> Self {
Self { enhanced_supported }
}
}
impl Drop for TerminalGuard {
fn drop(&mut self) {
if self.enhanced_supported {
let _ = execute!(io::stdout(), PopKeyboardEnhancementFlags);
}
let _ = disable_raw_mode();
let _ = execute!(io::stdout(), LeaveAlternateScreen);
}
}