Custom Instrumentation

Tideways provides a very flexible extension API to integrate with your applications for the best possible profiling experience.

The following extension points are available:

  1. Detecting Transaction Names
  2. Collecting Timespans
  3. Adding Metadata to Root Timespan
  4. Detecting Exceptions

Detecting Transaction Names

To seperate the profiling results for different endpoints in your applications you can name these Transactions. See the dedicated chapter for using this API.

Collecting Timespans

There are three APIs to collect additional timespans from your application to feed into the Timeline Profiler:

  • Explictly create, start and stop timespans
  • Watching function calls
  • Watching function calls with callback

Tideways collects a single root timespan for every request by default. These timespans are used to calculate the response time charts. You can add more spans to a trace that represent SQL, HTTP or any other kind of operation and see a full trace timeline inside the Tideways UI.

This is the code to create spans during PHP requests you want to trace:

<?php

$span = \Tideways\Profiler::createSpan('sql');
$span->startTimer();
$span->annotate(array('title' => 'insert users'));

$pdo->query('INSERT INTO users (id, name) values (1, "foo")');

$span->stopTimer();

Every span can have multiple start and stop timers, so you can re-use it for traces that share the same SQL query for example:

<?php

$span = \Tideways\Profiler::createSpan('sql');
$span->annotate(array('title' => 'select users'));
$stmt = $pdo->prepare('SELECT * FROM user WHERE id = ?');

foreach ($users as $userId) {
    $span->startTimer();

    $stmt->bindValue(1, $userId);
    $stmt->execute();

    $span->stopTimer();
}

To avoid cluttering your codebase with Tideways Span code you should integrate this deep into your libraries and abstractions for database and http clients.

Annotations are Key-Value pairs of additional information that we can display in the Tideways UI for each span. Annotations are unique per span only not for every start/stop timer.

Sometimes you can't or want to deeply integrate into a library. The Tideways extension offers a simple method to wrap spans around function calls by calling the method tideways_span_watch():

<?php

\Tideways\Profiler::start();

\Tideways\Profiler::watch('Acme\Library::doSomething');

If you have more special needs there is a more powerful function that allows creating spans using a PHP callback:

<?php

\Tideways\Profiler::start();
\Tideways\Profiler::watchCallback(
    'MyTemplateEngine::render',
    function($context) {
        $span = \Tideways\Profiler::createSpan('view');

        $templateName = $context['object']->getTemplateName();
        $span->annotate(array('title' => $templateName));

        return $span->getId();
    }
);

The $context variable has the following keys:

  • $context['fn'] contains the string of the function being called, in the above example MyTemplateEngine::render.
  • $context['object'] is set to the current instance where the method is being called on.
  • $context['args'] contains an list of arguments passed to the called function.

Adding metadata to Root Timespan

Every request has a root timespan in Tideways that measures the total amount of time spent. You can also add metadata to this span to help investigation of root causes for bottlenecks.

The API is very simple, just call \Tideways\Profiler::setCustomVariable($name, $value).

Lets assume we have an endpoint in our application that processes small to large amount of datasets. The number of datasets obviously affects the runtime, so why not display how many where part of the request?

<?php
class ImportController
{
    public function importAction()
    {
        $items = $this->parseItemsFromRequest();

        \Tideways\Profiler::setCustomVariable('imported_items', count($items));

        $this->importItems($items);
    }
}

You don't have to check if the Profiler is currently tracing the full request or just a sample, the internal API knows when to keep or discard the custom variables.

Detecting Exceptions

If your application catches and processes exceptions, then you can help Tideways find those exceptions and collect them. For this task you can pass the name of a function that is called with the Exception as any of the arguments:

function my_exception_handler(\Exception $e) {
    header("HTTP/1.1 500 Internal Server Error");
    exit;
}

\Tideways\Profiler::detectExceptionFunction('my_exception_handler');

This is not necessary to call for frameworks with automatic detection of exceptions.