mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 08:23:24 +01:00
Add options for filtering the log output from nu
(#13044)
# Description Add `--log-include` and `--log-exclude` options to filter the log output from `nu` to specific module prefixes. For example, ```nushell nu --log-level trace --log-exclude '[nu_parser]' ``` This avoids having to scan through parser spam when trying to debug something else at `TRACE` level, and should make it feel much more reasonable to add logging, particularly at `TRACE` level, to various places in the codebase. It can also be used to debug non-Nushell crates that support the Rust logging infrastructure, as many do. You can also include a specific module instead of excluding the parser log output: ```nushell nu --log-level trace --log-include '[nu_plugin]' ``` Pinging #13041 for reference, but hesitant to outright say that this closes that. I think it address that concern though. I've also struggled with debugging plugin stuff with all of the other output, so this will really help me there. # User-Facing Changes - New `nu` option: `--log-include` - New `nu` option: `--log-exclude`
This commit is contained in:
parent
a9c2349ada
commit
a3bc85bb5f
@ -33,9 +33,9 @@ pub(crate) fn gather_commandline_args() -> (Vec<String>, String, Vec<String>) {
|
|||||||
| "--env-config" | "-I" | "ide-ast" => args.next().map(|a| escape_quote_string(&a)),
|
| "--env-config" | "-I" | "ide-ast" => args.next().map(|a| escape_quote_string(&a)),
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
"--plugin-config" => args.next().map(|a| escape_quote_string(&a)),
|
"--plugin-config" => args.next().map(|a| escape_quote_string(&a)),
|
||||||
"--log-level" | "--log-target" | "--testbin" | "--threads" | "-t"
|
"--log-level" | "--log-target" | "--log-include" | "--log-exclude" | "--testbin"
|
||||||
| "--include-path" | "--lsp" | "--ide-goto-def" | "--ide-hover" | "--ide-complete"
|
| "--threads" | "-t" | "--include-path" | "--lsp" | "--ide-goto-def"
|
||||||
| "--ide-check" => args.next(),
|
| "--ide-hover" | "--ide-complete" | "--ide-check" => args.next(),
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
"--plugins" => args.next(),
|
"--plugins" => args.next(),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -97,6 +97,8 @@ pub(crate) fn parse_commandline_args(
|
|||||||
let env_file = call.get_flag_expr("env-config");
|
let env_file = call.get_flag_expr("env-config");
|
||||||
let log_level = call.get_flag_expr("log-level");
|
let log_level = call.get_flag_expr("log-level");
|
||||||
let log_target = call.get_flag_expr("log-target");
|
let log_target = call.get_flag_expr("log-target");
|
||||||
|
let log_include = call.get_flag_expr("log-include");
|
||||||
|
let log_exclude = call.get_flag_expr("log-exclude");
|
||||||
let execute = call.get_flag_expr("execute");
|
let execute = call.get_flag_expr("execute");
|
||||||
let table_mode: Option<Value> =
|
let table_mode: Option<Value> =
|
||||||
call.get_flag(engine_state, &mut stack, "table-mode")?;
|
call.get_flag(engine_state, &mut stack, "table-mode")?;
|
||||||
@ -155,39 +157,47 @@ pub(crate) fn parse_commandline_args(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_list(
|
||||||
|
expression: Option<&Expression>,
|
||||||
|
type_name: &str,
|
||||||
|
mut extract_item: impl FnMut(&Expression) -> Option<String>,
|
||||||
|
) -> Result<Option<Vec<Spanned<String>>>, ShellError> {
|
||||||
|
expression
|
||||||
|
.map(|expr| match &expr.expr {
|
||||||
|
Expr::List(list) => list
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
extract_item(item.expr())
|
||||||
|
.map(|s| s.into_spanned(item.expr().span))
|
||||||
|
.ok_or_else(|| ShellError::TypeMismatch {
|
||||||
|
err_message: type_name.into(),
|
||||||
|
span: item.expr().span,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<Spanned<String>>, _>>(),
|
||||||
|
_ => Err(ShellError::TypeMismatch {
|
||||||
|
err_message: format!("list<{type_name}>"),
|
||||||
|
span: expr.span,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
let commands = extract_contents(commands)?;
|
let commands = extract_contents(commands)?;
|
||||||
let testbin = extract_contents(testbin)?;
|
let testbin = extract_contents(testbin)?;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
let plugin_file = extract_path(plugin_file)?;
|
let plugin_file = extract_path(plugin_file)?;
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
let plugins = extract_list(plugins, "path", |expr| expr.as_filepath().map(|t| t.0))?;
|
||||||
let config_file = extract_path(config_file)?;
|
let config_file = extract_path(config_file)?;
|
||||||
let env_file = extract_path(env_file)?;
|
let env_file = extract_path(env_file)?;
|
||||||
let log_level = extract_contents(log_level)?;
|
let log_level = extract_contents(log_level)?;
|
||||||
let log_target = extract_contents(log_target)?;
|
let log_target = extract_contents(log_target)?;
|
||||||
|
let log_include = extract_list(log_include, "string", |expr| expr.as_string())?;
|
||||||
|
let log_exclude = extract_list(log_exclude, "string", |expr| expr.as_string())?;
|
||||||
let execute = extract_contents(execute)?;
|
let execute = extract_contents(execute)?;
|
||||||
let include_path = extract_contents(include_path)?;
|
let include_path = extract_contents(include_path)?;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
let plugins = plugins
|
|
||||||
.map(|expr| match &expr.expr {
|
|
||||||
Expr::List(list) => list
|
|
||||||
.iter()
|
|
||||||
.map(|item| {
|
|
||||||
item.expr()
|
|
||||||
.as_filepath()
|
|
||||||
.map(|(s, _)| s.into_spanned(item.expr().span))
|
|
||||||
.ok_or_else(|| ShellError::TypeMismatch {
|
|
||||||
err_message: "path".into(),
|
|
||||||
span: item.expr().span,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<Spanned<String>>, _>>(),
|
|
||||||
_ => Err(ShellError::TypeMismatch {
|
|
||||||
err_message: "list<path>".into(),
|
|
||||||
span: expr.span,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let help = call.has_flag(engine_state, &mut stack, "help")?;
|
let help = call.has_flag(engine_state, &mut stack, "help")?;
|
||||||
|
|
||||||
if help {
|
if help {
|
||||||
@ -224,6 +234,8 @@ pub(crate) fn parse_commandline_args(
|
|||||||
env_file,
|
env_file,
|
||||||
log_level,
|
log_level,
|
||||||
log_target,
|
log_target,
|
||||||
|
log_include,
|
||||||
|
log_exclude,
|
||||||
execute,
|
execute,
|
||||||
include_path,
|
include_path,
|
||||||
ide_goto_def,
|
ide_goto_def,
|
||||||
@ -262,6 +274,8 @@ pub(crate) struct NushellCliArgs {
|
|||||||
pub(crate) env_file: Option<Spanned<String>>,
|
pub(crate) env_file: Option<Spanned<String>>,
|
||||||
pub(crate) log_level: Option<Spanned<String>>,
|
pub(crate) log_level: Option<Spanned<String>>,
|
||||||
pub(crate) log_target: Option<Spanned<String>>,
|
pub(crate) log_target: Option<Spanned<String>>,
|
||||||
|
pub(crate) log_include: Option<Vec<Spanned<String>>>,
|
||||||
|
pub(crate) log_exclude: Option<Vec<Spanned<String>>>,
|
||||||
pub(crate) execute: Option<Spanned<String>>,
|
pub(crate) execute: Option<Spanned<String>>,
|
||||||
pub(crate) table_mode: Option<Value>,
|
pub(crate) table_mode: Option<Value>,
|
||||||
pub(crate) no_newline: Option<Spanned<String>>,
|
pub(crate) no_newline: Option<Spanned<String>>,
|
||||||
@ -403,6 +417,18 @@ impl Command for Nu {
|
|||||||
"set the target for the log to output. stdout, stderr(default), mixed or file",
|
"set the target for the log to output. stdout, stderr(default), mixed or file",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
.named(
|
||||||
|
"log-include",
|
||||||
|
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
||||||
|
"set the Rust module prefixes to include in the log output. default: [nu]",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"log-exclude",
|
||||||
|
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
||||||
|
"set the Rust module prefixes to exclude from the log output",
|
||||||
|
None,
|
||||||
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"stdin",
|
"stdin",
|
||||||
"redirect standard input to a command (with `-c`) or a script file",
|
"redirect standard input to a command (with `-c`) or a script file",
|
||||||
|
@ -66,9 +66,15 @@ fn set_write_logger(level: LevelFilter, config: Config, path: &Path) -> Result<(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Filters {
|
||||||
|
pub include: Option<Vec<String>>,
|
||||||
|
pub exclude: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configure(
|
pub fn configure(
|
||||||
level: &str,
|
level: &str,
|
||||||
target: &str,
|
target: &str,
|
||||||
|
filters: Filters,
|
||||||
builder: &mut ConfigBuilder,
|
builder: &mut ConfigBuilder,
|
||||||
) -> (LevelFilter, LogTarget) {
|
) -> (LevelFilter, LogTarget) {
|
||||||
let level = match Level::from_str(level) {
|
let level = match Level::from_str(level) {
|
||||||
@ -77,7 +83,20 @@ pub fn configure(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add allowed module filter
|
// Add allowed module filter
|
||||||
builder.add_filter_allow_str("nu");
|
if let Some(include) = filters.include {
|
||||||
|
for filter in include {
|
||||||
|
builder.add_filter_allow(filter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.add_filter_allow_str("nu");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ignored module filter
|
||||||
|
if let Some(exclude) = filters.exclude {
|
||||||
|
for filter in exclude {
|
||||||
|
builder.add_filter_ignore(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set level padding
|
// Set level padding
|
||||||
builder.set_level_padding(LevelPadding::Right);
|
builder.set_level_padding(LevelPadding::Right);
|
||||||
|
25
src/main.rs
25
src/main.rs
@ -25,7 +25,8 @@ use nu_cmd_base::util::get_init_cwd;
|
|||||||
use nu_lsp::LanguageServer;
|
use nu_lsp::LanguageServer;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::EngineState, report_error_new, ByteStream, PipelineData, ShellError, Span, Value,
|
engine::EngineState, report_error_new, ByteStream, PipelineData, ShellError, Span, Spanned,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
use nu_std::load_standard_library;
|
use nu_std::load_standard_library;
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::utils::perf;
|
||||||
@ -155,7 +156,10 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
let (args_to_nushell, script_name, args_to_script) = gather_commandline_args();
|
let (args_to_nushell, script_name, args_to_script) = gather_commandline_args();
|
||||||
let parsed_nu_cli_args = parse_commandline_args(&args_to_nushell.join(" "), &mut engine_state)
|
let parsed_nu_cli_args = parse_commandline_args(&args_to_nushell.join(" "), &mut engine_state)
|
||||||
.unwrap_or_else(|_| std::process::exit(1));
|
.unwrap_or_else(|err| {
|
||||||
|
report_error_new(&engine_state, &err);
|
||||||
|
std::process::exit(1)
|
||||||
|
});
|
||||||
|
|
||||||
// keep this condition in sync with the branches at the end
|
// keep this condition in sync with the branches at the end
|
||||||
engine_state.is_interactive = parsed_nu_cli_args.interactive_shell.is_some()
|
engine_state.is_interactive = parsed_nu_cli_args.interactive_shell.is_some()
|
||||||
@ -168,6 +172,8 @@ fn main() -> Result<()> {
|
|||||||
engine_state.history_enabled = parsed_nu_cli_args.no_history.is_none();
|
engine_state.history_enabled = parsed_nu_cli_args.no_history.is_none();
|
||||||
|
|
||||||
let use_color = engine_state.get_config().use_ansi_coloring;
|
let use_color = engine_state.get_config().use_ansi_coloring;
|
||||||
|
|
||||||
|
// Set up logger
|
||||||
if let Some(level) = parsed_nu_cli_args
|
if let Some(level) = parsed_nu_cli_args
|
||||||
.log_level
|
.log_level
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -187,7 +193,20 @@ fn main() -> Result<()> {
|
|||||||
.map(|target| target.item.clone())
|
.map(|target| target.item.clone())
|
||||||
.unwrap_or_else(|| "stderr".to_string());
|
.unwrap_or_else(|| "stderr".to_string());
|
||||||
|
|
||||||
logger(|builder| configure(&level, &target, builder))?;
|
let make_filters = |filters: &Option<Vec<Spanned<String>>>| {
|
||||||
|
filters.as_ref().map(|filters| {
|
||||||
|
filters
|
||||||
|
.iter()
|
||||||
|
.map(|filter| filter.item.clone())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let filters = logger::Filters {
|
||||||
|
include: make_filters(&parsed_nu_cli_args.log_include),
|
||||||
|
exclude: make_filters(&parsed_nu_cli_args.log_exclude),
|
||||||
|
};
|
||||||
|
|
||||||
|
logger(|builder| configure(&level, &target, filters, builder))?;
|
||||||
// info!("start logging {}:{}:{}", file!(), line!(), column!());
|
// info!("start logging {}:{}:{}", file!(), line!(), column!());
|
||||||
perf(
|
perf(
|
||||||
"start logging",
|
"start logging",
|
||||||
|
Loading…
Reference in New Issue
Block a user