Skip to content

Commit

Permalink
PHPLIB-1243: Adapter to pipe ext-mongodb logs to a PSR-3 logger
Browse files Browse the repository at this point in the history
Introduces an internal PsrLogAdapter class to connect PSR-3 loggers with PHPC. The internal class is exposed via addLogger and removeLogger functions.

Bumps ext-mongodb dependency to 1.17-dev for PHPC-2180.

Permit psr/log 1.x, 2.x, or 3.x for compatibility with PHP 7.4 and 8.0+.
  • Loading branch information
jmikola committed Sep 25, 2023
1 parent 05c326f commit eb0edc3
Show file tree
Hide file tree
Showing 9 changed files with 338 additions and 14 deletions.
8 changes: 4 additions & 4 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -766,13 +766,13 @@ axes:
display_name: Driver Version
values:
- id: "oldest-supported"
display_name: "PHPC 1.16.0"
display_name: "PHPC 1.17.0"
variables:
EXTENSION_VERSION: "1.16.0"
EXTENSION_BRANCH: "master"
- id: "latest-stable"
display_name: "PHPC (1.16.x)"
display_name: "PHPC (1.17.x)"
variables:
EXTENSION_VERSION: "stable"
EXTENSION_BRANCH: "master"
- id: "latest-dev"
display_name: "PHPC (1.17-dev)"
variables:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ on:

env:
PHP_VERSION: "8.2"
DRIVER_VERSION: "stable"
DRIVER_VERSION: "mongodb/mongo-php-driver@master"

jobs:
psalm:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coding-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ on:

env:
PHP_VERSION: "8.2"
DRIVER_VERSION: "stable"
DRIVER_VERSION: "mongodb/mongo-php-driver@master"

jobs:
phpcs:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/static-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ on:

env:
PHP_VERSION: "8.2"
DRIVER_VERSION: "stable"
DRIVER_VERSION: "mongodb/mongo-php-driver@master"

jobs:
psalm:
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,34 @@ jobs:
mongodb-version:
- "4.4"
driver-version:
- "stable"
- mongodb/mongo-php-driver@master"
topology:
- "server"
include:
- os: "ubuntu-20.04"
php-version: "8.0"
mongodb-version: "6.0"
driver-version: "stable"
driver-version: "mongodb/mongo-php-driver@master"
topology: "replica_set"
- os: "ubuntu-20.04"
php-version: "8.0"
mongodb-version: "6.0"
driver-version: "stable"
driver-version: "mongodb/mongo-php-driver@master"
topology: "sharded_cluster"
- os: "ubuntu-20.04"
php-version: "8.0"
mongodb-version: "5.0"
driver-version: "stable"
driver-version: "mongodb/mongo-php-driver@master"
topology: "server"
- os: "ubuntu-20.04"
php-version: "8.0"
mongodb-version: "4.4"
driver-version: "stable"
driver-version: "mongodb/mongo-php-driver@master"
topology: "replica_set"
- os: "ubuntu-20.04"
php-version: "8.0"
mongodb-version: "4.4"
driver-version: "stable"
driver-version: "mongodb/mongo-php-driver@master"
topology: "sharded_cluster"

steps:
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"php": "^7.4 || ^8.0",
"ext-hash": "*",
"ext-json": "*",
"ext-mongodb": "^1.16.0",
"ext-mongodb": "^1.17.0",
"jean85/pretty-package-versions": "^2.0.1",
"psr/log": "^1.1.4|^2|^3",
"symfony/polyfill-php80": "^1.27",
"symfony/polyfill-php81": "^1.27"
},
Expand Down
161 changes: 161 additions & 0 deletions src/PsrLogAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php
/*
* Copyright 2023-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace MongoDB;

use MongoDB\Driver\Monitoring\LogSubscriber;
use MongoDB\Exception\UnexpectedValueException;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use SplObjectStorage;

use function MongoDB\Driver\Monitoring\addSubscriber;
use function MongoDB\Driver\Monitoring\removeSubscriber;
use function sprintf;

/**
* Integrates libmongoc/PHPC logging with one or more PSR-3 loggers.
*
* This class is internal and should not be utilized by applications. Logging
* should be configured via the add_logger() and remove_logger() functions.
*
* @internal
*/
final class PsrLogAdapter implements LogSubscriber
{
public const LEVEL_EMERGENCY = 0;
public const LEVEL_ALERT = 1;
public const LEVEL_CRITICAL = 2;

Check failure on line 42 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

OverriddenInterfaceConstant

src/PsrLogAdapter.php:42:18: OverriddenInterfaceConstant: MongoDB\PsrLogAdapter::LEVEL_CRITICAL cannot override constant from MongoDB\Driver\Monitoring\LogSubscriber (see https://psalm.dev/306)

Check failure on line 42 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidClassConstantType

src/PsrLogAdapter.php:42:18: InvalidClassConstantType: The type "2" for MongoDB\PsrLogAdapter::LEVEL_CRITICAL does not satisfy the type "1" inherited from MongoDB\Driver\Monitoring\LogSubscriber::LEVEL_CRITICAL (see https://psalm.dev/309)
public const LEVEL_ERROR = 3;

Check failure on line 43 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

OverriddenInterfaceConstant

src/PsrLogAdapter.php:43:18: OverriddenInterfaceConstant: MongoDB\PsrLogAdapter::LEVEL_ERROR cannot override constant from MongoDB\Driver\Monitoring\LogSubscriber (see https://psalm.dev/306)

Check failure on line 43 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidClassConstantType

src/PsrLogAdapter.php:43:18: InvalidClassConstantType: The type "3" for MongoDB\PsrLogAdapter::LEVEL_ERROR does not satisfy the type "0" inherited from MongoDB\Driver\Monitoring\LogSubscriber::LEVEL_ERROR (see https://psalm.dev/309)
public const LEVEL_WARN = 4;
public const LEVEL_NOTICE = 5;
public const LEVEL_INFO = 6;

Check failure on line 46 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

OverriddenInterfaceConstant

src/PsrLogAdapter.php:46:18: OverriddenInterfaceConstant: MongoDB\PsrLogAdapter::LEVEL_INFO cannot override constant from MongoDB\Driver\Monitoring\LogSubscriber (see https://psalm.dev/306)

Check failure on line 46 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidClassConstantType

src/PsrLogAdapter.php:46:18: InvalidClassConstantType: The type "6" for MongoDB\PsrLogAdapter::LEVEL_INFO does not satisfy the type "4" inherited from MongoDB\Driver\Monitoring\LogSubscriber::LEVEL_INFO (see https://psalm.dev/309)
public const LEVEL_DEBUG = 7;

Check failure on line 47 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

OverriddenInterfaceConstant

src/PsrLogAdapter.php:47:18: OverriddenInterfaceConstant: MongoDB\PsrLogAdapter::LEVEL_DEBUG cannot override constant from MongoDB\Driver\Monitoring\LogSubscriber (see https://psalm.dev/306)

Check failure on line 47 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidClassConstantType

src/PsrLogAdapter.php:47:18: InvalidClassConstantType: The type "7" for MongoDB\PsrLogAdapter::LEVEL_DEBUG does not satisfy the type "5" inherited from MongoDB\Driver\Monitoring\LogSubscriber::LEVEL_DEBUG (see https://psalm.dev/309)
public const LEVEL_TRACE = 8;

private static ?self $instance = null;

/** @psalm-var SplObjectStorage<LoggerInterface, null> */
private SplObjectStorage $loggers;

private const SPEC_TO_PSR = [
self::LEVEL_EMERGENCY => LogLevel::EMERGENCY,
self::LEVEL_ALERT => LogLevel::ALERT,
self::LEVEL_CRITICAL => LogLevel::CRITICAL,
self::LEVEL_ERROR => LogLevel::ERROR,
self::LEVEL_WARN => LogLevel::WARNING,
self::LEVEL_NOTICE => LogLevel::NOTICE,
self::LEVEL_INFO => LogLevel::INFO,
self::LEVEL_DEBUG => LogLevel::DEBUG,
// PSR does not define a "trace" level, so map it to "debug"
self::LEVEL_TRACE => LogLevel::DEBUG,
];

private const MONGOC_TO_PSR = [
LogSubscriber::LEVEL_ERROR => LogLevel::ERROR,
/* libmongoc considers "critical" less severe than "error" so map it to
* "error" in the PSR logger. */
LogSubscriber::LEVEL_CRITICAL => LogLevel::ERROR,
LogSubscriber::LEVEL_WARNING => LogLevel::WARNING,
LogSubscriber::LEVEL_MESSAGE => LogLevel::NOTICE,
LogSubscriber::LEVEL_INFO => LogLevel::INFO,
LogSubscriber::LEVEL_DEBUG => LogLevel::DEBUG,
];

public static function addLogger(LoggerInterface $logger): void
{
$instance = self::getInstance();

$instance->loggers->attach($logger);

addSubscriber($instance);
}

/**
* Forwards a log message from libmongoc/PHPC to all registered PSR loggers.
*
* @see LogSubscriber::log()
*/
public function log(int $mongocLevel, string $domain, string $message): void
{
if (! isset(self::MONGOC_TO_PSR[$mongocLevel])) {
throw new UnexpectedValueException(sprintf(
'Expected level to be >= %d and <= %d, %d given for domain "%s" and message: %s',
LogSubscriber::LEVEL_ERROR,
LogSubscriber::LEVEL_DEBUG,
$mongocLevel,
$domain,
$message,
));
}

$instance = self::getInstance();
$psrLevel = self::MONGOC_TO_PSR[$mongocLevel];
$context = ['domain' => $domain];

foreach ($instance->loggers as $logger) {
$logger->log($psrLevel, $message, $context);
}
}

public static function removeLogger(LoggerInterface $logger): void
{
$instance = self::getInstance();
$instance->loggers->detach($logger);

if ($instance->loggers->count() === 0) {
removeSubscriber($instance);
}
}

/**
* Writes a log message to all registered PSR loggers.
*
* This function is intended for internal use within the library.
*/
public static function writeLog(string $specLevel, string $domain, string $message): void
{
if (! isset(self::SPEC_TO_PSR[$specLevel])) {

Check failure on line 132 in src/PsrLogAdapter.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidArrayOffset

src/PsrLogAdapter.php:132:21: InvalidArrayOffset: Cannot access value on variable MongoDB\PsrLogAdapter::SPEC_TO_PSR using a string offset, expecting int<0, 8> (see https://psalm.dev/115)
throw new UnexpectedValueException(sprintf(
'Expected level to be >= %d and <= %d, %d given for domain "%s" and message: %s',
self::LEVEL_EMERGENCY,
self::LEVEL_TRACE,
$specLevel,
$domain,
$message,
));
}

$instance = self::getInstance();
$psrLevel = self::SPEC_TO_PSR[$specLevel];
$context = ['domain' => $domain];

foreach ($instance->loggers as $logger) {
$logger->log($psrLevel, $message, $context);
}
}

private function __construct()
{
$this->loggers = new SplObjectStorage();
}

private static function getInstance(): self
{
return self::$instance ??= new self();
}
}
23 changes: 23 additions & 0 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use MongoDB\Exception\RuntimeException;
use MongoDB\Operation\ListCollections;
use MongoDB\Operation\WithTransaction;
use Psr\Log\LoggerInterface;
use ReflectionClass;
use ReflectionException;

Expand All @@ -46,6 +47,28 @@
use function MongoDB\BSON\toPHP;
use function substr;

/**
* Registers a PSR-3 logger to receive log messages from the driver/library.
*
* Calling this method again with a logger that has already been added will have
* no effect.
*/
function addLogger(LoggerInterface $logger): void
{
PsrLogAdapter::addLogger($logger);
}

/**
* Unregisters a PSR-3 logger.
*
* Calling this method with a logger that has not been added will have no
* effect.
*/
function removeLogger(LoggerInterface $logger): void
{
PsrLogAdapter::removeLogger($logger);
}

/**
* Check whether all servers support executing a write stage on a secondary.
*
Expand Down
Loading

0 comments on commit eb0edc3

Please sign in to comment.