Skip to content

Commit 3fc343f

Browse files
feat: Add support for Monolog 3.x (#5334)
* fix: Allow `ServiceException` creation with `null` as the message Some tests explicitly set the message to `null` which breaks the creation of parent exceptions with newer PHP versions. Setting a default `$message = ''` does not prevent the problem, so we declare it with `null` being allowed, and check and replace it in the `ServiceException` constructor. The change is made in the `ServiceException` instead of the tests calling it incorrectly to reduce the scope of updated files in the context of the PR that handles the problem. * feat: Enable the usage of `"psr/log": "^2.0|^3.0"` `phpdocumentor/reflection` started supporting `"psr/log": "^2.0|^3.0"` with v5.3. See phpDocumentor/Reflection#247 * feat: Enable the installation of Monolog V3 `psr/log` v1 included a TestLogger that was removed starting with v2. `fig/log-test` support all current versions of `psr/log`. * feat: Introduce V3 versions of AppEngine Formatters and Handlers * feat: Introduce LogMessageProcessors and their factory * add monolog 3 to root composer * remove provide * allow log-test 1.0 * remove log-test in favor of skipping tests * ignore cs warning Co-authored-by: Brent Shaffer <[email protected]>
1 parent 613681f commit 3fc343f

8 files changed

+220
-15
lines changed

composer.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
"yoast/phpunit-polyfills": "^1.0",
1313
"phpspec/prophecy": "^1.10.3",
1414
"squizlabs/php_codesniffer": "2.*",
15-
"phpdocumentor/reflection": "^3.0||^4.0",
15+
"phpdocumentor/reflection": "^3.0||^4.0||^5.3",
1616
"erusev/parsedown": "^1.6",
17+
"fig/log-test": "^1.0",
1718
"google/cloud-storage": "^1.3",
1819
"google/cloud-bigquery": "^1.0",
1920
"google/cloud-pubsub": "^1.0",
@@ -48,8 +49,5 @@
4849
"psr-4": {
4950
"Google\\Cloud\\Logging\\Tests\\": "tests"
5051
}
51-
},
52-
"conflict": {
53-
"psr/log": ">=3"
5452
}
5553
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/**
3+
* Copyright 2022 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Google\Cloud\Logging\LogMessageProcessor;
19+
20+
use Google\Cloud\Logging\LogMessageProcessorInterface;
21+
use Monolog\Formatter\NormalizerFormatter;
22+
use Monolog\Processor\PsrLogMessageProcessor;
23+
24+
/**
25+
* Uses the Monolog 1/2 PsrLogMessageProcessor to process a
26+
* record's message according to PSR-3 rules.
27+
*/
28+
final class MonologMessageProcessor implements LogMessageProcessorInterface
29+
{
30+
/**
31+
* @var NormalizerFormatter
32+
*/
33+
private $formatter;
34+
35+
/**
36+
* @var PsrLogMessageProcessor
37+
*/
38+
private $processor;
39+
40+
public function __construct()
41+
{
42+
$this->formatter = new NormalizerFormatter();
43+
$this->processor = new PsrLogMessageProcessor();
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
public function processLogMessage($message, $context)
50+
{
51+
$processor = $this->processor;
52+
53+
return $processor([
54+
'message' => (string) $message,
55+
'context' => $this->formatter->format($context)
56+
]);
57+
}
58+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* Copyright 2022 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Google\Cloud\Logging\LogMessageProcessor;
19+
20+
use Google\Cloud\Logging\LogMessageProcessorInterface;
21+
use Monolog\Level;
22+
use Monolog\LogRecord;
23+
use Monolog\Processor\PsrLogMessageProcessor;
24+
25+
/**
26+
* Uses the Monolog 3 PsrLogMessageProcessor to process a
27+
* record's message according to PSR-3 rules.
28+
*/
29+
final class MonologV3MessageProcessor implements LogMessageProcessorInterface
30+
{
31+
/**
32+
* @var PsrLogMessageProcessor
33+
*/
34+
private $processor;
35+
36+
public function __construct()
37+
{
38+
$this->processor = new PsrLogMessageProcessor();
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function processLogMessage($message, $context)
45+
{
46+
// The datetime, channel, and level are required but not relevant here
47+
$logRecord = new LogRecord(
48+
new \DateTimeImmutable(),
49+
'channel',
50+
Level::Info,
51+
(string) $message,
52+
$context
53+
);
54+
55+
$processor = $this->processor;
56+
$processed = $processor($logRecord);
57+
58+
return [
59+
'message' => $processed['message'],
60+
'context' => $processed['context'],
61+
];
62+
}
63+
}

src/LogMessageProcessorFactory.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright 2022 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Google\Cloud\Logging;
19+
20+
use Google\Cloud\Logging\LogMessageProcessor\MonologMessageProcessor;
21+
use Google\Cloud\Logging\LogMessageProcessor\MonologV3MessageProcessor;
22+
use Monolog\Logger as MonologLogger;
23+
use RuntimeException;
24+
25+
/**
26+
* Returns a LogMessageProcessor that can be used with the currently install Monolog version
27+
*/
28+
class LogMessageProcessorFactory
29+
{
30+
/**
31+
* @return LogMessageProcessorInterface
32+
*/
33+
public static function build()
34+
{
35+
$version = defined('\Monolog\Logger::API') ? MonologLogger::API : 1;
36+
37+
switch ($version) {
38+
case 1:
39+
case 2:
40+
return new MonologMessageProcessor();
41+
case 3:
42+
return new MonologV3MessageProcessor();
43+
default:
44+
throw new RuntimeException('Version not supported');
45+
}
46+
}
47+
}

src/LogMessageProcessorInterface.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
/**
3+
* Copyright 2022 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Google\Cloud\Logging;
19+
20+
/**
21+
* Interface representing a Log Message Processor used by the {@see PsrLogger}
22+
*/
23+
interface LogMessageProcessorInterface
24+
{
25+
/**
26+
* @param string|\Stringable $message
27+
* @param array $context
28+
*
29+
* @phpstan-return array{message: string, context: array}
30+
* @return array
31+
*/
32+
public function processLogMessage($message, $context);
33+
}

src/PsrLogger.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
use Google\Cloud\Core\Report\MetadataProviderInterface;
2424
use Google\Cloud\Core\Report\MetadataProviderUtils;
2525
use Google\Cloud\Core\Timestamp;
26-
use Monolog\Formatter\NormalizerFormatter;
27-
use Monolog\Processor\PsrLogMessageProcessor;
2826
use Psr\Log\InvalidArgumentException;
2927
use Psr\Log\LoggerInterface;
3028

@@ -89,6 +87,11 @@ class PsrLogger implements LoggerInterface, \Serializable
8987
*/
9088
private $logName;
9189

90+
/**
91+
* @var LogMessageProcessorInterface
92+
*/
93+
private $logMessageProcessor;
94+
9295
/**
9396
* @param Logger $logger The logger used to write entries.
9497
* @param string $messageKey The key in the `jsonPayload` used to contain
@@ -138,6 +141,7 @@ public function __construct(
138141
) {
139142
$this->logger = $logger;
140143
$this->logName = $logger->name();
144+
$this->logMessageProcessor = LogMessageProcessorFactory::build();
141145
$this->messageKey = $messageKey ?: 'message';
142146
$this->metadataProvider = isset($options['metadataProvider'])
143147
? $options['metadataProvider']
@@ -383,12 +387,7 @@ public function log($level, $message, array $context = [])
383387
unset($context['stackdriverOptions']);
384388
}
385389

386-
$formatter = new NormalizerFormatter();
387-
$processor = new PsrLogMessageProcessor();
388-
$processedData = $processor([
389-
'message' => (string) $message,
390-
'context' => $formatter->format($context)
391-
]);
390+
$processedData = $this->logMessageProcessor->processLogMessage($message, $context);
392391
$jsonPayload = [$this->messageKey => $processedData['message']];
393392

394393
// Adding labels for log request correlation.

tests/Unit/PsrLoggerCompatibilityTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424
use Prophecy\Argument;
2525
use Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
2626

27+
// phpcs:disable
28+
if (!class_exists(LoggerInterfaceTest::class)) {
29+
// We have to do this because fig/log-test does not support PHP 7.3 and below,
30+
// but is required when using psr/log v2 (PHP 8.0 and above).
31+
// This means that we cannot add that dependency to require-dev in the root
32+
// composer.json file. As a result, these tests are skipped on PHP 8.0 and above.
33+
return;
34+
}
35+
// phpcs:enable
36+
2737
/**
2838
* @group logging
2939
*/

tests/Unit/PsrLoggerTest.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@
1717

1818
namespace Google\Cloud\Logging\Tests\Unit;
1919

20-
use Google\Cloud\Core\Batch\BatchRunner;
21-
use Google\Cloud\Core\Batch\OpisClosureSerializer;
2220
use Google\Cloud\Core\Report\EmptyMetadataProvider;
2321
use Google\Cloud\Logging\Logger;
2422
use Google\Cloud\Logging\PsrLogger;
2523
use Google\Cloud\Logging\Connection\ConnectionInterface;
26-
use Prophecy\Argument;
2724
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
2825
use Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
2926

0 commit comments

Comments
 (0)