Skip to content

CakePHP Rhythm Plugin ​

Real-time application performance monitoring for CakePHP 5.x applications with comprehensive metric collection, storage, and visualization.

Features ​

Core Monitoring ​

  • Server Performance Monitoring - CPU, memory, disk usage, and system metrics
  • HTTP Request Tracking - Response times, status codes, and user request patterns
  • Database Query Performance - Slow query detection with configurable thresholds
  • Queue System Monitoring - Job processing, queue statistics, and performance metrics
  • Exception Tracking - Application exception monitoring and reporting
  • Outgoing Request Monitoring - External API calls and HTTP client performance
  • Cache Performance - Cache hit rates and performance metrics
  • Job Performance - Background job execution times and statistics

Advanced Features ​

  • Multi-Server Support - Monitor multiple servers with unified dashboard
  • Configurable Sampling - Adjustable sampling rates for different metric types
  • Grouping System - Automatic grouping of similar metrics to reduce cardinality
  • Real-time Dashboard - Live performance monitoring with interactive widgets
  • Flexible Storage - Database and Redis storage options with automatic aggregation
  • Event System - CakePHP event integration for custom metric recording
  • Command Line Tools - CLI commands for metric processing and server monitoring

Installation ​

Install the plugin via Composer:

bash
composer require crustum/rhythm

NOTE

This plugin should be registered in your config/plugins.php file.

bash
bin/cake plugin load Crustum/Rhythm

Or load the plugin in your Application.php:

php
public function bootstrap(): void
{
    parent::bootstrap();

    $this->addPlugin('Crustum/Rhythm', ['bootstrap' => true, 'routes' => true]);
}

TIP

After the plugin registers itself, it's recommended to install the configuration with the manifest system:

bash
bin/cake manifest install --plugin Crustum/Rhythm

The Rhythm plugin will create the config/rhythm.php configuration file and the config/rhythm_layouts.php file where you may register your application's widgets and layouts. Additionally, it will copy the migrations to the application's migrations directory and append the loading of the Rhythm configuration to the config/bootstrap.php file.

Database Migrations ​

Without manifest installation (plugin-level migrations):

bash
bin/cake migrations migrate -p Crustum/Rhythm

With manifest installation (migrations copied to application):

bash
bin/cake migrations migrate

NOTE

If you used the manifest system to install the plugin, the migrations have been copied to your application's config/Migrations directory and should be run using the application-level command without the -p flag.

Database Configuration ​

NOTE

Rhythm's storage implementation currently requires a MySQL, MariaDB, PostgreSQL.

By default, Rhythm.storage.database.connection is set to 'default' in the database configuration but you can change it to your own database connection.

Configuration ​

The plugin can be configured via the config/rhythm.php file:

