Skip to content

Instantly share code, notes, and snippets.

@hrmsk66
Last active August 29, 2023 09:37
Show Gist options
  • Save hrmsk66/c487b4f17e827db0fe51299d4fe2cbb0 to your computer and use it in GitHub Desktop.
Save hrmsk66/c487b4f17e827db0fe51299d4fe2cbb0 to your computer and use it in GitHub Desktop.

Sending Tracing Data to Fastly Logging Endpoint

This gist outlines the steps for sending data collected with the tracing crate, a Rust crate for application-level tracing, to a Fastly log endpoint. For simplicity, I will use the Subscriber and Layer implementations included in the tracing_subscriber crate. However, you can write your own custom implementations to modify the collected data and its format.

Step 1. Add Dependencies

Add the required dependencies to your Cargo.toml file.

tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["json"] }

Step 2. Implement MakeWriter

Write a MakeWriter implementation to send logs to a Fastly logging endpoint. Below is a simple example. Save the code in a file named log_writer.rs.

use std::io::Write;
use fastly::log::Endpoint;
use tracing_subscriber::fmt::MakeWriter;

pub struct FastlyLogWriter {
  endpoint: Endpoint
}

impl FastlyLogWriter {
  pub fn new(endpoint: &str) -> Self {
    FastlyLogWriter { endpoint: Endpoint::from_name(endpoint) }
  }
}

impl MakeWriter<'_> for FastlyLogWriter {
    type Writer = FastlyLogWriter;

    fn make_writer(&self) -> Self::Writer {
      FastlyLogWriter { endpoint: self.endpoint.clone() }
    }
}

impl Write for FastlyLogWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        // Collapse the formatting of the message into a String, so that we only call the
        // underlying `write` once. This prevents the message from being split into multiple
        // lines in surprising ways, depending on how the format arguments are structured.
        let msg = format!("{}", String::from_utf8_lossy(buf));
        write!(self.endpoint.clone(), "{}", msg)?;

        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

Step 3. Create Layer and Initialize Subscriber

Create a Layer using the tracing_subscriber::fmt and initialize a Subscriber with that Layer. Here, we are using a log endpoint named my_endpoint.

use fastly::http::StatusCode;
use fastly::{Error, Request, Response};

use tracing::instrument;
use tracing_subscriber::prelude::*;
use tracing_subscriber::Registry;

mod log_writer;
use log_writer::FastlyLogWriter;

#[fastly::main]
fn main(_req: Request) -> Result<Response, Error> {
    // MakeWriter implementation to send logs to a Fastly logging endpoint.
    let writer = FastlyLogWriter::new("my_endpoint");

    // Build a layer from tracing_subscriber::fmt.
    // This layer will format the traced data as JSON before it is sent to the Fastly log endpoint.
    let json_export_layer = tracing_subscriber::fmt::layer()
        .with_writer(writer)
        .json();

    // Add the layer to the Subscriber and initialize it.
    Registry::default()
        .with(json_export_layer)
        .init();

    some_task();
    Ok(Response::from_status(StatusCode::OK))
}

#[instrument]
fn some_task() {
    tracing::info!("yo!");
}

Step 4. Deploy the Service

fastly compute publish

Step 5. Add Log Endpoint

Add a log endpoint to your Fastly service. In this example, I'm using Papertrail.

fastly service-version clone --version=latest
fastly logging papertrail create --version=latest --placement=none --name=my_endpoint --address=xxx.papertrailapp.com --port=xxxxx
fastly service-version activate --version=latest
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment