Observability

Benchmark

Benchmark lifecycle marks, runtime telemetry and HTTP response benchmark metadata.

Overview

Benchmark runtime lives under Observability\Benchmark. Benchmark holds current run state and BenchmarkRun captures marks, elapsed time and memory deltas.

Provider wiring

BenchmarkServiceProvider registers benchmark runtime services used by HTTP and CLI kernels.

example.php
$container->singleton(Benchmark::class, Benchmark::class);
$container->singleton(BenchmarkResponseInjector::class, BenchmarkResponseInjector::class);

Lifecycle marks

Benchmark marks are recorded during bootstrap, middleware and dispatch flow. Only marks listed below are part of the current runtime implementation.

example.php
$run->mark('start');
$run->mark('kernel_start');
$run->mark('config_loaded');
$run->mark('core_logger_ready');
$run->mark('framework_providers_registered');
$run->mark('app_providers_registered');
$run->mark('routes_registered'); // HTTP kernel
$run->mark('request_received');
$run->mark('middleware_enter');
$run->mark('benchmark_middleware_enter');
$run->mark('route_match_start');
$run->mark('route_matched');
$run->mark('controller_resolve_start');
$run->mark('controller_resolved');
$run->mark('controller_action_start');
$run->mark('controller_action_finished');
$run->mark('response_created');
$run->mark('response_ready');
$run->mark('finish');

HTTP benchmark flow

HTTP benchmark starts in Kernel::handle(), continues through Framework::run(), BenchmarkMiddleware, route dispatch and controller resolution. BenchmarkResponseInjector attaches benchmark metadata to the outgoing response.

example.php
$response = $response
    ->withHeader('X-Benchmark-Time-Ms', $elapsedMs)
    ->withHeader('X-Benchmark-Memory-Delta', (string) $memoryDeltaBytes)
    ->withHeader('X-Benchmark-Peak-Memory', (string) $peakBytes)
    ->withHeader('X-Benchmark-Peak-Allocated-Memory', (string) $peakAllocatedBytes);

CLI benchmark flow

CLI starts benchmark in CliKernel::handle() and records bootstrap marks as part of CLI command lifecycle. CLI-specific bootstrap also records providers_registered.

Benchmark logging

When benchmark logging is enabled in LoggingConfigDefinition, BenchmarkMiddleware writes a request.benchmark event through LogManager::benchmark().

example.php
$this->logs->benchmark()->info('request.benchmark', [
    ...$run->toArray(),
    'status' => $status,
    'elapsed_ms' => round($run->elapsedMs(), 3),
    'memory_delta_bytes' => $run->memoryDeltaBytes(),
]);

Reading benchmark data

Benchmark data is available through response headers, optional HTML response comment (controlled by BenchmarkConfig::$injectHtmlComment) and benchmark log events. HTML comment injection is applied only for text/html responses.

Configuration

Benchmark HTML comment injection is configured in app/Config/Benchmark.yaml. Benchmark log channel settings live in app/Config/Logging.yaml. Both YAML documents are mapped into typed definitions and then resolved into runtime config DTOs by the existing framework config pipeline.

example.php
# app/Config/Benchmark.yaml
module: benchmark
config:
  inject_html_comment:
    $env: BENCHMARK_INJECT_HTML_COMMENT
    type: bool
    default: true

# app/Config/Logging.yaml
module: logging
config:
  benchmark:
    enabled:
      $env: BENCHMARK_LOG_ENABLED
      type: bool
      default: true
    path:
      $env: BENCHMARK_LOG_FILE
      type: string
      default: benchmark.log
    days:
      $env: BENCHMARK_LOG_DAYS
      type: int
      default: 7