Instrument CLI Scripts

This docs page explains how to enable instrumentation of PHP CLI scripts, for example Cron jobs, daemons or worker scripts.

Tideways does not automatically instrument the CLI, because the Profiler doesn’t know if the scripts are short or long running, part of a daemon or a one-time script. Additionally CLI scripts usually have a very different latency distribution than web requests and we don’t want to pollute the web monitoring data.

Enabling the Profiler on the CLI

By default Tideways is only running in monitoring mode when activated on the CLI and reports overall performance data and errors/exceptions. If you want to enable tracing on the CLI, then you must set the INI setting:

tideways.enable_cli=1

Be aware that if you have very long running Cron jobs executing thousands of SQL queries or other instrumented libraries, then Tideways might become a significant impact on your PHP memory limit. When Tideways detects the memory limit to cross 90% of the maximum, then it automatically deactivates any currently running trace.

When the INI setting "tideways.auto_start=1" (the default), then Tideways starts automatically on the CLI.

You can also use the programmatic API of the Profiler to start collecting traces and monitoring data on the CLI if you disable auto start for example or need more control over different sub tasks of a long running script that you want to separate into different tasks.

<?php

if (class_exists('Tideways\Profiler')) {
    \Tideways\Profiler::start();
}

The start() method accepts an array of options, including the api_key and sample_rate parameters which we can use to control how much tracing data gets collected from a CLI script. If you don’t pass these options, the INI settings tideways.api_key and tideways.sample_rate are used as defaults.

For workers that run in infinite loops with high throughput, its usually sufficient to use a sample rate of 1-5%. For Cron jobs that are running maybe once or twice a day/hour, it can make sense to set the sample rate to 50% or larger.

You can stop the Profiler after the instrumented code was run:

<?php

if (class_exists('Tideways\Profiler')) {
    \Tideways\Profiler::stop();
}

You don’t need to stop the Profiler, if its a Cron job or one-time script that is not running in an infinite loop and terminates earlier than 30 seconds. In this case stop is automatically executed when the script terminates. But if you run start() multiple times without calling stop() then the currently active trace is discarded and a new trace is started.

Monitoring latency of long running scripts has an upper limit of 30 seconds, everything above is collected as "slower" without a precise information how slow. Traces can run for as long as your PHP script has memory to collect data.

Additionally you should use Tideways\Profiler::setTransactionName() to give the CLI script a unique name for the Tideways UI.

Full example with a one-time script:

<?php

require_once 'vendor/autoload.php';

if (class_exists('Tideways\Profiler')) {
    \Tideways\Profiler::start(array(
        'api_key' => 'key here',
        'sample_rate' => 20,
    ));
    \Tideways\Profiler::setTransactionName('my_script.php');
}

run_your_application();

Instrumenting Long-Running Scripts (Daemons, Worker)

You can only instrument long running scripts with Tideways, when they perform subtasks similar to requests in a sequence, one at a time. Tideways will not work as expected if you have multiple tasks/requests running at the same time, for example when using React-PHP.

Long running use-cases that work with Tideways:

  • A Gearman/Beanstalk/RabbitMQ/Redis worker that executes a single task at a time.

  • A PHP based webserver that executes a single request at a time.

If you have such a worker, instrumentation requires you to call start() and stop() around the execution of each individual task or job. This requires extending your PHP code that is responsible for the infinite loop, fetching the next task from the queue and executing it.

A simple example:

<?php

while ($task = next_queue_item()) {
    // $task is an instance of a specific Task object here.
    if (class_exists('Tideways\Profiler')) {
        \Tideways\Profiler::start(array(
            'api_key' => 'key here',
            'sample_rate' => 20,
            'service' => 'worker',
        ));
        \Tideways\Profiler::setTransactionName(get_class($task));
    }

    try {
        $task->execute();
    } catch (\Throwable $e) {
        if (class_exists('Tideways\Profiler')) {
            \Tideways\Profiler::logException($e);
        }
    } finally {
        if (class_exists('Tideways\Profiler')) {
            \Tideways\Profiler::stop();
        }
    }
}
Still need help? Email [email protected]