From b14beef342c5c05f94566c1194b7a71fbefbf784 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Sat, 26 Sep 2026 19:00:02 +0200 Subject: [PATCH 1/2] Start log lines with the Lambda request ID Like the logs of Lambda's native runtimes: CloudWatch Logs Insights reads it as the @requestId field, so all the logs of an invocation can be found together with Lambda's START, END and REPORT lines. Claude-Session: https://claude.ai/code/session_0168N55uBK9UjfpL6r8oGjDm --- README.md | 8 ++++++++ src/StderrLogger.php | 7 +++++++ tests/StderrLoggerTest.php | 16 ++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/README.md b/README.md index 8419a1a..11b7fce 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,14 @@ ERROR This is an error {"message":"This is an error","level":"ERROR"} Messages under `info` are not logged. +On AWS Lambda with Bref, each line starts with the request ID of the current invocation, like the logs of Lambda's native runtimes: + +``` +8f507cfc-8b35-4e7e-9f26-f2a3a6e7e1a2 INFO This is an info {"message":"This is an info","level":"INFO"} +``` + +CloudWatch Logs Insights reads it as the `@requestId` field, like in Lambda's `START`, `END` and `REPORT` lines: `filter @requestId = '8f507cfc-...'` shows all the logs of an invocation. + ### Message placeholders [PSR-3 placeholders](https://www.php-fig.org/psr/psr-3/#12-message) can be used to insert information from the `$context` array into the message without having to concatenate strings manually: diff --git a/src/StderrLogger.php b/src/StderrLogger.php index 8fcf25a..844b4e2 100644 --- a/src/StderrLogger.php +++ b/src/StderrLogger.php @@ -82,6 +82,13 @@ public function log($level, $message, array $context = []): void $formattedMessage = sprintf("%s\t%s\t%s\n", strtoupper($level), $displayMessage, $this->toJson($this->normalize($data))); + // Bref sets the ID of the current Lambda invocation. Lambda's own runtimes start their lines with it: + // CloudWatch Logs Insights reads it as `@requestId`, like in Lambda's START, END and REPORT lines. + $requestId = $_SERVER['LAMBDA_REQUEST_ID'] ?? null; + if (is_string($requestId) && $requestId !== '') { + $formattedMessage = "$requestId\t$formattedMessage"; + } + fwrite($this->stream, $formattedMessage); } diff --git a/tests/StderrLoggerTest.php b/tests/StderrLoggerTest.php index 7a74380..40df28a 100644 --- a/tests/StderrLoggerTest.php +++ b/tests/StderrLoggerTest.php @@ -136,6 +136,22 @@ public function test_with_exception() $this->assertStringContainsString('"message":"Test error"', $logs); } + public function test_lines_start_with_the_lambda_request_id() + { + $_SERVER['LAMBDA_REQUEST_ID'] = '8f507cfc-8b35-4e7e-9f26-f2a3a6e7e1a2'; + try { + $this->logger->info('Test message'); + } finally { + unset($_SERVER['LAMBDA_REQUEST_ID']); + } + + $this->assertLogsMatch(<<<'LOGS' +8f507cfc-8b35-4e7e-9f26-f2a3a6e7e1a2 INFO Test message {"message":"Test message","level":"INFO"} + +LOGS + ); + } + private function assertLogsMatch(string $expectedLog): void { rewind($this->stream); From 25a5ebb95bf216afaf479ef28ac1b5121e6b9ecd Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Sat, 26 Sep 2026 20:27:47 +0200 Subject: [PATCH 2/2] Improve README Claude-Session: https://claude.ai/code/session_0168N55uBK9UjfpL6r8oGjDm --- README.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 11b7fce..f636df6 100644 --- a/README.md +++ b/README.md @@ -35,21 +35,15 @@ $logger->warning('This is a warning'); $logger->error('This is an error'); ``` -``` -INFO This is an info {"message":"This is an info","level":"INFO"} -WARNING This is a warning {"message":"This is a warning","level":"WARNING"} -ERROR This is an error {"message":"This is an error","level":"ERROR"} -``` - -Messages under `info` are not logged. - -On AWS Lambda with Bref, each line starts with the request ID of the current invocation, like the logs of Lambda's native runtimes: - ``` 8f507cfc-8b35-4e7e-9f26-f2a3a6e7e1a2 INFO This is an info {"message":"This is an info","level":"INFO"} +8f507cfc-8b35-4e7e-9f26-f2a3a6e7e1a2 WARNING This is a warning {"message":"This is a warning","level":"WARNING"} +8f507cfc-8b35-4e7e-9f26-f2a3a6e7e1a2 ERROR This is an error {"message":"This is an error","level":"ERROR"} ``` -CloudWatch Logs Insights reads it as the `@requestId` field, like in Lambda's `START`, `END` and `REPORT` lines: `filter @requestId = '8f507cfc-...'` shows all the logs of an invocation. +This follows the standard format used by official AWS Lambda runtimes. For example the initial request ID is recognized by CloudWatch so that `filter @requestId = '8f507cfc-...'` shows all the logs of a single request/invocation. + +Messages under `info` are not logged. ### Message placeholders