Skip to content

Commit ee99079

Browse files
authored
ext-mongodb 2.0 support (open-telemetry#324)
Being able to retrieve host, port + info from a CommandStartedEvent is deprecated in 1.20.0 and will be removed in 2.0. we have been advised to use SDAM subscription instead. - update tests to allow defining mongo server from env - require ext-mongodb 1.13 (which provides SDAM subscriber for server info) - use SDAM subscriber to get server info attributes. Subscribe to the serverChanged event and store the attributes in a class variable, organised by host/port. - document mongo todos
1 parent f892be9 commit ee99079

File tree

6 files changed

+117
-21
lines changed

6 files changed

+117
-21
lines changed

docker-compose.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ services:
1414
PHP_IDE_CONFIG: ${PHP_IDE_CONFIG:-''}
1515
RABBIT_HOST: ${RABBIT_HOST:-rabbitmq}
1616
KAFKA_HOST: ${KAFKA_HOST:-kafka}
17+
MONGODB_HOST: ${MONGODB_HOST:-mongodb}
18+
MONGODB_PORT: ${MONGODB_PORT:-27017}
1719
MYSQL_HOST: ${MYSQL_HOST:-mysql}
1820

19-
2021
zipkin:
2122
image: openzipkin/zipkin-slim
2223
ports:
@@ -63,6 +64,12 @@ services:
6364
volumes:
6465
- ./docker/kafka/update_run.sh:/tmp/update_run.sh
6566

67+
mongodb:
68+
image: mongo:4
69+
hostname: mongodb
70+
ports:
71+
- "27017:27017/tcp"
72+
6673
mysql:
6774
image: mysql:8.0
6875
hostname: mysql

src/Instrumentation/MongoDB/composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
"homepage": "https://opentelemetry.io/docs/php",
77
"readme": "./README.md",
88
"license": "Apache-2.0",
9-
"minimum-stability": "dev",
109
"require": {
1110
"php": ">=7.4",
12-
"ext-mongodb": "*",
11+
"ext-mongodb": "^1.13",
1312
"ext-json": "*",
1413
"mongodb/mongodb": "^1.15",
1514
"open-telemetry/api": "^1.0",
@@ -40,7 +39,8 @@
4039
},
4140
"config": {
4241
"allow-plugins": {
43-
"php-http/discovery": false
42+
"php-http/discovery": false,
43+
"tbachert/spi": false
4444
}
4545
}
4646
}

src/Instrumentation/MongoDB/phpstan.neon.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ parameters:
77
paths:
88
- src
99
- tests
10+
ignoreErrors:
11+
-
12+
message: "#Call to an undefined method .*#"
13+
paths:
14+
- src/MongoDBInstrumentationSubscriber.php

src/Instrumentation/MongoDB/src/MongoDBInstrumentationSubscriber.php

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
use MongoDB\Driver\Monitoring\CommandStartedEvent;
1010
use MongoDB\Driver\Monitoring\CommandSubscriber;
1111
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
12+
use MongoDB\Driver\Monitoring\SDAMSubscriber;
13+
use MongoDB\Driver\Monitoring\ServerChangedEvent;
14+
use MongoDB\Driver\Monitoring\ServerClosedEvent;
15+
use MongoDB\Driver\Monitoring\ServerHeartbeatFailedEvent;
16+
use MongoDB\Driver\Monitoring\ServerHeartbeatStartedEvent;
17+
use MongoDB\Driver\Monitoring\ServerHeartbeatSucceededEvent;
18+
use MongoDB\Driver\Monitoring\ServerOpeningEvent;
19+
use MongoDB\Driver\Monitoring\TopologyChangedEvent;
20+
use MongoDB\Driver\Monitoring\TopologyClosedEvent;
21+
use MongoDB\Driver\Monitoring\TopologyOpeningEvent;
1222
use OpenTelemetry\API\Instrumentation\CachedInstrumentation;
1323
use OpenTelemetry\API\Trace\Span;
1424
use OpenTelemetry\API\Trace\SpanBuilderInterface;
@@ -18,13 +28,17 @@
1828
use OpenTelemetry\SemConv\TraceAttributes;
1929
use Throwable;
2030

