<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Application Performance Monitoring (APM)</title> </head> <body><div class="manualnavbar" style="text-align: center;"> <div class="prev" style="text-align: left; float: left;"><a href="mongodb.tutorial.library.html">Using the PHP Library for MongoDB (PHPLIB)</a></div> <div class="next" style="text-align: right; float: right;"><a href="mongodb.architecture.html">Driver Architecture and Internals</a></div> <div class="up"><a href="mongodb.tutorial.html">Tutorials</a></div> <div class="home"><a href="index.html">PHP Manual</a></div> </div><hr /><div id="mongodb.tutorial.apm" class="chapter"> <h1>Application Performance Monitoring (APM)</h1> <p class="para"> Since version 1.3, the MongoDB driver contains an API for performance monitoring. This API allows you to find out how long specific operations take by setting up subscribers. Each subscriber is required to implement one or more interfaces under the <em>MongoDB\Driver\Monitoring</em> namespace. Currently, only the <a href="class.mongodb-driver-monitoring-commandsubscriber.html" class="classname">MongoDB\Driver\Monitoring\CommandSubscriber</a> interface is available. </p> <p class="para"> The <a href="class.mongodb-driver-monitoring-commandsubscriber.html" class="classname">MongoDB\Driver\Monitoring\CommandSubscriber</a> interface defines three methods: <em>commandStarted</em>, <em>commandSucceeded</em>, and <em>commandFailed</em>. Each of these three methods accept a single <code class="parameter">event</code> argument of a specific class for the respective event. For example, the <em>commandSucceeded</em>'s <code class="parameter">$event</code> argument is a <a href="class.mongodb-driver-monitoring-commandsucceededevent.html" class="classname">MongoDB\Driver\Monitoring\CommandSucceededEvent</a> object. </p> <p class="para"> In this tutorial we will implement a subscriber that creates a list of all the query profiles and the average time they took. </p> <div class="section"> <h2 class="title">Subscriber Class Scaffolding</h2> <p class="para"> We start with the framework for our subscriber: </p> <div class="example-contents"> <div class="phpcode"><code><span style="color: #000000"> <span style="color: #0000BB"><?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">QueryTimeCollector </span><span style="color: #007700">implements \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandSubscriber<br /></span><span style="color: #007700">{<br /> public function </span><span style="color: #0000BB">commandStarted</span><span style="color: #007700">( \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandStartedEvent $event </span><span style="color: #007700">)<br /> {<br /> }<br /><br /> public function </span><span style="color: #0000BB">commandSucceeded</span><span style="color: #007700">( \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandSucceededEvent $event </span><span style="color: #007700">)<br /> {<br /> }<br /><br /> public function </span><span style="color: #0000BB">commandFailed</span><span style="color: #007700">( \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandFailedEvent $event </span><span style="color: #007700">)<br /> {<br /> }<br />}<br /><br /></span><span style="color: #0000BB">?></span> </span> </code></div> </div> </div> <div class="section"> <h2 class="title">Registering the Subscriber</h2> <p class="para"> Once a subscriber object is instantiated, it needs to be registered with the driver's monitoring system. This is done by calling <span class="methodname"><a href="function.mongodb.driver.monitoring.addsubscriber.html" class="methodname">MongoDB\Driver\Monitoring\addSubscriber()</a></span>. </p> <div class="example-contents"> <div class="phpcode"><code><span style="color: #000000"> <span style="color: #0000BB"><?php<br /><br /></span><span style="color: #007700">\</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">addSubscriber</span><span style="color: #007700">( new </span><span style="color: #0000BB">QueryTimeCollector</span><span style="color: #007700">() );<br /><br /></span><span style="color: #0000BB">?></span> </span> </code></div> </div> </div> <div class="section"> <h2 class="title">Implementing the Logic</h2> <p class="para"> With the object registered, the only thing left is to implement the logic in the subscriber class. To correlate the two events that make up a successfully executed command (commandStarted and commandSucceeded), each event object exposes a <em>requestId</em> field. </p> <p class="para"> To record the average time per query shape, we will start by checking for a <em>find</em> command in the commandStarted event. We will then add an item to the <em>pendingCommands</em> property indexed by its <em>requestId</em> and with its value representing the query shape. </p> <p class="para"> If we receive a corresponding commandSucceeded event with the same <em>requestId</em>, we add the duration of the event (from <em>durationMicros</em>) to the total time and increment the operation count. </p> <p class="para"> If a corresponding commandFailed event is encountered, we just remove the entry from the <em>pendingCommands</em> property. </p> <div class="example-contents"> <div class="phpcode"><code><span style="color: #000000"> <span style="color: #0000BB"><?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">QueryTimeCollector </span><span style="color: #007700">implements \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandSubscriber<br /></span><span style="color: #007700">{<br /> private </span><span style="color: #0000BB">$pendingCommands </span><span style="color: #007700">= [];<br /> private </span><span style="color: #0000BB">$queryShapeStats </span><span style="color: #007700">= [];<br /><br /> </span><span style="color: #FF8000">/* Creates a query shape out of the filter argument. Right now it only<br /> * takes the top level fields into account */<br /> </span><span style="color: #007700">private function </span><span style="color: #0000BB">createQueryShape</span><span style="color: #007700">( array </span><span style="color: #0000BB">$filter </span><span style="color: #007700">)<br /> {<br /> return </span><span style="color: #0000BB">json_encode</span><span style="color: #007700">( </span><span style="color: #0000BB">array_keys</span><span style="color: #007700">( </span><span style="color: #0000BB">$filter </span><span style="color: #007700">) );<br /> }<br /><br /> public function </span><span style="color: #0000BB">commandStarted</span><span style="color: #007700">( \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandStartedEvent $event </span><span style="color: #007700">)<br /> {<br /> if ( </span><span style="color: #0000BB">array_key_exists</span><span style="color: #007700">( </span><span style="color: #DD0000">'find'</span><span style="color: #007700">, (array) </span><span style="color: #0000BB">$event</span><span style="color: #007700">-></span><span style="color: #0000BB">getCommand</span><span style="color: #007700">() ) )<br /> {<br /> </span><span style="color: #0000BB">$queryShape </span><span style="color: #007700">= </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">createQueryShape</span><span style="color: #007700">( (array) </span><span style="color: #0000BB">$event</span><span style="color: #007700">-></span><span style="color: #0000BB">getCommand</span><span style="color: #007700">()-></span><span style="color: #0000BB">filter </span><span style="color: #007700">);<br /> </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">pendingCommands</span><span style="color: #007700">[</span><span style="color: #0000BB">$event</span><span style="color: #007700">-></span><span style="color: #0000BB">getRequestId</span><span style="color: #007700">()] = </span><span style="color: #0000BB">$queryShape</span><span style="color: #007700">;<br /> }<br /> }<br /><br /> public function </span><span style="color: #0000BB">commandSucceeded</span><span style="color: #007700">( \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandSucceededEvent $event </span><span style="color: #007700">)<br /> {<br /> </span><span style="color: #0000BB">$requestId </span><span style="color: #007700">= </span><span style="color: #0000BB">$event</span><span style="color: #007700">-></span><span style="color: #0000BB">getRequestId</span><span style="color: #007700">();<br /> if ( </span><span style="color: #0000BB">array_key_exists</span><span style="color: #007700">( </span><span style="color: #0000BB">$requestId</span><span style="color: #007700">, </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">pendingCommands </span><span style="color: #007700">) )<br /> {<br /> </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">queryShapeStats</span><span style="color: #007700">[</span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">pendingCommands</span><span style="color: #007700">[</span><span style="color: #0000BB">$requestId</span><span style="color: #007700">]][</span><span style="color: #DD0000">'count'</span><span style="color: #007700">]++;<br /> </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">queryShapeStats</span><span style="color: #007700">[</span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">pendingCommands</span><span style="color: #007700">[</span><span style="color: #0000BB">$requestId</span><span style="color: #007700">]][</span><span style="color: #DD0000">'duration'</span><span style="color: #007700">] += </span><span style="color: #0000BB">$event</span><span style="color: #007700">-></span><span style="color: #0000BB">getDurationMicros</span><span style="color: #007700">();<br /> unset( </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">pendingCommands</span><span style="color: #007700">[</span><span style="color: #0000BB">$requestId</span><span style="color: #007700">] );<br /> }<br /> }<br /><br /> public function </span><span style="color: #0000BB">commandFailed</span><span style="color: #007700">( \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">CommandFailedEvent $event </span><span style="color: #007700">)<br /> {<br /> if ( </span><span style="color: #0000BB">array_key_exists</span><span style="color: #007700">( </span><span style="color: #0000BB">$event</span><span style="color: #007700">-></span><span style="color: #0000BB">getRequestId</span><span style="color: #007700">(), </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">pendingCommands </span><span style="color: #007700">) )<br /> {<br /> unset( </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">pendingCommands</span><span style="color: #007700">[</span><span style="color: #0000BB">$event</span><span style="color: #007700">-></span><span style="color: #0000BB">getRequestId</span><span style="color: #007700">()] );<br /> }<br /> }<br /><br /> public function </span><span style="color: #0000BB">__destruct</span><span style="color: #007700">()<br /> {<br /> foreach( </span><span style="color: #0000BB">$this</span><span style="color: #007700">-></span><span style="color: #0000BB">queryShapeStats </span><span style="color: #007700">as </span><span style="color: #0000BB">$shape </span><span style="color: #007700">=> </span><span style="color: #0000BB">$stats </span><span style="color: #007700">)<br /> {<br /> echo </span><span style="color: #DD0000">"Shape: "</span><span style="color: #007700">, </span><span style="color: #0000BB">$shape</span><span style="color: #007700">, </span><span style="color: #DD0000">" ("</span><span style="color: #007700">, </span><span style="color: #0000BB">$stats</span><span style="color: #007700">[</span><span style="color: #DD0000">'count'</span><span style="color: #007700">], </span><span style="color: #DD0000">")\n "</span><span style="color: #007700">,<br /> </span><span style="color: #0000BB">$stats</span><span style="color: #007700">[</span><span style="color: #DD0000">'duration'</span><span style="color: #007700">] / </span><span style="color: #0000BB">$stats</span><span style="color: #007700">[</span><span style="color: #DD0000">'count'</span><span style="color: #007700">], </span><span style="color: #DD0000">"µs\n\n"</span><span style="color: #007700">;<br /> }<br /> }<br />}<br /><br /></span><span style="color: #0000BB">$m </span><span style="color: #007700">= new \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Manager</span><span style="color: #007700">( </span><span style="color: #DD0000">'mongodb://localhost:27016' </span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">/* Add the subscriber */<br /></span><span style="color: #007700">\</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Monitoring</span><span style="color: #007700">\</span><span style="color: #0000BB">addSubscriber</span><span style="color: #007700">( new </span><span style="color: #0000BB">QueryTimeCollector</span><span style="color: #007700">() );<br /><br /></span><span style="color: #FF8000">/* Do a bunch of queries */<br /></span><span style="color: #0000BB">$query </span><span style="color: #007700">= new \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Query</span><span style="color: #007700">( [<br /> </span><span style="color: #DD0000">'region_slug' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'scotland-highlands'</span><span style="color: #007700">, </span><span style="color: #DD0000">'age' </span><span style="color: #007700">=> [ </span><span style="color: #DD0000">'$gte' </span><span style="color: #007700">=> </span><span style="color: #0000BB">20 </span><span style="color: #007700">]<br />] );<br /></span><span style="color: #0000BB">$cursor </span><span style="color: #007700">= </span><span style="color: #0000BB">$m</span><span style="color: #007700">-></span><span style="color: #0000BB">executeQuery</span><span style="color: #007700">( </span><span style="color: #DD0000">'dramio.whisky'</span><span style="color: #007700">, </span><span style="color: #0000BB">$query </span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">$query </span><span style="color: #007700">= new \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Query</span><span style="color: #007700">( [<br /> </span><span style="color: #DD0000">'region_slug' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'scotland-lowlands'</span><span style="color: #007700">, </span><span style="color: #DD0000">'age' </span><span style="color: #007700">=> [ </span><span style="color: #DD0000">'$gte' </span><span style="color: #007700">=> </span><span style="color: #0000BB">15 </span><span style="color: #007700">]<br />] );<br /></span><span style="color: #0000BB">$cursor </span><span style="color: #007700">= </span><span style="color: #0000BB">$m</span><span style="color: #007700">-></span><span style="color: #0000BB">executeQuery</span><span style="color: #007700">( </span><span style="color: #DD0000">'dramio.whisky'</span><span style="color: #007700">, </span><span style="color: #0000BB">$query </span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">$query </span><span style="color: #007700">= new \</span><span style="color: #0000BB">MongoDB</span><span style="color: #007700">\</span><span style="color: #0000BB">Driver</span><span style="color: #007700">\</span><span style="color: #0000BB">Query</span><span style="color: #007700">( [ </span><span style="color: #DD0000">'region_slug' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'scotland-lowlands' </span><span style="color: #007700">] );<br /></span><span style="color: #0000BB">$cursor </span><span style="color: #007700">= </span><span style="color: #0000BB">$m</span><span style="color: #007700">-></span><span style="color: #0000BB">executeQuery</span><span style="color: #007700">( </span><span style="color: #DD0000">'dramio.whisky'</span><span style="color: #007700">, </span><span style="color: #0000BB">$query </span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?></span> </span> </code></div> </div> </div> </div> <hr /><div class="manualnavbar" style="text-align: center;"> <div class="prev" style="text-align: left; float: left;"><a href="mongodb.tutorial.library.html">Using the PHP Library for MongoDB (PHPLIB)</a></div> <div class="next" style="text-align: right; float: right;"><a href="mongodb.architecture.html">Driver Architecture and Internals</a></div> <div class="up"><a href="mongodb.tutorial.html">Tutorials</a></div> <div class="home"><a href="index.html">PHP Manual</a></div> </div></body></html>