watchlog/laravel-apm

Lightweight APM integration for Laravel apps using Watchlog

0.1.4 2025-05-18 12:21 UTC

This package is not auto-updated.

Last update: 2025-06-16 10:51:23 UTC


README

๐ŸŽฏ Lightweight, production-ready Application Performance Monitoring (APM) middleware for Laravel apps, designed for Watchlog.

Tracks request duration, memory usage, and status codes โ€” stores metrics in a buffer file, and automatically flushes them to your Watchlog Agent every 10 seconds.

๐Ÿš€ Features

  • โœ… Compatible with Laravel 10 & 12+
  • ๐Ÿง  Smart file-based metric buffering (no database or queue needed)
  • ๐Ÿ” Aggregates metrics per route (e.g. /users/{id})
  • ๐Ÿ“Š Tracks duration, memory, and status codes
  • ๐Ÿ”’ Safe and non-blocking โ€” doesn't slow down your requests
  • ๐ŸŒ Sends data automatically every 10 seconds to your Watchlog Agent
  • ๐Ÿท๏ธ Auto-detects service name from .env (WATCHLOG_APM_SERVICE or fallback to APP_NAME)

๐Ÿ“ฆ Installation

1. Link local package (for development)

composer config repositories.watchlog-apm path ../laravel-watchlog-apm
composer require "watchlog/laravel-apm:*"

Adjust the path to match your local project structure.

โš™๏ธ Usage (Laravel 12+)

In your bootstrap/app.php, register the middleware:

use Watchlog\LaravelAPM\Middleware\WatchlogAPM;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(...)
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(WatchlogAPM::class);
    })
    ->create();

๐Ÿ› ๏ธ Service Identification

You can define your service name explicitly in .env:

WATCHLOG_APM_SERVICE=orders-api

If not set, it will fallback to:

APP_NAME=laravel-app

๐Ÿ“ค How it works

During each request:

  • The middleware tracks route, status code, duration, and memory.
  • The data is written to a file: storage/logs/apm-buffer.json

After the request ends:

  • A shutdown function checks if 10 seconds have passed since the last flush.
  • If yes, it:
    • Reads all pending metrics from apm-buffer.json
    • Aggregates them by route/method
    • Sends them to your Watchlog Agent as a JSON payload
    • Clears the buffer

๐Ÿ“ฆ What gets sent?

{
  "collected_at": "2025-05-18T12:00:00Z",
  "platformName": "laravel",
  "metrics": [
    {
      "type": "aggregated_request",
      "service": "orders-api",
      "path": "hello/{id}",
      "method": "GET",
      "request_count": 3,
      "error_count": 0,
      "avg_duration": 6.1,
      "max_duration": 8.2,
      "avg_memory": {
        "rss": 18432000,
        "heapUsed": 23998464,
        "heapTotal": 25600000
      }
    }
  ]
}

๐Ÿ“ File structure

laravel-watchlog-apm/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ Middleware/
โ”‚   โ”‚   โ””โ”€โ”€ WatchlogAPM.php
โ”‚   โ”œโ”€โ”€ Collector.php
โ”‚   โ””โ”€โ”€ Sender.php
โ”œโ”€โ”€ composer.json
โ”œโ”€โ”€ README.md

๐Ÿงช Debugging & Testing

You can manually flush metrics via tinker:

php artisan tinker
>>> (new \Watchlog\LaravelAPM\Sender())->flush();

๐Ÿ“ Recommended .gitignore

Add the following to your Laravel app's .gitignore:

/storage/logs/apm-buffer.json
/storage/logs/apm-debug.log
/storage/framework/cache/watchlog-apm.lock

โœ… Notes

  • The route path is captured via $request->route()?->uri() for correct dynamic segment grouping (/users/{id})
  • Multiple requests within 10 seconds are buffered, then aggregated and sent in a single payload
  • The flush lock uses a simple file timestamp (watchlog-apm.lock) to prevent oversending
  • The middleware is safe: flush failures won't crash your app

๐Ÿ“ License

MIT ยฉ Mohammadreza
Built for Watchlog.io