php
return [
    'Rhythm' => [
        'enabled' => env('RHYTHM_ENABLED', true),

        'storage' => [
            'driver' => env('RHYTHM_STORAGE_DRIVER', 'database'),
            'database' => [
                'connection' => 'default',
                'tables' => [
                    'entries' => 'rhythm_entries',
                    'aggregates' => 'rhythm_aggregates',
                ],
            ],
            'cache' => [
                'config' => 'default',
                'prefix' => 'rhythm:',
            ],
        ],

        'ingest' => [
            'driver' => env('RHYTHM_INGEST_DRIVER', 'redis'),
            'redis' => [
                'host' => env('REDIS_HOST', '127.0.0.1'),
                'port' => env('REDIS_PORT', 6379),
                'password' => env('REDIS_PASSWORD', null),
                'database' => env('REDIS_DATABASE', 0),
                'queue_key' => 'rhythm:metrics:queue',
                'processing_key' => 'rhythm:metrics:processing',
                'control_channel' => 'rhythm:control',
                'max_items_per_digest' => 100,
                'trim' => [
                    'keep' => '1 hour',
                ],
            ],
        ],

        'recorders' => [
            'servers' => [
                'className' => \Crustum\Rhythm\Recorder\ServersRecorder::class,
                'enabled' => env('RHYTHM_SERVER_ENABLED', true),
                'server_name' => env('RHYTHM_SERVER_NAME', gethostname()),
                'directories' => explode(':', env('RHYTHM_SERVER_DIRECTORIES', '/')),
                'ignore' => [
                    '/^\/tmp/',
                    '/^\/var\/log/',
                    '/^\/proc/',
                    '/^\/sys/',
                ],
            ],
            'user_requests' => [
                'className' => \Crustum\Rhythm\Recorder\UserRequestsRecorder::class,
                'enabled' => env('RHYTHM_USER_REQUESTS_ENABLED', true),
                'sample_rate' => env('RHYTHM_USER_REQUESTS_SAMPLE_RATE', 1.0),
                'ignore' => [
                    '#^/rhythm#',
                    '#^/health#',
                ],
            ],
            'slow_queries' => [
                'className' => \Crustum\Rhythm\Recorder\SlowQueriesRecorder::class,
                'enabled' => env('RHYTHM_SLOW_QUERIES_ENABLED', true),
                'threshold' => env('RHYTHM_SLOW_QUERIES_THRESHOLD', 1000),
                'sample_rate' => env('RHYTHM_SLOW_QUERIES_SAMPLE_RATE', 0.1),
            ],
            'exceptions' => [
                'className' => \Crustum\Rhythm\Recorder\ExceptionsRecorder::class,
                'enabled' => env('RHYTHM_EXCEPTIONS_ENABLED', true),
                'sample_rate' => env('RHYTHM_EXCEPTIONS_SAMPLE_RATE', 1.0),
            ],
            'queues' => [
                'className' => \Crustum\Rhythm\Recorder\QueuesRecorder::class,
                'enabled' => env('RHYTHM_QUEUES_ENABLED', true),
                'sample_rate' => env('RHYTHM_QUEUES_SAMPLE_RATE', 1.0),
            ],
            'slow_jobs' => [
                'className' => \Crustum\Rhythm\Recorder\SlowJobsRecorder::class,
                'enabled' => env('RHYTHM_SLOW_JOBS_ENABLED', true),
                'threshold' => env('RHYTHM_SLOW_JOBS_THRESHOLD', 5000),
                'sample_rate' => env('RHYTHM_SLOW_JOBS_SAMPLE_RATE', 0.1),
            ],
            'slow_requests' => [
                'className' => \Crustum\Rhythm\Recorder\SlowRequestsRecorder::class,
                'enabled' => env('RHYTHM_SLOW_REQUESTS_ENABLED', true),
                'threshold' => env('RHYTHM_SLOW_REQUESTS_THRESHOLD', 2000),
                'sample_rate' => env('RHYTHM_SLOW_REQUESTS_SAMPLE_RATE', 0.1),
            ],
            'outgoing_requests' => [
                'className' => \Crustum\Rhythm\Recorder\OutgoingRequestRecorder::class,
                'enabled' => env('RHYTHM_OUTGOING_REQUESTS_ENABLED', true),
                'threshold' => env('RHYTHM_OUTGOING_REQUESTS_THRESHOLD', 1000),
                'sample_rate' => env('RHYTHM_OUTGOING_REQUESTS_SAMPLE_RATE', 0.1),
                'groups' => [
                    '#^https?://api\.([^/]+)\.com/([^/]+)/(\d+)#' => 'https://api.*.com/*/*',
                    '#^https?://([^/]+)\.api\.([^/]+)\.com/([^/]+)#' => 'https://*.api.*.com/*',
                    '#^https?://([^/]+)\.com/api/v(\d+)/([^/]+)#' => 'https://*.com/api/v*/*',
                    '#^https?://([^/]+)\.com/([^/]+)/(\d+)#' => 'https://*.com/*/*',
                ],
            ],
        ],

        'buffer' => env('RHYTHM_BUFFER', 5000),
        'trim' => [
            'keep' => env('RHYTHM_TRIM_KEEP', '24 hours'),
        ],
    ],
];

Environment Variables ​

