Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: hook in runtime logs #568

Merged
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 17 additions & 7 deletions codegen/src/next/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,26 +262,36 @@ pub(crate) fn wasi_bindings(app: App) -> proc_macro2::TokenStream {
#[no_mangle]
#[allow(non_snake_case)]
pub extern "C" fn __SHUTTLE_Axum_call(
fd_3: std::os::wasi::prelude::RawFd,
fd_4: std::os::wasi::prelude::RawFd,
fd_5: std::os::wasi::prelude::RawFd,
logs_fd: std::os::wasi::prelude::RawFd,
parts_fd: std::os::wasi::prelude::RawFd,
body_read_fd: std::os::wasi::prelude::RawFd,
body_write_fd: std::os::wasi::prelude::RawFd,
) {
use axum::body::HttpBody;
use shuttle_common::wasm::Logger;
use std::io::{Read, Write};
use std::os::wasi::io::FromRawFd;
use tracing_subscriber::prelude::*;

println!("inner handler awoken; interacting with fd={},{},{}", fd_3, fd_4, fd_5);
println!("inner handler awoken; interacting with fd={},{},{},{}", logs_fd, parts_fd, body_read_fd, body_write_fd);

// file descriptor 2 for writing logs to
let logs_fd = unsafe { std::fs::File::from_raw_fd(logs_fd) };

tracing_subscriber::registry()
.with(Logger::new(logs_fd))
.init(); // this sets the subscriber as the global default and also adds a compatibility layer for capturing `log::Record`s

// file descriptor 3 for reading and writing http parts
let mut parts_fd = unsafe { std::fs::File::from_raw_fd(fd_3) };
let mut parts_fd = unsafe { std::fs::File::from_raw_fd(parts_fd) };

let reader = std::io::BufReader::new(&mut parts_fd);

// deserialize request parts from rust messagepack
let wrapper: shuttle_common::wasm::RequestWrapper = rmp_serde::from_read(reader).unwrap();

// file descriptor 4 for reading http body into wasm
let mut body_read_stream = unsafe { std::fs::File::from_raw_fd(fd_4) };
let mut body_read_stream = unsafe { std::fs::File::from_raw_fd(body_read_fd) };

let mut reader = std::io::BufReader::new(&mut body_read_stream);
let mut body_buf = Vec::new();
Expand All @@ -306,7 +316,7 @@ pub(crate) fn wasi_bindings(app: App) -> proc_macro2::TokenStream {
parts_fd.write_all(&response_parts).unwrap();

// file descriptor 5 for writing http body to host
let mut body_write_stream = unsafe { std::fs::File::from_raw_fd(fd_5) };
let mut body_write_stream = unsafe { std::fs::File::from_raw_fd(body_write_fd) };

// write body if there is one
if let Some(body) = futures_executor::block_on(body.data()) {
Expand Down
5 changes: 4 additions & 1 deletion common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
strum = { version = "0.24.1", features = ["derive"] }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, optional = true }
uuid = { workspace = true, features = ["v4", "serde"] }

[dev-dependencies]
cap-std = "1.0.2"
hyper = "0.14.3"

[features]
backend = ["async-trait", "axum"]
display = ["comfy-table", "crossterm"]
axum-wasm = ["http-serde", "http", "rmp-serde"]
tracing = ["serde_json"]
axum-wasm = ["http-serde", "http", "rmp-serde", "tracing", "tracing-subscriber"]
models = ["anyhow", "async-trait", "display", "http", "reqwest", "serde_json"]
2 changes: 2 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub mod log;
pub mod models;
pub mod project;
pub mod storage_manager;
#[cfg(feature = "tracing")]
pub mod tracing;
#[cfg(feature = "axum-wasm")]
pub mod wasm;

Expand Down
53 changes: 53 additions & 0 deletions common/src/tracing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use serde_json::json;
use tracing::field::Visit;

// Boilerplate for extracting the fields from the event
#[derive(Default)]
pub struct JsonVisitor {
pub fields: serde_json::Map<String, serde_json::Value>,
pub target: Option<String>,
pub file: Option<String>,
pub line: Option<u32>,
}

impl JsonVisitor {
/// Ignores log metadata as it is included in the other LogItem fields (target, file, line...)
fn filter_insert(&mut self, field: &tracing::field::Field, value: serde_json::Value) {
match field.name() {
"log.line" => self.line = value.as_u64().map(|u| u as u32),
"log.target" => self.target = value.as_str().map(ToOwned::to_owned),
"log.file" => self.file = value.as_str().map(ToOwned::to_owned),
"log.module_path" => {}
name => {
self.fields.insert(name.to_string(), json!(value));
}
}
}
}
impl Visit for JsonVisitor {
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
self.filter_insert(field, json!(value));
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
self.filter_insert(field, json!(value));
}
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
self.filter_insert(field, json!(value));
}
fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
self.filter_insert(field, json!(value));
}
fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
self.filter_insert(field, json!(value));
}
fn record_error(
&mut self,
field: &tracing::field::Field,
value: &(dyn std::error::Error + 'static),
) {
self.filter_insert(field, json!(value.to_string()));
}
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
self.filter_insert(field, json!(format!("{value:?}")));
}
}
Loading