[−][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 |