Skip to content
This repository was archived by the owner on Jun 8, 2021. It is now read-only.

Commit 922be7c

Browse files
authored
Merge pull request #694 from xanathar/master
Proposal: support for logging through the log crate
2 parents 7e9eee4 + 460632d commit 922be7c

File tree

5 files changed

+333
-7
lines changed

5 files changed

+333
-7
lines changed

.travis.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,25 @@ matrix:
77
env: GTK=3.14 FEATURES=
88
- os: linux
99
rust: nightly
10-
env: GTK=3.24 FEATURES=v2_48
10+
env: GTK=3.24 FEATURES="v2_48 log_macros"
1111
- os: linux
1212
rust: beta
1313
env: GTK=3.14 FEATURES=
1414
- os: linux
1515
rust: beta
16-
env: GTK=3.24 FEATURES=v2_48
16+
env: GTK=3.24 FEATURES="v2_48 log_macros"
1717
- os: linux
1818
rust: 1.40.0
1919
env: GTK=3.14 FEATURES=
2020
- os: linux
2121
rust: 1.40.0
22-
env: GTK=3.24 FEATURES=v2_48
22+
env: GTK=3.24 FEATURES="v2_48 log_macros"
2323
- os: linux
2424
rust: stable
2525
env: GTK=3.14 FEATURES=
2626
- os: linux
2727
rust: stable
28-
env: GTK=3.24 FEATURES=v2_48
28+
env: GTK=3.24 FEATURES="v2_48 log_macros"
2929
- os: osx
3030
rust: nightly
3131
env: GTK=3.14 FEATURES=
@@ -55,7 +55,7 @@ matrix:
5555
env: GTK=3.14 FEATURES= ARM=1 OTHER_TARGET="--target armv7-unknown-linux-gnueabihf"
5656
- os: linux
5757
rust: nightly
58-
env: GTK=3.24 FEATURES=v2_48 ARM=1 OTHER_TARGET="--target armv7-unknown-linux-gnueabihf"
58+
env: GTK=3.24 FEATURES="v2_48 log_macros" ARM=1 OTHER_TARGET="--target armv7-unknown-linux-gnueabihf"
5959
addons:
6060
apt:
6161
packages:

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ futures-channel = "0.3"
3232
glib-sys = { git = "https://github.com/gtk-rs/sys" }
3333
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
3434
glib-macros = { path = "glib-macros" }
35+
log = { version = "0.4", optional = true }
3536

3637
[dev-dependencies]
3738
tempfile = "3"
@@ -48,7 +49,8 @@ v2_58 = ["v2_56", "glib-sys/v2_58", "gobject-sys/v2_58"]
4849
v2_60 = ["v2_58", "glib-sys/v2_60"]
4950
v2_62 = ["v2_60", "glib-sys/v2_62", "gobject-sys/v2_62"]
5051
v2_64 = ["v2_62", "glib-sys/v2_64"]
51-
dox = ["glib-sys/dox", "gobject-sys/dox"]
52+
log_macros = ["log"]
53+
dox = ["glib-sys/dox", "gobject-sys/dox", "log_macros"]
5254

5355
[package.metadata.docs.rs]
5456
features = ["dox"]

