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