From e698a789b4a4c6e9d3f15e97a27faf8018791cb8 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Sun, 3 Oct 2021 13:08:24 -0700 Subject: [PATCH] add error handling (with caveat) --- src/lambda.zig | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/lambda.zig b/src/lambda.zig index 48188e7..355646d 100644 --- a/src/lambda.zig +++ b/src/lambda.zig @@ -13,7 +13,7 @@ pub fn run(event_handler: fn (*std.mem.Allocator, []const u8) anyerror![]const u const url = try std.fmt.allocPrint(allocator, "{s}{s}{s}/next", .{ prefix, lambda_runtime_uri, postfix }); defer allocator.free(url); - std.log.info("Event Url: {s}", .{url}); + std.log.notice("Bootstrap initializing with event url: {s}", .{url}); while (true) { var req_alloc = std.heap.ArenaAllocator.init(allocator); @@ -21,9 +21,14 @@ pub fn run(event_handler: fn (*std.mem.Allocator, []const u8) anyerror![]const u const req_allocator = &req_alloc.allocator; var client = try requestz.Client.init(req_allocator); // defer client.deinit(); + // Lambda freezes the process at this line of code. During warm start, + // the process will unfreeze and data will be sent in response to client.get var response = client.get(url, .{}) catch |err| { - // TODO: report error std.log.err("Get fail: {}", .{err}); + // Documentation says something about "exit immediately". The + // Lambda infrastrucutre restarts, so it's unclear if that's necessary. + // It seems as though a continue should be fine, and slightly faster + // std.os.exit(1); continue; }; defer response.deinit(); @@ -32,17 +37,51 @@ pub fn run(event_handler: fn (*std.mem.Allocator, []const u8) anyerror![]const u for (response.headers.items()) |h| { if (std.mem.indexOf(u8, h.name.value, "Lambda-Runtime-Aws-Request-Id")) |_| request_id = h.value; + // TODO: XRay uses an environment variable to do its magic. It's our + // responsibility to set this, but no zig-native setenv(3)/putenv(3) + // exists. I would kind of rather not link in libc for this, + // so we'll hold for now and think on this + // if (std.mem.indexOf(u8, h.name.value, "Lambda-Runtime-Trace-Id")) |_| + // std.process. + // std.os.setenv("AWS_LAMBDA_RUNTIME_API"); } if (request_id == null) { - // TODO: report error + // We can't report back an issue because the runtime error reporting endpoint + // uses request id in its path. So the best we can do is log the error and move + // on here. std.log.err("Could not find request id: skipping request", .{}); continue; } const req_id = request_id.?; const event_response = event_handler(req_allocator, response.body) catch |err| { - // TODO: report error - std.log.err("Error posting response for request id {s}: {}", .{ req_id, err }); + // Stack trace will return null if stripped + const return_trace = @errorReturnTrace(); + std.log.err("Caught error: {}. Return Trace: {}", .{ err, return_trace }); + const err_url = try std.fmt.allocPrint(req_allocator, "{s}{s}/runtime/invocation/{s}/error", .{ prefix, lambda_runtime_uri, req_id }); + defer req_allocator.free(err_url); + const content = + \\ {s} + \\ "errorMessage": "{s}", + \\ "errorType": "HandlerReturnedError", + \\ "stackTrace": [ "{}" ] + \\ {s} + ; + const content_fmt = try std.fmt.allocPrint(req_allocator, content, .{ "{", @errorName(err), return_trace, "}" }); + defer req_allocator.free(content_fmt); + std.log.err("Posting to {s}: Data {s}", .{ err_url, content_fmt }); + var headers = .{.{ "Lambda-Runtime-Function-Error-Type", "HandlerReturned" }}; + // TODO: Determine why this post is not returning + var err_resp = client.post(err_url, .{ + .content = content_fmt, + .headers = headers, + }) catch |post_err| { // Well, at this point all we can do is shout at the void + std.log.err("Error posting response for request id {s}: {}", .{ req_id, post_err }); + std.os.exit(0); + continue; + }; + std.log.err("Post complete", .{}); + defer err_resp.deinit(); continue; }; const response_url = try std.fmt.allocPrint(req_allocator, "{s}{s}{s}/{s}/response", .{ prefix, lambda_runtime_uri, postfix, req_id });