Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Prometheus Laravel Package

shureban/laravel-prometheus

Laravel package to expose Prometheus metrics. Install via Composer, publish config, switch Redis client to Predis, and create Counter/Gauge metrics via artisan commands or custom classes. Use DI to increment metrics with label values in your controllers.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Installation:

    composer require shureban/laravel-prometheus
    

    Register the service provider in config/app.php:

    Shureban\LaravelPrometheus\PrometheusServiceProvider::class,
    
  2. Publish Config:

    php artisan vendor:publish --provider="Shureban\LaravelPrometheus\PrometheusServiceProvider"
    

    Update .env to use REDIS_CLIENT=predis.

  3. First Use Case: Generate a counter metric via CLI:

    php artisan make:counter AuthEvents --name=auth_events --labels=event,user_id --description="Auth-related events"
    

    Use it in a controller:

    use App\Prometheus\AuthEvents;
    
    public function login(AuthEvents $authEvents) {
        $authEvents->withLabelsValues(['event' => 'login', 'user_id' => auth()->id()])->inc();
    }
    
  4. Verify Metrics Endpoint: Access /prometheus/metrics (default route) to see collected metrics in Prometheus format.


Implementation Patterns

1. Metric Creation Workflows

CLI-Generated Metrics

  • Use Artisan commands for quick setup:
    php artisan make:counter ApiRequests --name=api_requests --labels=endpoint,status --dynamic
    
    • --dynamic enables runtime label adjustments (e.g., per-request labels).

Manual Metric Classes

  • Organize custom metrics in app/Prometheus/:
    namespace App\Prometheus;
    
    use Shureban\LaravelPrometheus\{Counter, Name, Labels};
    
    class ApiRequests extends Counter {
        public function __construct() {
            parent::__construct(
                new Name('api_requests'),
                new Labels(['endpoint', 'status']),
                'Total API requests by endpoint and status'
            );
        }
    
        public function success(string $endpoint): void {
            $this->withLabelsValues(['endpoint' => $endpoint, 'status' => '200'])->inc();
        }
    }
    

Dependency Injection

  • Inject metrics into controllers/services:
    public function store(ApiRequests $apiRequests) {
        $apiRequests->success('users.create');
    }
    

2. Dynamic Label Patterns

Runtime Label Assignment

  • Use withLabels() for dynamic labels (e.g., user-specific metrics):
    $counter->withLabels(Labels::newFromArray([
        'event' => 'purchase',
        'user_id' => $user->id,
        'amount' => $amount
    ]))->inc();
    

Collection-Based Labels

  • Convert Eloquent models/arrays to labels:
    $counter->withLabels(Labels::newFromCollection($user->only(['country', 'browser'])))->inc();
    

Encapsulated Methods

  • Hide label logic in metric classes:
    class UserActivity extends Counter {
        public function login(string $ip): void {
            $this->withLabelsValues(['event' => 'login', 'ip' => $ip])->inc();
        }
    }
    

3. Integration with Laravel Features

Middleware for Automatic Metrics

  • Track HTTP requests globally:
    use Shureban\LaravelPrometheus\Http\Middleware\PrometheusMiddleware;
    
    Route::middleware([PrometheusMiddleware::class])->group(function () {
        // All routes here will auto-increment `http_requests_total`.
    });
    

Event Listeners

  • Log business events:
    use App\Prometheus\AuthEvents;
    
    public function handle(Registered $event, AuthEvents $authEvents) {
        $authEvents->withLabelsValues(['event' => 'registration'])->inc();
    }
    

Queue Job Metrics

  • Monitor job processing:
    use App\Prometheus\QueueMetrics;
    
    public function handle(QueueMetrics $queueMetrics) {
        $queueMetrics->withLabelsValues(['job' => self::class, 'status' => 'started'])->inc();
        // ... job logic ...
        $queueMetrics->withLabelsValues(['job' => self::class, 'status' => 'completed'])->inc();
    }
    

4. Prometheus Configuration

Custom Endpoint

  • Override the default /prometheus/metrics route in config/prometheus.php:
    'web_route' => 'metrics',
    

