Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ salsa = "0.23.0"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
tempfile = "3.20.0"
thiserror = "2.0.12"
tokio = { version = "1.45.0", features = ["full"] }
toml = "0.9.2"
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"

[workspace.lints.clippy]
Expand Down
1 change: 1 addition & 0 deletions crates/djls-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tower-lsp-server = { workspace = true }
tracing = { workspace = true }

[build-dependencies]
djls-dev = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions crates/djls-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod client;
mod db;
mod documents;
mod logging;
mod queue;
mod server;
mod session;
Expand Down
64 changes: 64 additions & 0 deletions crates/djls-server/src/logging.rs
Original file line number Diff line number Diff line change
@@ -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)*);
};
}
77 changes: 23 additions & 54 deletions crates/djls-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use tower_lsp_server::lsp_types::DidOpenTextDocumentParams;
use tower_lsp_server::lsp_types::InitializeParams;
use tower_lsp_server::lsp_types::InitializeResult;
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::SaveOptions;
use tower_lsp_server::lsp_types::ServerCapabilities;
Expand All @@ -25,7 +24,8 @@ use tower_lsp_server::lsp_types::WorkspaceFoldersServerCapabilities;
use tower_lsp_server::lsp_types::WorkspaceServerCapabilities;
use tower_lsp_server::LanguageServer;

use crate::client;
use crate::log_error;
use crate::log_info;
use crate::queue::Queue;
use crate::session::Session;

Expand Down Expand Up @@ -55,10 +55,7 @@ impl DjangoLanguageServer {
if let Some(s) = &*session {
f(s)
} else {
client::log_message(
MessageType::ERROR,
"Attempted to access session before initialization",
);
log_error!("Attempted to access session before initialization");
R::default()
}
}
Expand All @@ -72,10 +69,7 @@ impl DjangoLanguageServer {
if let Some(s) = &mut *session {
f(s)
} else {
client::log_message(
MessageType::ERROR,
"Attempted to access session before initialization",
);
log_error!("Attempted to access session before initialization");
R::default()
}
}
Expand All @@ -88,16 +82,16 @@ impl DjangoLanguageServer {
let session_arc = Arc::clone(&self.session);

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 {
client::log_message(MessageType::INFO, "Task submitted successfully");
log_info!("Task submitted successfully");
}
}
}

impl LanguageServer for DjangoLanguageServer {
async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> {
client::log_message(MessageType::INFO, "Initializing server...");
log_info!("Initializing server...");

let session = Session::new(&params);

Expand Down Expand Up @@ -145,10 +139,7 @@ impl LanguageServer for DjangoLanguageServer {

#[allow(clippy::too_many_lines)]
async fn initialized(&self, _params: InitializedParams) {
client::log_message(
MessageType::INFO,
"Server received initialized notification.",
);
log_info!("Server received initialized notification.");

self.with_session_task(|session_arc| async move {
let project_path_and_venv = {
Expand All @@ -168,16 +159,13 @@ impl LanguageServer for DjangoLanguageServer {
};

if let Some((path_display, venv_path)) = project_path_and_venv {
client::log_message(
MessageType::INFO,
format!("Task: Starting initialization for project at: {path_display}"),
log_info!(
"Task: Starting initialization for project at: {}",
path_display
);

if let Some(ref path) = venv_path {
client::log_message(
MessageType::INFO,
format!("Using virtual environment from config: {path}"),
);
log_info!("Using virtual environment from config: {}", path);
}

let init_result = {
Expand All @@ -197,17 +185,13 @@ impl LanguageServer for DjangoLanguageServer {

match init_result {
Ok(()) => {
client::log_message(
MessageType::INFO,
format!("Task: Successfully initialized project: {path_display}"),
);
log_info!("Task: Successfully initialized project: {}", path_display);
}
Err(e) => {
client::log_message(
MessageType::ERROR,
format!(
"Task: Failed to initialize Django project at {path_display}: {e}"
),
log_error!(
"Task: Failed to initialize Django project at {}: {}",
path_display,
e
);

// Clear project on error
Expand All @@ -218,10 +202,7 @@ impl LanguageServer for DjangoLanguageServer {
}
}
} else {
client::log_message(
MessageType::INFO,
"Task: No project instance found to initialize.",
);
log_info!("Task: No project instance found to initialize.");
}
Ok(())
})
Expand All @@ -233,10 +214,7 @@ impl LanguageServer for DjangoLanguageServer {
}

async fn did_open(&self, params: DidOpenTextDocumentParams) {
client::log_message(
MessageType::INFO,
format!("Opened document: {:?}", params.text_document.uri),
);
log_info!("Opened document: {:?}", params.text_document.uri);

self.with_session_mut(|session| {
let db = session.db();
Expand All @@ -246,10 +224,7 @@ impl LanguageServer for DjangoLanguageServer {
}

async fn did_change(&self, params: DidChangeTextDocumentParams) {
client::log_message(
MessageType::INFO,
format!("Changed document: {:?}", params.text_document.uri),
);
log_info!("Changed document: {:?}", params.text_document.uri);

self.with_session_mut(|session| {
let db = session.db();
Expand All @@ -259,10 +234,7 @@ impl LanguageServer for DjangoLanguageServer {
}

async fn did_close(&self, params: DidCloseTextDocumentParams) {
client::log_message(
MessageType::INFO,
format!("Closed document: {:?}", params.text_document.uri),
);
log_info!("Closed document: {:?}", params.text_document.uri);

self.with_session_mut(|session| {
session.documents_mut().handle_did_close(&params);
Expand Down Expand Up @@ -290,10 +262,7 @@ impl LanguageServer for DjangoLanguageServer {
}

async fn did_change_configuration(&self, _params: DidChangeConfigurationParams) {
client::log_message(
MessageType::INFO,
"Configuration change detected. Reloading settings...",
);
log_info!("Configuration change detected. Reloading settings...");

let project_path = self
.with_session(|session| session.project().map(|p| p.path().to_path_buf()))
Expand All @@ -305,7 +274,7 @@ impl LanguageServer for DjangoLanguageServer {
session.set_settings(new_settings);
}
Err(e) => {
client::log_message(MessageType::ERROR, format!("Error loading settings: {e}"));
log_error!("Error loading settings: {}", e);
}
})
.await;
Expand Down