src/bridged_logging.rs

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
// Copyright 2020, The Gtk-rs Project Developers.
2+
// See the COPYRIGHT file at the top-level directory of this distribution.
3+
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4+
5+
extern crate log as rs_log;
6+
7+
use glib_sys;
8+
use translate::*;
9+
10+
/// Enumeration of the possible formatting behaviours for a
11+
/// [`GlibLogger`](struct.GlibLogger.html).
12+
///
13+
/// In order to use this type, `glib` must be built with the `log` feature
14+
/// enabled.
15+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16+
pub enum GlibLoggerFormat {
17+
/// A simple format, writing only the message on output.
18+
Plain,
19+
/// A simple format, writing file, line and message on output.
20+
LineAndFile,
21+
/// A logger using glib structured logging. Structured logging is available
22+
/// only on features `v2_56` and later.
23+
#[cfg(any(feature = "v2_56", feature = "dox"))]
24+
Structured,
25+
}
26+
27+
/// Enumeration of the possible domain handling behaviours for a
28+
/// [`GlibLogger`](struct.GlibLogger.html).
29+
///
30+
/// In order to use this type, `glib` must be built with the `log` feature
31+
/// enabled.
32+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33+
pub enum GlibLoggerDomain {
34+
/// Logs will have no domain specified.
35+
None,
36+
/// Logs will use the `target` of the log crate as a domain; this allows
37+
/// Rust code like `warn!(target: "my-domain", "...");` to log to the glib
38+
/// logger using the specified domain.
39+
CrateTarget,
40+
/// Logs will use the crate path as the log domain.
41+
CratePath,
42+
}
43+
44+
/// An implementation of a [`log`](https://crates.io/crates/log) compatible
45+
/// logger which logs over glib logging facilities.
46+
///
47+
/// In order to use this type, `glib` must be built with the `log` feature
48+
/// enabled.
49+
///
50+
/// Example:
51+
///
52+
/// ```no_run
53+
/// extern crate log;
54+
///
55+
/// static glib_logger: glib::GlibLogger = glib::GlibLogger::new(
56+
/// glib::GlibLoggerFormat::Plain,
57+
/// glib::GlibLoggerDomain::CrateTarget,
58+
/// );
59+
///
60+
/// log::set_logger(&glib_logger);
61+
/// log::set_max_level(log::LevelFilter::Debug);
62+
///
63+
/// log::info!("This line will get logged by glib");
64+
/// ```
65+
#[derive(Debug)]
66+
pub struct GlibLogger {
67+
format: GlibLoggerFormat,
68+
domain: GlibLoggerDomain,
69+
}
70+
71+
impl GlibLogger {
72+
/// Creates a new instance of [`GlibLogger`](struct.GlibLogger.html).
73+
///
74+
/// Example:
75+
///
76+
/// ```no_run
77+
/// extern crate log;
78+
///
79+
/// static glib_logger: glib::GlibLogger = glib::GlibLogger::new(
80+
/// glib::GlibLoggerFormat::Plain,
81+
/// glib::GlibLoggerDomain::CrateTarget,
82+
/// );
83+
///
84+
/// log::set_logger(&glib_logger);
85+
/// log::set_max_level(log::LevelFilter::Debug);
86+
///
87+
/// log::info!("This line will get logged by glib");
88+
/// ```
89+
pub const fn new(format: GlibLoggerFormat, domain: GlibLoggerDomain) -> Self {
90+
GlibLogger { format, domain }
91+
}
92+
93+
fn level_to_glib(level: rs_log::Level) -> glib_sys::GLogLevelFlags {
94+
match level {
95+
// Errors are mapped to critical to avoid automatic termination
96+
rs_log::Level::Error => glib_sys::G_LOG_LEVEL_CRITICAL,
97+
rs_log::Level::Warn => glib_sys::G_LOG_LEVEL_WARNING,
98+
rs_log::Level::Info => glib_sys::G_LOG_LEVEL_INFO,
99+
rs_log::Level::Debug => glib_sys::G_LOG_LEVEL_DEBUG,
100+
// There is no equivalent to trace level in glib
101+
rs_log::Level::Trace => glib_sys::G_LOG_LEVEL_DEBUG,
102+
}
103+
}
104+
105+
fn write_log(domain: Option<&str>, level: rs_log::Level, message: &str) {
106+
unsafe {
107+
crate::glib_sys::g_log(
108+
domain.to_glib_none().0,
109+
GlibLogger::level_to_glib(level),
110+
message.replace("%", "%%").to_glib_none().0,
111+
);
112+
}
113+
}
114+
115+
#[cfg(any(feature = "v2_56", feature = "dox"))]
116+
fn write_log_structured(
117+
domain: Option<&str>,
118+
level: log::Level,
119+
file: Option<&str>,
120+
line: Option<u32>,
121+
func: Option<&str>,
122+
message: &str,
123+
) {
124+
let line_str = match line {
125+
None => None,
126+
Some(l) => Some(l.to_string()),
127+
};
128+
129+
unsafe {
130+
crate::glib_sys::g_log_structured_standard(
131+
domain.to_glib_none().0,
132+
GlibLogger::level_to_glib(level),
133+
file.to_glib_none().0,
134+
line_str.to_glib_none().0,
135+
func.to_glib_none().0,
136+
message.replace("%", "%%").to_glib_none().0,
137+
);
138+
}
139+
}
140+
}
141+
142+
impl rs_log::Log for GlibLogger {
143+
fn enabled(&self, _: &rs_log::Metadata) -> bool {
144+
true
145+
}
146+
147+
fn log(&self, record: &rs_log::Record) {
148+
if !self.enabled(record.metadata()) {
149+
return;
150+
}
151+
152+
let domain = match &self.domain {
153+
GlibLoggerDomain::None => None,
154+
GlibLoggerDomain::CrateTarget => Some(record.metadata().target()),
155+
GlibLoggerDomain::CratePath => record.module_path(),
156+
};
157+
158+
match self.format {
159+
GlibLoggerFormat::Plain => {
160+
let s = format!("{}", record.args());
161+
GlibLogger::write_log(domain, record.level(), &s)
162+
}
163+
GlibLoggerFormat::LineAndFile => {
164+
let s = match (record.file(), record.line()) {
165+
(Some(file), Some(line)) => format!("{}:{}: {}", file, line, record.args()),
166+
(Some(file), None) => format!("{}: {}", file, record.args()),
167+
_ => format!("{}", record.args()),
168+
};
169+
170+
GlibLogger::write_log(domain, record.level(), &s);
171+
}
172+
#[cfg(any(feature = "v2_56", feature = "dox"))]
173+
GlibLoggerFormat::Structured => {
174+
GlibLogger::write_log_structured(
175+
domain,
176+
record.level(),
177+
record.file(),
178+
record.line(),
179+
None,
180+
&format!("{}", record.args()),
181+
);
182+
}
183+
};
184+
}
185+
186+
fn flush(&self) {}
187+
}
188+
189+
/// A macro which behaves exactly as `log::error!` except that it sets the
190+
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
191+
/// to build if not defined).
192+
///
193+
/// In order to use this macro, `glib` must be built with the `log_macros`
194+
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
195+
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
196+
///
197+
/// ```no_run
198+
/// #[macro_use] extern crate glib;
199+
///
200+
/// static G_LOG_DOMAIN: &str = "my-domain";
201+
///
202+
/// error!("This will be logged under 'my-domain'");
203+
/// ```
204+
#[macro_export]
205+
#[cfg(any(feature = "dox", feature = "log_macros"))]
206+
macro_rules! error {
207+
(target: $target:expr, $($arg:tt)+) => (
208+
log::log!(target: $target, log::Level::Error, $($arg)+);
209+
);
210+
($($arg:tt)+) => (
211+
log::log!(target: G_LOG_DOMAIN, log::Level::Error, $($arg)+);
212+
)
213+
}
214+
215+
/// A macro which behaves exactly as `log::warn!` except that it sets the
216+
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
217+
/// to build if not defined).
218+
///
219+
/// In order to use this macro, `glib` must be built with the `log_macros`
220+
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
221+
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
222+
///
223+
/// ```no_run
224+
/// #[macro_use] extern crate glib;
225+
///
226+
/// static G_LOG_DOMAIN: &str = "my-domain";
227+
///
228+
/// warn!("This will be logged under 'my-domain'");
229+
/// ```
230+
#[macro_export]
231+
#[cfg(any(feature = "dox", feature = "log_macros"))]
232+
macro_rules! warn {
233+
(target: $target:expr, $($arg:tt)+) => (
234+
log::log!(target: $target, log::Level::Warn, $($arg)+);
235+
);
236+
($($arg:tt)+) => (
237+
log::log!(target: G_LOG_DOMAIN, log::Level::Warn, $($arg)+);
238+
)
239+
}
240+
241+
/// A macro which behaves exactly as `log::info!` except that it sets the
242+
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
243+
/// to build if not defined).
244+
///
245+
/// In order to use this macro, `glib` must be built with the `log_macros`
246+
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
247+
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
248+
///
249+
/// ```no_run
250+
/// #[macro_use] extern crate glib;
251+
///
252+
/// static G_LOG_DOMAIN: &str = "my-domain";
253+
///
254+
/// info!("This will be logged under 'my-domain'");
255+
/// ```
256+
#[macro_export]
257+
#[cfg(any(feature = "dox", feature = "log_macros"))]
258+
macro_rules! info {
259+
(target: $target:expr, $($arg:tt)+) => (
260+
log::log!(target: $target, log::Level::Info, $($arg)+);
261+
);
262+
($($arg:tt)+) => (
263+
log::log!(target: G_LOG_DOMAIN, log::Level::Info, $($arg)+);
264+
)
265+
}
266+
267+
/// A macro which behaves exactly as `log::debug!` except that it sets the
268+
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
269+
/// to build if not defined).
270+
///
271+
/// In order to use this macro, `glib` must be built with the `log_macros`
272+
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
273+
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
274+
///
275+
/// ```no_run
276+
/// #[macro_use] extern crate glib;
277+
///
278+
/// static G_LOG_DOMAIN: &str = "my-domain";
279+
///
280+
/// debug!("This will be logged under 'my-domain'");
281+
/// ```
282+
#[macro_export]
283+
#[cfg(any(feature = "dox", feature = "log_macros"))]
284+
macro_rules! debug {
285+
(target: $target:expr, $($arg:tt)+) => (
286+
log::log!(target: $target, log::Level::Debug, $($arg)+);
287+
);
288+
($($arg:tt)+) => (
289+
log::log!(target: G_LOG_DOMAIN, log::Level::Debug, $($arg)+);
290+
)
291+
}
292+
293+
/// A macro which behaves exactly as `log::trace!` except that it sets the
294+
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
295+
/// to build if not defined).
296+
///
297+
/// In order to use this macro, `glib` must be built with the `log_macros`
298+
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
299+
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
300+
///
301+
/// ```no_run
302+
/// #[macro_use] extern crate glib;
303+
///
304+
/// static G_LOG_DOMAIN: &str = "my-domain";
305+
///
306+
/// trace!("This will be logged under 'my-domain'");
307+
/// ```
308+
#[macro_export]
309+
#[cfg(any(feature = "dox", feature = "log_macros"))]
310+
macro_rules! trace {
311+
(target: $target:expr, $($arg:tt)+) => (
312+
log::log!(target: $target, log::Level::Trace, $($arg)+);
313+
);
314+
($($arg:tt)+) => (
315+
log::log!(target: G_LOG_DOMAIN, log::Level::Trace, $($arg)+);
316+
)
317+
}

src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ pub use quark::Quark;
190190
mod log;
191191
#[cfg(any(feature = "v2_46", feature = "dox"))]
192192
pub use log::log_set_handler;
193+
193194
// #[cfg(any(feature = "v2_50", feature = "dox"))]
194195
// pub use log::log_variant;
195196
pub use log::{
@@ -198,6 +199,12 @@ pub use log::{
198199
unset_print_handler, unset_printerr_handler, LogHandlerId, LogLevel, LogLevels,
199200
};
200201

202+
#[cfg(any(feature = "log", feature = "dox"))]
203+
#[macro_use]
204+
mod bridged_logging;
205+
#[cfg(any(feature = "log", feature = "dox"))]
206+
pub use bridged_logging::{GlibLogger, GlibLoggerDomain, GlibLoggerFormat};
207+
201208
pub mod send_unique;
202209
pub use send_unique::{SendUnique, SendUniqueCell};
203210

0 commit comments

Comments
 (0)