Skip to content

Commit 04b1825

Browse files
authored
Merge pull request #32 from Shopify/error-propagation
feat: structured error logging / propagation
2 parents 81aaff5 + 1a45023 commit 04b1825

File tree

4 files changed

+83
-30
lines changed

4 files changed

+83
-30
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "function-runner"
3-
version = "3.0.0"
3+
version = "3.1.0"
44
edition = "2021"
55

66
[profile.test]

src/engine.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use std::{
55
};
66
use wasmtime::{Config, Engine, Linker, Module, Store};
77

8-
use crate::function_run_result::FunctionRunResult;
8+
use crate::function_run_result::{
9+
FunctionOutput::{self, InvalidJsonOutput, JsonOutput},
10+
FunctionRunResult, InvalidOutput,
11+
};
912

1013
const KB_PER_PAGE: u64 = 64;
1114

@@ -32,6 +35,7 @@ pub fn run(function_path: PathBuf, input_path: PathBuf) -> Result<FunctionRunRes
3235

3336
let runtime: Duration;
3437
let memory_usage: u64;
38+
let mut error_logs: String = String::new();
3539

3640
{
3741
let mut linker = Linker::new(&engine);
@@ -76,24 +80,35 @@ pub fn run(function_path: PathBuf, input_path: PathBuf) -> Result<FunctionRunRes
7680
match module_result {
7781
Ok(_) => {}
7882
Err(e) => {
79-
eprintln!("Error:\n{}", e);
83+
error_logs = e.to_string();
8084
}
8185
}
8286
};
8387

84-
let logs = error_stream
88+
let raw_logs = error_stream
8589
.try_into_inner()
8690
.expect("Error stream reference still exists")
8791
.into_inner();
88-
let logs =
89-
std::str::from_utf8(&logs).map_err(|e| anyhow!("Couldn't print Function Logs: {}", e))?;
92+
let mut logs = std::string::String::from_utf8(raw_logs)
93+
.map_err(|e| anyhow!("Couldn't print Function Logs: {}", e))?;
94+
95+
logs.push_str(&error_logs);
9096

91-
let output = output_stream
97+
let raw_output = output_stream
9298
.try_into_inner()
9399
.expect("Output stream reference still exists")
94100
.into_inner();
95-
let output: serde_json::Value = serde_json::from_slice(output.as_slice())
96-
.map_err(|e| anyhow!("Couldn't decode Function Output: {}", e))?;
101+
102+
let output: FunctionOutput = match serde_json::from_slice(&raw_output) {
103+
Ok(json_output) => JsonOutput(json_output),
104+
Err(error) => InvalidJsonOutput(InvalidOutput {
105+
stdout: std::str::from_utf8(&raw_output)
106+
.map_err(|e| anyhow!("Couldn't print Function Output: {}", e))
107+
.unwrap()
108+
.to_owned(),
109+
error: error.to_string(),
110+
}),
111+
};
97112

98113
let name = function_path.file_name().unwrap().to_str().unwrap();
99114
let size = function_path.metadata()?.len();
@@ -103,8 +118,8 @@ pub fn run(function_path: PathBuf, input_path: PathBuf) -> Result<FunctionRunRes
103118
runtime,
104119
size,
105120
memory_usage,
106-
output,
107121
logs.to_string(),
122+
output,
108123
);
109124

110125
Ok(function_run_result)
@@ -149,9 +164,12 @@ mod tests {
149164
let function_run_result = run(
150165
Path::new("tests/benchmarks/stack_overflow.wasm").to_path_buf(),
151166
Path::new("tests/benchmarks/stack_overflow.json").to_path_buf(),
152-
);
167+
)
168+
.unwrap();
153169

154-
assert!(function_run_result.is_err());
170+
assert!(function_run_result
171+
.logs
172+
.contains("out of bounds memory access"));
155173
}
156174

157175
#[test]

src/function_run_result.rs

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
use colored::Colorize;
2-
use serde::Serialize;
2+
use serde::{Deserialize, Serialize};
33
use std::{fmt, time::Duration};
44

5+
#[derive(Serialize, Deserialize)]
6+
pub struct InvalidOutput {
7+
pub error: String,
8+
pub stdout: String,
9+
}
10+
11+
#[derive(Serialize, Deserialize)]
12+
pub enum FunctionOutput {
13+
JsonOutput(serde_json::Value),
14+
InvalidJsonOutput(InvalidOutput),
15+
}
16+
517
#[derive(Serialize)]
618
pub struct FunctionRunResult {
719
pub name: String,
820
pub runtime: Duration,
921
pub size: u64,
1022
pub memory_usage: u64,
1123
pub logs: String,
12-
pub output: serde_json::Value,
24+
pub output: FunctionOutput,
1325
}
1426

1527
impl FunctionRunResult {
@@ -18,8 +30,8 @@ impl FunctionRunResult {
1830
runtime: Duration,
1931
size: u64,
2032
memory_usage: u64,
21-
output: serde_json::Value,
2233
logs: String,
34+
output: FunctionOutput,
2335
) -> Self {
2436
FunctionRunResult {
2537
name,
@@ -37,26 +49,49 @@ impl FunctionRunResult {
3749
}
3850

3951
impl fmt::Display for FunctionRunResult {
40-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41-
let title = " Benchmark Results ".black().on_bright_green();
42-
write!(f, "{}\n\n", title)?;
43-
writeln!(f, "Name: {}", self.name)?;
44-
writeln!(f, "Runtime: {:?}", self.runtime)?;
45-
writeln!(f, "Memory Usage: {}KB", self.memory_usage)?;
46-
writeln!(f, "Size: {}KB\n", self.size / 1024)?;
52+
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
53+
let title = " Benchmark Results "
54+
.black()
55+
.on_truecolor(150, 191, 72);
56+
write!(formatter, "{}\n\n", title)?;
57+
writeln!(formatter, "Name: {}", self.name)?;
58+
writeln!(formatter, "Runtime: {:?}", self.runtime)?;
59+
writeln!(formatter, "Memory Usage: {}KB", self.memory_usage * 64)?;
60+
writeln!(formatter, "Size: {}KB\n", self.size / 1024)?;
4761

4862
writeln!(
49-
f,
63+
formatter,
5064
"{}\n\n{}",
51-
" Logs ".black().on_bright_blue(),
65+
" Logs ".black().on_bright_blue(),
5266
self.logs
5367
)?;
5468

55-
writeln!(
56-
f,
57-
"Output:\n{}",
58-
serde_json::to_string_pretty(&self.output).unwrap_or_else(|error| error.to_string())
59-
)?;
69+
match &self.output {
70+
FunctionOutput::JsonOutput(json_output) => {
71+
writeln!(
72+
formatter,
73+
"{}\n\n{}",
74+
" Output ".black().on_bright_green(),
75+
serde_json::to_string_pretty(&json_output)
76+
.unwrap_or_else(|error| error.to_string())
77+
)?;
78+
}
79+
FunctionOutput::InvalidJsonOutput(invalid_output) => {
80+
writeln!(
81+
formatter,
82+
"{}\n\n{}",
83+
" Invalid Output ".black().on_bright_red(),
84+
invalid_output.stdout
85+
)?;
86+
87+
writeln!(
88+
formatter,
89+
"{}\n\n{}",
90+
" JSON Error ".black().on_bright_red(),
91+
invalid_output.error
92+
)?;
93+
}
94+
}
6095

6196
Ok(())
6297
}

0 commit comments

Comments
 (0)