Skip to content
← Back to Functions
Code

Error Rate Monitor

Summarizes errors by route, severity, time period, and trend.

Function signature

ogCalculateErrorRateSummary(records = array(), options = array())

Categories

  • SEO and Routing

Parameters

recordsError records with route, severity, and timestamp keys.optionsReport options: total_requests, bucket_format. Recognized keys: `bucket_format`, `total_requests`.

Return value

Short public-safe status message.

  • total_errors
  • total_requests
  • error_rate_percent
  • by_route
  • by_severity
  • by_bucket

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.
 */

/**
 * Summarizes application error records by route, severity, and time bucket.
 *
 * This helper redacts obvious token-like values from route strings before
 * building the report so diagnostics remain useful without exposing secrets.
 *
 * @param array $records Error records with route, severity, and timestamp keys.
 * @param array $options Report options: total_requests, bucket_format.
 * @return array Structured error-rate summary.
 */
function ogCalculateErrorRateSummary($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();
	}

	$bucket_format = 'Y-m-d H:00:00';
	if (!empty($options['bucket_format'])) {
		$bucket_format = (string)$options['bucket_format'];
	}

	$total_requests = 0;
	if (!empty($options['total_requests'])) {
		$total_requests = (int)$options['total_requests'];
	}

	$total_errors = 0;
	$by_route = array();
	$by_severity = array();
	$by_bucket = array();

	foreach ($records as $record) {
		if (!is_array($record)) {
			continue;
		}

		$route = '/unknown';
		if (!empty($record['route'])) {
			$route = (string)$record['route'];
		}
		$route = preg_replace('/([?&](token|key|secret|password|session)=)[^&]+/i', '$1[redacted]', $route);

		$severity = 'error';
		if (!empty($record['severity'])) {
			$severity = strtolower(trim((string)$record['severity']));
		}
		if (empty($severity)) {
			$severity = 'error';
		}

		$timestamp = time();
		if (!empty($record['timestamp'])) {
			$timestamp = strtotime((string)$record['timestamp']);
			if (empty($timestamp)) {
				$timestamp = time();
			}
		}

		$bucket = date($bucket_format, $timestamp);
		$total_errors++;

		if (empty($by_route[$route])) {
			$by_route[$route] = 0;
		}
		$by_route[$route]++;

		if (empty($by_severity[$severity])) {
			$by_severity[$severity] = 0;
		}
		$by_severity[$severity]++;

		if (empty($by_bucket[$bucket])) {
			$by_bucket[$bucket] = 0;
		}
		$by_bucket[$bucket]++;
	}

	arsort($by_route);
	arsort($by_severity);
	ksort($by_bucket);

	$error_rate = 0;
	if ($total_requests > 0) {
		$error_rate = ($total_errors / $total_requests) * 100;
	}

	$result['success'] = true;
	$result['message'] = 'Error rate summary built.';
	$result['data'] = array(
		'total_errors' => $total_errors,
		'total_requests' => $total_requests,
		'error_rate_percent' => $error_rate,
		'by_route' => $by_route,
		'by_severity' => $by_severity,
		'by_bucket' => $by_bucket
	);

	return $result;
}