From 69f89b4c6980c6b2f8e0302f0711b59f05e3cd44 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Mon, 15 Aug 2022 15:25:59 +0300
Subject: [PATCH] feat: now can highlight relevant parts of logs

To highlight all the logging messages containing the given key do this:
* rust: `tlog::highlight_key(log_key, Some(color))`.
* lua: `picolib.log.highlight_key(log_key, color)`.

To disable highlight for the given key do this:
* rust: `tlog::highlight_key(log_key, None)`.
* lua: `picolib.log.highlight_key(log_key)`.

To disable highlighting of all keys do this:
* rust: `tlog::clear_highlight()`.
* lua: `picolib.log.clear_highlight()`.
---
 src/main.rs | 29 +++++++++++++++++++++++++++++
 src/tlog.rs | 43 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index 9ab9bafa12..4b7e95a83d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -114,6 +114,35 @@ fn picolib_setup(args: &args::Run) {
                 .propose_and_wait(traft::OpReturnOne, Duration::from_secs_f64(timeout))
         }),
     );
+    luamod.set("log", &[()]);
+    #[rustfmt::skip]
+    l.exec_with(
+        "picolib.log.highlight_key = ...",
+        tlua::function2(|key: String, color: Option<String>| -> Result<(), String> {
+            let color = match color.as_deref() {
+                None            => None,
+                Some("red")     => Some(tlog::Color::Red),
+                Some("green")   => Some(tlog::Color::Green),
+                Some("blue")    => Some(tlog::Color::Blue),
+                Some("cyan")    => Some(tlog::Color::Cyan),
+                Some("yellow")  => Some(tlog::Color::Yellow),
+                Some("magenta") => Some(tlog::Color::Magenta),
+                Some("white")   => Some(tlog::Color::White),
+                Some("black")   => Some(tlog::Color::Black),
+                Some(other) => {
+                    return Err(format!("unknown color: {other:?}"))
+                }
+            };
+            tlog::highlight_key(key, color);
+            Ok(())
+        }),
+    )
+    .unwrap();
+    l.exec_with(
+        "picolib.log.clear_highlight = ...",
+        tlua::function0(tlog::clear_highlight),
+    )
+    .unwrap();
     {
         l.exec(
             r#"
diff --git a/src/tlog.rs b/src/tlog.rs
index c7b3911896..4f78cb7204 100644
--- a/src/tlog.rs
+++ b/src/tlog.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
 pub struct Drain;
 
 pub fn root() -> slog::Logger {
@@ -54,10 +56,49 @@ struct StrSerializer {
     pub str: String,
 }
 
+#[rustfmt::skip]
+#[repr(u8)]
+#[derive(Clone, Copy)]
+pub enum Color {
+    Red     = 1,
+    Green   = 2,
+    Blue    = 4,
+    Cyan    = 6,
+    Yellow  = 3,
+    Magenta = 5,
+    White   = 7,
+    Black   = 0,
+}
+
+pub static mut HIGHLIGHT: Option<HashMap<String, Color>> = None;
+
+#[inline]
+pub fn clear_highlight() {
+    unsafe {
+        HIGHLIGHT = None;
+    }
+}
+
+#[inline]
+pub fn highlight_key(key: impl Into<String> + AsRef<str>, color: Option<Color>) {
+    let hi = unsafe { HIGHLIGHT.get_or_insert_with(HashMap::new) };
+    if let Some(color) = color {
+        hi.insert(key.into(), color);
+    } else {
+        hi.remove(key.as_ref());
+    }
+}
+
 impl slog::Serializer for StrSerializer {
     fn emit_arguments(&mut self, key: slog::Key, val: &std::fmt::Arguments) -> slog::Result {
         use std::fmt::Write;
-        write!(&mut self.str, ", {key}: {val}").unwrap();
+        match unsafe { HIGHLIGHT.as_ref() }.and_then(|h| h.get(key)) {
+            Some(&color) => {
+                let color = color as u8;
+                write!(&mut self.str, ", \x1b[3{color}m{key}: {val}\x1b[0m").unwrap();
+            }
+            _ => write!(&mut self.str, ", {key}: {val}").unwrap(),
+        }
         Ok(())
     }
 }
-- 
GitLab