mirror of
https://github.com/nushell/nushell
synced 2024-12-25 12:33:17 +00: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
3 changed files with 93 additions and 29 deletions
|
@ -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)),
|
||||
#[cfg(feature = "plugin")]
|
||||
"--plugin-config" => args.next().map(|a| escape_quote_string(&a)),
|
||||
"--log-level" | "--log-target" | "--testbin" | "--threads" | "-t"
|
||||
| "--include-path" | "--lsp" | "--ide-goto-def" | "--ide-hover" | "--ide-complete"
|
||||
| "--ide-check" => args.next(),
|
||||
"--log-level" | "--log-target" | "--log-include" | "--log-exclude" | "--testbin"
|
||||
| "--threads" | "-t" | "--include-path" | "--lsp" | "--ide-goto-def"
|
||||
| "--ide-hover" | "--ide-complete" | "--ide-check" => args.next(),
|
||||
#[cfg(feature = "plugin")]
|
||||
"--plugins" => args.next(),
|
||||
_ => None,
|
||||
|
@ -97,6 +97,8 @@ pub(crate) fn parse_commandline_args(
|
|||
let env_file = call.get_flag_expr("env-config");
|
||||
let log_level = call.get_flag_expr("log-level");
|
||||
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 table_mode: Option<Value> =
|
||||
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 testbin = extract_contents(testbin)?;
|
||||
#[cfg(feature = "plugin")]
|
||||
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 env_file = extract_path(env_file)?;
|
||||
let log_level = extract_contents(log_level)?;
|
||||
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 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")?;
|
||||
|
||||
if help {
|
||||
|
@ -224,6 +234,8 @@ pub(crate) fn parse_commandline_args(
|
|||
env_file,
|
||||
log_level,
|
||||
log_target,
|
||||
log_include,
|
||||
log_exclude,
|
||||
execute,
|
||||
include_path,
|
||||
ide_goto_def,
|
||||
|
@ -262,6 +274,8 @@ pub(crate) struct NushellCliArgs {
|
|||
pub(crate) env_file: Option<Spanned<String>>,
|
||||
pub(crate) log_level: 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) table_mode: Option<Value>,
|
||||
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",
|
||||
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(
|
||||
"stdin",
|
||||
"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(
|
||||
level: &str,
|
||||
target: &str,
|
||||
filters: Filters,
|
||||
builder: &mut ConfigBuilder,
|
||||
) -> (LevelFilter, LogTarget) {
|
||||
let level = match Level::from_str(level) {
|
||||
|
@ -77,7 +83,20 @@ pub fn configure(
|
|||
};
|
||||
|
||||
// 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
|
||||
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_path::canonicalize_with;
|
||||
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_utils::utils::perf;
|
||||
|
@ -155,7 +156,10 @@ fn main() -> Result<()> {
|
|||
|
||||
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)
|
||||
.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
|
||||
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();
|
||||
|
||||
let use_color = engine_state.get_config().use_ansi_coloring;
|
||||
|
||||
// Set up logger
|
||||
if let Some(level) = parsed_nu_cli_args
|
||||
.log_level
|
||||
.as_ref()
|
||||
|
@ -187,7 +193,20 @@ fn main() -> Result<()> {
|
|||
.map(|target| target.item.clone())
|
||||
.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!());
|
||||
perf(
|
||||
"start logging",
|
||||
|
|
Loading…
Reference in a new issue