---
# System prepended metadata

title: 如何將 Laravel Octane HTTP 類型 Metrics 整合到 RoadRunner Metrics

---

# 如何將 Laravel Octane HTTP 類型 Metrics 整合到 RoadRunner Metrics

###### tags: `Laravel`,`PHP`,`RoadRunner`,`Octane`

RoadRunner 本身就有提供一些基本的 Metrics 供 Prometheus 之類的監控工具監控，例如：`rr_http_workers_ready`、`rr_http_workers_working`，RoadRunner 可以透過簡單的 `.rr.yaml` 設定端口輸出這些 Metrics 數據，供 Prometheus 抓取。

```yaml
metrics:
  address: 0.0.0.0:2112 # Prometheus metrics 端口
```

重啟 Server 後開啟 http://localhost:2112/metrics 會出現類似下面的畫面，RoadRunner 提供很多基本 Metrics 數據。
![截圖 2025-10-22 下午3.26.40](https://hackmd.io/_uploads/ryHvvWU0ex.png)

實務上系統常需要監控 Reqeust 數量跟執行時間等等跟 HTTP Reqeust 相關的數據，RoadRunner 雖然有提供 [HTTP Metrics](https://docs.roadrunner.dev/docs/logging-and-observability/metrics#http-metrics) Middleware，可以監控 request 數量跟執行時間等等資訊。
但是如果使用 Laravel Octane 搭配 RoadRunner，RoadRunner 不會參與實際處理 HTTP Reqeust，而是把 Reqeust 轉交由 Laravel Octane 自己的 PHP 進程處理。由於跳過 RoadRunner HTTP Metrics Middleware，所以即使引用了 Middleware，Metrics 頁面並不會出現 `rr_http_request_total`、`rr_http_request_duration_seconds` 等數據。

那 Laravel Octane 要如何處理 HTTP Reqeust Metrics 呢？這有兩種做法：
1. 自行做統計，並另外新增 Metrics Port 和 Path(EX: http://localhost:80/metrics)，輸出這些統計資料。
2. 透過 RoadRunner Metrics Plugin，當 Laravel App 層處理 HTTP Reqeust 時，經過 RPC 觸發 RoadRunner Application Metrics，這樣就可以在原本的 http://localhost:2112/metrics 頁面呈現客製化的 Metrics 數據。

接下來要介紹一下第二種做法


## RoadRunner Application Metrics

如果希望沿用原本 RoadRunner Metrics 頁面(http://localhost:2112/metrics)，那就要透過 RoadRunner Metrics Plugin 處理 Metrics 數據：

```mermaid
graph LR
A(Laravel 端) --> |RPC| B(RoadRunner Application Metrics)
```

### RoadRunner 設定
首先在 RoadRunner `.rr.yaml` 的 `metrics.collect` 下設定客製化的 Metrics

```yaml
metrics:
  address: 0.0.0.0:2112 # Prometheus Metrics 端口

  collect:
    api_requests_total:
      type: counter
      help: "Total number of HTTP requests."
      labels: ["method", "path"]
    api_request_failures_total:
      type: counter
      help: "Total number of failed HTTP requests (non-2xx responses)."
      labels: ["method", "path"]
    api_request_duration_seconds:
      type: histogram
      help: "Request latency in seconds."
      labels: ["method", "path"]
      # 根據您的需求調整 buckets
      buckets: [0.1, 0.3, 0.5, 1, 2]
```

以上例子新增三種客製化的 Application Metrics(`api_requests_total`、`api_request_failures_total`、`api_request_duration_seconds`)，顧名思義就是 HTTP Requests 數目、非 200 Http Status Code Requests 數目 跟 API Request 執行時間。

- type：種類有四種：gauge、counter、summary、histogram，差異請參考 ChatGPT 整理表格
![截圖 2025-10-22 下午4.17.50](https://hackmd.io/_uploads/S1iU7MURlx.png)

    以上例子因為是作 Requests 數量統計跟執行時間(duration)分佈，所以使用 `counter` & `histogram` 兩種 Type
- buckets：`histogram` Type 需設定的執行時間分佈區間，設定為 0.1s、0.3s、0.5s、1s、2s，假設有個 Request 執行 0.8s，0.1s、0.3s、0.5s 這三個 buckets 會 +1，如果 0.05s，那就沒有 buckets 會累計了，以此類推。
- labels：可以增加額外的 Metadata，適合拿來做分類統計或過濾，以上例子採用 HTTP Method & Request Path 做分類，也就是 Requests 數量統計跟執行時間(duration)分佈會依照不同 HTTP Method & Request Path 做分類。


### Laravel 監聽事件

設定好 RoadRunner Application Metrics 後，接著要在 Laravel 端觸發 Application Metrics，這邊使用到一個套件 [spiral/roadrunner-metrics](https://github.com/roadrunner-php/metrics)，它是一個 PHP 與 RoadRunner Metrics Plugin 介接的橋樑，通過這橋樑提供 RoadRunner Metrics 資料，例如某個 Metrics 統計數量加 1 / 減 1 等等。

- 在 App\Providers 的 AppServiceProvider 註冊 Metrics 實例
    ```php
    $this->app->singleton(MetricsInterface::class, function () {
        // 從設定檔讀取 RPC 位址
        $rpcAddress = config('octane.rpc');

        // 透過 RPC 連線建立 RoadRunner Metrics Client 端
        return new Metrics(
            RPC::create($rpcAddress)
        );
    });
    ```
- 在 App\Listeners 下新增 OctaneMetricsReporter 監聽物件，有三種 Metrics(`api_requests_total`、`api_request_failures_total`、`api_request_duration_seconds`)要觸發
    ```php
    <?php

    namespace App\Listeners;

    use Spiral\RoadRunner\Metrics\MetricsInterface;

    class OctaneMetricsReporter
    {
        protected MetricsInterface $metrics;

        public function __construct(MetricsInterface $metrics)
        {
            $this->metrics = $metrics;
        }

        public function handle($event)
        {
            // 如果沒有 Response 物件，則直接返回
            if (!$event->response) {
                return;
            }

            $method = $event->request->getMethod();
            
            // 符合 Route 設定回傳 Route URI，不符合則一率為 unmatched
            $path = optional($event->request->route())->uri() ?? 'unmatched';
            $labels = [$method, $path];

            // 每次請求，將 api_requests_total 計數器加 1
            $this->metrics->add('api_requests_total', 1, $labels);

            // 如果不是 2xx，則累加 api_request_failures_total
            $statusCode = $event->response->getStatusCode();
            if ($statusCode < 200 || $statusCode >= 300) {
                $this->metrics->add('api_request_failures_total', 1, $labels);
            }

            // 計算請求執行期間並記錄 api_request_duration_seconds
            $startTime = $event->request->server('REQUEST_TIME_FLOAT');
            if ($startTime) {
                $latencyInSeconds = (microtime(true) - $startTime);
                $this->metrics->observe('api_request_duration_seconds', $latencyInSeconds, $labels);
            }
        }
    }
    ```
- 最後在 config 的 octane.php 新增 RoadRunner RPC Endpoint(預設是 `tcp://127.0.0.1:6001`)及在 RequestTerminated 階段設定監聽事件
    ```php
    return [
        ...
        'listeners' => [
            ...
            RequestTerminated::class => [
                // FlushUploadedFiles::class,
                App\Listeners\OctaneMetricsReporter::class,
            ],
        ],
        ...
        'rpc' => env('ROADRUNNER_RPC', 'tcp://127.0.0.1:6001'),
    ]
    ```

完成上述設定後，重啟 Laravel Octane Server，再呼叫一些 api，就能看到剛剛設定客製化 Metrics 了
![截圖 2025-10-22 下午4.52.23](https://hackmd.io/_uploads/rJvJTzIRex.png)


