Skip to content

Commit cf7d60b

Browse files
committed
feat: show caller name, file and line at the same time
1 parent 08891cd commit cf7d60b

File tree

7 files changed

+129
-105
lines changed

7 files changed

+129
-105
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ members = [
1111
[workspace.package]
1212
repository = "https://github.com/pamburus/hl"
1313
authors = ["Pavel Ivanov <mr.pavel.ivanov@gmail.com>"]
14-
version = "0.31.1-alpha.2"
14+
version = "0.31.1-alpha.3"
1515
edition = "2024"
1616
license = "MIT"
1717

etc/defaults/config-ecs.yaml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,28 @@ fields:
66
# Configuration of the predefined set of fields.
77
predefined:
88
time:
9-
names: ['@timestamp']
9+
names: ["@timestamp"]
1010
logger:
11-
names: ['log.logger']
11+
names: ["log.logger"]
1212
level:
1313
variants:
14-
- names: ['log.level']
14+
- names: ["log.level"]
1515
values:
16-
error: ['error', 'err', 'fatal', 'critical', 'panic', 'e']
17-
warning: ['warning', 'warn', 'wrn', 'w']
18-
info: ['info', 'inf', 'i', 'informational']
19-
debug: ['debug', 'dbg', 'd']
20-
trace: ['trace', 'trc', 't']
16+
error: ["error", "err", "fatal", "critical", "panic", "e"]
17+
warning: ["warning", "warn", "wrn", "w"]
18+
info: ["info", "inf", "i", "informational"]
19+
debug: ["debug", "dbg", "d"]
20+
trace: ["trace", "trc", "t"]
2121
message:
22-
names: ['message']
22+
names: ["message"]
2323
caller:
24-
names: []
24+
names: ["log.origin.function"]
2525
caller-file:
26-
names: ['log.origin.file.name']
26+
names: ["log.origin.file.name"]
2727
caller-line:
28-
names: ['log.origin.file.line']
28+
names: ["log.origin.file.line"]
2929
# List of wildcard field names to ignore.
30-
ignore: ['_*']
30+
ignore: ["_*"]
3131
# List of exact field names to hide.
3232
hide:
33-
- 'log'
33+
- "log"

etc/defaults/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ formatting:
8080
string-opening-quote: "'"
8181
string-closing-quote: "'"
8282
source-location-separator: ""
83+
caller-name-file-separator: " @ "
8384
hidden-fields-indicator: " ..."
8485
level-left-separator: "["
8586
level-right-separator: "]"

src/formatting.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
datefmt::DateTimeFormatter,
1111
filtering::IncludeExcludeSetting,
1212
fmtx::{OptimizedBuf, Push, aligned_left, centered},
13-
model::{self, Caller, Level, RawValue},
13+
model::{self, Level, RawValue},
1414
settings::Formatting,
1515
theme::{Element, StylingPush, Theme},
1616
};
@@ -211,26 +211,28 @@ impl RecordFormatter {
211211
//
212212
// caller
213213
//
214-
if let Some(caller) = &rec.caller {
214+
if !rec.caller.is_empty() {
215+
let caller = rec.caller;
215216
s.element(Element::Caller, |s| {
216217
s.batch(|buf| {
217218
buf.push(b' ');
218-
buf.extend_from_slice(self.cfg.punctuation.source_location_separator.as_bytes())
219+
buf.extend(self.cfg.punctuation.source_location_separator.as_bytes())
219220
});
220221
s.element(Element::CallerInner, |s| {
221222
s.batch(|buf| {
222-
match caller {
223-
Caller::Text(text) => {
224-
buf.extend_from_slice(text.as_bytes());
223+
if !caller.name.is_empty() {
224+
buf.extend(caller.name.as_bytes());
225+
}
226+
if !caller.file.is_empty() || !caller.line.is_empty() {
227+
if !caller.name.is_empty() {
228+
buf.extend(self.cfg.punctuation.caller_name_file_separator.as_bytes());
225229
}
226-
Caller::FileLine(file, line) => {
227-
buf.extend_from_slice(file.as_bytes());
228-
if line.len() != 0 {
229-
buf.push(b':');
230-
buf.extend_from_slice(line.as_bytes());
231-
}
230+
buf.extend(caller.file.as_bytes());
231+
if caller.line.len() != 0 {
232+
buf.push(b':');
233+
buf.extend(caller.line.as_bytes());
232234
}
233-
};
235+
}
234236
});
235237
});
236238
});
@@ -920,7 +922,7 @@ mod tests {
920922
use super::*;
921923
use crate::{
922924
datefmt::LinuxDateFormat,
923-
model::{RawObject, Record, RecordFields},
925+
model::{Caller, RawObject, Record, RecordFields},
924926
settings::Punctuation,
925927
theme::Theme,
926928
themecfg::testing,
@@ -1002,7 +1004,7 @@ mod tests {
10021004
message: Some(RawValue::String(EncodedString::json(r#""tm""#))),
10031005
level: Some(Level::Debug),
10041006
logger: Some("tl"),
1005-
caller: Some(Caller::Text("tc")),
1007+
caller: Caller::with_name("tc"),
10061008
fields: RecordFields::from_slice(&[("k_a", RawValue::from(RawObject::Json(&ka)))]),
10071009
..Default::default()
10081010
};
@@ -1450,4 +1452,19 @@ mod tests {
14501452

14511453
assert_eq!(&formatter.format_to_string(&rec), "a={ b={ c={ d=1 ... } ... } }");
14521454
}
1455+
1456+
#[test]
1457+
fn test_caller() {
1458+
let rec = Record {
1459+
caller: Caller {
1460+
name: "test_function".into(),
1461+
file: "test_file.rs".into(),
1462+
line: "42".into(),
1463+
},
1464+
..Default::default()
1465+
};
1466+
1467+
let result = format_no_color(&rec);
1468+
assert_eq!(&result, " @ test_function :: test_file.rs:42", "{}", result);
1469+
}
14531470
}

src/model.rs

Lines changed: 77 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ pub struct Record<'a> {
328328
pub message: Option<RawValue<'a>>,
329329
pub level: Option<Level>,
330330
pub logger: Option<&'a str>,
331-
pub caller: Option<Caller<'a>>,
331+
pub caller: Caller<'a>,
332332
pub(crate) fields: RecordFields<'a>,
333333
pub(crate) predefined: heapless::Vec<(&'a str, RawValue<'a>), MAX_PREDEFINED_FIELDS>,
334334
}
@@ -355,7 +355,7 @@ impl<'a> Record<'a> {
355355
message: None,
356356
level: None,
357357
logger: None,
358-
caller: None,
358+
caller: Default::default(),
359359
fields: RecordFields::with_capacity(capacity),
360360
predefined: heapless::Vec::new(),
361361
}
@@ -372,10 +372,36 @@ pub trait RecordWithSourceConstructor<'r, 's> {
372372

373373
// ---
374374

375-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
376-
pub enum Caller<'a> {
377-
Text(&'a str),
378-
FileLine(&'a str, &'a str),
375+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
376+
pub struct Caller<'a> {
377+
pub name: &'a str,
378+
pub file: &'a str,
379+
pub line: &'a str,
380+
}
381+
382+
impl<'a> Caller<'a> {
383+
pub fn none() -> Self {
384+
Self::default()
385+
}
386+
387+
pub fn with_name(name: &'a str) -> Self {
388+
Self {
389+
name,
390+
..Default::default()
391+
}
392+
}
393+
394+
pub fn with_file_line(file: &'a str, line: &'a str) -> Self {
395+
Self {
396+
file,
397+
line,
398+
..Default::default()
399+
}
400+
}
401+
402+
pub fn is_empty(&self) -> bool {
403+
self.name.is_empty() && self.file.is_empty() && self.line.is_empty()
404+
}
379405
}
380406

381407
// ---
@@ -782,32 +808,13 @@ impl FieldSettings {
782808
true
783809
}
784810
Self::Caller => {
785-
to.caller = value
786-
.parse::<&str>()
787-
.ok()
788-
.filter(|x| !x.is_empty())
789-
.map(|x| Caller::Text(x));
811+
to.caller.name = value.parse::<&str>().ok().unwrap_or_default();
812+
true
813+
}
814+
Self::CallerFile => {
815+
to.caller.file = value.parse::<&str>().ok().unwrap_or_default();
790816
true
791817
}
792-
Self::CallerFile => match &mut to.caller {
793-
None => {
794-
to.caller = value
795-
.parse::<&str>()
796-
.ok()
797-
.filter(|x| !x.is_empty())
798-
.map(|x| Caller::FileLine(x, ""));
799-
to.caller.is_some()
800-
}
801-
Some(Caller::FileLine(file, _)) => {
802-
if let Some(value) = value.parse::<&str>().ok().filter(|x| !x.is_empty()) {
803-
*file = value;
804-
true
805-
} else {
806-
false
807-
}
808-
}
809-
_ => false,
810-
},
811818
Self::CallerLine => {
812819
let value = match value {
813820
RawValue::Number(value) => value,
@@ -821,11 +828,7 @@ impl FieldSettings {
821828
_ => return false,
822829
};
823830

824-
match &mut to.caller {
825-
None => to.caller = Some(Caller::FileLine("", value)),
826-
Some(Caller::FileLine(_, line)) => *line = value,
827-
Some(Caller::Text(_)) => return false,
828-
}
831+
to.caller.line = value;
829832
true
830833
}
831834
Self::Nested(_) => false,
@@ -1550,8 +1553,8 @@ impl RecordFilter for FieldFilter {
15501553
}
15511554
}
15521555
FieldKind::Caller => {
1553-
if let Some(Caller::Text(caller)) = record.caller {
1554-
self.match_value(Some(caller), false)
1556+
if !record.caller.name.is_empty() {
1557+
self.match_value(Some(record.caller.name), false)
15551558
} else {
15561559
false
15571560
}
@@ -2354,50 +2357,50 @@ mod tests {
23542357
}
23552358

23562359
#[rstest]
2357-
#[case(br#"{"caller":""}"#, None)]
2358-
#[case(br#"{"caller":"x"}"#, Some(Caller::Text("x")))]
2359-
#[case(br#"caller="""#, None)]
2360-
#[case(br#"caller="#, None)]
2361-
#[case(br#"caller=x"#, Some(Caller::Text("x")))]
2362-
#[case(br#"caller="x""#, Some(Caller::Text("x")))]
2363-
fn test_caller(#[case] input: &[u8], #[case] expected: Option<Caller>) {
2360+
#[case(br#"{"caller":""}"#, Caller::none())]
2361+
#[case(br#"{"caller":"x"}"#, Caller::with_name("x"))]
2362+
#[case(br#"caller="""#, Caller::none())]
2363+
#[case(br#"caller="#, Caller::none())]
2364+
#[case(br#"caller=x"#, Caller::with_name("x"))]
2365+
#[case(br#"caller="x""#, Caller::with_name("x"))]
2366+
fn test_caller(#[case] input: &[u8], #[case] expected: Caller) {
23642367
let parser = Parser::new(ParserSettings::default());
23652368
let record = RawRecord::parser().parse(input).next().unwrap().unwrap();
23662369
let record = parser.parse(&record.record);
23672370
assert_eq!(record.caller, expected);
23682371
}
23692372

23702373
#[rstest]
2371-
#[case(br#"{"file":""}"#, None)] // 1
2372-
#[case(br#"{"file":"x"}"#, Some(Caller::FileLine("x", "")))] // 2
2373-
#[case(br#"file="""#, None)] // 3
2374-
#[case(br#"file="#, None)] // 4
2375-
#[case(br#"file=x"#, Some(Caller::FileLine("x", "")))] // 5
2376-
#[case(br#"file="x""#, Some(Caller::FileLine("x", "")))] // 6
2377-
#[case(br#"{"line":""}"#, None)] // 7
2378-
#[case(br#"{"line":"8"}"#, Some(Caller::FileLine("", "8")))] // 8
2379-
#[case(br#"line="""#, None)] // 9
2380-
#[case(br#"line="#, None)] // 10
2381-
#[case(br#"line=11"#, Some(Caller::FileLine("", "11")))] // 11
2382-
#[case(br#"line="12""#, Some(Caller::FileLine("", "12")))] // 12
2383-
#[case(br#"{"file":"","line":""}"#, None)] // 13
2384-
#[case(br#"{"file":"x","line":"14"}"#, Some(Caller::FileLine("x", "14")))] // 14
2385-
#[case(br#"file="" line="""#, None)] // 15
2386-
#[case(br#"file= line="#, None)] // 16
2387-
#[case(br#"file=x line=17"#, Some(Caller::FileLine("x", "17")))] // 17
2388-
#[case(br#"file="x" line="18""#, Some(Caller::FileLine("x", "18")))] // 18
2389-
#[case(br#"{"file":"","line":"19"}"#, Some(Caller::FileLine("", "19")))] // 19
2390-
#[case(br#"{"file":"x","line":""}"#, Some(Caller::FileLine("x", "")))] // 20
2391-
#[case(br#"file="" line="21""#, Some(Caller::FileLine("", "21")))] // 21
2392-
#[case(br#"file= line=22"#, Some(Caller::FileLine("", "22")))] // 22
2393-
#[case(br#"file=x line="#, Some(Caller::FileLine("x", "")))] // 23
2394-
#[case(br#"file="x" line="#, Some(Caller::FileLine("x", "")))] // 24
2395-
#[case(br#"file="x" line=21 line=25"#, Some(Caller::FileLine("x", "25")))] // 25
2396-
#[case(br#"file=x line=26 file=y"#, Some(Caller::FileLine("y", "26")))] // 26
2397-
#[case(br#"{"file":123, "file": {}, "line":27}"#, Some(Caller::FileLine("123", "27")))] // 27
2398-
#[case(br#"{"caller":"a", "file": "b", "line":28}"#, Some(Caller::Text("a")))] // 28
2399-
#[case(br#"{"file": "b", "line":{}}"#, Some(Caller::FileLine("b", "")))] // 29
2400-
fn test_caller_file_line(#[case] input: &[u8], #[case] expected: Option<Caller>) {
2374+
#[case(br#"{"file":""}"#, Caller::none())] // 1
2375+
#[case(br#"{"file":"x"}"#, Caller::with_file_line("x", ""))] // 2
2376+
#[case(br#"file="""#, Caller::none())] // 3
2377+
#[case(br#"file="#, Caller::none())] // 4
2378+
#[case(br#"file=x"#, Caller::with_file_line("x", ""))] // 5
2379+
#[case(br#"file="x""#, Caller::with_file_line("x", ""))] // 6
2380+
#[case(br#"{"line":""}"#, Caller::none())] // 7
2381+
#[case(br#"{"line":"8"}"#, Caller::with_file_line("", "8"))] // 8
2382+
#[case(br#"line="""#, Caller::none())] // 9
2383+
#[case(br#"line="#, Caller::none())] // 10
2384+
#[case(br#"line=11"#, Caller::with_file_line("", "11"))] // 11
2385+
#[case(br#"line="12""#, Caller::with_file_line("", "12"))] // 12
2386+
#[case(br#"{"file":"","line":""}"#, Caller::none())] // 13
2387+
#[case(br#"{"file":"x","line":"14"}"#, Caller::with_file_line("x", "14"))] // 14
2388+
#[case(br#"file="" line="""#, Caller::none())] // 15
2389+
#[case(br#"file= line="#, Caller::none())] // 16
2390+
#[case(br#"file=x line=17"#, Caller::with_file_line("x", "17"))] // 17
2391+
#[case(br#"file="x" line="18""#, Caller::with_file_line("x", "18"))] // 18
2392+
#[case(br#"{"file":"","line":"19"}"#, Caller::with_file_line("", "19"))] // 19
2393+
#[case(br#"{"file":"x","line":""}"#, Caller::with_file_line("x", ""))] // 20
2394+
#[case(br#"file="" line="21""#, Caller::with_file_line("", "21"))] // 21
2395+
#[case(br#"file= line=22"#, Caller::with_file_line("", "22"))] // 22
2396+
#[case(br#"file=x line="#, Caller::with_file_line("x", ""))] // 23
2397+
#[case(br#"file="x" line="#, Caller::with_file_line("x", ""))] // 24
2398+
#[case(br#"file="x" line=21 line=25"#, Caller::with_file_line("x", "25"))] // 25
2399+
#[case(br#"file=x line=26 file=y"#, Caller::with_file_line("y", "26"))] // 26
2400+
#[case(br#"{"file":123, "file": {}, "line":27}"#, Caller::with_file_line("", "27"))] // 27
2401+
#[case(br#"{"caller":"a", "file": "b", "line":28}"#, Caller{name:"a", file:"b",line:"28"})] // 28
2402+
#[case(br#"{"file": "b", "line":{}}"#, Caller::with_file_line("b", ""))] // 29
2403+
fn test_caller_file_line(#[case] input: &[u8], #[case] expected: Caller) {
24012404
let mut predefined = PredefinedFields::default();
24022405
predefined.caller_file = Field {
24032406
names: vec!["file".into()],

src/settings.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ pub struct Punctuation {
403403
pub string_opening_quote: String,
404404
pub string_closing_quote: String,
405405
pub source_location_separator: String,
406+
pub caller_name_file_separator: String,
406407
pub hidden_fields_indicator: String,
407408
pub level_left_separator: String,
408409
pub level_right_separator: String,
@@ -424,6 +425,7 @@ impl Default for Punctuation {
424425
string_opening_quote: "'".into(),
425426
string_closing_quote: "'".into(),
426427
source_location_separator: "@ ".into(),
428+
caller_name_file_separator: " ".into(),
427429
hidden_fields_indicator: " ...".into(),
428430
level_left_separator: "|".into(),
429431
level_right_separator: "|".into(),
@@ -448,6 +450,7 @@ impl Punctuation {
448450
string_opening_quote: "'".into(),
449451
string_closing_quote: "'".into(),
450452
source_location_separator: "@ ".into(),
453+
caller_name_file_separator: " :: ".into(),
451454
hidden_fields_indicator: " ...".into(),
452455
level_left_separator: "|".into(),
453456
level_right_separator: "|".into(),

0 commit comments

Comments
 (0)