Skip to content

Commit 722f437

Browse files
authored
Merge pull request #1542 from swsnr/1537-deduplicate-metadata
Deduplicate metadata for structured logging
2 parents 17f4593 + 92a318a commit 722f437

File tree

3 files changed

+192
-33
lines changed

3 files changed

+192
-33
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- { name: "cairo", features: "png,pdf,svg,ps,use_glib,v1_18,freetype,script,xcb,xlib,win32-surface", nightly: "--features 'png,pdf,svg,ps,use_glib,v1_18,freetype,script,xcb,xlib,win32-surface'", test_sys: true }
2424
- { name: "gdk-pixbuf", features: "v2_42", nightly: "--all-features", test_sys: true }
2525
- { name: "gio", features: "v2_80", nightly: "--all-features", test_sys: true }
26-
- { name: "glib", features: "v2_80", nightly: "--all-features", test_sys: true }
26+
- { name: "glib", features: "v2_80,log", nightly: "--all-features", test_sys: true }
2727
- { name: "graphene", features: "", nightly: "", test_sys: true }
2828
- { name: "pango", features: "v1_54", nightly: "--all-features", test_sys: true }
2929
- { name: "pangocairo", features: "", nightly: "--all-features", test_sys: true }

glib/src/bridged_logging.rs

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Take a look at the license at the top of the repository in the LICENSE file.
22

3-
use crate::{log as glib_log, translate::*};
3+
use crate::{gstr, log as glib_log, log_structured_array, translate::*, LogField};
44

55
// rustdoc-stripper-ignore-next
66
/// Enumeration of the possible formatting behaviours for a
@@ -141,19 +141,36 @@ impl GlibLogger {
141141
func: Option<&str>,
142142
message: &str,
143143
) {
144-
let line = line.map(|l| l.to_string());
145-
let line = line.as_deref();
146-
147-
crate::log_structured!(
148-
domain.unwrap_or("default"),
149-
GlibLogger::level_to_glib(level),
150-
{
151-
"CODE_FILE" => file.unwrap_or("<unknown file>");
152-
"CODE_LINE" => line.unwrap_or("<unknown line>");
153-
"CODE_FUNC" => func.unwrap_or("<unknown module path>");
154-
"MESSAGE" => message;
155-
}
156-
);
144+
// Write line number into a static array to avoid allocating its string
145+
// representation. 16 bytes allow 10^15 lines, which should be more than
146+
// sufficient.
147+
let mut line_buffer = [0u8; 16];
148+
let line = {
149+
use std::io::{Cursor, Write};
150+
let mut c = Cursor::new(line_buffer.as_mut_slice());
151+
match line {
152+
Some(lineno) => write!(&mut c, "{lineno}").ok(),
153+
None => write!(&mut c, "<unknown line>").ok(),
154+
};
155+
let pos = c.position() as usize;
156+
&line_buffer[..pos]
157+
};
158+
let glib_level = GlibLogger::level_to_glib(level);
159+
let fields = [
160+
LogField::new(gstr!("PRIORITY"), glib_level.priority().as_bytes()),
161+
LogField::new(
162+
gstr!("CODE_FILE"),
163+
file.unwrap_or("<unknown file>").as_bytes(),
164+
),
165+
LogField::new(gstr!("CODE_LINE"), line),
166+
LogField::new(
167+
gstr!("CODE_FUNC"),
168+
func.unwrap_or("<unknown module path>").as_bytes(),
169+
),
170+
LogField::new(gstr!("MESSAGE"), message.as_bytes()),
171+
LogField::new(gstr!("GLIB_DOMAIN"), domain.unwrap_or("default").as_bytes()),
172+
];
173+
log_structured_array(glib_level, &fields);
157174
}
158175
}
159176

@@ -200,25 +217,19 @@ impl rs_log::Log for GlibLogger {
200217
}
201218
GlibLoggerFormat::Structured => {
202219
let args = record.args();
203-
if let Some(s) = args.as_str() {
204-
GlibLogger::write_log_structured(
205-
domain,
206-
record.level(),
207-
record.file(),
208-
record.line(),
209-
record.module_path(),
210-
s,
211-
);
220+
let message = if let Some(s) = args.as_str() {
221+
s
212222
} else {
213-
GlibLogger::write_log_structured(
214-
domain,
215-
record.level(),
216-
record.file(),
217-
record.line(),
218-
record.module_path(),
219-
&args.to_string(),
220-
);
221-
}
223+
&args.to_string()
224+
};
225+
GlibLogger::write_log_structured(
226+
domain,
227+
record.level(),
228+
record.file(),
229+
record.line(),
230+
record.module_path(),
231+
message,
232+
);
222233
}
223234
};
224235
}

