[][src]Crate fast_logger

A simple log-level based logger

General

When spawning a logger, a thread is created and a handle structure returned. This handle can be copied and shared among threads. All it does is hold some mutexes and atomic references to the logger. Actually logging pushes the data over an asynchronous channel with size limits.

The logger requires a Display type to be provided so the logger can actually print data. The reason for this is that it cuts down on serialization cost for the caller, leaving the logger to serialize numbers into strings and perform other formatting work.

Compatibility mode

There are many loggers out there in the wild, and different libraries may use different loggers. To allow program developers to log from different sources without agreeing on a logger to use, one can interface with Compatibility. Logging macros work with Compatibility.

Log Levels

Logger features two log level controls: per-context and "global" (Note: The logger has no globals, everything is local to the logger object). When logging a message, the global log level is checked, and if the current message has a lower priority than the global log level, nothing will be sent to the logger.

Once the logger has received the message, it checks its internal mapping from context to log level, if the message's log level has a lower priority than the context log level, it is dropped.

We normally use the helper functions trace, debug, info, warn, and error, which have the corresponding log levels: 255, 192, 128, 64, 0, respectively.

Trace is disabled with debug_assertions off.

Note: an error message has priority 0, and log levels are always unsigned, so an error message can never be filtered.

Example - Generic

Note that generic logging requires indirection at runtime, and may slow down your program. Still, generic logging is very desirable because it is easy to use. There are two ways to do generic logging depending on your needs:

use fast_logger::{info, Generic, Logger};

fn main() {
    let mut logger = Logger::<Generic>::spawn();
    info![logger, "context", "Message {}", "More"; "key" => "value", "three" => 3];
}

The other macros are trace!, debug!, warn!, error!, and the generic log!.

If you wish to mix this with static logging, you can do the following:

use fast_logger::{info, Generic, Logger};

enum MyMsg {
    Static(&'static str),
    Dynamic(Generic),
}

impl std::fmt::Display for MyMsg {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MyMsg::Static(string) => write![f, "{}", string],
            MyMsg::Dynamic(handle) => handle.fmt(f),
        }
    }
}

impl From<Generic> for MyMsg {
    fn from(f: Generic) -> Self {
        MyMsg::Dynamic(f)
    }
}

fn main() {
    // Setup
    let mut logger = Logger::<MyMsg>::spawn();
    info![logger, "context", "Message {}", "More"; "key" => "value", "three" => 3];
}

Example of static logging

Here is an example of static-logging only, the macros do not work for this, as these generate a Generic. Anything implementing Into for the type of the logger will be accepted into the logging functions. This is the fast way to log, as no Box is used.

use fast_logger::Logger;

// You need to define your own message type
enum MyMessageEnum {
    SimpleMessage(&'static str)
}

// It needs to implement std::fmt::Display
impl std::fmt::Display for MyMessageEnum {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MyMessageEnum::SimpleMessage(string) => write![f, "{}", string],
        }
    }
}

fn main() {
    // Setup
    let mut logger = Logger::<MyMessageEnum>::spawn();

    // Actual logging
    logger.info("ctx", MyMessageEnum::SimpleMessage("Hello world!"));

    // Various logging levels
    logger.trace("ctx", MyMessageEnum::SimpleMessage("Hello world!"));
    logger.debug("ctx", MyMessageEnum::SimpleMessage("Hello world!"));
    logger.info("ctx", MyMessageEnum::SimpleMessage("Hello world!"));
    logger.warn("ctx", MyMessageEnum::SimpleMessage("Hello world!"));
    logger.error("ctx", MyMessageEnum::SimpleMessage("Hello world!"));
}

Example with log levels

Here is an example where we set a context specific log level.

use fast_logger::Logger;

// You need to define your own message type
enum MyMessageEnum {
    SimpleMessage(&'static str)
}

// It needs to implement std::fmt::Display
impl std::fmt::Display for MyMessageEnum {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MyMessageEnum::SimpleMessage(string) => write![f, "{}", string],
        }
    }
}

fn main() {
    // Setup
    let mut logger = Logger::<MyMessageEnum>::spawn();

    // Set the log level of `ctx` to 70, this filters
    // All future log levels 71-255 out.
    logger.set_context_specific_log_level("ctx", 70);

    // This gets printed, because `warn` logs at level 64 <= 70
    logger.warn("ctx", MyMessageEnum::SimpleMessage("1"));

    // This gets printed, because 50 <= 70
    logger.log(50, "ctx", MyMessageEnum::SimpleMessage("2"));

    // This does not get printed, because !(80 <= 70)
    logger.log(80, "ctx", MyMessageEnum::SimpleMessage("3"));

    // This gets printed, because the context is different
    logger.log(128, "ctx*", MyMessageEnum::SimpleMessage("4"));
}

Example with just strings

If you really don't care about formatting overhead on the caller's side, you can just use a String as the message type.

use fast_logger::Logger;

fn main() {
    // Setup
    let mut logger = Logger::<String>::spawn();

    // Set the log level of `ctx` to 70, this filters
    // All future log levels 71-255 out.
    logger.set_context_specific_log_level("ctx", 70);

    // This gets printed, because `warn` logs at level 64 <= 70
    logger.warn("ctx", format!("1"));

    // This gets printed, because 50 <= 70
    logger.log(50, "ctx", format!("2"));

    // This does not get printed, because !(80 <= 70)
    logger.log(80, "ctx", format!("3"));

    // This gets printed, because the context is different
    logger.log(128, "ctx*", format!("4"));
}

Non-Copy Data

Data that is non-copy may be hard to pass to the logger, to alleviate this, there's a builtin clone directive in the macros:

use fast_logger::{info, Generic, InDebug, Logger};

#[derive(Clone, Debug)]
struct MyStruct();

fn main() {
    let mut logger = Logger::<Generic>::spawn();
    let my_struct = MyStruct();
    info![logger, "context", "Message {}", "More"; "key" => InDebug(&my_struct); clone
    my_struct];
    info![logger, "context", "Message {}", "More"; "key" => InDebug(&my_struct)];
}

Macros

debug

Equivalent to log! with a level of 192

error

Equivalent to log! with a level of 0

info

Equivalent to log! with a level of 128

log

Equivalent to logging to the crate::LoggerV2Async::log function with an appropriate level, context, and a Generic.

trace

Equivalent to log! with a level of 255

warn

Equivalent to log! with a level of 64

Structs

Generic

A handle for generic logging data, used by macros

InDebug

Print the value of the key-value pair as debug

InDebugPretty

Print the value of the key-value pair as debug-pretty

InHex

Print the value of the key-value pair as hexadecimal

LoggerV2Async

The fastest logger in the west

Enums

Logpass

A passthrough-logger

Traits

GenericLogger

Trait for a generic logger, allows Logpass to pass Generic to this logger

Type Definitions

Compatibility

Compatibility type when using loggers across library boundaries

Logger

The logger which dependent crates should use