Skip to content

Usage Tracking

Jean-Marc Strauven edited this page Dec 23, 2025 · 1 revision

Usage Tracking

Learn how to track and monitor API version usage for analytics and migration planning.


Overview

Usage tracking helps you:

  • Monitor traffic per API version
  • Identify deprecated version usage
  • Plan migration timelines
  • Detect usage patterns

Enable Tracking

Configuration

// config/apiroute.php
'tracking' => [
    'enabled' => env('API_VERSION_TRACKING', true),
    'driver' => 'database',      // 'database', 'redis', 'null'
    'table' => 'api_version_stats',
    'aggregate' => 'hourly',     // 'realtime', 'hourly', 'daily'
],

Environment Variable

# .env
API_VERSION_TRACKING=true

Publish Migrations

php artisan vendor:publish --tag="apiroute-migrations"
php artisan migrate

Migration Structure

Schema::create('api_version_stats', function (Blueprint $table) {
    $table->id();
    $table->string('version', 10)->index();
    $table->string('endpoint')->index();
    $table->string('method', 10);
    $table->unsignedInteger('requests_count')->default(0);
    $table->unsignedInteger('success_count')->default(0);
    $table->unsignedInteger('error_count')->default(0);
    $table->date('date')->index();
    $table->unsignedTinyInteger('hour')->nullable();
    $table->timestamps();

    $table->unique(['version', 'endpoint', 'method', 'date', 'hour']);
});

Storage Drivers

Database Driver

Default driver. Stores stats in a MySQL/PostgreSQL table.

'tracking' => [
    'driver' => 'database',
    'table' => 'api_version_stats',
],

Pros:

  • Queryable with SQL
  • Persistent storage
  • Easy reporting

Cons:

  • Slightly higher latency
  • Database space usage

Redis Driver

High-performance option for high-traffic APIs.

'tracking' => [
    'driver' => 'redis',
],

Pros:

  • Very fast
  • Low latency
  • Scalable

Cons:

  • Requires Redis
  • Less persistent (unless configured)

Null Driver

Disables tracking (for testing).

'tracking' => [
    'driver' => 'null',
],

Aggregation Levels

Realtime

Track every request individually.

'aggregate' => 'realtime',

Use case: Small APIs, detailed analytics

Hourly (Default)

Aggregate stats per hour.

'aggregate' => 'hourly',

Use case: Medium traffic, balanced storage

Daily

Aggregate stats per day.

'aggregate' => 'daily',

Use case: High traffic, long-term storage


What Is Tracked

Each tracked request captures:

Field Description
version API version (v1, v2, etc.)
endpoint Request path
method HTTP method (GET, POST, etc.)
requests_count Total request count
success_count Successful requests (2xx, 3xx)
error_count Failed requests (4xx, 5xx)
date Date of request
hour Hour of request (if hourly aggregation)

Viewing Statistics

Command Line

# All versions (last 30 days)
php artisan api:stats

# Specific period
php artisan api:stats --period=7

# Specific version
php artisan api:stats --api-version=v1

# JSON output
php artisan api:stats --json

Sample Output

API Version Usage Statistics (Last 30 days)

Total Requests: 1,234,567

┌──────────────────────┬────────────┬────────────┬──────────┬────────┐
│ Version              │ Requests   │ Percentage │ Success  │ Errors │
├──────────────────────┼────────────┼────────────┼──────────┼────────┤
│ v2                   │ 967,901    │ 78.4%      │ 960,123  │ 7,778  │
│ v1 (deprecated)      │ 240,741    │ 19.5%      │ 238,456  │ 2,285  │
│ v3                   │ 25,925     │ 2.1%       │ 25,800   │ 125    │
└──────────────────────┴────────────┴────────────┴──────────┴────────┘

Programmatic Access

Using the Tracker Interface

use Grazulex\ApiRoute\Contracts\VersionTrackerInterface;

class ApiAnalyticsController extends Controller
{
    public function __construct(
        private VersionTrackerInterface $tracker
    ) {}

    public function stats()
    {
        // Get all stats for last 30 days
        $stats = $this->tracker->getAllStats(30);

        // Get stats for specific version
        $v1Stats = $this->tracker->getStats('v1', 30);

        return response()->json([
            'all' => $stats,
            'v1' => $v1Stats,
        ]);
    }
}

Response Format

{
    "all": {
        "v1": {
            "total_requests": 240741,
            "success_requests": 238456,
            "error_requests": 2285
        },
        "v2": {
            "total_requests": 967901,
            "success_requests": 960123,
            "error_requests": 7778
        }
    },
    "v1": {
        "total_requests": 240741,
        "success_requests": 238456,
        "error_requests": 2285
    }
}

Custom Tracking

Creating a Custom Tracker

<?php

namespace App\Tracking;

use Grazulex\ApiRoute\Contracts\VersionTrackerInterface;

class CustomTracker implements VersionTrackerInterface
{
    public function track(
        string $version,
        string $endpoint,
        string $method,
        int $status
    ): void {
        // Custom tracking logic
        // Send to analytics service, etc.
    }

    public function getStats(string $version, int $days): array
    {
        // Custom stats retrieval
    }

    public function getAllStats(int $days): array
    {
        // Custom all stats retrieval
    }
}

Register Custom Tracker

// In AppServiceProvider
use App\Tracking\CustomTracker;
use Grazulex\ApiRoute\Contracts\VersionTrackerInterface;

public function register(): void
{
    $this->app->bind(VersionTrackerInterface::class, CustomTracker::class);
}

Performance Considerations

Async Tracking

Tracking is done asynchronously after the response is sent:

dispatch(function () use ($request, $response) {
    $this->tracker->track(...);
})->afterResponse();

This ensures zero impact on API response time.

Redis for High Traffic

For high-traffic APIs, use Redis:

'tracking' => [
    'driver' => 'redis',
    'aggregate' => 'hourly',  // Reduce write frequency
],

Data Retention

Implement cleanup for old stats:

// In a scheduled command
DB::table('api_version_stats')
    ->where('date', '<', now()->subDays(90))
    ->delete();

Notifications Based on Stats

High Deprecated Usage Warning

// config/apiroute.php
'notifications' => [
    'enabled' => true,
    'events' => [
        'high_deprecated_usage' => 50, // Warn if > 50% on deprecated
    ],
],

Custom Alert Logic

use Grazulex\ApiRoute\Contracts\VersionTrackerInterface;

class DeprecatedUsageChecker
{
    public function __construct(
        private VersionTrackerInterface $tracker
    ) {}

    public function check(): void
    {
        $stats = $this->tracker->getAllStats(7);
        $total = array_sum(array_column($stats, 'total_requests'));

        foreach ($stats as $version => $data) {
            $percentage = ($data['total_requests'] / $total) * 100;

            if ($version === 'v1' && $percentage > 20) {
                // Send alert
                Notification::send(
                    $admins,
                    new HighDeprecatedUsageNotification($version, $percentage)
                );
            }
        }
    }
}

Dashboard Integration

Example Dashboard Query

class ApiDashboardController extends Controller
{
    public function index()
    {
        $stats = DB::table('api_version_stats')
            ->select('version', 'date')
            ->selectRaw('SUM(requests_count) as requests')
            ->selectRaw('SUM(success_count) as success')
            ->selectRaw('SUM(error_count) as errors')
            ->where('date', '>=', now()->subDays(30))
            ->groupBy('version', 'date')
            ->orderBy('date')
            ->get();

        return view('dashboard.api', compact('stats'));
    }
}

Next Steps

Clone this wiki locally