glib/tests/bridged_logging.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#![cfg(feature = "log")]
2+
3+
use std::sync::{Arc, Mutex};
4+
5+
use glib::LogLevel;
6+
use rs_log::Log;
7+
8+
#[derive(Debug, PartialEq, Eq)]
9+
struct LoggedEvent {
10+
level: LogLevel,
11+
fields: Vec<(String, Option<String>)>,
12+
}
13+
14+
fn setup_log_collector() -> Arc<Mutex<Vec<LoggedEvent>>> {
15+
let events = Arc::new(Mutex::new(Vec::new()));
16+
let event_writer = events.clone();
17+
glib::log_set_writer_func(move |level, fields| {
18+
let fields = fields
19+
.iter()
20+
.map(|field| {
21+
(
22+
field.key().to_string(),
23+
field.value_str().map(|s| s.to_owned()),
24+
)
25+
})
26+
.collect();
27+
event_writer
28+
.lock()
29+
.unwrap()
30+
.push(LoggedEvent { level, fields });
31+
glib::LogWriterOutput::Handled
32+
});
33+
events
34+
}
35+
36+
/// Test the glib Rust logger with different formats.
37+
///
38+
/// We put everything into one test because we can only set the log writer func once.
39+
#[test]
40+
fn glib_logger_formats() {
41+
let events = setup_log_collector();
42+
43+
let record = rs_log::RecordBuilder::new()
44+
.target("test_target")
45+
.level(rs_log::Level::Info)
46+
.args(format_args!("test message"))
47+
.file(Some("/path/to/a/test/file.rs"))
48+
.line(Some(42))
49+
.module_path(Some("foo::bar"))
50+
.build();
51+
52+
glib::GlibLogger::new(
53+
glib::GlibLoggerFormat::Plain,
54+
glib::GlibLoggerDomain::CrateTarget,
55+
)
56+
.log(&record);
57+
let event = events.lock().unwrap().pop().unwrap();
58+
assert_eq!(
59+
event,
60+
LoggedEvent {
61+
level: glib::LogLevel::Info,
62+
fields: vec![
63+
("GLIB_OLD_LOG_API".to_string(), Some("1".to_string())),
64+
("MESSAGE".to_string(), Some("test message".to_string())),
65+
("PRIORITY".to_string(), Some("6".to_string())),
66+
("GLIB_DOMAIN".to_string(), Some("test_target".to_string()))
67+
]
68+
}
69+
);
70+
events.lock().unwrap().clear();
71+
72+
glib::GlibLogger::new(
73+
glib::GlibLoggerFormat::LineAndFile,
74+
glib::GlibLoggerDomain::CrateTarget,
75+
)
76+
.log(&record);
77+
let event = events.lock().unwrap().pop().unwrap();
78+
assert_eq!(
79+
event,
80+
LoggedEvent {
81+
level: glib::LogLevel::Info,
82+
fields: vec![
83+
("GLIB_OLD_LOG_API".to_string(), Some("1".to_string())),
84+
(
85+
"MESSAGE".to_string(),
86+
Some("/path/to/a/test/file.rs:42: test message".to_string())
87+
),
88+
("PRIORITY".to_string(), Some("6".to_string())),
89+
("GLIB_DOMAIN".to_string(), Some("test_target".to_string()))
90+
]
91+
}
92+
);
93+
94+
glib::GlibLogger::new(
95+
glib::GlibLoggerFormat::Structured,
96+
glib::GlibLoggerDomain::CrateTarget,
97+
)
98+
.log(&record);
99+
let event = events.lock().unwrap().pop().unwrap();
100+
assert_eq!(
101+
event,
102+
LoggedEvent {
103+
level: glib::LogLevel::Info,
104+
fields: vec![
105+
("PRIORITY".to_string(), Some("6".to_string())),
106+
(
107+
"CODE_FILE".to_string(),
108+
Some("/path/to/a/test/file.rs".to_string())
109+
),
110+
("CODE_LINE".to_string(), Some("42".to_string())),
111+
("CODE_FUNC".to_string(), Some("foo::bar".to_string())),
112+
("MESSAGE".to_string(), Some("test message".to_string())),
113+
("GLIB_DOMAIN".to_string(), Some("test_target".to_string()))
114+
]
115+
}
116+
);
117+
118+
// Structured logging without location fields
119+
glib::GlibLogger::new(
120+
glib::GlibLoggerFormat::Structured,
121+
glib::GlibLoggerDomain::CrateTarget,
122+
)
123+
.log(
124+
&rs_log::RecordBuilder::new()
125+
.target("test_target")
126+
.level(rs_log::Level::Info)
127+
.args(format_args!("test message"))
128+
.build(),
129+
);
130+
let event = events.lock().unwrap().pop().unwrap();
131+
assert_eq!(
132+
event,
133+
LoggedEvent {
134+
level: glib::LogLevel::Info,
135+
fields: vec![
136+
("PRIORITY".to_string(), Some("6".to_string())),
137+
("CODE_FILE".to_string(), Some("<unknown file>".to_string())),
138+
("CODE_LINE".to_string(), Some("<unknown line>".to_string())),
139+
(
140+
"CODE_FUNC".to_string(),
141+
Some("<unknown module path>".to_string())
142+
),
143+
("MESSAGE".to_string(), Some("test message".to_string())),
144+
("GLIB_DOMAIN".to_string(), Some("test_target".to_string()))
145+
]
146+
}
147+
);
148+
}

0 commit comments

Comments
 (0)