21-
final class MongoDBInstrumentationSubscriber implements CommandSubscriber
31+
final class MongoDBInstrumentationSubscriber implements CommandSubscriber, SDAMSubscriber
2232
{
2333
private CachedInstrumentation $instrumentation;
2434
/**
2535
* @var Closure(object):?string
2636
*/
2737
private Closure $commandSerializer;
38+
/**
39+
* @var array<string, array<int, array<string, mixed>>>
40+
*/
41+
private array $serverAttributes = [];
2842

2943
/**
3044
* @param (callable(object):?string) $commandSerializer
@@ -41,16 +55,26 @@ public function __construct(CachedInstrumentation $instrumentation, callable $co
4155
};
4256
}
4357

58+
/**
59+
* @psalm-suppress MixedAssignment,MixedArrayTypeCoercion,MixedArrayOffset,MixedArgument
60+
*/
4461
public function commandStarted(CommandStartedEvent $event): void
4562
{
4663
$command = $event->getCommand();
4764
$collectionName = MongoDBCollectionExtractor::extract($command);
4865
$databaseName = $event->getDatabaseName();
4966
$commandName = $event->getCommandName();
50-
$server = $event->getServer();
51-
$info = $server->getInfo();
52-
$port = $server->getPort();
53-
$host = $server->getHost();
67+
/** @phpstan-ignore-next-line */
68+
if (version_compare(phpversion('mongodb'), '1.20.0', '>=')) {
69+
$host = $event->getHost();
70+
$port = $event->getPort();
71+
} else {
72+
$server = $event->getServer();
73+
$host = $server->getHost();
74+
$port = $server->getPort();
75+
}
76+
$attributes = $this->serverAttributes[$host][$port] ?? [];
77+
5478
$isSocket = str_starts_with($host, '/');
5579
/** @psalm-suppress RiskyTruthyFalsyComparison **/
5680
$scopedCommand = ($collectionName ? $collectionName . '.' : '') . $commandName;
@@ -65,17 +89,10 @@ public function commandStarted(CommandStartedEvent $event): void
6589
->setAttribute(TraceAttributes::NETWORK_TRANSPORT, $isSocket ? 'unix' : 'tcp')
6690
->setAttribute(TraceAttributes::DB_STATEMENT, ($this->commandSerializer)($command))
6791
->setAttribute(TraceAttributes::DB_MONGODB_COLLECTION, $collectionName)
68-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_MASTER, $info['ismaster'] ?? null)
69-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_READ_ONLY, $info['readOnly'] ?? null)
70-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_CONNECTION_ID, $info['connectionId'] ?? null)
7192
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_REQUEST_ID, $event->getRequestId())
7293
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_OPERATION_ID, $event->getOperationId())
73-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_MAX_WIRE_VERSION, $info['maxWireVersion'] ?? null)
74-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_MIN_WIRE_VERSION, $info['minWireVersion'] ?? null)
75-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_MAX_BSON_OBJECT_SIZE_BYTES, $info['maxBsonObjectSize'] ?? null)
76-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_MAX_MESSAGE_SIZE_BYTES, $info['maxMessageSizeBytes'] ?? null)
77-
->setAttribute(MongoDBTraceAttributes::DB_MONGODB_MAX_WRITE_BATCH_SIZE, $info['maxWriteBatchSize'] ?? null);
78-
94+
->setAttributes($attributes)
95+
;
7996
$parent = Context::getCurrent();
8097
$span = $builder->startSpan();
8198
Context::storage()->attach($span->storeInContext($parent));
@@ -118,4 +135,61 @@ private static function endSpan(?Throwable $exception = null): void
118135

