---
title: "Metrics"
description: "List of Prometheus-compatible metrics exposed by FrankenPHP via Caddy, covering PHP threads, workers, request times, queue depth, crashes, and restarts."
sidebar:
  order: 5
seo:
  title: "FrankenPHP Prometheus metrics for threads and workers"
---
<div class="fp-callout" data-callout="tip">
<p class="fp-callout-title">Tip</p>

For a complete observability setup including real-time dashboards and production monitoring, see the [Observability](/docs/observability) page.

</div>

## Prometheus Export

When [Caddy metrics](https://caddyserver.com/docs/metrics) are enabled, FrankenPHP exposes the following metrics:

- `frankenphp_total_threads`: The total number of PHP threads.
- `frankenphp_busy_threads`: The number of PHP threads currently processing a request (running workers always consume a thread).
- `frankenphp_queue_depth`: The number of regular queued requests.
- `frankenphp_total_workers{worker="[worker_name]"}`: The total number of workers.
- `frankenphp_busy_workers{worker="[worker_name]"}`: The number of workers currently processing a request.
- `frankenphp_worker_request_time{worker="[worker_name]"}`: The time spent processing requests by all workers.
- `frankenphp_worker_request_count{worker="[worker_name]"}`: The number of requests processed by all workers.
- `frankenphp_ready_workers{worker="[worker_name]"}`: The number of workers that have called `frankenphp_handle_request` at least once.
- `frankenphp_worker_crashes{worker="[worker_name]"}`: The number of times a worker has unexpectedly terminated.
- `frankenphp_worker_restarts{worker="[worker_name]"}`: The number of times a worker has been deliberately restarted.
- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: The number of queued requests.
- `frankenphp_opcache_restarts{reason="[reason]"}`: (experimental) The number of restarts of opcache's shared memory scheduled, by reason (`oom`, `hash`, `manual`). PHP 8.4 and up.

For worker metrics, the `[worker_name]` placeholder is replaced by the worker name in the Caddyfile, otherwise the absolute path of the worker file will be used.

opcache schedules a restart when its cache is full and more than `opcache.max_wasted_percentage` of it was wasted by invalidations. The restart runs at the next request start; caching is off until then, and running PHP threads may still reference the old memory, which can crash the process. The counter should stay at zero: raise `opcache.memory_consumption`, `opcache.max_accelerated_files` or `opcache.max_wasted_percentage`. `manual` should never appear, FrankenPHP overrides `opcache_reset()`. Each restart is also logged. This metric will be removed once opcache handles restarts safely under ZTS.

## Threads State Endpoint

FrankenPHP exposes a `/frankenphp/threads` endpoint through the [Caddy admin API](https://caddyserver.com/docs/api).
It returns a JSON snapshot of all active PHP threads, useful for debugging and building observability tools.

```console
curl -s http://localhost:2019/frankenphp/threads | jq .
```

### Response Format

The endpoint returns a JSON object with the following structure:

```json
{
    "ThreadDebugStates": [
        {
            "Index": 0,
            "Name": "worker-/path/to/worker.php",
            "State": "ready",
            "IsWaiting": true,
            "IsBusy": false,
            "WaitingSinceMilliseconds": 1234,
            "CurrentURI": "",
            "CurrentMethod": "",
            "RequestStartedAt": 0,
            "RequestCount": 42,
            "MemoryUsage": 2097152
        }
    ],
    "ReservedThreadCount": 3
}
```

### Fields

| Field | Type | Description |
|---|---|---|
| `ReservedThreadCount` | integer | The number of threads reserved for autoscaling that are not yet active. |

Each entry in `ThreadDebugStates` contains:

| Field | Type | Description |
|---|---|---|
| `Index` | integer | The index of the thread. |
| `Name` | string | The name of the thread (e.g., the worker file path). |
| `State` | string | The internal state of the thread (e.g., `ready`, `shutting down`). |
| `IsWaiting` | boolean | Whether the thread is waiting for a request. |
| `IsBusy` | boolean | Whether the thread is currently processing a request. |
| `WaitingSinceMilliseconds` | integer | How long the thread has been idle, in milliseconds. `0` if the thread is busy. |
| `CurrentURI` | string | The URI currently being processed. Empty if the thread is idle. |
| `CurrentMethod` | string | The HTTP method of the current request (e.g., `GET`, `POST`). Empty if the thread is idle. |
| `RequestStartedAt` | integer | Unix timestamp in milliseconds of when the current request started. `0` if the thread is idle. |
| `RequestCount` | integer | The total number of requests this thread has processed since it started. |
| `MemoryUsage` | integer | The current PHP memory usage of the thread, in bytes. |
