From 197f534d42591eb32ebfee60e9c92b5192be39a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Fri, 22 Jul 2022 13:04:51 +0200 Subject: [PATCH] Add basic support to Google Cloud Logging format (#1719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Google Cloud Logging doesn't show the correct level log when using JsonFormatter, making observability a bit trickier. This applies minor tweaks to the default format, allowing log entries to be properly represented. There are alternative packages to this but they add fields that aren't strictly required - also performing `debug_backtrace()` calls that are usually not desired when in production mode. This is a backport of https://github.com/Seldaek/monolog/pull/1690 Signed-off-by: Luís Cobucci --- .../Formatter/GoogleCloudLoggingFormatter.php | 39 ++++++++++++++ .../GoogleCloudLoggingFormatterTest.php | 54 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/Monolog/Formatter/GoogleCloudLoggingFormatter.php create mode 100644 tests/Monolog/Formatter/GoogleCloudLoggingFormatterTest.php diff --git a/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php b/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php new file mode 100644 index 00000000..0cd287f5 --- /dev/null +++ b/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use DateTimeInterface; +use Monolog\LogRecord; + +/** + * Encodes message information into JSON in a format compatible with Cloud logging. + * + * @see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry + * + * @author Luís Cobucci + */ +final class GoogleCloudLoggingFormatter extends JsonFormatter +{ + /** {@inheritdoc} **/ + public function format(array $record): string + { + // Re-key level for GCP logging + $record['severity'] = $record['level_name']; + $record['timestamp'] = $record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED); + + // Remove keys that are not used by GCP + unset($record['level'], $record['level_name'], $record['datetime']); + + return parent::format($record); + } +} + diff --git a/tests/Monolog/Formatter/GoogleCloudLoggingFormatterTest.php b/tests/Monolog/Formatter/GoogleCloudLoggingFormatterTest.php new file mode 100644 index 00000000..27aea2ca --- /dev/null +++ b/tests/Monolog/Formatter/GoogleCloudLoggingFormatterTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use DateTimeInterface; +use Monolog\Test\TestCase; +use function json_decode; + +class GoogleCloudLoggingFormatterTest extends TestCase +{ + /** + * @test + * + * @covers \Monolog\Formatter\JsonFormatter + * @covers \Monolog\Formatter\GoogleCloudLoggingFormatter::format + */ + public function formatProvidesRfc3339Timestamps(): void + { + $formatter = new GoogleCloudLoggingFormatter(); + $record = $this->getRecord(); + + $formatted_decoded = json_decode($formatter->format($record), true); + $this->assertArrayNotHasKey("datetime", $formatted_decoded); + $this->assertArrayHasKey("timestamp", $formatted_decoded); + $this->assertSame($record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED), $formatted_decoded["timestamp"]); + } + + /** + * @test + * + * @covers \Monolog\Formatter\JsonFormatter + * @covers \Monolog\Formatter\GoogleCloudLoggingFormatter::format + */ + public function formatIntroducesLogSeverity(): void + { + $formatter = new GoogleCloudLoggingFormatter(); + $record = $this->getRecord(); + + $formatted_decoded = json_decode($formatter->format($record), true); + $this->assertArrayNotHasKey("level", $formatted_decoded); + $this->assertArrayNotHasKey("level_name", $formatted_decoded); + $this->assertArrayHasKey("severity", $formatted_decoded); + $this->assertSame($record['level_name'], $formatted_decoded["severity"]); + } +}