codebook_lsp/
lsp_logger.rs

1use log::{Level, LevelFilter, Log, Metadata, Record};
2use std::collections::VecDeque;
3use std::io::{self, IsTerminal};
4use std::sync::{Mutex, OnceLock};
5use tokio::sync::mpsc::{self, Sender};
6use tower_lsp::Client;
7use tower_lsp::lsp_types::MessageType;
8
9const BUFFER_SIZE: usize = 1000;
10
11pub struct LspLogger {
12    sender: Mutex<Option<Sender<LogMessage>>>,
13    level: Mutex<LevelFilter>,
14    // Buffer for storing logs before LSP client is available
15    buffer: Mutex<VecDeque<LogMessage>>,
16    stderr_is_tty: bool,
17}
18
19struct LogMessage {
20    level: Level,
21    message: String,
22}
23
24impl LogMessage {
25    fn clone(&self) -> Self {
26        LogMessage {
27            level: self.level,
28            message: self.message.clone(),
29        }
30    }
31}
32
33// Global static logger instance
34static LOGGER: OnceLock<&'static LspLogger> = OnceLock::new();
35
36impl LspLogger {
37    /// Initialize the logger early without an LSP client
38    /// Logs will be sent to stderr and buffered
39    pub fn init_early(level: LevelFilter) -> Result<(), log::SetLoggerError> {
40        let stderr_is_tty = io::stderr().is_terminal();
41        let logger = Box::leak(Box::new(LspLogger {
42            sender: Mutex::new(None),
43            level: Mutex::new(level),
44            buffer: Mutex::new(VecDeque::with_capacity(BUFFER_SIZE)),
45            stderr_is_tty,
46        }));
47
48        // Try to set the logger, ignore if already initialized
49        let _ = LOGGER.set(logger);
50
51        // Get the logger (either the one we just set or the existing one)
52        let logger = LOGGER.get().expect("Logger should be initialized");
53        log::set_logger(*logger).map(|()| log::set_max_level(level))
54    }
55
56    /// Attach the LSP client to the logger and flush buffered logs
57    pub fn attach_client(client: Client, level: LevelFilter) {
58        if let Some(logger) = LOGGER.get() {
59            // Update log level
60            *logger.level.lock().unwrap() = level;
61            log::set_max_level(level);
62
63            // Create a channel for log messages
64            let (sender, mut receiver) = mpsc::channel::<LogMessage>(BUFFER_SIZE);
65
66            // Get buffered messages before we update the sender
67            let buffered_messages: Vec<LogMessage> = {
68                let buffer = logger.buffer.lock().unwrap();
69                buffer.iter().map(|msg| msg.clone()).collect()
70            };
71
72            // Update the sender
73            *logger.sender.lock().unwrap() = Some(sender.clone());
74
75            // Set up a background task to send logs to LSP client
76            let client_clone = client.clone();
77            tokio::spawn(async move {
78                while let Some(log_msg) = receiver.recv().await {
79                    let lsp_level = match log_msg.level {
80                        Level::Error => MessageType::ERROR,
81                        Level::Warn => MessageType::WARNING,
82                        Level::Info => MessageType::INFO,
83                        Level::Debug | Level::Trace => MessageType::LOG,
84                    };
85
86                    // Ignore any errors from sending log messages
87                    let _ = client_clone.log_message(lsp_level, log_msg.message).await;
88                }
89            });
90
91            // Flush buffered messages to the LSP client
92            for msg in buffered_messages {
93                // Send to the channel, ignore if it fails
94                let _ = sender.try_send(msg);
95            }
96
97            // Clear the buffer since we've sent everything
98            logger.buffer.lock().unwrap().clear();
99        }
100    }
101}
102
103impl Log for LspLogger {
104    fn enabled(&self, metadata: &Metadata) -> bool {
105        metadata.level() <= *self.level.lock().unwrap()
106    }
107
108    fn log(&self, record: &Record) {
109        if !self.enabled(record.metadata()) {
110            return;
111        }
112
113        // Format the log message with module path
114        let message = format!(
115            "[{}] {}: {}",
116            record.target(),
117            record.level(),
118            record.args()
119        );
120
121        let log_msg = LogMessage {
122            level: record.level(),
123            message: message.clone(),
124        };
125
126        // If we have a sender, use it
127        let sender_guard = self.sender.lock().unwrap();
128        if let Some(sender) = sender_guard.as_ref() {
129            // Send log message to channel, ignore if channel is full
130            let _ = sender.try_send(log_msg);
131        } else {
132            // No LSP client yet, log to stderr when interactive or the log level is important
133            if self.stderr_is_tty || matches!(log_msg.level, Level::Warn | Level::Error) {
134                eprintln!("{}", message);
135            }
136
137            // Drop the sender guard before locking buffer to avoid potential deadlock
138            drop(sender_guard);
139
140            // Add to buffer
141            let mut buffer = self.buffer.lock().unwrap();
142            if buffer.len() >= BUFFER_SIZE {
143                // Remove oldest message if buffer is full
144                buffer.pop_front();
145            }
146            buffer.push_back(log_msg);
147        }
148    }
149
150    fn flush(&self) {}
151}