From 9ee6907009c024d647f558ec0c6d866712ef3ab7 Mon Sep 17 00:00:00 2001 From: Petr Heinz Date: Wed, 2 Sep 2026 17:47:14 +0200 Subject: [PATCH] T-16672 Throw on ingesting errors so AWS retries the invocation Co-Authored-By: Claude Fable 5 --- index.js | 3 ++- test/index.test.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 04826eb..f0d2484 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,8 @@ import { parseRecords } from "./src/parser.js" if (!process.env.BETTER_STACK_SOURCE_TOKEN) { throw new Error("Better Stack source token has not been set in ENV variable BETTER_STACK_SOURCE_TOKEN.") } -const options = {} +// Fail the Lambda invocation when logs can't be delivered, so AWS retries it +const options = { throwExceptions: true } if (process.env.BETTER_STACK_ENTRYPOINT) { options.endpoint = process.env.BETTER_STACK_ENTRYPOINT } diff --git a/test/index.test.js b/test/index.test.js index 57c784a..b54ba43 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -125,3 +125,55 @@ test("handler sends gzip-compressed MessagePack logs", async t => { request_id: requestId, }) }) + +test("handler rejects when ingesting fails, so AWS retries the invocation", async t => { + let requestCount = 0 + const server = createServer((request, response) => { + request.on("data", () => {}) + request.on("end", () => { + requestCount++ + response.writeHead(500) + response.end() + }) + }) + + server.listen(0, "127.0.0.1") + await once(server, "listening") + + const previousToken = process.env.BETTER_STACK_SOURCE_TOKEN + const previousEntrypoint = process.env.BETTER_STACK_ENTRYPOINT + const { port } = server.address() + + process.env.BETTER_STACK_SOURCE_TOKEN = "source-token" + process.env.BETTER_STACK_ENTRYPOINT = `http://127.0.0.1:${port}` + + t.after(async () => { + if (previousToken === undefined) { + delete process.env.BETTER_STACK_SOURCE_TOKEN + } else { + process.env.BETTER_STACK_SOURCE_TOKEN = previousToken + } + + if (previousEntrypoint === undefined) { + delete process.env.BETTER_STACK_ENTRYPOINT + } else { + process.env.BETTER_STACK_ENTRYPOINT = previousEntrypoint + } + + await new Promise((resolve, reject) => { + server.close(error => error ? reject(error) : resolve()) + }) + }) + + const event = { source: "unknown" } + const context = { + functionName: "forward-cloudwatch-logs", + invokedFunctionArn: "arn:aws:lambda:eu-west-1:123456789012:function:forward-cloudwatch-logs", + } + + // Query string busts the module cache so the logger is created with this test's entrypoint + const { handler } = await import("../index.js?failing-ingest") + await assert.rejects(handler(event, context)) + + assert.ok(requestCount >= 1) +})