Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Introduction to structured logging with slog

vtta edited this pageFeb 21, 2020 ·8 revisions

Overview

slog separates concepts of creating logging information (eg.info!(log, "aborting operation")) from actually handling log records ("writing them down somewhere" - eg. to a file). For log record creationslog providesLoggers that can form hierarchies. For log record handling the concept ofDrains (logging "outputs") is introduced.

Drains

Since usually only the end application itself, knows the details on which logs, when, and where should be stored, rootDrain is usually created somewhere early in themain function.

slog's Drains are simplystructs implementing aDrain trait.

Let's take a look at the simplestDrain: aslog::Discard . It's a drain that simply drops any logging records that are send to it. Because of that, it can never return any error:type Error = Never.

A more interestingDrain is aslog::LevelFilter. It's a generic struct that wraps another drain (D : Drain), and forwards to it only loggingRecords that have logging level equal or higher than a given value. Because the underlying drain can potentially return an error,LevelFilter will return such error (type Error = D::Error).

LevelFilter shows another benefit ofslogDrains - they are composable. Due to Rust generic system, multiple drains can be combined together, and compiled to very efficient code, with endless possibilities for writing newDrains providing new features.

A rootDrain is a drain that never returns any Errors (type Error = Never). It's an important distinction sinceLoggers can only be build on top ofDrains that can't return an error. Depending on the application requirements, aslog::Fuse orslog::IgnoreErr can typically be used to either panic on errors, or ignore them completely. More sophisticated custom strategies like fallback, etc. can easily be implemented, with most useful ones possibly added to standardslog or one of associated crates.

Libraries typically should not build their own drains, and instead rely onLoggers provided by the library user. A typicallog crate-backward-compatibile approach has been documented inslog-using example library.

Let's take a look at the example drain hierarchy:

let file =File::create("/tmp/myloggingfile").unwrap();let stream = slog_stream::stream(file, slog_json::new().build());let syslog = slog_syslog::unix_3164(slog_syslog::Facility::LOG_DAEMON);let root =Logger::root(Duplicate::new(LevelFilter::new(stream,Level::Info),LevelFilter::new(syslog,Level::Warning),).fuse(),o!());

from andrain-graph.rs example

The graph illustrating the drain-hierarchy created:

slog drain hierarchy example

Loggers

Inslog any logging statement requires aLogger object.

The firstLogger created will always have to be a rootLogger usingslog::Logger::root. Any otherLogger objects can be build from the existing ones as it's child, usingslog::Logger::new.

EachLogger has a list of key-value pairs associated with it. Each time a childLogger is created it inherits all the pairs from its parent. These allows building a contextual information, that corresponds to the logical execution-level structure of the application, as opposed to the structure of the code itself.

Eg. An application has it's own context data like:

  • time it was build and revision of the code used
  • time when it was started

Then inside application there might be different components like:

  • web server with information on:
    • the ip and port it is listening on
  • (multiple) job processing threads each with:
    • directory it's working on

A web server handles request from multiple peers, each described by:

  • user id
  • IP

A job processing handles different jobs described by:

  • user id
  • file
  • type

For each of the above components, typically a newLogger object would be created, adding more information to the parent-component. So then, when an error case is handled, logging can be just:

error!(job.logger, "write failed"; 'error' => error);

And that would cause application to log a message containing, both the error itself, and information about it's logical runtime context: which file, what type of a job, for which user, and so on.

Crates

TBD

Clone this wiki locally

[8]ページ先頭

©2009-2025 Movatter.jp