Skip to content

Instantly share code, notes, and snippets.

@hrmsk66
Last active May 16, 2023 03:39
Show Gist options
  • Save hrmsk66/ac96446fdf5368ffa58c2204146ca294 to your computer and use it in GitHub Desktop.
Save hrmsk66/ac96446fdf5368ffa58c2204146ca294 to your computer and use it in GitHub Desktop.

Collect and log client information

sequenceDiagram
    participant Client
    participant C@E
    participant WeatherAPI
    participant LogService
    Client->>C@E: Beacon
    C@E->>Client: 204
    Note over C@E: Check Geo and User-Agent
    C@E->>WeatherAPI: Request Weather
    WeatherAPI->>C@E: 200
    Note over C@E: Generate JSON data
    C@E->>LogService: Log
    LogService->>C@E: 200

main.rs

use std::time::Instant;

use fastly::geo::geo_lookup;
use fastly::http::{StatusCode, header};
use fastly::{Request, Response};
use serde::Deserialize;
use serde_json::json;
use woothee::parser::Parser;
use log::LevelFilter::*;

fn main() -> anyhow::Result<()> {
    let timing = Instant::now();
    println!("request received ({} ms)", timing.elapsed().as_millis());

    // Immediately return a 204 No Content response to the client
    let resp = Response::from_status(StatusCode::NO_CONTENT)
        .with_header(header::CACHE_CONTROL, "no-store, private");
    resp.send_to_client();

    println!("response returned ({} ms)", timing.elapsed().as_millis());

    // Get the client's request and IP address
    let req = Request::from_client();
    let client_ip = req.get_client_ip_addr().unwrap();

    // Use the Fastly geo_lookup function to get geographical information based on the IP address
    let geo = geo_lookup(client_ip).unwrap();
    let latitude = geo.latitude();
    let longitude = geo.longitude();

    // Use the geographical information to get weather data
    let mut beresp =  Request::get(format!("https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current_weather=true"))
        .with_pass(true) // Don't cache response
        .send("weather_api")?;

    // Deserialize the weather data
    let weather = beresp.take_body_json::<WeatherResponse>()?;

    // Parse the User-Agent header to get information about the client's device
    let raw_user_agent = req.get_header_str(header::USER_AGENT).unwrap();
    let parser = Parser::new();
    let user_agent = parser.parse(raw_user_agent).unwrap();

    // Build a JSON string with the gathered data
    let data = json!(
        {
            "ip": client_ip.to_string(),
            "as_name": geo.as_name(),
            "latitude": latitude,
            "longitude": longitude,
            "conn_speed": geo.conn_speed(),
            "device_type": user_agent.category,
            "app_name": user_agent.name,
            "weather_code": weather.current_weather.weathercode,
            "temperature": weather.current_weather.temperature
        }
    );

    // Log the JSON data
    log_fastly::init_simple("papertrail", Info);
    log::info!("{data}");

    println!("data logged ({} ms)", timing.elapsed().as_millis());

    Ok(())
}

// Define a struct for deserializing the weather data
#[derive(Debug, Deserialize)]
struct Weather {
    temperature: f64,
    weathercode: i32,
}

// Define a struct for deserializing the full response
#[derive(Debug, Deserialize)]
struct WeatherResponse {
    current_weather: Weather,
}

Cargo.toml

[package]
name = "fastly-compute-project"
version = "0.1.0"
edition = "2021"
# Remove this line if you want to be able to publish this crate as open source on crates.io.
# Otherwise, `publish = false` prevents an accidental `cargo publish` from revealing private source.
publish = false

[profile.release]
debug = 1

[dependencies]
anyhow = "1.0.71"
fastly = "^0.9.2"
log = "0.4.17"
log-fastly = "=0.9.1"
serde = { version = "1.0.163", features = ["derive"] }
serde_json = "1.0.96"
woothee = "0.13.0"

faslty.toml

# This file describes a Fastly Compute@Edge package. To learn more visit:
# https://developer.fastly.com/reference/fastly-toml/

authors = ["foo@example"]
description = ""
language = "rust"
manifest_version = 3
name = "logging"
service_id = "MKUK9EASSwkvZvi8Cs1bQ7"

[local_server]

  [local_server.backends]

    [local_server.backends.weather_api]
      override_host = "api.open-meteo.com"
      url = "https://api.open-meteo.com/"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment