Polyscope - The agent-first dev environment for Laravel

Metrics

Metrics stats

Downloads
6
Stars
20
Open Issues
0
Forks
0

View on GitHub →

Record metrics in your Laravel application

A simple and elegant way to record metrics in your Laravel application.


Metrics provides a simple, elegant way to record and query metrics in your Laravel application. Track page views, API calls, user signups, or any other countable events with ease.

Index

Requirements

  • PHP >= 8.1
  • Laravel >= 9.0

Installation

You can install the package via composer:

composer require directorytree/metrics

Then, publish the migrations:

php artisan vendor:publish --tag="metrics-migrations"

Finally, run the migrations:

php artisan migrate

Setup

Optionally, you can publish the configuration file:

php artisan vendor:publish --tag="metrics-config"

This will create a config/metrics.php file where you can configure queueing behavior:

return [
// ...
 
'queue' => env('METRICS_QUEUE', false) ? [
'name' => env('METRICS_QUEUE_NAME'),
'connection' => env('METRICS_QUEUE_CONNECTION', env('QUEUE_CONNECTION', 'sync')),
] : false,
];

Usage

Recording Metrics

Record a metric using the Metrics facade:

use DirectoryTree\Metrics\MetricData;
use DirectoryTree\Metrics\Facades\Metrics;
 
Metrics::record(new MetricData('signups'));

Or using the metric global helper:

metric('signups')->record();

Or using the PendingMetric class:

use DirectoryTree\Metrics\PendingMetric;
 
PendingMetric::make('signups')->record();

Which ever method you use, metrics are recorded in the same way. Use whichever you prefer.

For the rest of the documentation, we will use the metric helper for consistency.

Metric Values

By default, metrics have a value of 1. You can specify a custom value:

// Track multiple API calls at once
metric('api_calls')->record(10);
 
// Track batch job completions
metric('jobs_completed')->record(250);

If you record the same metric multiple times, the values will be summed:

metric('logins')->record(); // value: 1
metric('logins')->record(); // value: 1
 
// Database will contain one metric with value: 2

Recording with Categories

Organize metrics into categories:

// Track API calls by endpoint
metric('api_calls')->category('users')->record();
metric('api_calls')->category('orders')->record();
 
// Track errors by severity
metric('errors')->category('critical')->record();
metric('errors')->category('warning')->record();
 
// Track purchases by payment method
metric('purchases')->category('stripe')->record();
metric('purchases')->category('paypal')->record();

These will be stored as separate metrics, allowing you to track the same metric across different contexts.

Recording with Dates

By default, metrics are recorded with today's date. You can specify a custom date:

use Carbon\Carbon;
 
// Backfill signup data from an import
metric('signups')
->date(Carbon::parse('2025-01-15'))
->record(50);
 
// Record yesterday's batch job completions
metric('jobs_completed')
->date(Carbon::yesterday())
->record(1250);

Recording for Models

Associate metrics with Eloquent models using the HasMetrics trait:

use DirectoryTree\Metrics\HasMetrics;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
use HasMetrics;
}

Then record metrics for a specific model:

$user = User::find(1);
 
// Track logins per user
metric('logins')->measurable($user)->record();
 
// Track orders per customer
$customer = Customer::find(1);
metric('orders')->measurable($customer)->record();
 
// Track API calls per client
$apiClient = ApiClient::find(1);
metric('api_requests')->measurable($apiClient)->record();

Query metrics for a model:

// Get total logins for a user
$totalLogins = $user->metrics()
->where('name', 'logins')
->sum('value');
 
// Get orders this month for a customer
$ordersThisMonth = $customer->metrics()
->where('name', 'orders')
->thisMonth()
->sum('value');

Capturing & Committing

For high-performance scenarios, you can capture metrics in memory and commit them in batches:

use DirectoryTree\Metrics\Facades\Metrics;
 
Metrics::capture();
 
// Record multiple metrics...
metric('signups')->record();
metric('emails_sent')->category('welcome')->record();
metric('signups')->record();
 
// Commit all captured metrics at once
Metrics::commit();

Captured metrics are automatically committed when the application terminates. You can also stop capturing manually:

Metrics::stopCapturing();

To enable capturing for your entire application, start capturing in your AppServiceProvider:

// app/Providers/AppServiceProvider.php
 
use DirectoryTree\Metrics\Facades\Metrics;
 
class AppServiceProvider extends ServiceProvider
{
// ...
 
public function boot(): void
{
Metrics::capture();
}
}

This will batch all metrics recorded during the request and commit them automatically when the application terminates, reducing database queries and improving performance.

Example: High-Traffic Controller

public function store(Request $request)
{
Metrics::capture();
 
// Process multiple operations
$user = User::create($request->validated());
metric('signups')->record();
 
$user->sendWelcomeEmail();
metric('emails_sent')->category('welcome')->record();
 
event(new UserRegistered($user));
metric('events_dispatched')->record();
 
// All metrics committed automatically at end of request
return response()->json($user);
}

Example: Batch Job Processing

public function handle()
{
Metrics::capture();
 
Order::pending()->chunk(100, function ($orders) {
foreach ($orders as $order) {
$order->process();
 
metric('orders_processed')->record();
}
});
 
Metrics::commit(); // Batch commit all metrics
}

Querying Metrics

The Metric model includes a powerful query builder with date filtering methods:

use DirectoryTree\Metrics\Metric;
 
// Get today's metrics
$metrics = Metric::today()->get();
 
// Get this week's metrics
$metrics = Metric::thisWeek()->get();
 
// Get this month's metrics
$metrics = Metric::thisMonth()->get();
 
// Get this year's metrics
$metrics = Metric::thisYear()->get();
 
// Get yesterday's metrics
$metrics = Metric::yesterday()->get();
 
// Get last week's metrics
$metrics = Metric::lastWeek()->get();
 
// Get last month's metrics
$metrics = Metric::lastMonth()->get();
 
// Get last year's metrics
$metrics = Metric::lastYear()->get();
 
// Get metrics between specific dates
$metrics = Metric::betweenDates(
Carbon::parse('2025-01-01'),
Carbon::parse('2025-12-31')
)->get();
 
// Get metrics on a specific date
$metrics = Metric::onDate(Carbon::parse('2025-10-15'))->get();

Chain with standard Eloquent methods:

// Get today's signups
$signups = Metric::today()
->where('name', 'signups')
->sum('value');
 
// Get this week's purchases
$purchases = Metric::thisWeek()
->where('name', 'purchases')
->sum('value');
 
// Get API usage by endpoint this month
$apiUsage = Metric::thisMonth()
->where('name', 'api_calls')
->get()
->groupBy('category')
->map->sum('value');
 
// Count unique metrics recorded today
$count = Metric::today()->count();

Example: Compare Growth

// Compare this month vs last month signups
$thisMonth = Metric::thisMonth()
->where('name', 'signups')
->sum('value');
 
$lastMonth = Metric::lastMonth()
->where('name', 'signups')
->sum('value');
 
$growth = (($thisMonth - $lastMonth) / $lastMonth) * 100;

Example: Calculate Error Rate

// Get error rate for the year
$errors = Metric::thisYear()
->where('name', 'errors')
->sum('value');
 
$requests = Metric::thisYear()
->where('name', 'api_calls')
->sum('value');
 
$errorRate = ($errors / $requests) * 100;

Testing

Metrics includes a fake implementation for testing:

use DirectoryTree\Metrics\Measurable;
use DirectoryTree\Metrics\Facades\Metrics;
 
public function test_user_signup_records_metric()
{
Metrics::fake();
 
// Your code that records metrics...
$this->post('register', [
'email' => 'user@example.com',
'password' => 'password',
]);
 
// Assert metrics were recorded
Metrics::assertRecorded('signups');
}
 
public function test_api_call_records_metric_with_endpoint()
{
Metrics::fake();
 
$this->getJson('api/users');
 
// Assert metrics were recorded with a closure
Metrics::assertRecorded(fn (Measurable $metric) =>
$metric->name() === 'api_calls' &&
$metric->category() === 'users'
);
}
 
public function test_failed_login_records_metric()
{
Metrics::fake();
 
$this->post('login', [
'email' => 'wrong@example.com',
'password' => 'wrong',
]);
 
// Assert metrics were not recorded
Metrics::assertNotRecorded('logins');
 
// Assert failed login was recorded
Metrics::assertRecorded('failed_logins');
}
 
public function test_purchase_records_metric_for_user()
{
Metrics::fake();
 
$user = User::factory()->create();
 
$this->actingAs($user)->post('/purchases', [
'product_id' => 1,
]);
 
// Assert metrics were recorded with model
Metrics::assertRecorded(fn ($metric) =>
$metric->name() === 'purchases' &&
$metric->measurable()?->is($user)
);
}
 
public function test_batch_job_records_metrics()
{
Metrics::fake();
 
// Run your batch job
Artisan::call('orders:process');
 
// Assert metrics were recorded a specific number of times
Metrics::assertRecordedTimes('orders_processed', 100);
}

Access recorded metrics in tests:

Metrics::fake();
 
metric('api_calls')->category('users')->record();
 
// Get all recorded metrics
$all = Metrics::recorded();
 
// Get metrics by name
$apiCalls = Metrics::recorded('api_calls');
 
// Get metrics with a closure
$userEndpoint = Metrics::recorded(fn ($metric) =>
$metric->category() === 'users'
);

Extending & Customizing

Custom Metric Manager

Create your own metric manager by implementing the MetricManager interface:

namespace App\Metrics;
 
use DirectoryTree\Metrics\Measurable;
use DirectoryTree\Metrics\MetricManager;
 
class CustomMetricManager implements MetricManager
{
public function record(Measurable $metric): void
{
// Your custom recording logic...
}
 
public function commit(): void
{
// Your custom commit logic...
}
 
public function capture(): void
{
// Your custom capture logic...
}
 
public function isCapturing(): bool
{
// Your custom capturing check...
}
 
public function stopCapturing(): void
{
// Your custom stop capturing logic...
}
}

Then bind it in your AppServiceProvider:

use App\Metrics\CustomMetricManager;
use DirectoryTree\Metrics\MetricManager;
 
public function register(): void
{
$this->app->singleton(MetricManager::class, CustomMetricManager::class);
}

Custom Metric Repository

Create a custom repository for storing captured metrics:

namespace App\Metrics;
 
use DirectoryTree\Metrics\Measurable;
use DirectoryTree\Metrics\MetricRepository;
 
class CustomMetricRepository implements MetricRepository
{
public function add(Measurable $metric): void
{
// Your custom add logic...
}
 
public function all(): array
{
// Your custom retrieval logic...
}
 
public function flush(): void
{
// Your custom flush logic...
}
}

Then bind it in your AppServiceProvider:

use App\Metrics\CustomMetricRepository;
use DirectoryTree\Metrics\MetricRepository;
 
public function register(): void
{
$this->app->singleton(MetricRepository::class, CustomMetricRepository::class);
}
DirectoryTree photo

An open source company.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.


Directorytree Metrics Related Articles

Metrics: Simple, Elegant Metric Tracking for Laravel image

Metrics: Simple, Elegant Metric Tracking for Laravel

Read article
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Get expert guidance in a few days with a Laravel code review logo

Get expert guidance in a few days with a Laravel code review

Expert code review! Get clear, practical feedback from two Laravel devs with 10+ years of experience helping teams build better apps.

Get expert guidance in a few days with a Laravel code review
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
DreamzTech logo

DreamzTech

Hire 6-10+ Yrs. experienced skilled Laravel Developers from DreamzTech. We ensure NDA protected, 100% quality delivery. Contact Us & Discuss Your Need.

DreamzTech
LoadForge logo

LoadForge

Scalable load testing for web apps & APIs. Simulate real-world traffic and identify breaking points and performance limits with powerful, scalable load tests designed for Laravel.

LoadForge
Celebian logo

Celebian

Celebian is a social media marketing agency specializing in helping their clients go viral on TikTok. Whether you're looking to reach a bigger audience or gain more Tiktok followers, likes, and views, they've got you covered.

Celebian