Expand description
A configurable logging library for Rust
The hclog
(highly configurable log) library for Rust is designed to be a highly configurable
logging solution. Its primary goal is to offer a customizable logging backend that caters to
individual user needs, providing detailed control over logging options.
Users have the flexibility to select from a variety of logging backends, specify the information
included in log messages, and choose the desired module or logging level.
§Idea of this crate
Existing log crates often provide a limited set of logging levels, such as error
, info
, and
debug
. While this may be sufficient for smaller applications, larger applications - especially
those with modularized environments - require more precise control over logging output. In a
modularized environment, different components like the GUI, network stack and storage subsystem
generate log events, making it challenging to manage and analyze logs.
A limited set of logging levels can lead to inaccurate, misleading or a massive amount of log
messages which are not helpful for debugging or monitoring. A more fine-grained control over
the desired log level can increase the quality of the log messages and reduce the complexity of
the log output.
hclog is designed to address this issue(s) by providing fine-grained control over logging output. Each module has it’s own scope with its own LogKeys, which can be configured and turned on and off individually. This allows developers to tailor logging settings to specific components, ensuring that log events are accurate, informative, and actionable. For example, a web application might define separate scopes for its web server, database, and authentication modules, each with its own logging configuration.
§Concept of this crate
This library’s core concept is a key-based logging system, where each logger or logging backend
is uniquely identified by a LogKey
. Each LogKey can be individually configured with its own
logging Level
and facade variant FacadeVariant
.
To avoid naming conflicts between different modules or crates, LogKey
s are organized within a
scope, which serves as a namespace or container. When an application integrates multiple
libraries, each defines its own set of LogKeys, which are stored within separate scopes.
This hierarchical structure allows for organized and collision-free logging management.
Each LogKey
is a unique identifier which can be configured on his own. Note that all
identifiers should be exclusive to the scope and not be used in any other scopes.
Assume a sample application with three modules A, B and C that look like this:
+---------------+
| Application |
+-------+-------+
|
| +---+---+ +---+---+ +---+---+
+--------+ Mod A +----+ Mod B +----+ Mod C +
| +---+---+ +---+---+ +---+---+
| | | |
+-------+------------+------------+------------+-----+
| App Scope | ModA Scope | ModB Scope | ModC Scope |
+-------------+------------+------------+------------+
| AA | MAA | MBA | MCA |
| AB | MAB | MBB | MCB |
| AC | MAC | MBC | MCC |
| AD | MAD | MBC | MCD |
+----------------------------------------------------+
The usage and examples below show how a sample imlementation of this structure could look like.
§Usage
The basic logging functionality is provided by the lI!
, lD1!
, lE!
and so on macros.
These macros expect a LogKey
- as defined on initialization - and a format string similar to
common print!
macros in rust. The LogKey
is used to identify the logger and the format
string is the message to be printed. Taken the App Scope from the example above the minimal
usage would look like this:
hclog::lI!(AA, "This is an info message");
hclog::lD5!(AB, "This is a debug message with level {}", Level::Debug5);
Avoid writing expressions with side-effects in log statements. They may not be evaluated.
§In Libraries
The example above just shows the basic usage of the logging macros without the actual
initialization of those keys. The following example shows how to initialize the library and
define the LogKey
s for the ModA
library:
use hclog::{Scope, LogKey, Level, FacadeVariant, options::Options, Result};
#[derive(Copy, Clone, Debug)]
enum ModAKeys {
MAA,
MAB,
MAC,
MAD,
}
use ModAKeys::*;
impl std::fmt::Display for ModAKeys {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
Self::MAA => "MAA",
Self::MAB => "MAB",
Self::MAC => "MAC",
Self::MAD => "MAD",
})
}
}
impl Scope for ModAKeys {
fn default_level() -> Level { Level::Info }
fn init<S: std::fmt::Display>(
name: S, level: Level, facade: FacadeVariant, options: Options
) -> Result<()> {
hclog::init::<Self, S>(name, level, facade, options)?;
hclog::add_submodules(&[Self::MAA, Self::MAB, Self::MAC, Self::MAD])
}
}
impl LogKey for ModAKeys {
fn log_key(&self) -> usize {
match self {
Self::MAA => 0,
Self::MAB => 1,
Self::MAC => 2,
Self::MAD => 3,
}
}
}
fn do_log() {
hclog::lI!(MAA, "this is a info message in Library Scope");
hclog::LD10!(MAA, "this is a debug message in Library Scope");
}
fn init_mod_a(level: Level, facade: FacadeVariant, options: Options) -> Result<()> {
/* main initialization part of the library */
ModAKeys::init("ModA", level, facade, options)?;
Ok(())
}
The LogKey
s are defined as an enum implementing the LogKey
and Scope
traits. Implementing
the Scope
trait is necessary to initialize the library - if not already done - and also
creates the namespace for this set of LogKey
s. The LogKey
trait is used to define the
unique identifier for the loggers in this scope.
Additionally, the Display
trait is required - as a bound by LogKey
- to display the name
in the log output.
§In Applications/executables
Taking the example above a sample application could look like this:
Note: The example below uses the same Level
and FacadeVariant
for the application
and the libraries. This is not necessary and can be configured individually.
use hclog::{Scope, LogKey, Level, FacadeVariant, options::Options, Result};
// use moda; // import the 'module' above
#[derive(Copy, Clone, Debug)]
enum AppKeys {
AA,
AB,
AC,
AD,
}
use AppKeys::*;
impl std::fmt::Display for AppKeys {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
Self::AA => "AA",
Self::AB => "AB",
Self::AC => "AC",
Self::AD => "AD",
})
}
}
impl Scope for AppKeys {
fn init<S: std::fmt::Display>(
name: S, level: Level, facade: FacadeVariant, options: Options
) -> Result<()> {
hclog::init::<Self, S>(name, level, facade, options)?;
hclog::add_submodules(&[Self::AA, Self::AB, Self::AC])?;
// call initialization routine in the library
// moda::init_mod_a(level, facade, options)?;
Ok(())
}
}
impl LogKey for AppKeys {
fn log_key(&self) -> usize {
match self {
Self::AA => 0,
Self::AB => 1,
Self::AC => 2,
Self::AD => 3,
}
}
}
fn main() {
AppKeys::init("MyApp", Level::Debug1, FacadeVariant::StdOut, Options::default()).unwrap();
// moda::do_log(); // logs in the scope of the library
hclog::lI!(AA, "this is a info message in App Scope");
}
Applications or executables also define their own set of LogKey
s which are used to log
messages within the scope of the application. The initialization of the application is done
by calling the init
function of the Scope
implementation of the LogKey
type.
The application can also initialize the loggers of the libraries it uses.
If a user of this Application wants to get just the logging output of librarys ModA
LogKey
MAA
he can either initialize the library with a desired level and disable logging in the
application or provide a commandline option to filter the output of the application which might
look like this:
[foo ~]# ./myapp -l "MAA:debug1"
This would print the lI!
message in the library but not the lD10!
message.
§Usage with crate hclog_macros
The hclog_macros
crate provides an attributable derive macro for the usage with this crate.
It allows you to derive all traits necessary to initialize the library and configure default
values at compile time.
For more informations consult the documentation of the hclog_macros
crate.
use hclog::{Level, FacadeVariant};
use hclog_macros::HCLog;
#[derive(HCLog, Copy, Clone)]
#[hclog(default_level = Level::Info, default_facade = FacadeVariant::StdOut)]
enum ModBKeys {
#[hclog(name = "A", level = Level::Debug1)]
MBA,
#[hclog(name = "B", facade = FacadeVariant::StdErr)]
MBB,
#[hclog(name = "C")]
MBC,
#[hclog(name = "D")]
MBD,
}
use ModBKeys::*;
fn main() {
ModBKeys::init_with_defaults("MyLogKeys").unwrap();
hclog::lD1!(MBB, "this won't get logged because of lvl Info for Key DB");
hclog::lD1!(MBA, "this will get logged because of lvl Debug1 for Key DA");
hclog::lE!(MBB, "this will be logged to stderr");
}
§Warning
The library internal context may be init only once.
§Crate Feature Flags
The following feature flags are available for this crate. They are configured in your Cargo.toml:
std
: Enabled by default. This flag does not enable any additional features.derive
: This flag enables the derive macro for theHCLog
trait.
[dependencies]
hclog = { version = "0.1", features = ["derive"] }
§Version compatibility
This crate is currently compatible with Rust 1.74.1 and later. We will try to keep this compatibility as long as possible. If you need a specific version of Rust please open an issue on the GitHub repository. Hence this is still a version 0.1.0 crate the API is not stable and might change in the future. We will try to keep the changes as minimal as possible and provide a migration guide or compability layer if necessary.
Modules§
- options
- Available Options to configure the log output of this crate
Macros§
- hclog
- Log a message with severity
$lvl
viaLogKey
- lA
- Log a message with severity
Alert
viaLogKey
- lC
- Log a message with severity
Crit
viaLogKey
- lD1
- Log a message with severity
Debug1
viaLogKey
- lD2
- Log a message with severity
Debug2
viaLogKey
- lD3
- Log a message with severity
Debug3
viaLogKey
- lD4
- Log a message with severity
Debug4
viaLogKey
- lD5
- Log a message with severity
Debug5
viaLogKey
- lD6
- Log a message with severity
Debug6
viaLogKey
- lD7
- Log a message with severity
Debug7
viaLogKey
- lD8
- Log a message with severity
Debug8
viaLogKey
- lD9
- Log a message with severity
Debug9
viaLogKey
- lD10
- Log a message with severity
Debug10
viaLogKey
- lE
- Log a message with severity
Error
viaLogKey
- lEM
- Log a message with severity
Emerg
viaLogKey
- lI
- Log a message with severity
Info
viaLogKey
- lN
- Log a message with severity
Notice
viaLogKey
- lW
- Log a message with severity
Warn
viaLogKey
- tA
- Test if severity
Alert
is enabled forLogKey
- tC
- Test if severity
Crit
is enabled forLogKey
- tD1
- Test if severity
Debug1
is enabled forLogKey
- tD2
- Test if severity
Debug2
is enabled forLogKey
- tD3
- Test if severity
Debug3
is enabled forLogKey
- tD4
- Test if severity
Debug4
is enabled forLogKey
- tD5
- Test if severity
Debug5
is enabled forLogKey
- tD6
- Test if severity
Debug6
is enabled forLogKey
- tD7
- Test if severity
Debug7
is enabled forLogKey
- tD8
- Test if severity
Debug8
is enabled forLogKey
- tD9
- Test if severity
Debug9
is enabled forLogKey
- tD10
- Test if severity
Debug10
is enabled forLogKey
- tE
- Test if severity
Error
is enabled forLogKey
- tEM
- Test if severity
Emerg
is enabled forLogKey
- tI
- Test if severity
Info
is enabled forLogKey
- tN
- Test if severity
Notice
is enabled forLogKey
- tW
- Test if severity
Warn
is enabled forLogKey
- tX
- Test if a given
Level
is enabled for a givenLogKey
Enums§
- Error
Kind - A list of possible errors that can occur in this library
- Facade
Variant - Declaration of the different available log facacdes (log targets).
- Internal
LogKeys - Internal keys for the hclog library.
- Level
- Declaration of available Log Levels which describe the importance of a log message.
- Scope
Key - Identifier of the
Scope
Traits§
Functions§
- add_
submodules - Add a list of
LogKey
s to the aScope
- dump
- Dump the internal state of the logging system to the supplied writer
- has_
module - Check if a module is initialized in a given
Scope
- init
- Initialize the logging system.
- init_
log_ compat - Initialize the compatibility layer to the [
crate-log
] crate - init_
modules - Initialize a
Scope
with a list of providedLogKey
s. - list_
modules - Print a list of all available modules to the supplied writer
w
- log
- reset_
module_ options - Reset the options of a given LogKey
K
- scope
- set_
level - Set a
Level
for a single LogKeyK
- set_
logdest - Set the log destination
FacadeVariant
for a given LogKeyK
- set_
mod_ level - Set the log level for a list of modules.
- set_
module_ options - Set one or multiple
Options
for a given LogKey - test_
log - unset_
module_ options - Unset one or multiple
Options
for a given LogKey
Type Aliases§
- Context
Key - Alias for the Index of the Scope in the context
- Result
- Result type for this crate