Add logging macros for tracing migration (#176)

This commit is contained in:
Josh Thomas 2025-08-17 16:55:58 -05:00 committed by GitHub
parent 7e9339ba86
commit 1ceb972f0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 93 additions and 55 deletions

View file

@ -23,10 +23,13 @@ salsa = "0.23.0"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140" serde_json = "1.0.140"
tempfile = "3.20.0" tempfile = "3.20.0"
thiserror = "2.0.12"
tokio = { version = "1.45.0", features = ["full"] } tokio = { version = "1.45.0", features = ["full"] }
toml = "0.9.2" toml = "0.9.2"
tower-lsp-server = { version = "0.22.0", features = ["proposed"] } tower-lsp-server = { version = "0.22.0", features = ["proposed"] }
thiserror = "2.0.12" tracing = "0.1.41"
tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "fmt", "time"] }
which = "8.0.0" which = "8.0.0"
[workspace.lints.clippy] [workspace.lints.clippy]

View file

@ -20,6 +20,7 @@ serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tower-lsp-server = { workspace = true } tower-lsp-server = { workspace = true }
tracing = { workspace = true }
[build-dependencies] [build-dependencies]
djls-dev = { workspace = true } djls-dev = { workspace = true }

View file

@ -1,6 +1,7 @@
mod client; mod client;
mod db; mod db;
mod documents; mod documents;
mod logging;
mod queue; mod queue;
mod server; mod server;
mod session; mod session;

View file

@ -0,0 +1,64 @@
//! Temporary logging macros for dual-dispatch to both LSP client and tracing.
//!
//! These macros bridge the gap during our migration from `client::log_message`
//! to the tracing infrastructure. They ensure messages are sent to both systems
//! so we maintain LSP client visibility while building out tracing support.
//!
//! Each macro supports two invocation patterns to handle the different APIs:
//!
//! 1. String literal:
//! ```rust,ignore
//! log_info!("Server initialized");
//! log_warn!("Configuration not found");
//! log_error!("Failed to parse document");
//! ```
//!
//! 2. Format string with arguments:
//! ```rust,ignore
//! log_info!("Processing {} documents", count);
//! log_warn!("Timeout after {}ms for {}", ms, path);
//! log_error!("Failed to open {}: {}", file, err);
//! ```
//!
//! The difference in the macro arms exists because of how each system works:
//!
//! - `client::log_message` expects a single string value
//! - `tracing` macros can handle format strings natively for structured logging
//! - For format strings, we format once for the client but pass the original
//! format string and args to tracing to preserve structured data
#[macro_export]
macro_rules! log_info {
($msg:literal) => {
$crate::client::log_message(tower_lsp_server::lsp_types::MessageType::INFO, $msg);
tracing::info!($msg);
};
($fmt:literal, $($arg:tt)*) => {
$crate::client::log_message(tower_lsp_server::lsp_types::MessageType::INFO, format!($fmt, $($arg)*));
tracing::info!($fmt, $($arg)*);
};
}
#[macro_export]
macro_rules! log_warn {
($msg:literal) => {
$crate::client::log_message(tower_lsp_server::lsp_types::MessageType::WARNING, $msg);
tracing::warn!($msg);
};
($fmt:literal, $($arg:tt)*) => {
$crate::client::log_message(tower_lsp_server::lsp_types::MessageType::WARNING, format!($fmt, $($arg)*));
tracing::warn!($fmt, $($arg)*);
};
}
#[macro_export]
macro_rules! log_error {
($msg:literal) => {
$crate::client::log_message(tower_lsp_server::lsp_types::MessageType::ERROR, $msg);
tracing::error!($msg);
};
($fmt:literal, $($arg:tt)*) => {
$crate::client::log_message(tower_lsp_server::lsp_types::MessageType::ERROR, format!($fmt, $($arg)*));
tracing::error!($fmt, $($arg)*);
};
}

View file

