tokio-xmpp: Add syntax highlighting to debug logs

This uses syntect, and has been checked to have zero overhead when it is
disabled.
This commit is contained in:
Emmanuel Gil Peyrot 2023-06-18 14:21:49 +02:00
parent 99f3811c71
commit 17335136fe
2 changed files with 47 additions and 6 deletions

View file

@ -30,6 +30,7 @@ minidom = "0.15"
rxml = "0.9.1" rxml = "0.9.1"
webpki-roots = { version = "0.23", optional = true } webpki-roots = { version = "0.23", optional = true }
rand = "^0.8" rand = "^0.8"
syntect = { version = "5", optional = true }
[dev-dependencies] [dev-dependencies]
env_logger = "0.10" env_logger = "0.10"
@ -41,3 +42,4 @@ rustc_version = "0.4"
default = ["tls-native"] default = ["tls-native"]
tls-rust = ["tokio-rustls", "webpki-roots"] tls-rust = ["tokio-rustls", "webpki-roots"]
tls-native = ["tokio-native-tls", "native-tls"] tls-native = ["tokio-native-tls", "native-tls"]
syntax-highlighting = ["syntect"]

View file

@ -10,9 +10,43 @@ use std::collections::HashMap;
use std::default::Default; use std::default::Default;
use std::fmt::Write; use std::fmt::Write;
use std::io; use std::io;
#[cfg(feature = "syntax-highlighting")]
use std::sync::OnceLock;
use tokio_util::codec::{Decoder, Encoder}; use tokio_util::codec::{Decoder, Encoder};
use xmpp_parsers::Element; use xmpp_parsers::Element;
#[cfg(feature = "syntax-highlighting")]
static PS: OnceLock<syntect::parsing::SyntaxSet> = OnceLock::new();
#[cfg(feature = "syntax-highlighting")]
static SYNTAX: OnceLock<syntect::parsing::SyntaxReference> = OnceLock::new();
#[cfg(feature = "syntax-highlighting")]
static THEME: OnceLock<syntect::highlighting::Theme> = OnceLock::new();
#[cfg(feature = "syntax-highlighting")]
fn init_syntect() {
let ps = syntect::parsing::SyntaxSet::load_defaults_newlines();
let syntax = ps.find_syntax_by_extension("xml").unwrap();
let ts = syntect::highlighting::ThemeSet::load_defaults();
let theme = ts.themes["Solarized (dark)"].clone();
SYNTAX.set(syntax.clone()).unwrap();
PS.set(ps).unwrap();
THEME.set(theme).unwrap();
}
#[cfg(feature = "syntax-highlighting")]
fn highlight_xml(xml: &str) -> String {
let mut h = syntect::easy::HighlightLines::new(SYNTAX.get().unwrap(), THEME.get().unwrap());
let ranges: Vec<_> = h.highlight_line(&xml, PS.get().unwrap()).unwrap();
let escaped = syntect::util::as_24_bit_terminal_escaped(&ranges[..], false);
format!("{}\x1b[0m", escaped)
}
#[cfg(not(feature = "syntax-highlighting"))]
fn highlight_xml(xml: &str) -> &str {
xml
}
/// Anything that can be sent or received on an XMPP/XML stream /// Anything that can be sent or received on an XMPP/XML stream
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum Packet { pub enum Packet {
@ -40,6 +74,10 @@ impl XMPPCodec {
pub fn new() -> Self { pub fn new() -> Self {
let stanza_builder = TreeBuilder::new(); let stanza_builder = TreeBuilder::new();
let driver = PushDriver::wrap(Lexer::new(), RawParser::new()); let driver = PushDriver::wrap(Lexer::new(), RawParser::new());
#[cfg(feature = "syntax-highlighting")]
if log::log_enabled!(log::Level::Debug) && PS.get().is_none() {
init_syntect();
}
XMPPCodec { XMPPCodec {
ns: None, ns: None,
driver, driver,
@ -88,19 +126,19 @@ impl Decoder for XMPPCodec {
}, },
)) ))
.collect(); .collect();
debug!("<< {}", String::from(root)); debug!("<< {}", highlight_xml(&String::from(root)));
return Ok(Some(Packet::StreamStart(attrs))); return Ok(Some(Packet::StreamStart(attrs)));
} else if self.stanza_builder.depth() == 1 { } else if self.stanza_builder.depth() == 1 {
self.driver.release_temporaries(); self.driver.release_temporaries();
if let Some(stanza) = self.stanza_builder.unshift_child() { if let Some(stanza) = self.stanza_builder.unshift_child() {
debug!("<< {}", String::from(&stanza)); debug!("<< {}", highlight_xml(&String::from(&stanza)));
return Ok(Some(Packet::Stanza(stanza))); return Ok(Some(Packet::Stanza(stanza)));
} }
} else if let Some(_) = self.stanza_builder.root.take() { } else if let Some(_) = self.stanza_builder.root.take() {
self.driver.release_temporaries(); self.driver.release_temporaries();
debug!("<< </stream:stream>"); debug!("<< {}", highlight_xml("</stream:stream>"));
return Ok(Some(Packet::StreamEnd)); return Ok(Some(Packet::StreamEnd));
} }
} }
@ -140,7 +178,7 @@ impl Encoder<Packet> for XMPPCodec {
write!(buf, ">\n").map_err(to_io_err)?; write!(buf, ">\n").map_err(to_io_err)?;
let utf8 = std::str::from_utf8(dst)?; let utf8 = std::str::from_utf8(dst)?;
debug!(">> {}", utf8); debug!(">> {}", highlight_xml(utf8));
write!(dst, "{}", buf)? write!(dst, "{}", buf)?
} }
Packet::Stanza(stanza) => { Packet::Stanza(stanza) => {
@ -148,15 +186,16 @@ impl Encoder<Packet> for XMPPCodec {
.write_to(&mut WriteBytes::new(dst)) .write_to(&mut WriteBytes::new(dst))
.map_err(|e| to_io_err(format!("{}", e)))?; .map_err(|e| to_io_err(format!("{}", e)))?;
let utf8 = std::str::from_utf8(dst)?; let utf8 = std::str::from_utf8(dst)?;
debug!(">> {}", utf8); debug!(">> {}", highlight_xml(utf8));
} }
Packet::Text(text) => { Packet::Text(text) => {
let _ = write_text(&text, dst).map_err(to_io_err)?; let _ = write_text(&text, dst).map_err(to_io_err)?;
let utf8 = std::str::from_utf8(dst)?; let utf8 = std::str::from_utf8(dst)?;
debug!(">> {}", utf8); debug!(">> {}", highlight_xml(utf8));
} }
Packet::StreamEnd => { Packet::StreamEnd => {
let _ = write!(dst, "</stream:stream>\n").map_err(to_io_err); let _ = write!(dst, "</stream:stream>\n").map_err(to_io_err);
debug!(">> {}", highlight_xml("</stream:stream>"));
} }
} }