18 releases
0.4.4 | Aug 6, 2021 |
---|---|
0.4.3 | Jan 31, 2021 |
0.4.1 | Oct 20, 2020 |
0.4.0 | Jul 20, 2020 |
0.1.3 | Nov 10, 2018 |
#762 in Debugging
Used in spirit
350KB
4K
SLoC
Spirit-log
Helpers and configuration fragments to integrate logging into the spirit configuration framework.
See the docs and the examples.
License
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
lib.rs
:
A spirit
fragments and helpers to configure and control logging.
The Fragment
s here allow to configure relatively complex logging (multiple loggers,
different formats, different destinations), both from command line and the configuration. It
allows runtime reloading of them.
Internally it is based on the fern
crate and just adds the configuration and runtime
reloading (through log-reroute
).
It assumes the application doesn't set the global logger itself. It also sets the panic hook
through the log_panics
crate. The with-backtrace
cargo feature is propagated through.
Features
background
: Includes the ability to log asynchronously ‒ the writing to log files happens in a background thread and allows the rest of the application to not block on IO.cfg-help
: Support for configuration options help at runtime. On by default.with-backtrace
: Thelog_panics
logs with backtraces. On by default.syslog
: Adds the support for logging into syslog.
Startup
When using the automatic management with a pipeline, this is how a startup happens:
- As soon as the pipeline is registered, a logging on the
WARN
level is sent tostderr
. - After command line arguments are parsed the
stderr
logging is updated to reflect that (or left on theWARN
level if nothing is set by the user). - After configuration is loaded from the files, full logging is configured according to that.
Integration with other loggers
If you need something specific (for example sentry
), you
can plug in additional loggers through the pipeline ‒ the Dispatch
allows adding arbitrary
loggers. The Pipeline::map
is a good place to do
it.
Performance warning
This allows the user to create arbitrary number of loggers. Furthermore, the logging is synchronous by default and not buffered. When writing a lot of logs or sending them over the network, this could become a bottleneck.
Background logging
The background
feature flag adds the ability to do the actual logging in a background thread.
This allows not blocking the actual application by IO or other expensive operations.
On the other hand, if the application crashes, some logs may be lost (or, depending on setup,
when the logging thread doesn't keep up). Also, you need to flush the logger on shutdown, by
using the FlushGuard
.
It is done through the Background
transformation.
Planned features
These pieces are planned some time in future, but haven't happened yet (pull requests are welcome).
- Reconnecting to the remote server if a TCP connection is lost.
- Log file rotation.
- Colors on
stdout
/stderr
.
Usage without Pipelines
It is possible to use without the Pipeline
, manually. However,
certain care needs to be taken to initialize everything that needs to be initialized.
It is either possible to just get the Dispatch
object and call apply
,
that however is a single-shot initialization and the logger can't be replaced.
The helper functions init
and install
can be used to gain the ability to replace
Dispatch
loggers multiple times.
Examples
Manual single use installation
use spirit::AnyError;
use spirit::prelude::*;
use spirit_log::Cfg;
// Well, you'd get it somewhere from configuration, but…
let cfg = Cfg::default();
let logger = cfg.create("logger")?;
logger.apply()?;
Manual multiple-use installation
use spirit::AnyError;
use spirit::prelude::*;
use spirit_log::Cfg;
spirit_log::init();
// This part can be done multiple times.
let cfg = Cfg::default();
let logger = cfg.create("logger")?;
spirit_log::install(logger);
Automatic usage with a Pipeline, reloading and command line options
use log::info;
use serde::Deserialize;
use spirit::{Pipeline, Spirit};
use spirit::prelude::*;
use spirit_log::{Cfg as LogCfg, CfgAndOpts as LogBoth, Opts as LogOpts};
use structopt::StructOpt;
#[derive(Clone, Debug, StructOpt)]
struct Opts {
#[structopt(flatten)]
logging: LogOpts,
}
impl Opts {
fn logging(&self) -> LogOpts {
self.logging.clone()
}
}
#[derive(Clone, Debug, Default, Deserialize)]
struct Cfg {
#[serde(default, skip_serializing_if = "LogCfg::is_empty")]
logging: LogCfg,
}
impl Cfg {
fn logging(&self) -> LogCfg {
self.logging.clone()
}
}
fn main() {
Spirit::<Opts, Cfg>::new()
.with(
Pipeline::new("logging").extract(|opts: &Opts, cfg: &Cfg| LogBoth {
cfg: cfg.logging(),
opts: opts.logging(),
}),
)
.run(|_spirit| {
info!("Hello world");
Ok(())
});
}
The configuration could look something like this:
[[logging]]
level = "DEBUG"
type = "file"
filename = "/tmp/example.log"
clock = "UTC"
Dependencies
~6–13MB
~140K SLoC