@ -13,7 +13,6 @@ use tower_lsp_server::lsp_types::DidOpenTextDocumentParams;
use tower_lsp_server::lsp_types::InitializeParams; use tower_lsp_server::lsp_types::InitializeParams;
use tower_lsp_server::lsp_types::InitializeResult; use tower_lsp_server::lsp_types::InitializeResult;
use tower_lsp_server::lsp_types::InitializedParams; use tower_lsp_server::lsp_types::InitializedParams;
use tower_lsp_server::lsp_types::MessageType;
use tower_lsp_server::lsp_types::OneOf; use tower_lsp_server::lsp_types::OneOf;
use tower_lsp_server::lsp_types::SaveOptions; use tower_lsp_server::lsp_types::SaveOptions;
use tower_lsp_server::lsp_types::ServerCapabilities; use tower_lsp_server::lsp_types::ServerCapabilities;
@ -25,7 +24,8 @@ use tower_lsp_server::lsp_types::WorkspaceFoldersServerCapabilities;
use tower_lsp_server::lsp_types::WorkspaceServerCapabilities; use tower_lsp_server::lsp_types::WorkspaceServerCapabilities;
use tower_lsp_server::LanguageServer; use tower_lsp_server::LanguageServer;
use crate::client; use crate::log_error;
use crate::log_info;
use crate::queue::Queue; use crate::queue::Queue;
use crate::session::Session; use crate::session::Session;
@ -55,10 +55,7 @@ impl DjangoLanguageServer {
if let Some(s) = &*session { if let Some(s) = &*session {
f(s) f(s)
} else { } else {
client::log_message( log_error!("Attempted to access session before initialization");
MessageType::ERROR,
"Attempted to access session before initialization",
);
R::default() R::default()
} }
} }
@ -72,10 +69,7 @@ impl DjangoLanguageServer {
if let Some(s) = &mut *session { if let Some(s) = &mut *session {
f(s) f(s)
} else { } else {
client::log_message( log_error!("Attempted to access session before initialization");
MessageType::ERROR,
"Attempted to access session before initialization",
);
R::default() R::default()
} }
} }
@ -88,16 +82,16 @@ impl DjangoLanguageServer {
let session_arc = Arc::clone(&self.session); let session_arc = Arc::clone(&self.session);
if let Err(e) = self.queue.submit(async move { f(session_arc).await }).await { if let Err(e) = self.queue.submit(async move { f(session_arc).await }).await {
client::log_message(MessageType::ERROR, format!("Failed to submit task: {e}")); log_error!("Failed to submit task: {}", e);
} else { } else {
client::log_message(MessageType::INFO, "Task submitted successfully"); log_info!("Task submitted successfully");
} }
} }
} }
impl LanguageServer for DjangoLanguageServer { impl LanguageServer for DjangoLanguageServer {
async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> { async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> {
client::log_message(MessageType::INFO, "Initializing server..."); log_info!("Initializing server...");
let session = Session::new(&params); let session = Session::new(&params);
@ -145,10 +139,7 @@ impl LanguageServer for DjangoLanguageServer {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
async fn initialized(&self, _params: InitializedParams) { async fn initialized(&self, _params: InitializedParams) {
client::log_message( log_info!("Server received initialized notification.");
MessageType::INFO,
"Server received initialized notification.",
);
self.with_session_task(|session_arc| async move { self.with_session_task(|session_arc| async move {
let project_path_and_venv = { let project_path_and_venv = {
@ -168,16 +159,13 @@ impl LanguageServer for DjangoLanguageServer {
}; };
if let Some((path_display, venv_path)) = project_path_and_venv { if let Some((path_display, venv_path)) = project_path_and_venv {
client::log_message( log_info!(
MessageType::INFO, "Task: Starting initialization for project at: {}",
format!("Task: Starting initialization for project at: {path_display}"), path_display
); );
if let Some(ref path) = venv_path { if let Some(ref path) = venv_path {
client::log_message( log_info!("Using virtual environment from config: {}", path);
MessageType::INFO,
format!("Using virtual environment from config: {path}"),
);
} }
let init_result = { let init_result = {
@ -197,17 +185,13 @@ impl LanguageServer for DjangoLanguageServer {
match init_result { match init_result {
Ok(()) => { Ok(()) => {
client::log_message( log_info!("Task: Successfully initialized project: {}", path_display);
MessageType::INFO,
format!("Task: Successfully initialized project: {path_display}"),
);
} }
Err(e) => { Err(e) => {
client::log_message( log_error!(
MessageType::ERROR, "Task: Failed to initialize Django project at {}: {}",
format!( path_display,
"Task: Failed to initialize Django project at {path_display}: {e}" e
),
); );
// Clear project on error // Clear project on error
@ -218,10 +202,7 @@ impl LanguageServer for DjangoLanguageServer {
} }
} }
} else { } else {
client::log_message( log_info!("Task: No project instance found to initialize.");
MessageType::INFO,
"Task: No project instance found to initialize.",
);
} }
Ok(()) Ok(())
}) })
@ -233,10 +214,7 @@ impl LanguageServer for DjangoLanguageServer {
} }
async fn did_open(&self, params: DidOpenTextDocumentParams) { async fn did_open(&self, params: DidOpenTextDocumentParams) {
client::log_message( log_info!("Opened document: {:?}", params.text_document.uri);
MessageType::INFO,
format!("Opened document: {:?}", params.text_document.uri),
);
self.with_session_mut(|session| { self.with_session_mut(|session| {
let db = session.db(); let db = session.db();
@ -246,10 +224,7 @@ impl LanguageServer for DjangoLanguageServer {
} }
async fn did_change(&self, params: DidChangeTextDocumentParams) { async fn did_change(&self, params: DidChangeTextDocumentParams) {
client::log_message( log_info!("Changed document: {:?}", params.text_document.uri);
MessageType::INFO,
format!("Changed document: {:?}", params.text_document.uri),
);
self.with_session_mut(|session| { self.with_session_mut(|session| {
let db = session.db(); let db = session.db();
@ -259,10 +234,7 @@ impl LanguageServer for DjangoLanguageServer {
} }
async fn did_close(&self, params: DidCloseTextDocumentParams) { async fn did_close(&self, params: DidCloseTextDocumentParams) {
client::log_message( log_info!("Closed document: {:?}", params.text_document.uri);
MessageType::INFO,
format!("Closed document: {:?}", params.text_document.uri),
);
self.with_session_mut(|session| { self.with_session_mut(|session| {
session.documents_mut().handle_did_close(&params); session.documents_mut().handle_did_close(&params);
@ -290,10 +262,7 @@ impl LanguageServer for DjangoLanguageServer {
} }
async fn did_change_configuration(&self, _params: DidChangeConfigurationParams) { async fn did_change_configuration(&self, _params: DidChangeConfigurationParams) {
client::log_message( log_info!("Configuration change detected. Reloading settings...");
MessageType::INFO,
"Configuration change detected. Reloading settings...",
);
let project_path = self let project_path = self
.with_session(|session| session.project().map(|p| p.path().to_path_buf())) .with_session(|session| session.project().map(|p| p.path().to_path_buf()))
@ -305,7 +274,7 @@ impl LanguageServer for DjangoLanguageServer {
session.set_settings(new_settings); session.set_settings(new_settings);
} }
Err(e) => { Err(e) => {
client::log_message(MessageType::ERROR, format!("Error loading settings: {e}")); log_error!("Error loading settings: {}", e);
} }
}) })
.await; .await;