1 unstable release
0.1.0 | Nov 7, 2022 |
---|
#15 in #aptos
110KB
2.5K
SLoC
This crates provides an API for logging.
Instrumenting with Logs
Basic instrumenting with Logs
A set of logging macros (info!
, error!
, warn!
, debug!
, and trace!
) is provided for
emitting logs at different levels. All of these macros support the addition of providing
structured data along with a formatted text message. For guidelines on which level to use,
see the coding guidelines.
The below examples do no type checking for structured log fields, and instead just serialize whatever is given.
use aptos_logger::info;
let world = "world!";
// Formatted message, similar to `printf!`
info!("hello {}", world);
// => '{"level":"info", "message": "hello world!"}'
// Structured data can be logged using the format 'key = value'
// where value implements Serialize. This can be used for indexing and search later.
let value1 = 5;
info!(key1 = value1);
// => '{"level":"info", "data": {"key1": 5}}'
// You can even set multiple key/value pairs and a format message together
let value2 = false;
info!(key1 = value1, key2 = value2, "hello {}", world);
// => '{"level":"info", "data": {"key1": 5, "key2": false}, "message": "hello world!"}'
// Structured data can also use `Display` or `Debug` outputs instead.
// Using the sigil `?` for debug and `%` for display.
let value1 = 5;
info!(debug_key = ?value1, display_key = %value1);
// => '{"level":"info", "data": {"display_key": 5, "debug_key": 5}}'
Note
Arguments used in a formatted message are not captured and included as structured data.
Everything after the format string literal e.g. "hello {}"
are only used in the format string.
Preferred instrumenting with Logs (Typed Schemas)
The Schema
trait can be used to implement typed logging schemas. This can either be
implemented by hand or derived using the Schema
derive proc-macro, implementing the Schema
trait for the struct as well as providing setters for all fields.
use aptos_logger::{info, Schema};
#[derive(Schema)]
struct LogSchema<'a> {
// Log using this type's Serialize impl
a: usize,
// Log using this type's Debug impl
#[schema(debug)]
b: Option<Vec<bool>>,
// Log using this type's Display impl
#[schema(display)]
c: Option<&'a str>,
}
let log = LogSchema { a: 5, b: None, c: None };
// Automatic setters are named based on the field names, and handle `Option`
// None fields will be ignored
info!(log.c("radiant"));
// => '{"level":"info", "data": { "a": 5, "c": "radiant"}}'
#[derive(Schema)]
struct OtherSchema<'a> {
val: Option<&'a str>
}
let log = LogSchema { a: 5, b: None, c: None };
let other = OtherSchema { val: None };
// Schemas can be combined
info!(
other.val("awesome"), // First schema
log // Second schema has fields added to it all
);
// => '{"level":"info", "data": { "a": 5, "val":"awesome"}}'
let log = LogSchema { a: 5, b: None, c: None };
let other = OtherSchema { val: None };
// Schemas can be combined with one off fields and messages like above
info!(
other.val("awesome"), // First schema
log, // Second schema has fields added to it all
new_field = "new", // Basic structured fields
"Message: {}", // Format messages
"Some message" // Format message fields (not added to indexed fields)
);
// => {"level":"info", "message": "Message: Some message",
// "data": { "a": 5, "val":"awesome", "new_field": "new"}}'
Sampling logs
Sometimes logging a large amount of data is expensive. In order to log information only part
of the time, we've added a sample!
macro that's configurable on how often we want to execute some code.
SampleRate
determines how often the sampled statement will occur.
use aptos_logger::{info, sample, sample::{SampleRate, Sampling}};
use std::time::Duration;
// Sampled based on frequency of events, log only every 2 logs
sample!(SampleRate::Frequency(2), info!("Long log"));
// Sampled based on time passed, log at most once a minute
sample!(SampleRate::Duration(Duration::from_secs(60)), info!("Long log"));
Configuration
In order for logs to be captured and emitted a Logger needs to be instantiated. This can be
done by using the Logger
type:
use aptos_logger::{Level, Logger};
Logger::builder().level(Level::Info).build();
Dependencies
~10–20MB
~290K SLoC