Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ license = "MIT"
[dependencies]
log = "0.4.0"
glib-sys = { version = "0.9.1", features = ["v2_44"] }

[features]
redefine_log_macros = []

[dev-dependencies]
serial_test = "0.4.0"

[[example]]
name = "override_macro_domain"
required-features = [ "redefine_log_macros" ]
142 changes: 132 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
A simple logger that integrates with [glib message
logging](https://developer.gnome.org/glib/unstable/glib-Message-Logging.html)
mechanism. The logger is useful when one wants to integrate a piece of Rust
code into a larger application which is already using glib/gio stack.
code into a larger application which is already using the glib/gio stack, or
for Rust applications and libraries using gtk-rs or similar infrastructure.

### Example
### Simplest example

```rust
use std::env;

#[macro_use]
use log;

fn main() {
Expand All @@ -16,9 +18,9 @@ fn main() {
glib_logger::init(&glib_logger::SIMPLE);
log::set_max_level(log::LevelFilter::Debug);

log::info!("info message: {}", 2);
log::warn!("warning message: {}", "foobar");
log::debug!("Hello, world!");
info!("info message: {}", 2);
warn!("warning message: {}", "foobar");
debug!("Hello, world!");
}
```

Expand All @@ -44,7 +46,61 @@ $ ./glib_logger_test
** (process:39403): DEBUG: 20:18:34.076: src/main.rs:15: Hello, world!
```

### Details
### Examples

The crate provides a series of examples in the `examples/` subdir.

- simple.rs : The simplest usage example possible, zero customization.
- simple_domain.rs : How to use a simple, custom glib log domain.
- target_domain.rs : How to map the Rust log target to the glib log domain.
- g_macro_domain.rs : How to use macros to have the glib log domain specified
in `G_LOG_DOMAIN`.
- override_macro_domain.rs : How to use the optional feature to remap the
g_trace etc. macros over the standard log macro names
- custom_log_function.rs : How to specify a custom log function using
the `glib_sys` crate.


### Logger types

The crate makes two logger types available, plus the functionality to build a
custom logger.

The two predefined logger types are:

- `glib_logger::SIMPLE`: a simple logger which prints the message, decorated
with file and line number, without a domain

- `glib_logger::STRUCTURED`: an experimental logger using glib structured
logging capabilities

Custom loggers can be defined with `glib_logger::custom`, specifying both how
the message is composed and how the logging domain is composed.

`LoggerType` can be:

- `LoggerType::Simple`: a simple logger which prints the message, decorated
with file and line number

- `LoggerType::SimplePlain`: a simple logger which prints the message, without
decorating the message with file and line number

- `LoggerType::Structured`: an experimental logger using glib structured
logging capabilities

`LoggerDomain` can be:

- `LoggerDomain::None`: a logger which uses an empty domain

- `LoggerDomain::Custom(&'static str)`: a logger using a predefined domain; note
that the domain would then be the same across all Rust crates

- `LoggerDomain::Context`: a logger using the logging context of the `log` crate
as the glib logging domain

See the domain section for further details.

### Log levels

Due to slight differences between the meaning of respective log levels, the
crate takes certain liberties. Specifically the log level mappings are:
Expand All @@ -58,9 +114,75 @@ The G_LOG_LEVEL_ERROR (as produced via `g_error()` macro in C) is not mapped to
any of `log::Level` enum values. The reason is that `g_error()` is fatal, while
`log::error!()` is not.

The formatting is done fully in Rust. However, log filtering based on level is
done in Glib. It is advisable to set `G_MESSAGES_DEBUG=all` environment variable.
The formatting is done fully in Rust. Log filtering based on level is done in
both Glib and the Rust `log` crate.

It is advisable to set `G_MESSAGES_DEBUG=all` environment variable if a custom
glib log handler is not used, to set the glib logger to debug level.

Additionally log level will be filtered also by the `log` crate in Rust itself;
so to enable lower level of logs you might need to set the log level explicitely
using calls similar to `log::set_max_level(log::LevelFilter::Debug);`.

### Domain

Using Glib a domain can be set per file by using `#define G_LOG_DOMAIN
"my-domain"` directly in C code. This functionality is not avaialble when using
`glib_logger`, all logs are emitted with a NULL domain.
"my-domain"` directly in C code.

This functionality is not available by default when using the predefined
`glib_logger` loggers, so a custom logger must be created using the `custom`
function.

The closest option to get this functionality is using a custom logger with
LoggerDomain set as `LoggerDomain::Target`.

This example shows how to use the target to set the domain using standard Rust
log functions:

```rust
// initialize a static custom logger
static CUSTOM: Logger = custom(LoggerType::Simple, LoggerDomain::Target);
// set the logger as active
glib_logger::init(&CUSTOM);
// implicit; will use the current crate/file as domain
warn!("some log message");
// explicit; will use the "my-domain" string as a domain
warn!(target: "my-domain", "some log message");
```

Alternatively, you can use macros and `G_LOG_DOMAIN` in the same vein of glib
to support the domain functionality:

```rust
// See the "g_macro_domain" example for a more extensive usage example.
#[macro_use]
extern crate glib_logger;
static G_LOG_DOMAIN: &str = "my-global-domain";
static CUSTOM: Logger = custom(LoggerType::Simple, LoggerDomain::Target);
// ...
// set the logger as active
glib_logger::init(&CUSTOM);
// implicit; will use the domain in G_LOG_DOMAIN
g_warn!("some log message");
// explicit; will use the "my-domain" string as a domain
g_warn!(target: "my-domain", "some log message");
```

Finally, you can use macros in the same vein of glib ones to support the
domain functionality:

```rust
// This requires the "redefine_log_macros" to be enabled in Cargo.toml.
// See the "override_macro_domain" example for a more extensive usage example.
#[macro_use]
extern crate glib_logger;
static G_LOG_DOMAIN: &str = "my-global-domain";
static CUSTOM: Logger = custom(LoggerType::Simple, LoggerDomain::Target);
// ...
// set the logger as active
glib_logger::init(&CUSTOM);
// implicit; will use the domain in G_LOG_DOMAIN
warn!("some log message");
// explicit; will use the "my-domain" string as a domain
warn!(target: "my-domain", "some log message");
```
64 changes: 64 additions & 0 deletions examples/custom_log_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// This example shows basic usage of a custom log function in glib for simple
// (non-structured) log formats. This is actually all done by the `glib_sys`
// crate, but example is included here because it's relevant to the logging
// use-case.
//
// To run this example, use:
//
// cargo run --example custom_log_function
//

extern crate glib_logger;
use std::env;
#[macro_use]
extern crate log;
use std::os::raw::c_char;

static DOMAIN_LOGGER: glib_logger::Logger = glib_logger::custom(
glib_logger::LoggerType::Simple,
glib_logger::LoggerDomain::Custom("mydomain")
);

fn main() {
env::set_var("G_MESSAGES_DEBUG", "all");

// setup the custom log handler
unsafe {
glib_sys::g_log_set_default_handler(
Some(log_handler), // our "extern" function to perform logging
std::ptr::null_mut() // a pointer to optional "user data"
);
}

glib_logger::init(&DOMAIN_LOGGER);
log::set_max_level(log::LevelFilter::Debug);

info!("info message: {}", 2);
warn!("warning message: {}", "foobar");
debug!("Hello, world!");
}

unsafe extern "C" fn log_handler(
domain_ptr: *const c_char,
level: glib_sys::GLogLevelFlags,
message_ptr: *const c_char,
_data_ptr: glib_sys::gpointer,
) {
use std::ffi::CStr;

let message = if !message_ptr.is_null() {
Some(CStr::from_ptr(message_ptr).to_string_lossy().into_owned())
} else {
None
};

let domain = if !domain_ptr.is_null() {
Some(CStr::from_ptr(domain_ptr).to_string_lossy().into_owned())
} else {
None
};

println!("LOG: {:?} - {:?} - {:?}", level, domain, message);
}

39 changes: 39 additions & 0 deletions examples/g_macro_domain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// This example shows basic usage of custom domains using a G_LOG_DOMAIN
// variable and `g_trace` .. `g_error` macros. The variable should be static,
// but it can be different for each module.
//
// To run this example, use:
//
// cargo run --example g_macro_domain
//

#[macro_use]
extern crate glib_logger;
use std::env;

static DOMAIN_LOGGER: glib_logger::Logger = glib_logger::custom(
glib_logger::LoggerType::Simple,
glib_logger::LoggerDomain::Target
);

static G_LOG_DOMAIN: &str = "my-global-domain";

fn main() {
env::set_var("G_MESSAGES_DEBUG", "all");

glib_logger::init(&DOMAIN_LOGGER);
log::set_max_level(log::LevelFilter::Debug);

g_info!("info message: {}", 2);
g_warn!(target: "my-custom-domain", "warning message: {}", "foobar");
different_domain::log_on_different_domain();
g_debug!("Hello, world!");
}

mod different_domain {
static G_LOG_DOMAIN: &str = "my-scoped-domain";
pub fn log_on_different_domain() {
g_error!("this will be in a scoped domain");
}
}
44 changes: 44 additions & 0 deletions examples/override_macro_domain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// This example shows basic usage of custom domains using a G_LOG_DOMAIN
// variable while keeping, at the same time, usage of standard `log`-like
// macros. The variable should be static, but it can be different for each
// module.
//
// If you use similar code in your example, you'll need to enable the
// `redefine_log_macros` feature in `Cargo.toml`.
//
// To run this example, use:
//
// cargo run --example override_macro_domain --features="redefine_log_macros"
//

#[macro_use]
extern crate glib_logger;

use std::env;

static DOMAIN_LOGGER: glib_logger::Logger = glib_logger::custom(
glib_logger::LoggerType::Simple,
glib_logger::LoggerDomain::Target
);

static G_LOG_DOMAIN: &str = "my-global-domain";

fn main() {
env::set_var("G_MESSAGES_DEBUG", "all");

glib_logger::init(&DOMAIN_LOGGER);
log::set_max_level(log::LevelFilter::Debug);

info!("info message: {}", 2);
warn!(target: "my-custom-domain", "warning message: {}", "foobar");
different_domain::log_on_different_domain();
debug!("Hello, world!");
}

mod different_domain {
static G_LOG_DOMAIN: &str = "my-scoped-domain";
pub fn log_on_different_domain() {
error!("this will be in a scoped domain");
}
}
23 changes: 23 additions & 0 deletions examples/simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// This example shows basic usage of the glib logging library
//
// To run this example, use:
//
// cargo run --example simple
//

extern crate glib_logger;
#[macro_use]
extern crate log;
use std::env;

fn main() {
env::set_var("G_MESSAGES_DEBUG", "all");

glib_logger::init(&glib_logger::SIMPLE);
log::set_max_level(log::LevelFilter::Debug);

info!("info message: {}", 2);
warn!("warning message: {}", "foobar");
debug!("Hello, world!");
}
28 changes: 28 additions & 0 deletions examples/simple_domain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// This example shows basic usage of custom, fixed domains
//
// To run this example, use:
//
// cargo run --example simple_domain
//

extern crate glib_logger;
use std::env;
#[macro_use]
extern crate log;

static DOMAIN_LOGGER: glib_logger::Logger = glib_logger::custom(
glib_logger::LoggerType::Simple,
glib_logger::LoggerDomain::Custom("mydomain")
);

fn main() {
env::set_var("G_MESSAGES_DEBUG", "all");

glib_logger::init(&DOMAIN_LOGGER);
log::set_max_level(log::LevelFilter::Debug);

info!("info message: {}", 2);
warn!("warning message: {}", "foobar");
debug!("Hello, world!");
}
Loading