Skip to content
← Back to Functions
Code

Slow Request Analyzer

Groups slow request logs by route, percentile, and suspected cause.

Function signature

ogAnalyzeSlowRequests(records = array(), options = array())

Categories

  • SEO and Routing

Parameters

recordsRequest records with route and duration_ms keys.optionsOptions: threshold_ms. Recognized keys: `threshold_ms`.

Return value

Short public-safe status message.

  • count
  • average_ms
  • max_ms
  • p95_ms
  • slow_count
  • slow_percent
  • threshold_ms
  • routes

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, policy arrays, URLs, signatures, and caller-owned allowlists before use; keep secrets and internal paths out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Groups request timing records by route and reports slow-request metrics.
 *
 * @param array $records Request records with route and duration_ms keys.
 * @param array $options Options: threshold_ms.
 * @return array Route-level timing summary.
 */
function ogAnalyzeSlowRequests($records = array(), $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($records)) {
		$result['message'] = 'Records must be an array.';
		return $result;
	}

	if (!is_array($options)) {
		$options = array();
	}

	$threshold_ms = 1000;
	if (!empty($options['threshold_ms'])) {
		$threshold_ms = (int)$options['threshold_ms'];
	}
	if ($threshold_ms < 1) {
		$threshold_ms = 1000;
	}

	$route_values = array();
	foreach ($records as $record) {
		if (!is_array($record)) {
			continue;
		}
		if (empty($record['duration_ms']) || !is_numeric($record['duration_ms'])) {
			continue;
		}

		$route = '/unknown';
		if (!empty($record['route'])) {
			$route = (string)$record['route'];
		}
		$duration = (float)$record['duration_ms'];

		if (empty($route_values[$route])) {
			$route_values[$route] = array();
		}
		$route_values[$route][] = $duration;
	}

	$route_report = array();
	foreach ($route_values as $route => $values) {
		sort($values, SORT_NUMERIC);
		$count = count($values);
		$slow_count = 0;
		foreach ($values as $duration) {
			if ($duration >= $threshold_ms) {
				$slow_count++;
			}
		}

		$p95_index = (int)ceil($count * 0.95) - 1;
		if ($p95_index < 0) {
			$p95_index = 0;
		}
		if ($p95_index >= $count) {
			$p95_index = $count - 1;
		}

		$route_report[$route] = array(
			'count' => $count,
			'average_ms' => array_sum($values) / $count,
			'max_ms' => max($values),
			'p95_ms' => $values[$p95_index],
			'slow_count' => $slow_count,
			'slow_percent' => ($slow_count / $count) * 100
		);
	}

	uasort($route_report, function($left, $right) {
		if ($left['p95_ms'] == $right['p95_ms']) {
			return 0;
		}
		if ($left['p95_ms'] < $right['p95_ms']) {
			return 1;
		}
		return -1;
	});

	$result['success'] = true;
	$result['message'] = 'Slow request report built.';
	$result['data'] = array(
		'threshold_ms' => $threshold_ms,
		'routes' => $route_report
	);

	return $result;
}