codebook_lsp/
lsp_logger.rs

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