Skip to main content

Logging

Using logging is a simple way to assist in debugging without deploying the module(s) to a p2p node.

Using logger

The logger feature allows you to use a special logger that is based at the top of the log crate. To enable logging please specify the logger feature of the Marine SDK in Config.toml and add the log crate:

toml
[dependencies]
log = "0.4.14"
marine-rs-sdk = { version = "0.7.1", features = ["logger"] }
toml
[dependencies]
log = "0.4.14"
marine-rs-sdk = { version = "0.7.1", features = ["logger"] }

The logger should be initialized before its usage. This can be done in the main function as shown in the example below:

rust
use marine_rs_sdk::marine;
use marine_rs_sdk::WasmLoggerBuilder;
pub fn main() {
WasmLoggerBuilder::new()
// with_log_level can be skipped,
// logger will be initialized with Info level in this case.
.with_log_level(log::LevelFilter::Info)
.build()
.unwrap();
}
#[marine]
pub fn put(file_name: String, _file_content: Vec<u8>) -> String {
log::info!("put called with file name {}", file_name);
unimplemented!()
}
rust
use marine_rs_sdk::marine;
use marine_rs_sdk::WasmLoggerBuilder;
pub fn main() {
WasmLoggerBuilder::new()
// with_log_level can be skipped,
// logger will be initialized with Info level in this case.
.with_log_level(log::LevelFilter::Info)
.build()
.unwrap();
}
#[marine]
pub fn put(file_name: String, _file_content: Vec<u8>) -> String {
log::info!("put called with file name {}", file_name);
unimplemented!()
}

To play with this logging stuff in REPL you need to

  • specify the logger_enabled=true for this module in the configuration file (more details in this section)
  • use environment variables for REPL to guide it about minimal log level (details here)

Using target map

In addition to the standard log creation features, the Fluence logger allows the so-called target map to be configured during the initialization step. This allows you to filter out logs by logging_mask, which can be set for each module in the service configuration. Let's consider an example:

rust
use marine_rs_sdk::marine;
const TARGET_MAP: [(&str, i32); 4] = [
("instruction", 1 << 1),
("data_cache", 1 << 2),
("next_peer_pks", 1 << 3),
("subtree_complete", 1 << 4),
];
pub fn main() {
use std::collections::HashMap;
let target_map = HashMap::from_iter(TARGET_MAP.iter().cloned());
marine_rs_sdk::WasmLoggerBuilder::new()
.with_target_map(target_map)
.build()
.unwrap();
}
#[marine]
pub fn foo() {
log::info!(target: "instruction", "this will print if (logging_mask & 1) != 0");
log::info!(target: "data_cache", "this will print if (logging_mask & 2) != 0");
}
rust
use marine_rs_sdk::marine;
const TARGET_MAP: [(&str, i32); 4] = [
("instruction", 1 << 1),
("data_cache", 1 << 2),
("next_peer_pks", 1 << 3),
("subtree_complete", 1 << 4),
];
pub fn main() {
use std::collections::HashMap;
let target_map = HashMap::from_iter(TARGET_MAP.iter().cloned());
marine_rs_sdk::WasmLoggerBuilder::new()
.with_target_map(target_map)
.build()
.unwrap();
}
#[marine]
pub fn foo() {
log::info!(target: "instruction", "this will print if (logging_mask & 1) != 0");
log::info!(target: "data_cache", "this will print if (logging_mask & 2) != 0");
}

Here, an array called TARGET_MAP is defined and provided to a logger in the main function of a module. Each entry of this array contains a string (a target) and a number that represents the bit position in the 64-bit mask logging_mask. When you write a log message request log::info!, its target must coincide with one of the strings (the targets) defined in the TARGET_MAP array. The log will be printed if logging_mask for the module has the corresponding target bit set.

A more complex example of this feature usage can be found in the Aqua interpreter.