mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-09-09 20:06:26 +00:00
add basic logging using the tracing crate
This commit is contained in:
parent
68ea842821
commit
e784ffa350
7 changed files with 91 additions and 37 deletions
|
@ -28,8 +28,13 @@ debug:
|
|||
tmux has-session -t djls-debug 2>/dev/null && tmux kill-session -t djls-debug
|
||||
pkill -f "lsp-devtools" || true
|
||||
|
||||
tmux new-session -d -s djls-debug "nvim tests/project/djls_app/templates/djls_app/base.html"
|
||||
# Get XDG cache directory for logs
|
||||
LOG_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/django-language-server"
|
||||
LOG_FILE="$LOG_DIR/djls.$(date +%Y-%m-%d).log"
|
||||
|
||||
tmux new-session -d -s djls-debug "RUST_LOG=djls_server=debug nvim tests/project/djls_app/templates/djls_app/base.html"
|
||||
tmux split-window -h -p 20 "just dev devtools record"
|
||||
tmux split-window -v -p 50 "mkdir -p '$LOG_DIR' && touch '$LOG_FILE' && tail -f '$LOG_FILE'"
|
||||
|
||||
tmux select-pane -L
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ tokio = { version = "1.45.0", features = ["full"] }
|
|||
toml = "0.8.22"
|
||||
tower-lsp-server = { version = "0.21.1", features = ["proposed"] }
|
||||
thiserror = "2.0.12"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
tracing-appender = "0.2"
|
||||
which = "7.0.3"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -98,8 +98,11 @@ impl DjangoLanguageServer {
|
|||
impl LanguageServer for DjangoLanguageServer {
|
||||
async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> {
|
||||
client::log_message(MessageType::INFO, "Initializing server...");
|
||||
tracing::info!("LSP server initializing");
|
||||
tracing::debug!("Initialize params: {:?}", params);
|
||||
|
||||
let session = Session::new(¶ms);
|
||||
tracing::debug!("Session created successfully");
|
||||
|
||||
{
|
||||
let mut session_lock = self.session.write().await;
|
||||
|
@ -149,6 +152,7 @@ impl LanguageServer for DjangoLanguageServer {
|
|||
MessageType::INFO,
|
||||
"Server received initialized notification.",
|
||||
);
|
||||
tracing::info!("LSP server initialized and ready");
|
||||
|
||||
self.with_session_task(|session_arc| async move {
|
||||
let project_path_and_venv = {
|
||||
|
@ -237,6 +241,8 @@ impl LanguageServer for DjangoLanguageServer {
|
|||
MessageType::INFO,
|
||||
format!("Opened document: {:?}", params.text_document.uri),
|
||||
);
|
||||
tracing::info!("Document opened: {:?}", params.text_document.uri);
|
||||
tracing::debug!("Document language: {}", params.text_document.language_id);
|
||||
|
||||
self.with_session_mut(|session| {
|
||||
let db = session.db();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use djls_conf::Settings;
|
||||
use djls_project::DjangoProject;
|
||||
use salsa::StorageHandle;
|
||||
use tower_lsp_server::lsp_types::ClientCapabilities;
|
||||
use tower_lsp_server::lsp_types::InitializeParams;
|
||||
|
||||
|
@ -16,35 +15,6 @@ pub struct Session {
|
|||
#[allow(dead_code)]
|
||||
client_capabilities: ClientCapabilities,
|
||||
|
||||
/// A thread-safe Salsa database handle that can be shared between threads.
|
||||
///
|
||||
/// This implements the insight from [this Salsa Zulip discussion](https://salsa.zulipchat.com/#narrow/channel/145099-Using-Salsa/topic/.E2.9C.94.20Advice.20on.20using.20salsa.20from.20Sync.20.2B.20Send.20context/with/495497515)
|
||||
/// where we're using the `StorageHandle` to create a thread-safe handle that can be
|
||||
/// shared between threads. When we need to use it, we clone the handle to get a new reference.
|
||||
///
|
||||
/// This handle allows us to create database instances as needed.
|
||||
/// Even though we're using a single-threaded runtime, we still need
|
||||
/// this to be thread-safe because of LSP trait requirements.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```rust,ignore
|
||||
/// // Use the StorageHandle in Session
|
||||
/// let db_handle = StorageHandle::new(None);
|
||||
///
|
||||
/// // Clone it to pass to different threads
|
||||
/// let db_handle_clone = db_handle.clone();
|
||||
///
|
||||
/// // Use it in an async context
|
||||
/// async_fn(move || {
|
||||
/// // Get a database from the handle
|
||||
/// let storage = db_handle_clone.into_storage();
|
||||
/// let db = ServerDatabase::new(storage);
|
||||
///
|
||||
/// // Use the database
|
||||
/// db.some_query(args)
|
||||
/// });
|
||||
/// ```
|
||||
db_handle: StorageHandle<ServerDatabase>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
|
@ -67,7 +37,6 @@ impl Session {
|
|||
project,
|
||||
documents: Store::default(),
|
||||
settings,
|
||||
db_handle: StorageHandle::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,10 +66,20 @@ impl Session {
|
|||
|
||||
/// Get a database instance directly from the session
|
||||
///
|
||||
/// This creates a usable database from the handle, which can be used
|
||||
/// to query and update data in the database.
|
||||
/// This creates a usable database with Salsa event logging
|
||||
pub fn db(&self) -> ServerDatabase {
|
||||
let storage = self.db_handle.clone().into_storage();
|
||||
let storage = salsa::Storage::new(if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
Some(Box::new({
|
||||
move |event: salsa::Event| {
|
||||
if matches!(event.kind, salsa::EventKind::WillCheckCancellation) {
|
||||
return;
|
||||
}
|
||||
tracing::debug!("Salsa event: {event:?}");
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
});
|
||||
ServerDatabase::new(storage)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,12 @@ djls-server = { workspace = true }
|
|||
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
directories = { workspace = true }
|
||||
pyo3 = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
tracing-appender = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
djls-dev = { workspace = true }
|
||||
|
|
|
@ -2,6 +2,9 @@ use std::fmt::Write;
|
|||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use directories::ProjectDirs;
|
||||
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::args::Args;
|
||||
use crate::commands::Command;
|
||||
|
@ -20,23 +23,76 @@ pub struct Cli {
|
|||
pub args: Args,
|
||||
}
|
||||
|
||||
/// Initialize tracing with file logging to XDG directories
|
||||
fn initialize_tracing() -> Result<tracing_appender::non_blocking::WorkerGuard> {
|
||||
// Get XDG-compliant log directory
|
||||
let log_dir = if let Some(proj_dirs) = ProjectDirs::from("com", "djls", "django-language-server") {
|
||||
proj_dirs.cache_dir().to_path_buf()
|
||||
} else {
|
||||
// Fallback to current directory if XDG not available
|
||||
std::env::current_dir()?.join("logs")
|
||||
};
|
||||
|
||||
// Create log directory if it doesn't exist
|
||||
std::fs::create_dir_all(&log_dir)?;
|
||||
|
||||
// Set up rolling file appender (daily rotation)
|
||||
let file_appender = RollingFileAppender::builder()
|
||||
.rotation(Rotation::DAILY)
|
||||
.filename_prefix("djls")
|
||||
.filename_suffix("log")
|
||||
.build(&log_dir)?;
|
||||
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
// Initialize tracing subscriber with env filter
|
||||
tracing_subscriber::fmt()
|
||||
.with_writer(non_blocking)
|
||||
.with_env_filter(
|
||||
EnvFilter::from_default_env()
|
||||
.add_directive("djls=debug".parse()?)
|
||||
.add_directive("djls_server=debug".parse()?)
|
||||
.add_directive("djls_project=info".parse()?)
|
||||
)
|
||||
.with_ansi(false) // Disable ANSI colors for file output
|
||||
.init();
|
||||
|
||||
tracing::info!("Tracing initialized, logs writing to: {}", log_dir.display());
|
||||
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Parse CLI arguments, execute the chosen command, and handle results
|
||||
pub fn run(args: Vec<String>) -> Result<()> {
|
||||
// Initialize tracing first
|
||||
let _guard = initialize_tracing()?;
|
||||
|
||||
tracing::info!("Django Language Server starting");
|
||||
tracing::debug!("CLI args: {:?}", args);
|
||||
|
||||
let cli = Cli::try_parse_from(args).unwrap_or_else(|e| {
|
||||
tracing::error!("Failed to parse CLI arguments: {}", e);
|
||||
e.exit();
|
||||
});
|
||||
|
||||
let result = match &cli.command {
|
||||
DjlsCommand::Serve(cmd) => cmd.execute(&cli.args),
|
||||
DjlsCommand::Serve(cmd) => {
|
||||
tracing::info!("Starting LSP server");
|
||||
cmd.execute(&cli.args)
|
||||
}
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(exit) => exit.process_exit(),
|
||||
Ok(exit) => {
|
||||
tracing::info!("Command completed successfully");
|
||||
exit.process_exit()
|
||||
}
|
||||
Err(e) => {
|
||||
let mut msg = e.to_string();
|
||||
if let Some(source) = e.source() {
|
||||
let _ = write!(msg, ", caused by {source}");
|
||||
}
|
||||
tracing::error!("Command failed: {}", msg);
|
||||
Exit::error().with_message(msg).process_exit()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue