Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHPLIB-1309 Add addSubscriber/removeSubscriber to Client class to ease configuration #1195

Merged
merged 4 commits into from Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,7 @@
# CHANGELOG


## 1.18.0 (unreleased)

* Add `addSubscriber` and `removeSubscriber` methods to the `Client` class to ease dependency injection configuration

@@ -0,0 +1,9 @@
arg_name: param
name: $subscriber
type: :php:`MongoDB\\Driver\\Monitoring\\Subscriber <class.mongodb-driver-monitoring-subscriber>`
description: |
A monitoring event subscriber to register with this Client.
interface: phpmethod
operation: ~
optional: false
...
@@ -0,0 +1,9 @@
arg_name: param
name: $subscriber
type: :php:`MongoDB\\Driver\\Monitoring\\Subscriber <class.mongodb-driver-monitoring-subscriber>`
description: |
A monitoring event subscriber to unregister with this Client.
interface: phpmethod
operation: ~
optional: false
...
2 changes: 2 additions & 0 deletions docs/reference/class/MongoDBClient.txt
Expand Up @@ -30,6 +30,7 @@ Methods

/reference/method/MongoDBClient__construct
/reference/method/MongoDBClient__get
/reference/method/MongoDBClient-addSubscriber
/reference/method/MongoDBClient-createClientEncryption
/reference/method/MongoDBClient-dropDatabase
/reference/method/MongoDBClient-getManager
Expand All @@ -39,6 +40,7 @@ Methods
/reference/method/MongoDBClient-getWriteConcern
/reference/method/MongoDBClient-listDatabaseNames
/reference/method/MongoDBClient-listDatabases
/reference/method/MongoDBClient-removeSubscriber
/reference/method/MongoDBClient-selectCollection
/reference/method/MongoDBClient-selectDatabase
/reference/method/MongoDBClient-startSession
Expand Down
132 changes: 132 additions & 0 deletions docs/reference/method/MongoDBClient-addSubscriber.txt
@@ -0,0 +1,132 @@
================================
MongoDB\\Client::addSubscriber()
================================

.. versionadded:: 1.18

.. default-domain:: mongodb
GromNaN marked this conversation as resolved.
Show resolved Hide resolved

.. contents:: On this page
:local:
:backlinks: none
:depth: 1
:class: singlecol

Definition
----------

.. phpmethod:: MongoDB\\Client::addSubscriber()

Registers a monitoring event subscriber with this Client. The subscriber
will be notified of all events for this Client.

.. code-block:: php

function addSubscriber(MongoDB\Driver\Monitoring\Subscriber $subscriber): void

This method has the following parameters:

.. include:: /includes/apiargs/MongoDBClient-method-addSubscriber-param.rst

.. note::

If ``$subscriber`` is already registered with this Client, this function
is a no-op. If ``$subscriber`` is also registered globally, it will still
only be notified once of each event for this Client.

Errors/Exceptions
-----------------

.. include:: /includes/extracts/error-invalidargumentexception.rst
GromNaN marked this conversation as resolved.
Show resolved Hide resolved

:php:`MongoDB\\Driver\\Exception\\InvalidArgumentException <mongodb-driver-exception-invalidargumentexception>`
if subscriber is a :php:`MongoDB\\Driver\\Monitoring\\LogSubscriber <class.mongodb-driver-monitoring-logsubscriber>`,
since loggers can only be registered globally with
:php:`MongoDB\\Driver\\Monitoring\\addSubscriber <function.mongodb.driver.monitoring.addsubscriber>`.

Example
-------

Create a :phpclass:`MongoDB\\Driver\\Monitoring\\CommandSubscriber` that
logs all events:
Comment on lines +50 to +51
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created this example as the example on Application Performance Monitoring (APM) seemed complicated to me

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have example code in the library for an APM subscriber.

It'd make more sense to create a tutorial page that includes APM code alongside surrounding documentation, as we've done for codecs and encryption. That sounds like a separate ticket, so you can just make a note there to come back and add a link to the tutorial to this method as a "See Also" link.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I update the example to use the client method. Here is the ticket for the tutorial: https://jira.mongodb.org/browse/PHPLIB-1314


.. code-block:: php

<?php

use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Monitoring\CommandStartedEvent;
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
use MongoDB\Driver\Monitoring\CommandFailedEvent;

class LogCommandSubscriber implements CommandSubscriber
{
private $stream;
public function __construct($stream)
{
$this->stream = $stream;
}

public function commandStarted(CommandStartedEvent $event): void
{
fwrite($this->stream, sprintf(
'Started command #%d "%s": %s%s',
$event->getRequestId(),
$event->getCommandName(),
Document::fromPHP($event->getCommand())->toCanonicalExtendedJSON(),
PHP_EOL,
));
}

public function commandSucceeded(CommandSucceededEvent $event): void
{
fwrite($this->stream, sprintf(
'Succeeded command #%d "%s" in %d microseconds: %s%s',
$event->getRequestId(),
$event->getCommandName(),
$event->getDurationMicros(),
json_encode($event->getReply()),
PHP_EOL,
));
}

public function commandFailed(CommandFailedEvent $event): void
{
fwrite($this->stream, sprintf(
'Failed command #%d "%s" in %d microseconds: %s%s',
$event->getRequestId(),
$event->getCommandName(),
$event->getDurationMicros(),
$event->getError()->getMessage(),
PHP_EOL,
));
}
}

The subscriber can then be registered with a Client:

.. code-block:: php

<?php

$client = new MongoDB\Client();
$subscriber = new LogCommandSubscriber(STDERR);

$client->addSubscriber($subscriber);

$client->test->users->insertOne(['username' => 'alice']);

The above code will write the following to stderr output:

.. code-block:: text

Started command #1 "insert": { "insert" : "users", "ordered" : true, "$db" : "test", "lsid" : { "id" : { "$binary" : { "base64" : "dKTBhZD7Qvi0vUhvR58mCA==", "subType" : "04" } } }, "documents" : [ { "username" : "alice", "_id" : { "$oid" : "655d1fca12e81018340a4fc2" } } ] }
Succeeded command #1 "insert" in 876 microseconds: {"n":1,"ok":1}

See Also
--------

- :phpmethod:`MongoDB\\Client::removeSubscriber()`
- :php:`Application Performance Monitoring (APM) <manual/en/mongodb.tutorial.apm>`
- :php:`MongoDB\\Driver\\Manager::addSubscriber() <manual/en/mongodb-driver-manager.addsubscriber>`
- :php:`MongoDB\Driver\Monitoring\CommandSubscriber <manual/en/class.mongodb-driver-monitoring-commandsubscriber>`
46 changes: 46 additions & 0 deletions docs/reference/method/MongoDBClient-removeSubscriber.txt
@@ -0,0 +1,46 @@
================================
MongoDB\\Client::removeSubscriber()
================================

.. versionadded:: 1.18

.. default-domain:: mongodb
GromNaN marked this conversation as resolved.
Show resolved Hide resolved

.. contents:: On this page
:local:
:backlinks: none
:depth: 1
:class: singlecol

Definition
----------

.. phpmethod:: MongoDB\\Client::removeSubscriber()

Unregisters a monitoring event subscriber with this Client.

.. code-block:: php

function removeSubscriber(MongoDB\Driver\Monitoring\Subscriber $subscriber): void

This method has the following parameters:

.. include:: /includes/apiargs/MongoDBClient-method-removeSubscriber-param.rst

.. note::

If ``$subscriber`` is not registered with this Client, this function
is a no-op.

Errors/Exceptions
-----------------

.. include:: /includes/extracts/error-invalidargumentexception.rst

See Also
--------

- :phpmethod:`MongoDB\\Client::addSubscriber()`
- :php:`Application Performance Monitoring (APM) <manual/en/mongodb.tutorial.apm>`
- :php:`MongoDB\\Driver\\Manager::removeSubscriber() <manual/en/mongodb-driver-manager.addsubscriber>`
- :php:`MongoDB\Driver\Monitoring\CommandSubscriber <manual/en/class.mongodb-driver-monitoring-commandsubscriber>`
2 changes: 1 addition & 1 deletion examples/command_logger.php
Expand Up @@ -56,7 +56,7 @@ public function commandFailed(CommandFailedEvent $event): void

$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/');

$client->getManager()->addSubscriber(new CommandLogger());
$client->addSubscriber(new CommandLogger());

$collection = $client->test->command_logger;
$collection->drop();
Expand Down
21 changes: 21 additions & 0 deletions src/Client.php
Expand Up @@ -23,6 +23,7 @@
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Monitoring\Subscriber;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Session;
Expand Down Expand Up @@ -163,6 +164,16 @@ public function __toString()
return $this->uri;
}

/**
* Registers a monitoring event subscriber with this Client's Manager
*
* @see Manager::addSubscriber()
*/
final public function addSubscriber(Subscriber $subscriber): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create documentation for the new methods. There should be enough prior art to copy content from other methods. Also, let's start at CHANGELOG-1.18.md file in the root directory of the project.

{
$this->manager->addSubscriber($subscriber);
}

/**
* Returns a ClientEncryption instance for explicit encryption and decryption
*
Expand Down Expand Up @@ -296,6 +307,16 @@ public function listDatabases(array $options = [])
return $operation->execute($server);
}

/**
* Unregisters a monitoring event subscriber with this Client's Manager
*
* @see Manager::removeSubscriber()
*/
final public function removeSubscriber(Subscriber $subscriber): void
{
$this->manager->removeSubscriber($subscriber);
}

/**
* Select a collection.
*
Expand Down
18 changes: 18 additions & 0 deletions tests/ClientFunctionalTest.php
Expand Up @@ -4,7 +4,9 @@

use MongoDB\Client;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Command;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Session;
use MongoDB\Model\DatabaseInfo;
use MongoDB\Model\DatabaseInfoIterator;
Expand Down Expand Up @@ -119,4 +121,20 @@ public function testStartSession(): void
{
$this->assertInstanceOf(Session::class, $this->client->startSession());
}

public function testAddAndRemoveSubscriber(): void
{
$client = new Client(static::getUri());

$addedSubscriber = $this->createMock(CommandSubscriber::class);
$addedSubscriber->expects($this->once())->method('commandStarted');
$client->addSubscriber($addedSubscriber);

$removedSubscriber = $this->createMock(CommandSubscriber::class);
$removedSubscriber->expects($this->never())->method('commandStarted');
$client->addSubscriber($removedSubscriber);
$client->removeSubscriber($removedSubscriber);

$client->getManager()->executeCommand('admin', new Command(['ping' => 1]));
}
}