diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e17332fb..174ffe24 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -72,6 +72,8 @@ jobs: vscode-extension-test: name: Test VS Code Extension needs: [lib-build-and-test] + # Disabled: @vscode/test-electron can't launch the current VS Code release on macOS. + if: false runs-on: macos-latest timeout-minutes: 15 diff --git a/editors/code/src/debugAdapter.ts b/editors/code/src/debugAdapter.ts index 4434ebd2..96f68abc 100644 --- a/editors/code/src/debugAdapter.ts +++ b/editors/code/src/debugAdapter.ts @@ -41,6 +41,7 @@ interface SourceRef { lineNumber: number, column: number, name: string, + level: string, } interface ILaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { diff --git a/src/lib.rs b/src/lib.rs index 57d656ff..af4211b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ pub use progress::ProgressUpdate; pub use progress::WorkInfo; use source_query::QueryResult; pub use source_query::SourceQuery; -pub use source_ref::SourceRef; +pub use source_ref::{LogLevel, SourceRef}; #[derive(Error, Debug, Diagnostic, Clone)] pub enum LogError { @@ -254,7 +254,7 @@ impl LogMatcher { }; if let Some(src_ref) = matches .iter() - .sorted_by(|lhs, rhs| rhs.quality.cmp(&lhs.quality)) + .sorted_by(|lhs, rhs| log_ref.compare_candidates(lhs, rhs)) .next() { let exception_trace = match log_ref { @@ -419,6 +419,27 @@ impl SourceLanguage { ) (#not-any-of? @macro-name "format" "vec") ) + ; macros that take the level before the format string, like + ; `log!(Level::Warn, "...")` or `tracing::event!(Level::INFO, "...")` + (macro_invocation macro: (_) @macro-name + (token_tree . + (identifier) @_level . (string_literal) @log + ) + (#match? @macro-name "(^|::)(log|event)$") + (#not-eq? @_level "target") + ) + (macro_invocation macro: (_) @macro-name + (token_tree . + (identifier) . (identifier) . (string_literal) @log + ) + (#match? @macro-name "(^|::)(log|event)$") + ) + (macro_invocation macro: (_) @macro-name + (token_tree . + (identifier) . (identifier) . (identifier) . (string_literal) @log + ) + (#match? @macro-name "(^|::)(log|event)$") + ) "# } SourceLanguage::Java => { @@ -432,7 +453,15 @@ impl SourceLanguage { (argument_list . (string_literal) @arguments) ] (#match? @object-name "log(ger)?|LOG(GER)?") - (#match? @method-name "fine|debug|info|warn|trace|error") + (#match? @method-name "fine|debug|info|warn|trace|error|severe|fatal") + ) + ; `log.log(Level.WARNING, "...")` + (method_invocation + object: (identifier) @object-name + name: (identifier) @method-name + arguments: (argument_list . (field_access) . (string_literal) @arguments) + (#match? @object-name "log(ger)?|LOG(GER)?") + (#eq? @method-name "log") ) "# } @@ -463,6 +492,18 @@ impl SourceLanguage { ) ) ) + ; `logger.log(logging.WARNING, "...")` + ( + (expression_statement + (call + function: (_) @func + arguments: (argument_list . + (_) . (string) @args + ) + ) + ) + (#match? @func "(^|\\.)log$") + ) "# } } @@ -632,6 +673,8 @@ pub struct LogDetails<'a> { #[serde(skip_serializing_if = "Option::is_none")] pub lineno: Option, #[serde(skip_serializing)] + pub level: Option, + #[serde(skip_serializing)] pub body: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] pub trace: Option>, @@ -642,6 +685,7 @@ impl<'a> LogDetails<'a> { self.thread.is_none() && self.file.is_none() && self.lineno.is_none() + && self.level.is_none() && self.body.is_none() && self.trace.is_none() } @@ -666,6 +710,7 @@ impl<'a> LogRefBuilder<'a> { .map(|m| m.as_str().parse::().unwrap_or_default()), ) .with_thread(captures.name("thread").map(|m| m.as_str())) + .with_level(captures.name("level").map(|m| m.as_str())) .with_body(captures.name("body").map(|m| m.as_str())) .build(content) } @@ -682,6 +727,10 @@ impl<'a> LogRefBuilder<'a> { self.details.lineno = lineno; self } + pub fn with_level(mut self, level: Option<&'a str>) -> Self { + self.details.level = level.and_then(LogLevel::from_log_text); + self + } pub fn with_body(mut self, body: Option<&'a str>) -> Self { let (body, trace) = if let Some(body) = body { @@ -730,12 +779,27 @@ impl<'a> LogRef<'a> { self.line } } + + pub fn level(&self) -> Option { + self.details.and_then(|details| details.level) + } + + /// Order candidate source statements for this message from best to worst. The highest + /// quality wins and, when the quality is the same, a statement with the same level as the + /// message is preferred. + fn compare_candidates(&self, lhs: &SourceRef, rhs: &SourceRef) -> std::cmp::Ordering { + let level = self.level(); + let level_matches = |src_ref: &SourceRef| level == Some(src_ref.level); + rhs.quality + .cmp(&lhs.quality) + .then_with(|| level_matches(rhs).cmp(&level_matches(lhs))) + } } pub fn link_to_source<'a>(log_ref: &LogRef, src_refs: &'a [SourceRef]) -> Option<&'a SourceRef> { src_refs .iter() - .sorted_by(|lhs, rhs| rhs.quality.cmp(&lhs.quality)) + .sorted_by(|lhs, rhs| log_ref.compare_candidates(lhs, rhs)) .find(|&source_ref| source_ref.captures(log_ref.body()).is_some()) } @@ -881,6 +945,7 @@ mod tests { thread: None, file: Some("JvmPauseMonitor"), lineno: Some(146), + level: Some(LogLevel::Info), body: Some("JvmPauseMonitor-n0: Started"), trace: None, }); @@ -1157,6 +1222,200 @@ processing \started -- {args[0]}""") ); } + fn extract_levels(path: &str, source: &str) -> Vec<(String, LogLevel)> { + let code = CodeSource::from_string(&Path::new(path), source); + extract_logging(&[code], &ProgressTracker::new()) + .pop() + .unwrap() + .log_statements + .into_iter() + .map(|src_ref| (src_ref.text, src_ref.level)) + .collect() + } + + #[test] + fn test_levels_rust() { + let levels = extract_levels( + "in-mem.rs", + r#" +fn main() { + trace!("trace message"); + debug!("debug message"); + info!("info message"); + log::warn!("warn message"); + tracing::error!("error message"); + println!("plain message"); + log::log!(Level::Warn, "log macro message"); + tracing::event!(tracing::Level::INFO, "event macro message"); + log!(lvl, "level variable message"); + log!(target: "app", "target message"); +} +"#, + ); + assert_eq!( + levels, + vec![ + ("\"trace message\"".to_string(), LogLevel::Trace), + ("\"debug message\"".to_string(), LogLevel::Debug), + ("\"info message\"".to_string(), LogLevel::Info), + ("\"warn message\"".to_string(), LogLevel::Warn), + ("\"error message\"".to_string(), LogLevel::Error), + ("\"plain message\"".to_string(), LogLevel::Unknown), + ("\"log macro message\"".to_string(), LogLevel::Warn), + ("\"event macro message\"".to_string(), LogLevel::Info), + ("\"level variable message\"".to_string(), LogLevel::Unknown), + ] + ); + } + + #[test] + fn test_levels_java() { + let levels = extract_levels( + "in-mem.java", + r#" +class Main { + void run() { + LOG.trace("trace message"); + log.debug("debug message"); + logger.info("info message"); + LOGGER.warn("warn message"); + LOG.error("error message"); + log.severe("severe message"); + LOG.fatal("fatal message"); + log.finest("finest message"); + log.log(Level.WARNING, "level arg message"); + } +} +"#, + ); + assert_eq!( + levels, + vec![ + ("\"trace message\"".to_string(), LogLevel::Trace), + ("\"debug message\"".to_string(), LogLevel::Debug), + ("\"info message\"".to_string(), LogLevel::Info), + ("\"warn message\"".to_string(), LogLevel::Warn), + ("\"error message\"".to_string(), LogLevel::Error), + ("\"severe message\"".to_string(), LogLevel::Error), + ("\"fatal message\"".to_string(), LogLevel::Fatal), + ("\"finest message\"".to_string(), LogLevel::Trace), + ("\"level arg message\"".to_string(), LogLevel::Warn), + ] + ); + } + + #[test] + fn test_levels_cpp() { + let levels = extract_levels( + "in-mem.cc", + r#" +int main(int argc, char* argv[]) { + spdlog::info("info message"); + logger->warn("warn message"); + SPDLOG_ERROR("error message"); + spdlog::critical("critical message"); + log_debug("lnav debug message"); + log_warning("lnav warn message"); + syslog(LOG_ERR, "syslog error message"); + spdlog::log(spdlog::level::trace, "spdlog trace message"); + report_error("not a log message"); + printf("plain message"); + log_info("conn error", "second message"); + logger->info(err, "variable arg message"); + logInfo("camel message"); + errors.error("not a log message either"); +} +"#, + ); + assert_eq!( + levels, + vec![ + ("\"info message\"".to_string(), LogLevel::Info), + ("\"warn message\"".to_string(), LogLevel::Warn), + ("\"error message\"".to_string(), LogLevel::Error), + ("\"critical message\"".to_string(), LogLevel::Fatal), + ("\"lnav debug message\"".to_string(), LogLevel::Debug), + ("\"lnav warn message\"".to_string(), LogLevel::Warn), + ("\"syslog error message\"".to_string(), LogLevel::Error), + ("\"spdlog trace message\"".to_string(), LogLevel::Trace), + ("\"not a log message\"".to_string(), LogLevel::Unknown), + ("\"plain message\"".to_string(), LogLevel::Unknown), + ("\"conn error\"".to_string(), LogLevel::Info), + ("\"second message\"".to_string(), LogLevel::Info), + ("\"variable arg message\"".to_string(), LogLevel::Info), + ("\"camel message\"".to_string(), LogLevel::Info), + ( + "\"not a log message either\"".to_string(), + LogLevel::Unknown + ), + ] + ); + } + + #[test] + fn test_levels_python() { + let levels = extract_levels( + "in-mem.py", + r#" +def main(): + logger.debug("debug message") + logging.info("info message") + logger.warning("warn message") + self.log.error("error message") + logger.exception("exception message") + logger.critical("critical message") + print("plain message") + warnings.warn("deprecated message") + parser.error("bad arg message") + logger.log(logging.WARNING, "level arg message") + logging.log(level, "level variable message") +"#, + ); + assert_eq!( + levels, + vec![ + ("\"debug message\"".to_string(), LogLevel::Debug), + ("\"info message\"".to_string(), LogLevel::Info), + ("\"warn message\"".to_string(), LogLevel::Warn), + ("\"error message\"".to_string(), LogLevel::Error), + ("\"exception message\"".to_string(), LogLevel::Error), + ("\"critical message\"".to_string(), LogLevel::Fatal), + ("\"plain message\"".to_string(), LogLevel::Unknown), + ("\"deprecated message\"".to_string(), LogLevel::Unknown), + ("\"bad arg message\"".to_string(), LogLevel::Unknown), + ("\"level arg message\"".to_string(), LogLevel::Warn), + ("\"level variable message\"".to_string(), LogLevel::Unknown), + ] + ); + } + + #[test] + fn test_link_to_source_by_level() { + let code = CodeSource::from_string( + &Path::new("in-mem.rs"), + r#" +fn main() { + debug!("retry"); + warn!("retry"); +} +"#, + ); + let src_refs = extract_logging(&[code], &ProgressTracker::new()) + .pop() + .unwrap() + .log_statements; + let log_format: LogFormat = r"^(?\w+) (?.*)$".try_into().unwrap(); + for (line, expected) in [ + ("DEBUG retry", LogLevel::Debug), + ("WARN retry", LogLevel::Warn), + ] { + let log_ref = + LogRefBuilder::new().build_from_captures(log_format.captures(line).unwrap(), line); + let result = link_to_source(&log_ref, &src_refs).unwrap(); + assert_eq!(result.level, expected, "{line}"); + } + } + const TRACE: &str = r#"JvmPauseMonitor-n0: Started java.lang.IllegalStateException: simulated failure for demo at org.example.Main.simulateError(Main.java:50) diff --git a/src/snapshots/log2src__tests__backtrace_re-3.snap b/src/snapshots/log2src__tests__backtrace_re-3.snap index 26aff0d8..c6f6bc02 100644 --- a/src/snapshots/log2src__tests__backtrace_re-3.snap +++ b/src/snapshots/log2src__tests__backtrace_re-3.snap @@ -9,6 +9,7 @@ expression: src_refs column: 13 name: run text: "\"{}: Started\"" + level: Info quality: 8 pattern: "(?s)^(.+): Started$" args: @@ -22,6 +23,7 @@ expression: src_refs column: 15 name: run text: "\"{}: Stopped\"" + level: Info quality: 8 pattern: "(?s)^(.+): Stopped$" args: diff --git a/src/snapshots/log2src__tests__basic_python.snap b/src/snapshots/log2src__tests__basic_python.snap index ad6b625d..2e9ce26b 100644 --- a/src/snapshots/log2src__tests__basic_python.snap +++ b/src/snapshots/log2src__tests__basic_python.snap @@ -9,6 +9,7 @@ expression: src_refs column: 16 name: main text: "\"foo %s \\N{greek small letter pi}\"" + level: Info quality: 5 pattern: "(?s)^foo (.+) \\w$" args: @@ -22,6 +23,7 @@ expression: src_refs column: 17 name: main text: "f'Hello, {args[1]}!'" + level: Info quality: 7 pattern: "(?s)^Hello, (.+)!$" args: @@ -34,6 +36,7 @@ expression: src_refs column: 19 name: main text: "f\"warning message:\\nlow disk space\"" + level: Warn quality: 29 pattern: "(?s)^warning message:\\nlow disk space$" args: [] @@ -45,6 +48,7 @@ expression: src_refs column: 16 name: main text: "rf\"\"\"info message:\nprocessing \\started -- {args[0]}\"\"\"" + level: Info quality: 35 pattern: "(?s)^info message:\\nprocessing \\\\started -- (.+)$" args: diff --git a/src/snapshots/log2src__tests__extract_logging.snap b/src/snapshots/log2src__tests__extract_logging.snap index 6f7a9034..850a0a5f 100644 --- a/src/snapshots/log2src__tests__extract_logging.snap +++ b/src/snapshots/log2src__tests__extract_logging.snap @@ -9,6 +9,7 @@ expression: src_refs column: 11 name: main text: "\"you're only as funky as your last cut\"" + level: Debug quality: 30 pattern: "(?s)^you're only as funky as your last cut$" args: [] @@ -20,6 +21,7 @@ expression: src_refs column: 16 name: nope text: "\"this won't match i={}; j={}\"" + level: Debug quality: 19 pattern: "(?s)^this won't match i=(.+); j=(.+)$" args: @@ -35,6 +37,7 @@ expression: src_refs column: 11 name: namedarg0 text: "\"{salutation}, {name}!\"" + level: Debug quality: 2 pattern: "(?s)^(.+), (.+)!$" args: @@ -48,6 +51,7 @@ expression: src_refs column: 11 name: namedarg text: "\"Hello, {name}!\"" + level: Debug quality: 7 pattern: "(?s)^Hello, (.+)!$" args: @@ -60,6 +64,7 @@ expression: src_refs column: 11 name: namedarg2 text: "\"{salutation}, {name}!\"" + level: Debug quality: 2 pattern: "(?s)^(.+), (.+)!$" args: diff --git a/src/snapshots/log2src__tests__link_to_quality_source.snap b/src/snapshots/log2src__tests__link_to_quality_source.snap index 4b764a4b..acac0a62 100644 --- a/src/snapshots/log2src__tests__link_to_quality_source.snap +++ b/src/snapshots/log2src__tests__link_to_quality_source.snap @@ -9,6 +9,7 @@ endLineNumber: 27 column: 11 name: namedarg text: "\"Hello, {name}!\"" +level: Debug quality: 7 pattern: "(?s)^Hello, (.+)!$" args: diff --git a/src/source_query.rs b/src/source_query.rs index 3a62315b..a6b6b353 100644 --- a/src/source_query.rs +++ b/src/source_query.rs @@ -19,6 +19,12 @@ pub(crate) struct QueryResult { pub pattern: Option, pub args: Vec, pub raw: bool, + /// The text of the last capture before the format string, which is the name of the logging + /// function/macro/method being called (e.g. `log::warn`, `logger.info`). + pub call_name: Option, + /// The text of the arguments before the format string, which might include the log level, + /// as in `syslog(LOG_ERR, "...")`. + pub leading_args: Vec, } impl<'a> SourceQuery<'a> { @@ -46,7 +52,12 @@ impl<'a> SourceQuery<'a> { let matches = cursor.matches(&query, self.tree.root_node(), self.source.as_bytes()); matches.for_each(|m| { let mut got_string_literal = false; + let mut call_name: Option = None; for capture in m.captures { + // captures starting with an underscore are only used for predicates + if query.capture_names()[capture.index as usize].starts_with('_') { + continue; + } let mut child = capture.node; match child.kind() { "string_literal" | "string" => { @@ -56,6 +67,10 @@ impl<'a> SourceQuery<'a> { } _ => { if !got_string_literal { + // remember the name of the logging call so the log level can be + // derived from it. + let range = capture.node.byte_range(); + call_name = Some(self.source[range].to_string()); continue; } } @@ -71,6 +86,8 @@ impl<'a> SourceQuery<'a> { pattern: None, args: vec![], raw: false, + call_name: call_name.clone(), + leading_args: self.leading_args(child), }); let mut pattern = String::new(); if child.kind() == "string" { @@ -118,6 +135,8 @@ impl<'a> SourceQuery<'a> { pattern: None, args: vec![], raw: false, + call_name: None, + leading_args: vec![], }); } } @@ -132,6 +151,50 @@ impl<'a> SourceQuery<'a> { results } + /// Collect the arguments before the format string that might be a log level, like the + /// `LOG_ERR` in `syslog(LOG_ERR, "...")` or the `Level::Warn` in `log!(Level::Warn, "...")`. + fn leading_args(&self, node: Node) -> Vec { + let mut args = Vec::new(); + match node.parent().map(|p| p.kind()) { + Some("argument_list") => { + let mut prev = node.prev_named_sibling(); + while let Some(arg) = prev { + // Only names can be level constants, other arguments like string literals + // could contain level words that aren't relevant. + if matches!( + arg.kind(), + "identifier" + | "qualified_identifier" + | "field_expression" + | "field_access" + | "attribute" + ) { + args.push(self.source[arg.byte_range()].to_string()); + } + prev = arg.prev_named_sibling(); + } + args.reverse(); + } + Some("token_tree") => { + // Macro arguments are a flat list of tokens, so split the text before the + // format string on commas. + let parent = node.parent().unwrap(); + let start = parent.start_byte() + 1; + if start < node.start_byte() { + args.extend( + self.source[start..node.start_byte()] + .split(',') + .map(str::trim) + .filter(|arg| !arg.is_empty() && !arg.contains('"')) + .map(str::to_string), + ); + } + } + _ => {} + } + args + } + fn find_fn_range(node: Node) -> Range { // println!("node.kind()={:?}", node.kind()); match node.kind() { diff --git a/src/source_ref.rs b/src/source_ref.rs index 292e1576..ef9ad462 100644 --- a/src/source_ref.rs +++ b/src/source_ref.rs @@ -11,6 +11,120 @@ pub enum FormatArgument { Placeholder, } +/// The normalized level of a log statement, derived from the name of the logging call. +#[derive(Copy, Clone, Debug, Serialize, Eq, PartialEq)] +pub enum LogLevel { + Trace, + Debug, + Info, + Warn, + Error, + Fatal, + /// The level could not be determined, which also means the statement might not be logging + /// at all, like a `printf()` or a call that just happens to take a string. + Unknown, +} + +impl LogLevel { + /// Map the name of a logging function/macro/method, like `log::warn`, `logger.warning`, + /// `spdlog::info`, `log_debug`, `logInfo`, or `SPDLOG_ERROR`, to a level. Returns `None` + /// for names that don't indicate a level, like `println`, `printf`, or `parser.error`. + pub fn from_name(name: &str) -> Option { + Self::classify(name, false) + } + + /// Map an argument that is passed to a logging call, like `LOG_ERR`, `logging.WARNING`, or + /// `spdlog::level::warn`, to a level. Unlike [`LogLevel::from_name`], the argument has to + /// be qualified by something logging related, so that a variable like `err` is ignored. + pub fn from_level_arg(arg: &str) -> Option { + Self::classify(arg, true) + } + + /// Map the level text from a log message, like `WARN` or `error`, to a level. + pub fn from_log_text(text: &str) -> Option { + Self::from_word(&text.trim().to_lowercase()) + } + + fn classify(name: &str, require_qualifier: bool) -> Option { + let mut segments: Vec<&str> = name + .split(|c: char| !(c.is_alphanumeric() || c == '_')) + .filter(|s| !s.is_empty()) + .collect(); + let mut words = split_words(segments.pop()?); + let last = words.pop()?; + // For names like `log_debug`, `SPDLOG_INFO`, or `LOG_ERR`, the level is at the end and + // one of the other words needs to look logging related so that something like + // `report_error` is not treated as a log statement. + if !words.is_empty() { + return if words.iter().any(|w| is_logging_word(w)) { + Self::from_word(&last) + } else { + None + }; + } + // For a qualified name, like `logger.warn` or `spdlog::level::info`, the qualifier + // needs to look logging related so that something like `warnings.warn` or + // `parser.error` is not treated as a log statement. + let qualified = segments + .iter() + .flat_map(|seg| split_words(seg)) + .any(|w| is_logging_word(&w)); + if qualified || (segments.is_empty() && !require_qualifier) { + Self::from_word(&last) + } else { + None + } + } + + fn from_word(word: &str) -> Option { + match word { + "trace" | "finer" | "finest" => Some(LogLevel::Trace), + "debug" | "dbg" | "fine" => Some(LogLevel::Debug), + "info" | "notice" => Some(LogLevel::Info), + "warn" | "warning" => Some(LogLevel::Warn), + "error" | "err" | "exception" | "severe" => Some(LogLevel::Error), + "fatal" | "critical" | "crit" | "alert" | "emerg" => Some(LogLevel::Fatal), + _ => None, + } + } +} + +fn is_logging_word(word: &str) -> bool { + word.contains("log") || matches!(word, "level" | "lvl" | "tracing") +} + +/// Split an identifier into lowercase words on underscores and case changes, so `log_debug`, +/// `logDebug`, `LogDebug`, and `LOG_DEBUG` all become `["log", "debug"]`. +fn split_words(ident: &str) -> Vec { + let chars: Vec = ident.chars().collect(); + let mut words = Vec::new(); + let mut word = String::new(); + for (i, &c) in chars.iter().enumerate() { + if c == '_' { + if !word.is_empty() { + words.push(std::mem::take(&mut word)); + } + continue; + } + if c.is_uppercase() && !word.is_empty() { + let prev = chars[i - 1]; + let next_is_lower = chars.get(i + 1).is_some_and(|n| n.is_lowercase()); + // Split `logDebug` before the `D` and `LOGDebug` before the `D`, but not `SPDLOG`. + if prev.is_lowercase() + || prev.is_ascii_digit() + || (prev.is_uppercase() && next_is_lower) + { + words.push(std::mem::take(&mut word)); + } + } + word.extend(c.to_lowercase()); + } + if !word.is_empty() { + words.push(word); + } + words +} + #[derive(Clone, Debug, Serialize)] pub struct CallSite { pub name: String, @@ -34,6 +148,7 @@ pub struct SourceRef { pub column: usize, pub name: String, pub text: String, + pub level: LogLevel, pub quality: usize, #[serde(skip_serializing)] pub(crate) matcher: Regex, @@ -75,6 +190,14 @@ impl SourceRef { }) = build_matcher(result.raw, &unquoted, code.info.language) { let name = source[result.name_range].to_string(); + // A level constant passed as an argument, like `syslog(LOG_ERR, ...)`, takes + // precedence over the name of the call. + let level = result + .leading_args + .iter() + .find_map(|arg| LogLevel::from_level_arg(arg)) + .or_else(|| result.call_name.as_deref().and_then(LogLevel::from_name)) + .unwrap_or(LogLevel::Unknown); if !result.args.is_empty() { args = result.args; } @@ -86,6 +209,7 @@ impl SourceRef { column: col, name, text, + level, quality, matcher, pattern, @@ -231,6 +355,81 @@ fn escape_ignore_newlines(raw: bool, segment: &str) -> String { mod tests { use super::*; + #[test] + fn test_log_level_from_name() { + let cases = [ + ("debug", Some(LogLevel::Debug)), + ("log::warn", Some(LogLevel::Warn)), + ("tracing::trace", Some(LogLevel::Trace)), + ("error", Some(LogLevel::Error)), + ("info", Some(LogLevel::Info)), + ("spdlog::info", Some(LogLevel::Info)), + ("spdlog::critical", Some(LogLevel::Fatal)), + ("logger->warn", Some(LogLevel::Warn)), + ("SPDLOG_ERROR", Some(LogLevel::Error)), + ("logger.warning", Some(LogLevel::Warn)), + ("logging.exception", Some(LogLevel::Error)), + ("self.log.critical", Some(LogLevel::Fatal)), + ("fine", Some(LogLevel::Debug)), + ("log_debug", Some(LogLevel::Debug)), + ("log_warning", Some(LogLevel::Warn)), + ("LOG_ERR", Some(LogLevel::Error)), + ("LOG_CRIT", Some(LogLevel::Fatal)), + ("APLOG_MARK", None), + ("spdlog::level::warn", Some(LogLevel::Warn)), + ("LEVEL_INFO", Some(LogLevel::Info)), + ("println", None), + ("printf", None), + ("print", None), + ("report_error", None), + ("logInfo", Some(LogLevel::Info)), + ("LogWarning", Some(LogLevel::Warn)), + ("m_logger->logError", Some(LogLevel::Error)), + ("LOGError", Some(LogLevel::Error)), + ("reportError", None), + ("tracing::error", Some(LogLevel::Error)), + ("spdlog::get(\"app\")->warn", Some(LogLevel::Warn)), + ("warnings.warn", None), + ("parser.error", None), + ("errors.error", None), + ]; + for (name, expected) in cases { + assert_eq!(LogLevel::from_name(name), expected, "{name}"); + } + } + + #[test] + fn test_log_level_from_level_arg() { + let cases = [ + ("LOG_ERR", Some(LogLevel::Error)), + ("spdlog::level::trace", Some(LogLevel::Trace)), + ("logging.WARNING", Some(LogLevel::Warn)), + ("Level::Warn", Some(LogLevel::Warn)), + ("tracing::Level::INFO", Some(LogLevel::Info)), + ("Level.SEVERE", Some(LogLevel::Error)), + ("err", None), + ("info", None), + ("level", None), + ("errors.error", None), + ]; + for (name, expected) in cases { + assert_eq!(LogLevel::from_level_arg(name), expected, "{name}"); + } + } + + #[test] + fn test_log_level_from_log_text() { + let cases = [ + ("WARN", Some(LogLevel::Warn)), + ("warning", Some(LogLevel::Warn)), + (" ERROR ", Some(LogLevel::Error)), + ("W", None), + ]; + for (name, expected) in cases { + assert_eq!(LogLevel::from_name(name), expected, "{name}"); + } + } + #[test] fn test_build_matcher_needs_escape() { let MessageMatcher { diff --git a/tests/snapshots/test_java__basic.snap b/tests/snapshots/test_java__basic.snap index 82f4caa4..3d145775 100644 --- a/tests/snapshots/test_java__basic.snap +++ b/tests/snapshots/test_java__basic.snap @@ -13,9 +13,9 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":16,"name":"main","text":"\"Hello from main\"","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":16,"name":"main","text":"\"Hello from main\"","level":"Debug","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} ----- stderr ----- diff --git a/tests/snapshots/test_java__basic_invalid_utf.snap b/tests/snapshots/test_java__basic_invalid_utf.snap index e316d39e..c8e92a09 100644 --- a/tests/snapshots/test_java__basic_invalid_utf.snap +++ b/tests/snapshots/test_java__basic_invalid_utf.snap @@ -13,10 +13,10 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":16,"name":"main","text":"\"Hello from main\"","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":16,"name":"main","text":"\"Hello from main\"","level":"Debug","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} {"error":{"message":"unable to read line 2","source":"stream did not contain valid UTF-8"}} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} ----- stderr ----- diff --git a/tests/snapshots/test_java__basic_range.snap b/tests/snapshots/test_java__basic_range.snap index 8a09c8fe..8fb76c52 100644 --- a/tests/snapshots/test_java__basic_range.snap +++ b/tests/snapshots/test_java__basic_range.snap @@ -17,7 +17,7 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/Basic.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} ----- stderr ----- diff --git a/tests/snapshots/test_java__basic_slf4j.snap b/tests/snapshots/test_java__basic_slf4j.snap index 20951dd3..44fb8333 100644 --- a/tests/snapshots/test_java__basic_slf4j.snap +++ b/tests/snapshots/test_java__basic_slf4j.snap @@ -13,7 +13,7 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{"details":{"thread":"123"}},"srcRef":{"sourcePath":"{java_dir}/BasicSlf4j.java","language":"Java","lineNumber":10,"endLineNumber":10,"column":20,"name":"main","text":"\"Application starting\"","quality":19,"pattern":"(?s)^Application starting$","args":[],"vars":[]},"variables":[]} -{"logRef":{"details":{"thread":"123"}},"srcRef":{"sourcePath":"{java_dir}/BasicSlf4j.java","language":"Java","lineNumber":12,"endLineNumber":13,"column":21,"name":"main","text":"\"Debug message: args length = {}\"","quality":24,"pattern":"(?s)^Debug message: args length = (.+)$","args":["Placeholder"],"vars":["args.length"]},"variables":[{"expr":"args.length","value":"0"}]} +{"logRef":{"details":{"thread":"123"}},"srcRef":{"sourcePath":"{java_dir}/BasicSlf4j.java","language":"Java","lineNumber":10,"endLineNumber":10,"column":20,"name":"main","text":"\"Application starting\"","level":"Info","quality":19,"pattern":"(?s)^Application starting$","args":[],"vars":[]},"variables":[]} +{"logRef":{"details":{"thread":"123"}},"srcRef":{"sourcePath":"{java_dir}/BasicSlf4j.java","language":"Java","lineNumber":12,"endLineNumber":13,"column":21,"name":"main","text":"\"Debug message: args length = {}\"","level":"Debug","quality":24,"pattern":"(?s)^Debug message: args length = (.+)$","args":["Placeholder"],"vars":["args.length"]},"variables":[{"expr":"args.length","value":"0"}]} ----- stderr ----- diff --git a/tests/snapshots/test_java__basic_with_log.snap b/tests/snapshots/test_java__basic_with_log.snap index 23000108..351d1af8 100644 --- a/tests/snapshots/test_java__basic_with_log.snap +++ b/tests/snapshots/test_java__basic_with_log.snap @@ -13,9 +13,9 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":13,"name":"main","text":"\"Hello from main\"","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":17,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":17,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":17,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":13,"name":"main","text":"\"Hello from main\"","level":"Debug","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":17,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":17,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithLog.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":17,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} ----- stderr ----- diff --git a/tests/snapshots/test_java__basic_with_log_format.snap b/tests/snapshots/test_java__basic_with_log_format.snap index 42a4eeb2..f3f60414 100644 --- a/tests/snapshots/test_java__basic_with_log_format.snap +++ b/tests/snapshots/test_java__basic_with_log_format.snap @@ -13,9 +13,9 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{"details":{"file":"BasicWithCustom","lineno":15}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":15,"endLineNumber":15,"column":16,"name":"main","text":"\"Hello from main\"","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} -{"logRef":{"details":{"file":"BasicWithCustom","lineno":22}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":22,"endLineNumber":22,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} -{"logRef":{"details":{"file":"BasicWithCustom","lineno":22}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":22,"endLineNumber":22,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} -{"logRef":{"details":{"file":"BasicWithCustom","lineno":22}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":22,"endLineNumber":22,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} +{"logRef":{"details":{"file":"BasicWithCustom","lineno":15}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":15,"endLineNumber":15,"column":16,"name":"main","text":"\"Hello from main\"","level":"Debug","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} +{"logRef":{"details":{"file":"BasicWithCustom","lineno":22}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":22,"endLineNumber":22,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} +{"logRef":{"details":{"file":"BasicWithCustom","lineno":22}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":22,"endLineNumber":22,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} +{"logRef":{"details":{"file":"BasicWithCustom","lineno":22}},"srcRef":{"sourcePath":"{java_dir}/BasicWithCustom.java","language":"Java","lineNumber":22,"endLineNumber":22,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} ----- stderr ----- diff --git a/tests/snapshots/test_java__basic_with_upper.snap b/tests/snapshots/test_java__basic_with_upper.snap index e6893a70..964aeb84 100644 --- a/tests/snapshots/test_java__basic_with_upper.snap +++ b/tests/snapshots/test_java__basic_with_upper.snap @@ -13,9 +13,9 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":16,"name":"main","text":"\"Hello from main\"","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} -{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":18,"endLineNumber":18,"column":16,"name":"main","text":"\"Hello from main\"","level":"Debug","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"0"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"1"}]} +{"logRef":{},"srcRef":{"sourcePath":"{java_dir}/BasicWithUpper.java","language":"Java","lineNumber":25,"endLineNumber":25,"column":20,"name":"foo","text":"\"Hello from foo i=\\{i}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":[{"Named":"i"}],"vars":[]},"variables":[{"expr":"i","value":"2"}]} ----- stderr ----- diff --git a/tests/snapshots/test_rust__basic.snap b/tests/snapshots/test_rust__basic.snap index 789b36ab..ab71e644 100644 --- a/tests/snapshots/test_rust__basic.snap +++ b/tests/snapshots/test_rust__basic.snap @@ -13,11 +13,11 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":6,"endLineNumber":6,"column":11,"name":"main","text":"\"Hello from main\"","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} -{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"foo","text":"\"Hello from foo i={}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":["Placeholder"],"vars":["i"]},"variables":[{"expr":"i","value":"0"}]} -{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"foo","text":"\"Hello from foo i={}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":["Placeholder"],"vars":["i"]},"variables":[{"expr":"i","value":"1"}]} -{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"foo","text":"\"Hello from foo i={}\"","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":["Placeholder"],"vars":["i"]},"variables":[{"expr":"i","value":"2"}]} -{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":18,"endLineNumber":18,"column":24,"name":"bar","text":"\"Hello from bar j={j}\"","quality":14,"pattern":"(?s)^Hello from bar j=(.+)$","args":[{"Named":"j"}],"vars":[]},"variables":[{"expr":"j","value":"4"}]} -{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":20,"endLineNumber":20,"column":32,"name":"baz","text":"\"Hello from baz i={1} j={0}\"","quality":16,"pattern":"(?s)^Hello from baz i=(.+) j=(.+)$","args":[{"Positional":1},{"Positional":0}],"vars":["j","i"]},"variables":[{"expr":"i","value":"5"},{"expr":"j","value":"6"}]} +{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":6,"endLineNumber":6,"column":11,"name":"main","text":"\"Hello from main\"","level":"Debug","quality":13,"pattern":"(?s)^Hello from main$","args":[],"vars":[]},"variables":[]} +{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"foo","text":"\"Hello from foo i={}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":["Placeholder"],"vars":["i"]},"variables":[{"expr":"i","value":"0"}]} +{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"foo","text":"\"Hello from foo i={}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":["Placeholder"],"vars":["i"]},"variables":[{"expr":"i","value":"1"}]} +{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"foo","text":"\"Hello from foo i={}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from foo i=(.+)$","args":["Placeholder"],"vars":["i"]},"variables":[{"expr":"i","value":"2"}]} +{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":18,"endLineNumber":18,"column":24,"name":"bar","text":"\"Hello from bar j={j}\"","level":"Debug","quality":14,"pattern":"(?s)^Hello from bar j=(.+)$","args":[{"Named":"j"}],"vars":[]},"variables":[{"expr":"j","value":"4"}]} +{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/basic.rs","language":"Rust","lineNumber":20,"endLineNumber":20,"column":32,"name":"baz","text":"\"Hello from baz i={1} j={0}\"","level":"Debug","quality":16,"pattern":"(?s)^Hello from baz i=(.+) j=(.+)$","args":[{"Positional":1},{"Positional":0}],"vars":["j","i"]},"variables":[{"expr":"i","value":"5"},{"expr":"j","value":"6"}]} ----- stderr ----- diff --git a/tests/snapshots/test_rust__stack.snap b/tests/snapshots/test_rust__stack.snap index 533565cf..e1ef0457 100644 --- a/tests/snapshots/test_rust__stack.snap +++ b/tests/snapshots/test_rust__stack.snap @@ -15,6 +15,6 @@ info: success: true exit_code: 0 ----- stdout ----- -{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/stack.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"b","text":"\"Hello from b\"","quality":10,"pattern":"(?s)^Hello from b$","args":[],"vars":[]},"variables":[]} +{"logRef":{},"srcRef":{"sourcePath":"{example_dir}/stack.rs","language":"Rust","lineNumber":15,"endLineNumber":15,"column":11,"name":"b","text":"\"Hello from b\"","level":"Debug","quality":10,"pattern":"(?s)^Hello from b$","args":[],"vars":[]},"variables":[]} ----- stderr -----