---
# System prepended metadata

title: logging
tags: [' published', RFC]

---

---
title: logging
tags: RFC, published
---

+ Feature name: `logging`
+ Start date: 2020-05-23
+ RFC PR: [iotaledger/bee-rfcs#00](https://github.com/iotaledger/bee-rfcs/pull/00)
+ Bee issue: [iotaledger/bee#00](https://github.com/iotaledger/bee/issues/00)

# Summary

This RFC introduces a logging library and some recommendations on how to log in the Bee project.

# Motivation

Logging is done across almost all binary and library crates in the Bee project so concistency is needed to provide a better user experience.

# Detailed design

Logging in Bee should be done through the [log](https://crates.io/crates/log) crate (`A Rust library providing a lightweight logging facade`) which is the Rust standard logging library.

The `log` crate itself is just a frontend and support the implementation of many different backends.

> It provides a single logging API that abstracts over the actual logging implementation. Libraries can use the logging API provided by this crate, and the consumer of those libraries can choose the logging implementation that is most suitable for its use case.

## Frontend

> Libraries should link only to the log crate, and use the provided macros to log whatever information will be useful to downstream consumers.

The `log` crate only provides macros for logging.

- `trace!`
- `debug!`
- `info!`
- `warn!`
- `error!`

These macros have a usage similar to the `println!` macro.

A log request consists of a `target`, a `level`, and a `message`.

The target is a string which defaults to the module path of the location of the log request but may be overridden.

Example with default target:
```rust
info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed);
```

Example with overriden target:
```rust
info!(target: "connection_events", "Successfull connection, port: {}, speed: {}", conn_info.port, conn_info.speed);
```

## Backend

> Executables should choose a logging implementation and initialize it early in the runtime of the program. Logging implementations will typically include a function to do this. Any log messages generated before the implementation is initialized will be ignored.

There are a lot of [available backends](https://docs.rs/log/0.4.8/log/#available-logging-implementations) for the `log` frontend.

This RFC opt for the [fern](https://docs.rs/fern) backend which is a very complete one with an advanced configuration.

### Initialization

The backend API is limited to one function call to initialize the logger. It is designed to be updatable without breaking changes by taking a `LoggerConfig` configuration object and returning a `Result`.

```rust
fn logger_init(config: LoggerConfig) -> Result<(), LoggerError>;
```

### Configuration

The following configuration - compliant with [RFC 31](https://github.com/iotaledger/bee-rfcs/blob/master/text/0031-configuration.md) - primarily allows configuring different outputs with different log levels.

`config.rs`
```rust
#[derive(Clone)]
struct LoggerOutputConfig {
    name: String,
    level: LevelFilter,
    ...
}

#[derive(Clone)]
struct LoggerConfig {
    color: bool,
    outputs: Vec<LoggerOutputConfig>,
    ...
}
```

`config.toml`
```toml
[logger]
color = true
[[logger.outputs]]
name  = "stdout"
level = "info"
[[logger.outputs]]
name  = "errors.log"
level = "error"
```

**Note**: `stdout` is a reserved name for the standard output of the console.

### Errors

Since different backends may have different errors, we need to abstract them  to provide a consistent `logger_init` function.

```rust
#[derive(Debug)]
#[non_exhaustive]
enum LoggerError {
    ...
}
```

**Note**: The error has to be `non_exhaustive` to allow further improving / extending the logger without risking a breaking change.

## Format

The following elements should appear in this order:
- Date in the `%Y-%m-%d` format e.g. `2020-05-25`;
- Time in the `%H:%M:%S` format e.g. `10:23:03`;
- Target e.g. `bee_node::node`;
    - The default target - module path - is preferred but can be overriden if needed;
- Level e.g. `INFO`;
- Message e.g. `Initializing...`;

All elements should be enclosed in brackets `[...]` with only a space between the level and the message.

Example: `[2020-05-25][10:23:03][bee_node::node][INFO] Initializing...`

**Note**: All messages should end with a `...` if the event is happening or with a `.` if the event happened.

# Drawbacks

No specific drawbacks using this library.

# Rationale and alternatives

- The `log` crate is maintained by the rust team;
- Most of the logging crates in the Rust ecosystem are actually backends for the `log` crate with different features;
- It should be very easy to switch to a different backend in the future by only changing the initialization function;
- There are a lot of [available backends](https://docs.rs/log/0.4.8/log/#available-logging-implementations) but `fern` offers a very fine-grained logging configuration;

# Unresolved questions

There are no open questions as this RFC is pretty straightforward and the topic of logging is not a complex one.