Set these environment variables to configure Rhythm:

bash
# Enable Rhythm
RHYTHM_ENABLED=true

# Storage configuration
RHYTHM_STORAGE_DRIVER=database

# Ingest configuration
RHYTHM_INGEST_DRIVER=redis

# Redis configuration (if using Redis ingest)
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=your_password
REDIS_DATABASE=0

# Recorder configuration
RHYTHM_SERVER_ENABLED=true
RHYTHM_USER_REQUESTS_ENABLED=true
RHYTHM_SLOW_QUERIES_ENABLED=true
RHYTHM_EXCEPTIONS_ENABLED=true
RHYTHM_QUEUES_ENABLED=true
RHYTHM_SLOW_JOBS_ENABLED=true
RHYTHM_SLOW_REQUESTS_ENABLED=true
RHYTHM_OUTGOING_REQUESTS_ENABLED=true

Usage ​

Data Flow Architecture ​

Rhythm uses a two-phase approach for efficient metric collection:

  1. Ingest Phase: Metrics are collected in memory during requests and stored in a persistent queue
  2. Digest Phase: Queued metrics are processed into final storage with automatic aggregation

This approach solves PHP's "run and die" problem by persisting metrics between requests.

Automatic Monitoring ​

The plugin automatically monitors:

  • Server performance metrics (CPU, memory, disk)
  • HTTP requests and response times
  • Database query performance
  • Application exceptions
  • Queue system performance
  • Background job execution
  • Outgoing HTTP requests
  • Cache performance

Manual Metric Recording ​

You can also record custom metrics:

php
// In a controller or service
$rhythm = $this->getContainer()->get(\Crustum\Rhythm\Rhythm::class);

// Record a simple metric
$rhythm->record('custom_metric', 'user_action', 150);

// Set a metric value
$rhythm->set('config_value', 'cache_hit_rate', '85.5');

// Record with aggregations
$rhythm->record('performance', 'api_call', 250)
    ->count()
    ->avg()
    ->max();

Processing Metrics ​

Metrics are automatically ingested when the buffer is full. To process queued metrics:

Redis Ingest (Default) ​

bash
# Process metrics from Redis queue
bin/cake rhythm digest

Null Ingest (Testing/Development) ​

The Null Ingest driver discards all metrics, useful for:

  • Testing environments where you don't want metric overhead
  • Temporarily disabling metrics without changing configuration
  • Development when you want zero metric collection
php
// In config/rhythm.php
'ingest' => [
    'driver' => 'null', // Discard all metrics
],

No additional commands needed - all metrics are silently discarded.

Recommended: Set up a cron job to run digest periodically:

bash
# Every 5 minutes
*/5 * * * * cd /path/to/your/app && bin/cake rhythm digest

Server Monitoring ​

Monitor server performance with the check command:

bash
# Take a single server snapshot
bin/cake rhythm check --once

# Monitor continuously with 5-second intervals
bin/cake rhythm check --interval=5

Dashboard Access ​

Access the Rhythm dashboard at /rhythm/dashboard to view: Server state and performance metrics

  • Queue activity and statistics
  • Slow queries and requests
  • Exception tracking
  • Cache performance
  • Outgoing request monitoring
  • Job performance metrics

Available Recorders ​

ServersRecorder ​

Monitors server performance metrics including CPU, memory, and disk usage.

UserRequestsRecorder ​

Tracks HTTP requests, response times, and user request patterns.

SlowQueriesRecorder ​

Monitors database query performance and identifies slow queries.

ExceptionsRecorder ​

Tracks application exceptions for debugging and monitoring.

QueuesRecorder ​

Monitors queue system performance and job processing statistics.

SlowJobsRecorder ​

Tracks background job execution times and identifies slow jobs.

SlowRequestsRecorder ​

Identifies slow HTTP requests based on configurable thresholds.

OutgoingRequestRecorder ​

Monitors external API calls and HTTP client performance with automatic URI grouping.

GitRecorder ​

