compute-rust-sentry 0.1.3

Send error reports from Compute@Edge to Sentry
Documentation
use std::collections::HashMap;

use fastly::Request;
use serde::Serialize;
use time::OffsetDateTime;

#[derive(Serialize)]
pub struct EventPayload {
    pub event_id: String,
    #[serde(rename = "type")]
    pub event_type: String,
    pub timestamp: OffsetDateTime,
    pub platform: Platform,
    pub level: Level,
    pub transaction: Option<String>,
    pub server_name: Option<String>,
    pub release: Option<String>,
    pub environment: Option<String>,
    pub exception: Vec<Exception>,
    pub request: Option<RequestMetadata>,
}

#[derive(Serialize)]
pub struct Exception {
    #[serde(rename = "type")]
    name: String,
    value: String,
}

#[derive(Serialize)]
pub struct RequestMetadata {
    method: String,
    url: String,
    headers: HashMap<String, String>,
    env: HashMap<String, String>,
}

#[derive(Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Platform {
    C,
    Native,
    Other,
}

#[derive(Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Level {
    Fatal,
    Error,
    Warning,
    Info,
    Debug,
}

impl Default for EventPayload {
    fn default() -> Self {
        EventPayload {
            event_id: uuid::Uuid::new_v4().to_string(),
            event_type: "event".to_string(),
            timestamp: OffsetDateTime::now_utc(),
            platform: Platform::Other,
            level: Level::Fatal,
            transaction: None,
            server_name: Some(std::env::var("FASTLY_HOSTNAME").unwrap()),
            release: Some(std::env::var("FASTLY_SERVICE_VERSION").unwrap()),
            environment: Some(std::env::var("FASTLY_SERVICE_ID").unwrap()),
            exception: Vec::new(),
            request: None,
        }
    }
}

impl<T: std::error::Error> From<T> for EventPayload {
    fn from(error: T) -> Self {
        EventPayload {
            exception: vec![Exception {
                name: format!("{:?}", error)
                    .chars()
                    .take_while(|&ch| ch != '(' && ch != ' ')
                    .collect::<String>(),
                value: error.to_string(),
            }],
            ..Default::default()
        }
    }
}

impl From<&Request> for RequestMetadata {
    fn from(request: &Request) -> Self {
        let mut headers = HashMap::new();

        request.get_header_names().for_each(|k| {
            headers.insert(
                k.to_string(),
                request.get_header(k).unwrap().to_str().unwrap().to_string(),
            );
        });

        let mut env = HashMap::new();

        if let Some(addr) = request.get_client_ip_addr() {
            env.insert("REMOTE_ADDR".to_string(), addr.to_string());
        }

        RequestMetadata {
            method: request.get_method().to_string(),
            url: request.get_url().to_string(),
            headers,
            env,
        }
    }
}