Manual Rendering

  • For non-web contexts (e.g., CLI scripts):
    use Shureban\LaravelPrometheus\RenderTextFormat;
    
    $renderer = new RenderTextFormat();
    file_put_contents('metrics.txt', $renderer->render());
    

Redis Backend

  • Configure Redis connection in config/prometheus.php:
    'redis' => [
        'connection' => 'cache', // or 'redis'
        'prefix' => 'prometheus:',
    ],
    

Gotchas and Tips

Pitfalls

  1. Redis Dependency:

    • Issue: Metrics require Redis. If Redis is down, metrics collection fails silently.
    • Fix: Add Redis health checks and retry logic:
      try {
          Redis::connection()->ping();
      } catch (\Predis\Connection\ConnectionException $e) {
          Log::error('Redis unavailable; metrics collection paused.', ['error' => $e]);
      }
      
  2. Label Cardinality Explosion:

    • Issue: High-cardinality labels (e.g., user_id) can bloat Redis memory.
    • Fix: Use static labels for dimensions with low cardinality (e.g., event_type) and dynamic labels for high-cardinality fields (e.g., user_id).
  3. Middleware Overhead:

    • Issue: Prometheus middleware adds latency to every request.
    • Fix: Exclude non-critical routes or use sampling:
      Route::middleware([PrometheusMiddleware::class, 'sample_rate=0.1'])->group(...);
      
  4. Dynamic Metrics Naming:

    • Issue: Dynamically generated metric names (e.g., api_requests_{endpoint}) may conflict with static metrics.
    • Fix: Prefix dynamic metrics:
      $counter = new Counter(new Name('custom_api_requests_{$endpoint}'), ...);
      
  5. Prometheus Scrape Failures:

    • Issue: Prometheus may fail to scrape /metrics due to auth or network issues.
    • Fix: Add a health check endpoint:
      Route::get('/health', function () {
          return response()->json(['status' => 'ok']);
      });
      
      Configure Prometheus to scrape this instead if metrics are secondary.

Debugging Tips

  1. Verify Metrics Collection:

    • Check Redis for metric keys:
      redis-cli KEYS "prometheus:*"
      
    • Inspect raw metric data:
      redis-cli HGETALL prometheus:api_requests
      
  2. Log Metric Operations:

    • Extend metric classes to log increments:
      class DebugCounter extends Counter {
          public function inc(): void {
              Log::debug("Incrementing {$this->name} with labels: " . json_encode($this->labels));
              parent::inc();
          }
      }
      
  3. Test Locally with Prometheus:

    • Run Prometheus locally to validate scraping:
      # prometheus.yml
      scrape_configs:
        - job_name: 'laravel'
          static_configs:
            - targets: ['host.docker.internal:8000'] # or your Laravel URL
      
    • Query metrics in Prometheus UI (http://localhost:9090).
  4. Profile Redis Usage:

    • Monitor Redis memory with:
      redis-cli INFO memory
      
    • Set maxmemory policies in config/database.php:
      'options' => [
          'maxmemory-policy' => 'allkeys-lru',
      ],
      

Extension Points

  1. Custom Metric Types:

    • Extend the base classes for new metric types (e.g., histograms):
      namespace Shureban\LaravelPrometheus;
      
      class Histogram extends Metric {
          // Custom implementation
      }
      
  2. Alternative Backends:

    • Replace Redis with another storage (e.g., database) by implementing Shureban\LaravelPrometheus\Contracts\MetricStorage:
      class DatabaseStorage implements MetricStorage {
          public function store(Metric $metric) {
              DB::table('prometheus_metrics')->insert([...]);
          }
      }
      
  3. Metric Aggregation:

    • Pre-aggregate metrics in a queue job to reduce Redis load:
      public function handle() {
          $metrics = collect(Redis::hgetall('prometheus:api_requests'));
          $aggregated = $metrics->groupBy('endpoint')->map(fn ($group) => $group->sum('value'));
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony
spatie/flare-daemon-runtime