119136
$span->end();
120137
}
138+
139+
/**
140+
* @todo In a load-balanced scenario, the hello response may be empty.
141+
*/
142+
public function serverChanged(ServerChangedEvent $event): void
143+
{
144+
$host = $event->getHost();
145+
$port = $event->getPort();
146+
$info = $event->getNewDescription()->getHelloResponse();
147+
$attributes = [
148+
MongoDBTraceAttributes::DB_MONGODB_MASTER => $info['ismaster'] ?? null,
149+
MongoDBTraceAttributes::DB_MONGODB_READ_ONLY => $info['readOnly'] ?? null,
150+
MongoDBTraceAttributes::DB_MONGODB_CONNECTION_ID => $info['connectionId'] ?? null,
151+
MongoDBTraceAttributes::DB_MONGODB_MAX_WIRE_VERSION => $info['maxWireVersion'] ?? null,
152+
MongoDBTraceAttributes::DB_MONGODB_MIN_WIRE_VERSION => $info['minWireVersion'] ?? null,
153+
MongoDBTraceAttributes::DB_MONGODB_MAX_BSON_OBJECT_SIZE_BYTES => $info['maxBsonObjectSize'] ?? null,
154+
MongoDBTraceAttributes::DB_MONGODB_MAX_MESSAGE_SIZE_BYTES => $info['maxMessageSizeBytes'] ?? null,
155+
MongoDBTraceAttributes::DB_MONGODB_MAX_WRITE_BATCH_SIZE => $info['maxWriteBatchSize'] ?? null,
156+
];
157+
$this->serverAttributes[$host][$port] = $attributes;
158+
}
159+
160+
public function serverOpened(ServerOpeningEvent $event): void
161+
{
162+
}
163+
164+
public function serverClosed(ServerClosedEvent $event): void
165+
{
166+
}
167+
168+
public function serverOpening(ServerOpeningEvent $event): void
169+
{
170+
}
171+
172+
public function serverHeartbeatFailed(ServerHeartbeatFailedEvent $event): void
173+
{
174+
}
175+
176+
public function serverHeartbeatStarted(ServerHeartbeatStartedEvent $event): void
177+
{
178+
}
179+
180+
public function serverHeartbeatSucceeded(ServerHeartbeatSucceededEvent $event): void
181+
{
182+
}
183+
184+
public function topologyChanged(TopologyChangedEvent $event): void
185+
{
186+
}
187+
188+
public function topologyClosed(TopologyClosedEvent $event): void
189+
{
190+
}
191+
192+
public function topologyOpening(TopologyOpeningEvent $event): void
193+
{
194+
}
121195
}

src/Instrumentation/MongoDB/src/MongoDBTraceAttributes.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
namespace OpenTelemetry\Contrib\Instrumentation\MongoDB;
66

7+
/**
8+
* @todo These attributes are not part of the specification and should be removed before this package goes stable,
9+
* unless they are on the way to being added to the specification. See https://github.com/open-telemetry/opentelemetry-specification/blob/v1.40.0/specification/telemetry-stability.md
10+
*/
711
interface MongoDBTraceAttributes
812
{
913
public const DB_MONGODB_MASTER = 'db.mongodb.master';

src/Instrumentation/MongoDB/tests/Integration/MongoDBInstrumentationTest.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,19 @@ class MongoDBInstrumentationTest extends TestCase
2222
{
2323
private const DATABASE_NAME = 'db';
2424
private const COLLECTION_NAME = 'coll';
25+
private string $host;
26+
private int $port;
27+
private string $uri;
2528
private ScopeInterface $scope;
2629
/** @var ArrayObject<int,ImmutableSpan> */
2730
private ArrayObject $storage;
2831
private ?ImmutableSpan $span = null;
2932

3033
public function setUp(): void
3134
{
35+
$this->host = $_SERVER['MONGODB_HOST'] ?? '127.0.0.1';
36+
$this->port = (int) ($_SERVER['MONGODB_PORT'] ?? 27017);
37+
$this->uri = "mongodb://$this->host:$this->port";
3238
/** @psalm-suppress MixedPropertyTypeCoercion */
3339
$this->storage = new ArrayObject();
3440
$tracerProvider = new TracerProvider(
@@ -49,7 +55,7 @@ public function tearDown(): void
4955

5056
public function test_mongodb_find_one(): void
5157
{
52-
$manager = new Manager('mongodb://127.0.0.1:27017');
58+
$manager = new Manager($this->uri);
5359

5460
$find = new FindOne(self::DATABASE_NAME, self::COLLECTION_NAME, ['a' => 'b']);
5561

@@ -67,8 +73,8 @@ public function test_mongodb_find_one(): void
6773
self::assertSame(self::DATABASE_NAME, $attributes->get(TraceAttributes::DB_NAME));
6874
self::assertSame('find', $attributes->get(TraceAttributes::DB_OPERATION));
6975
self::assertSame(self::COLLECTION_NAME, $attributes->get(TraceAttributes::DB_MONGODB_COLLECTION));
70-
self::assertSame('127.0.0.1', $attributes->get(TraceAttributes::SERVER_ADDRESS));
71-
self::assertSame(27017, $attributes->get(TraceAttributes::SERVER_PORT));
76+
self::assertSame($this->host, $attributes->get(TraceAttributes::SERVER_ADDRESS));
77+
self::assertSame($this->port, $attributes->get(TraceAttributes::SERVER_PORT));
7278
self::assertSame('tcp', $attributes->get(TraceAttributes::NETWORK_TRANSPORT));
7379
self::assertTrue($attributes->get(MongoDBTraceAttributes::DB_MONGODB_MASTER));
7480
self::assertFalse($attributes->get(MongoDBTraceAttributes::DB_MONGODB_READ_ONLY));

0 commit comments

Comments
 (0)