Better Logging for the Web Application
Tags: rust, webapp, beginners
Published: 2019-08-06 13:45:32

---
title: Better Logging for the Web Application
published: true
tags: Rust,WebApp,beginners
canonical_url: https://rust.graystorm.com/2019/07/20/better-logging-for-the-web-application/
cover_image: https://rust.graystorm.com/wp-content/uploads/2019/06/plant-wood-old-rust-equipment-gear-1278587-pxhere.com_.jpg
series: Designing a Full-Featured WebApp with Rust
---

![A journal ledger of accounts... like logging but only uses numbers](https://i2.wp.com/rust.graystorm.com/wp-content/uploads/2019/07/writing-retro-number-old-pattern-line-613428-pxhere.com_.jpg?ssl=1)<figcaption>See… structured logging…</figcaption>

When I last [left my sample web application](https://rust.graystorm.com/2019/07/15/its-not-a-web-application-without-a-database/), I was doing simple logging to the terminal which is not useful for very long. I want [structured logging](https://stackify.com/what-is-structured-logging-and-why-developers-need-it/) and I want the app and `Rocket` to write to (separate) files. So, let’s switch out the crates `log` and `simple_log` for `slog` and `sloggers` to get better logging for my web application.

## Application Logging

[Slog](https://crates.io/crates/slog)‘s “ambition is to be The Logging Library for Rust” and it sure seems adaptable. There are several “helper” crates to log to the terminal or syslog, log as json, etc. However, `slog` is complex! Which seems to be the main reason `sloggers` came about. `Sloggers` brings you the most useful features of `slog` without the complex setup. It happens to include an example of configuring via an inline TOML string. I’ve already got my `CONFIG` global pulling from a TOML file, so I add this to `conf/development.toml`:

```
<conf/development.toml>

...
[logconfig]
type = "file"
format = "compact"
source_location = "module_and_line"
timezone = "local"
level = "debug"
path = "log/error.log"
rotate_size = 1073741824
rotate_keep = 2

[webservice]
weblog_path = "log/access.log"
...
```

Plus, I add `logging.rs` to set up the global `LOGGING` instance so everyone can log; just like everyone can pull from the global `CONFIG` instance:

```
<logging.rs>

use crate::settings::CONFIG;
use crate::sloggers::{Build, Config};
use once_cell::sync::Lazy;

#[derive(Debug)]
pub struct Logging {
    pub logger: slog::Logger,
}

pub static LOGGING: Lazy<Logging> = Lazy::new(|| {
    let logconfig = &CONFIG.logconfig;
    let builder = logconfig.try_to_builder().unwrap();
    let logger = builder.build().unwrap();

    Logging { logger: logger }
});
```

`Sloggers` is really easy to use to set up a `slog` instance. Here, I simply pull the logconfig from my global `CONFIG`, build the logger, and store it in my new [OnceCell](https://crates.io/crates/once_cell) Lazy `LOGGING` global.

## And then, Web Access Logging

![A spider web, wet from rain...
](https://i0.wp.com/rust.graystorm.com/wp-content/uploads/2019/07/nature-dew-wing-morning-fauna-material-792967-pxhere.com_.jpg?ssl=1)

At this point, application logging is going to my new `log/error.log` file, but `Rocket` logging is still coming to the screen. This is actually good – as I mentioned, I want to have them going to separate files anyway. So now with some `slogging` experience, it’s a simple matter to set up a second file, specifically for `Rocket`. I do need to add yet another crate, this time [rocket_slog](https://crates.io/crates/rocket-slog) – so that `Rocket` internals can glom on to the `slog` instance that I make. Here is the new `start_webservice` function:

```
<src/lib.src>

...
pub fn start_webservice() {
    let logger = &LOGGING.logger;
    let weblog_path = &CONFIG.webservice.weblog_path;
    let bind_address = &CONFIG.webservice.bind_address;
    let bind_port = &CONFIG.webservice.bind_port;

    // start rocket webservice
    let version = include_str!("version.txt");
    let mut builder = FileLoggerBuilder::new(weblog_path);

    builder.level(Severity::Debug);

    let weblogger = builder.build().unwrap();
    let fairing = SlogFairing::new(weblogger);
    warn!(logger, "Waiting for connections...";
        "address" => bind_address,
        "port" => bind_port,
        "version" => version);

    rocket::ignite()
        .attach(fairing)
        .attach(Template::fairing())
        .attach(SpaceHelmet::default())
        .mount("/", routes![routes::index, routes::favicon])
        .mount("/img", StaticFiles::from("src/view/static/img"))
        .mount("/css", StaticFiles::from("src/view/static/css"))
        .mount("/js", StaticFiles::from("src/view/static/js"))
        .launch();
}
```

Now I have `log/error.log` for application logging (I’ll rename that later to `log/app.log`) and `log/access.log` for `Rocket` logging. But I still don’t have structured logs!

## Finally, Structured Logs

I try several ways (with my limited Rust knowledge and experience) but it appears that with `sloggers` ease of use, you trade away options if you want to get fancy. At least, **I** couldn’t figure out how to get `slog_json` or something called `slog_bunyan` to work with `sloggers`. It looks like I have to deal with `slog` directly.

Later, on another night, I tackle these changes. I dump `sloggers` and, though much searching around, I end up with a merge of two examples I find (from `slog` and `slog`-bunyan). Thankfully, I am getting a little better at understanding the API docs that are posted for crates! Anyway, here is how the app logging setup looks now:

```
<src/logging.rs>

...
pub static LOGGING: Lazy<Logging> = Lazy::new(|| {
    let logconfig = &CONFIG.logconfig;
    let logfile = &logconfig.applog_path;
    let file = OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open(logfile)
        .unwrap();
    let applogger = slog::Logger::root(
        Mutex::new(slog_bunyan::default(file)).fuse(),
        o!("location" => FnValue(move |info| {
            format!("{}:{} {}", info.file(), info.line(), info.module(), )
          })
        ),
    );
    Logging { logger: applogger } });
```

Notice I still pull the `applog_path` from the `CONFIG` and then create that filehandle. Next, a single but complicated call into `slog` returns a `Logger` instance, which I store into my global. The `o!` macro, provided by the `slog` `crate`, lets me add a JSON field to every log record that gets written. I copy from an example and add the file, line number, and module name where the log was generated. I’ll probably come back to this later and add more data. Also, notice I am using [slog_bunyan](https://crates.io/crates/slog-bunyan) for the JSON formatting. `slog-json` actually recommends using `slog_bunyan` for a “more complete output format.” [Bunyan logging](https://nodejs.org/es/blog/module/service-logging-in-json-with-bunyan/) seems to originate as a node.js module. Lastly, I also change the weblogger in the same way – now both are JSON logs and still log to separate files.

This actually wasn’t as complicated as I feared. I’ve lost some of the easily configurable `sloggers` features like auto file rotation, but I’ll figure that out as well – it’s probably actually just a `slog` feature that I need to enable.

## Results, JSON Style

Here is how both look now – I think next post I’ll work on a JSON log viewer so we can watch these easier, as we develop.

```
<log/app.log>

{"msg":"Service starting","v":0,"name":"slog-rs","level":40,"time":"2019-07-20T03:40:13.781382592+00:00","hostname":"ip-169-254-169-254","pid":6368,"location":"src/lib.rs:48 ppslib","run_level":"development"}
{"msg":"Waiting for connections...","v":0,"name":"slog-rs","level":40,"time":"2019-07-20T03:40:13.784899718+00:00","hostname":"ip-169-254-169-254","pid":6368,"location":"src/lib.rs:76 ppslib","version":"0.1.0","port":3000,"address":"0.0.0.0"}

<log/access.log>

{"msg":"listening","v":0,"name":"slog-rs","level":30,"time":"2019-07-20T05:31:21.068959173+00:00","hostname":"ip-169-254-169-254","pid":7700,"address":"http://0.0.0.0:3000"}
{"msg":"request","v":0,"name":"slog-rs","level":30,"time":"2019-07-20T05:31:22.402815352+00:00","hostname":"ip-169-254-169-254","pid":7700,"uri":"\"/\"","method":"Get"}
{"msg":"response","v":0,"name":"slog-rs","level":30,"time":"2019-07-20T05:31:22.404425952+00:00","hostname":"ip-169-254-169-254","pid":7700,"status":"200 OK","route":"\u001b[32mGET\u001b[0m \u001b[34m/\u001b[0m \u001b[36m(\u001b[0m\u001b[35mindex\u001b[0m\u001b[36m)\u001b[0m"}
```

Again, if all that was complicated to follow and you prefer to look at the git commits as I went through this ordeal, check out the [repository](https://github.com/jculverhouse/pinpoint_shooting).

The post [Better Logging for the Web Application](https://rust.graystorm.com/2019/07/20/better-logging-for-the-web-application/) appeared first on [Learning Rust](https://rust.graystorm.com).