summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2023-12-08 17:04:56 +0000
committerGitHub <noreply@github.com>2023-12-08 17:04:56 +0000
commit123d9ea047a2e10803e260ebf00f31fcc98463ee (patch)
tree9b816532e907c7ae6490a77fedb1cc5872c3e8a5
parentddfbe71cedbfe2ac31dbc7dbcf25761e5a7a1dce (diff)
feat(lsp): debug log file (#21500)
-rw-r--r--cli/lsp/config.rs10
-rw-r--r--cli/lsp/language_server.rs2
-rw-r--r--cli/lsp/logging.rs88
-rw-r--r--cli/lsp/repl.rs1
4 files changed, 97 insertions, 4 deletions
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index 36d4c95c6..42478b593 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -454,6 +454,10 @@ pub struct WorkspaceSettings {
#[serde(default)]
pub internal_debug: bool,
+ /// Write logs to a file in a project-local directory.
+ #[serde(default)]
+ pub log_file: bool,
+
/// A flag that indicates if linting is enabled for the workspace.
#[serde(default = "default_to_true")]
pub lint: bool,
@@ -502,6 +506,7 @@ impl Default for WorkspaceSettings {
import_map: None,
code_lens: Default::default(),
internal_debug: false,
+ log_file: false,
lint: true,
document_preload_limit: default_document_preload_limit(),
suggest: Default::default(),
@@ -1071,6 +1076,10 @@ impl Config {
paths
}
+ pub fn log_file(&self) -> bool {
+ self.settings.unscoped.log_file
+ }
+
pub fn update_capabilities(
&mut self,
capabilities: &lsp::ClientCapabilities,
@@ -1321,6 +1330,7 @@ mod tests {
test: true,
},
internal_debug: false,
+ log_file: false,
lint: true,
document_preload_limit: 1_000,
suggest: DenoCompletionSettings {
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index a893ab3a8..225b46eac 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -102,6 +102,7 @@ use crate::factory::CliFactory;
use crate::file_fetcher::FileFetcher;
use crate::graph_util;
use crate::http_util::HttpClient;
+use crate::lsp::logging::init_log_file;
use crate::lsp::tsc::file_text_changes_to_workspace_edit;
use crate::lsp::urls::LspUrlKind;
use crate::npm::create_cli_npm_resolver_for_lsp;
@@ -3242,6 +3243,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
{
let mut ls = self.0.write().await;
+ init_log_file(ls.config.log_file());
if let Err(err) = ls.update_tsconfig().await {
ls.client.show_message(MessageType::WARNING, err);
}
diff --git a/cli/lsp/logging.rs b/cli/lsp/logging.rs
index b8c8c99e6..39a53f2b8 100644
--- a/cli/lsp/logging.rs
+++ b/cli/lsp/logging.rs
@@ -1,13 +1,85 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use chrono::DateTime;
+use chrono::Utc;
+use deno_core::parking_lot::Mutex;
+use std::fs;
+use std::io::prelude::*;
+use std::path::Path;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
+use std::thread;
+use std::time::SystemTime;
static LSP_DEBUG_FLAG: AtomicBool = AtomicBool::new(false);
static LSP_LOG_LEVEL: AtomicUsize = AtomicUsize::new(log::Level::Info as usize);
static LSP_WARN_LEVEL: AtomicUsize =
AtomicUsize::new(log::Level::Warn as usize);
+static LOG_FILE: LogFile = LogFile {
+ enabled: AtomicBool::new(true),
+ buffer: Mutex::new(String::new()),
+};
+
+pub struct LogFile {
+ enabled: AtomicBool,
+ buffer: Mutex<String>,
+}
+
+impl LogFile {
+ pub fn write_line(&self, s: &str) {
+ if LOG_FILE.enabled.load(Ordering::Relaxed) {
+ let mut buffer = self.buffer.lock();
+ buffer.push_str(s);
+ buffer.push('\n');
+ }
+ }
+
+ fn commit(&self, path: &Path) {
+ let unbuffered = {
+ let mut buffer = self.buffer.lock();
+ if buffer.is_empty() {
+ return;
+ }
+ // We clone here rather than take so the buffer can retain its capacity.
+ let unbuffered = buffer.clone();
+ buffer.clear();
+ unbuffered
+ };
+ if let Ok(file) = fs::OpenOptions::new().append(true).open(path) {
+ write!(&file, "{}", unbuffered).ok();
+ }
+ }
+}
+
+pub fn init_log_file(enabled: bool) {
+ let prepare_path = || {
+ if !enabled {
+ return None;
+ }
+ let cwd = std::env::current_dir().ok()?;
+ let now = SystemTime::now();
+ let now: DateTime<Utc> = now.into();
+ let now = now.to_rfc3339().replace(':', "_");
+ let path = cwd.join(format!(".deno_lsp/log_{}.txt", now));
+ fs::create_dir_all(path.parent()?).ok()?;
+ fs::write(&path, "").ok()?;
+ Some(path)
+ };
+ let Some(path) = prepare_path() else {
+ LOG_FILE.enabled.store(false, Ordering::Relaxed);
+ LOG_FILE.buffer.lock().clear();
+ return;
+ };
+ thread::spawn(move || loop {
+ LOG_FILE.commit(&path);
+ thread::sleep(std::time::Duration::from_secs(1));
+ });
+}
+
+pub fn write_line_to_log_file(s: &str) {
+ LOG_FILE.write_line(s);
+}
pub fn set_lsp_debug_flag(value: bool) {
LSP_DEBUG_FLAG.store(value, Ordering::SeqCst)
@@ -53,7 +125,9 @@ macro_rules! lsp_log {
if lsp_log_level == log::Level::Debug {
$crate::lsp::logging::lsp_debug!($($arg)+)
} else {
- log::log!(lsp_log_level, $($arg)+)
+ let s = std::format!($($arg)+);
+ $crate::lsp::logging::write_line_to_log_file(&s);
+ log::log!(lsp_log_level, "{}", s)
}
)
}
@@ -67,7 +141,9 @@ macro_rules! lsp_warn {
if lsp_log_level == log::Level::Debug {
$crate::lsp::logging::lsp_debug!($($arg)+)
} else {
- log::log!(lsp_log_level, $($arg)+)
+ let s = std::format!($($arg)+);
+ $crate::lsp::logging::write_line_to_log_file(&s);
+ log::log!(lsp_log_level, "{}", s)
}
}
)
@@ -75,8 +151,12 @@ macro_rules! lsp_warn {
macro_rules! lsp_debug {
($($arg:tt)+) => (
- if crate::lsp::logging::lsp_debug_enabled() {
- log::debug!($($arg)+)
+ {
+ let s = std::format!($($arg)+);
+ $crate::lsp::logging::write_line_to_log_file(&s);
+ if $crate::lsp::logging::lsp_debug_enabled() {
+ log::debug!("{}", s)
+ }
}
)
}
diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs
index 2f023197d..04905e3bd 100644
--- a/cli/lsp/repl.rs
+++ b/cli/lsp/repl.rs
@@ -300,6 +300,7 @@ pub fn get_repl_workspace_settings() -> WorkspaceSettings {
import_map: None,
code_lens: Default::default(),
internal_debug: false,
+ log_file: false,
lint: false,
document_preload_limit: 0, // don't pre-load any modules as it's expensive and not useful for the repl
tls_certificate: None,