Captures Git repository information including current branch, recent commits, and repository status. Automatically detects Git repositories and provides commit classification and metadata tracking.

Available Widgets ​

ServerStateWidget ​

Server State Widget

Shows real-time server performance with CPU, memory, and disk usage. Displays sparkline charts and status indicators for each server. Shows online/offline status.

QueuesWidget ​

Queues Widget

Shows job queues with charts showing job states (queued, processing, processed, failed). Shows queue depth, health, and wait times with success rate tracking.

SlowQueriesWidget ​

Slow Queries Widget

Shows slow database queries with execution times and locations. Shows top 10 slowest queries with sorting options to help find performance issues.

ExceptionsWidget ​

Exceptions Widget

Shows application errors and exceptions. Groups by error type and shows frequency counts with sorting by frequency or latest occurrence.

SlowRequestsWidget ​

Slow Requests Widget

Shows slow HTTP requests with method, path, and response times. Displays top 10 slowest requests with status codes to identify web performance issues.

SlowOutgoingRequestsWidget ​

Shows external API calls with grouped URLs and response times. Shows slowest external requests to help optimize API usage.

SlowJobsWidget ​

Shows slow background jobs with execution times and status. Shows top 10 slowest jobs with sorting options for job performance monitoring.

UsageWidget ​

Displays system resource usage including memory, response times, and request counts. Shows status indicators for each metric.

AppInfoWidget ​

App Info Widget

Displays comprehensive application information including CakePHP version, PHP version, debug mode status, system configuration, and database information. Provides a quick overview of your application's environment and configuration with detailed system metrics.

GitWidget ​

Git Status Widget

Displays current Git repository information including the active branch, recent commits, and repository status. Helps track deployment status and recent changes with commit classification and metadata.

Command Line Tools ​

rhythm digest ​

Process queued metrics into final storage.

rhythm check ​

Monitor server performance and collect metrics.

rhythm clear ​

Clear all stored metrics and aggregates.

rhythm purge ​

Purge specific metric types or all data.

Event System ​

Rhythm dispatches events for external integrations:

Available Events ​

  • Rhythm.metric.recorded - When a metric is recorded
  • Rhythm.digest.completed - When metrics are processed to final storage
  • Rhythm.server.beat - Server performance snapshot events

Listening to Events ​

php
// In a service or component
public function implementedEvents(): array
{
    return [
        'Rhythm.metric.recorded' => 'handleMetricRecorded',
        'Rhythm.digest.completed' => 'handleDigestCompleted',
    ];
}

public function handleMetricRecorded(EventInterface $event): void
{
    $data = $event->getData();
    // Handle metric recording
}

public function handleDigestCompleted(EventInterface $event): void
{
    $data = $event->getData();
    // Handle digest completion
}

Database Schema ​

The plugin creates these main tables:

rhythm_entries ​

Stores raw metric entries with timestamps and values.

rhythm_aggregates ​

Stores pre-aggregated metrics for efficient querying and dashboard display.

API Reference ​

Rhythm Class ​

  • record(string $type, string $key, ?int $value = null, ?int $timestamp = null): RhythmEntry
  • set(string $type, string $key, string $value, ?int $timestamp = null): RhythmValue
  • ingest(): int - Process queued entries
  • aggregate(string $type, array|string $aggregates, int $intervalMinutes): Collection
  • aggregateTotal(string $type, array|string $aggregates, int $intervalMinutes): float|Collection
  • graph(string $type, string $aggregate, int $intervalMinutes): Collection
  • values(string $type, ?array $keys = null): Collection - Get values for a type
  • report(Throwable $exception): $this - Report exceptions
  • trim(): void - Remove old data
  • purge(?array $types = null): void - Clear data

Storage Interface ​

  • store(CollectionInterface $entries): void
  • aggregate(string $type, array|string $aggregates, int $intervalMinutes): CollectionInterface
  • trim(): void - Remove old data
  • purge(?array $types = null): void - Clear data

Contributing ​

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

License ​

This plugin is licensed under the MIT License.

Released under the MIT License.