website/content/blog/2020-09-06-leveraging-env-vars-in-rust-apps.md
2024-04-06 00:28:37 +02:00

2.8 KiB

+++ title = "Leveraging Env Vars in Rust Apps" [categories] tags = [ "rust", "config" ] categories = [ "programming", "apps"] +++ Environment variable have gained a lot of importance since the rise of the container based deployments and (consequently) the popularity of the 12 factor app.

It also has become very practical with the widespread support of the .env file in the project folder which makes configuring apps during development very practical.

Using environment in Rust

The std::env package gives access to the environment variables, and also information about the working directory, the location of the program executing, temp folder, etc...

The method we really are interested in is var.

match env::var("MQTT_BROKER") {
    Ok(mqtt_broker) => mqtt_init(&mqtt_broker).await,
    Err(e) => error!("No broker specified in MQTT_BROKER environment variable.({})", e)
}

It returns a Result<String, VarError> which we can easily pattern match on and give readable feedback to the user.

I thing this is perfectly fine for simple, small apps I am likely to write in the foreseeable future.

Controlling Logging from the Environment

Another thing needed for smallisch apps is a logging system with the following requirements:

  • Controllable via environment
  • Add a timestamp
  • Output to stdout or stderr (a 12 factor thing)
  • Namespace modules
  • Override config for specific modules

Rust has a standard logging API defined in the log crate crate for which a large selection of implementations is available.

The first one on the list with implementations fit all my requirements, so that's fine.

All we need to do is initialize it after reading the environment variables from the .env file :

async fn main() {
    dotenv::dotenv().ok();
    env_logger::init();
    ...

and we are logging using the standard debug!{.verbatim}, info!{.verbatim}, warn!{.verbatim}, ... macros.

Scaling to larger apps

When apps grow (or just when they live long enough) they tend to accumulate config options and layers of modules making logging also a headache.

When confronted with these issues I saw that the config and envy crates offer nice layered configuration support and straightforward pouring in type safe structs.

Similarly there are more flexible, and consequently more complex, logging frameworks like log4rs. There are also structured logging libraries but I still need to see how these can work in containers without adding additional hoops to jump through.

Let's hope my apps stay small and simple and do not need this additional complexity.