June 29, 2023

Exploring Spin Application Variables

The Fermyon Team

config rust spin

Exploring Spin Application Variables

Application variables are dynamic values that can be injected into an application to modify its behavior without making changes to the code. These variables are often employed as a mechanism to control various aspects of an application’s functionality, such as database connection strings, API keys and secrets, feature toggles, logging levels, and many more.

Since the release of Fermyon’s Spin v1.3.0, variables can be externalized from your code and deployed to Fermyon Cloud. Using the spin cloud variables command, you can seamlessly update a deployed application’s variables without having to restart or modify code!

In this post we will discuss several use cases for variables when developing and deploying Spin applications to Fermyon Cloud and then explore a concrete example application of using variables to control the applications logging verbosity.

Use application variables to …

  • manage sensitive data: Variables allows you to isolate your application’s sensitive data such as passwords, certificates, encryption keys, or API tokens away from your application’s business logic. By using variables in conjunction with spin cloud variables, you can easily manage and update your application’s secrets as needed, maintaining a strong security posture for your deployed application.

  • integrate with external services: Your application may interface external databases, message queues, or third-party APIs. Using variables you can inject information, such as database connection strings or API endpoints, about these systems at deploy time and update as needed without modifying the application code. For example, you might develop an application that reads a variable to communicate with a local database, and when deploying set the variable to point to a production grade database.

  • control logging verbosity: When you deploy an application, you’ll pick a default logging level that’s informative but not too verbose. But if you start seeing unexpected errors or behavior, it’s useful to be able to increase the verbosity of your logging at runtime to get real time, detailed, production information to diagnose problems or bugs - and of course to decrease it afterwards!

An Example

In this example we will build a simple Rust application that demonstrates how to dynamically configure the logging level for a Spin application without redeploying or modifying the code.

Before continuing, ensure that you have Spin v1.3.0 or greater installed. You can follow the official Fermyon Cloud Quickstart guide to install Spin and log in to Fermyon Cloud.

Let’s start by creating a new Spin application using the http-rust template. Follow the steps below:

$ spin new http-rust my-logging-app
Project description: A Spin app that logs the request method and URL
HTTP base: /
HTTP path: /...

Adding the variable to our application

Next, let’s add our loglevel variable to our Spin.toml:

# Add this above the [component] section
[variables]
loglevel = { required = true }

Then we will add a [component.config] section that effectively gives our component access to our dynamic variable:

# Add this below the `[component.build] section`
[component.config]
loglevel = "{{ loglevel }}"

In total, our spin.toml should now look something like:

spin_manifest_version = "1"
description = "A Spin app that logs the request method and URL"
name = "my-logging-app"
trigger = { type = "http", base = "/" }
version = "0.1.0"

[variables]
loglevel = { required = true }

[[component]]
id = "my-logging-app"
source = "target/wasm32-wasi/release/my_logging_app.wasm"
[component.trigger]
route = "/..."
[component.build]
command = "cargo build --target wasm32-wasi --release"
[component.config]
loglevel = "{{ loglevel }}"

Implementing our application

Before we dive in, let’s add the Rust log and simple_logger dependencies to our Cargo.toml:

$ cargo add log simple_logger

Now that we have declared our variable and added our Rust dependencies, we are ready to implement our Spin application.

In src/lib.rs:

use log::*;
use anyhow::{anyhow, Result};
use simple_logger::SimpleLogger;
use spin_sdk::{
    config,
    http::{Request, Response},
    http_component,
};

// This function will initialize logging for our application. The logger
// will be configured to log at the level indicated by the value of our
// dynamic variable "loglevel". 
fn init_logger() -> Result<()> {
    // The name of our dynamic application variable.
    const LOG_LEVEL_CONFIG_VARIABLE: &str = "loglevel";

    // Use the config SDK to get the value of loglevel and 
    // transform it into a known `log::LevelFilter`.
    let level: LevelFilter = config::get(LOG_LEVEL_CONFIG_VARIABLE)?
        .parse()
        .map_err(|e| anyhow!("parsing log level: {e}"))?;

    // Finally initialize the logger.
    SimpleLogger::new()
        .with_level(level)
        .init()?;

    Ok(())
}

// Our HTTP event handler
#[http_component]
fn handle_my_logging_app(req: Request) -> Result<Response> {
    // Initialize the logger.
    //
    // NOTE: For each incoming request we reconfigure the logger
    // by fetching the current value of the "loglevel" variable.
    init_logger()?;

    // Log the method and URI at the TRACE level.
    trace!("method={}, uri={}", req.method(), req.uri());

    // ... do some work and handle the request ...

    // Log message indicating success at INFO level.
    info!("returning 200");
    
    Ok(http::Response::builder()
        .status(200)
        .body(None)?)
}

Now that we have implemented our simple application, let’s build and deploy to Fermyon Cloud initially setting the loglevel to info:

$ spin build
$ spin deploy --variable loglevel=info
Uploading my-logging-app version 0.1.0+rb410cbfe...
Deploying...
Waiting for application to become ready......... ready
Available Routes:
  my-logging-app: https://my-logging-app-uaeligif.fermyon.app (wildcard)

To verify that our variable has been successfully associated with our app, we can use spin cloud variables list command to inspect the set of variables:

$ spin cloud variables list --app my-logging-app
loglevel

If we execute a request to our available route:

$ curl https://my-logging-app-uaeligif.fermyon.app

… we see an INFO log message in the cloud.fermyon.com application’s dashboard, similar to:

2023-06-28T23:26:17.976Z INFO  [my-logging-app] returning 200

Notice how we don’t see the TRACE level log of the method and URI because when we deployed our application we set the value of our variable to info.

However, we can use spin cloud variables set to update the value of our variable loglevel to TRACE:

$ spin cloud variables set loglevel=trace --app my-logging-app

If we again execute the request from above, we will see our application is now logging our request’s method and URI:

2023-06-28T23:27:26.612Z TRACE [my-logging-app] method=GET, uri=/
2023-06-28T23:27:26.612Z INFO  [my-logging-app] returning 200

Until next time

As we have seen, there are many valuable uses of Fermyon’s application variables feature. We’d love to hear yours! If you want to share your ideas or ask questions drop in to the Fermyon Discord server or say hello on Twitter @fermyontech and @spinframework.

To dig deeper, be sure to check out the documentation for Application Variables and the Spin reference for Dynamic Configuration

 

 

 


🔥 Recommended Posts


Quickstart Your Serveless Apps with Spin

Get Started