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

Guzzle Stopwatch Middleware Laravel Package

csa/guzzle-stopwatch-middleware

Adds a Guzzle middleware that measures request/response timing with a stopwatch, making it easy to profile HTTP calls in your app. Install via Composer and plug into your Guzzle handler stack to track durations per request.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require csa/guzzle-stopwatch-middleware
    

    Add the middleware to your Guzzle client stack:

    use CSA\GuzzleStopwatchMiddleware\StopwatchMiddleware;
    
    $stack = HandlerStack::create();
    $stack->push(StopwatchMiddleware::class);
    $client = new Client(['handler' => $stack]);
    
  2. First Use Case Inject the client into a service and make a request:

    $response = $client->get('https://api.example.com/data');
    

    The middleware will automatically log request duration to monolog (if configured) or output to stderr.


Where to Look First

  • Configuration: Check config/guzzle-stopwatch.php (if published via service provider).
  • Logging: Verify Monolog integration in config/logging.php.
  • Middleware Class: Review StopwatchMiddleware for customization points (e.g., onRequest, onResponse).

Implementation Patterns

Core Workflow

  1. Middleware Injection Add to your Guzzle client stack in a service provider or config:

    $stack->push(StopwatchMiddleware::class, [
        'logger' => $this->app->make('logger'), // Optional: Inject custom logger
        'log_level' => 'debug',               // Optional: Override log level
    ]);
    
  2. Request/Response Hooks Extend the middleware for custom logic:

    class CustomStopwatchMiddleware extends StopwatchMiddleware
    {
        protected function onRequest(RequestInterface $request)
        {
            // Pre-request logic (e.g., tagging)
            $request->setMetadata('custom_tag', 'value');
        }
    
        protected function onResponse(ResponseInterface $response, $duration)
        {
            // Post-response logic (e.g., alerting)
            if ($duration > 1.0) {
                $this->logger->warning("Slow request: {$duration}s");
            }
        }
    }
    
  3. Integration with Laravel HTTP Clients Use with Laravel’s HTTP client facade:

    $client = app(\Illuminate\Http\Client\PendingRequest::class);
    $client->withOptions(['handler' => $stack]);
    

Advanced Patterns

  • Conditional Logging Filter requests by URI or method:

    $stack->push(StopwatchMiddleware::class, [
        'filter' => function ($request) {
            return strpos($request->getUri(), '/admin') !== false;
        }
    ]);
    
  • Performance Metrics Store durations in a database or cache for analytics:

    $this->onResponse(function (ResponseInterface $response, $duration) {
        cache()->put("request_duration_{$response->getRequest()->getUri()}", $duration, 3600);
    });
    
  • Async Processing Use with Guzzle’s async requests:

    $promises = [];
    foreach ($urls as $url) {
        $promises[] = $client->getAsync($url)->then(
            function ($response) use ($url) {
                $this->logger->info("Async request to {$url} completed");
            }
        );
    }
    

Gotchas and Tips

Common Pitfalls

  1. Logger Misconfiguration

    • Issue: No logs appear if Monolog isn’t properly configured.
    • Fix: Ensure config/logging.php includes a valid channel (e.g., single or stack).
    • Debug: Temporarily log to stderr:
      $stack->push(StopwatchMiddleware::class, ['logger' => new \Monolog\Logger('test', [new \Monolog\Handler\StreamHandler('php://stderr')])]);
      
  2. Middleware Order

    • Issue: Stopwatch may not capture full duration if placed after other middleware (e.g., retries).
    • Fix: Add it as the first middleware in the stack:
      $stack->push(StopwatchMiddleware::class); // Earliest position
      
  3. Async Requests

    • Issue: Async requests (getAsync) may not log durations if not handled explicitly.
    • Fix: Use then() or wait() to ensure completion:
      $client->getAsync($url)->then(function () {
          // Log duration via custom logic
      })->wait();
      

Debugging Tips

  • Check Middleware Execution Override onRequest/onResponse to verify timing:

    protected function onRequest(RequestInterface $request)
    {
        $this->logger->debug('Stopwatch started for: ' . $request->getUri());
    }
    
  • Log Duration Manually For complex workflows, manually track time:

    $start = microtime(true);
    $response = $client->get($url);
    $this->logger->info("Manual duration: " . (microtime(true) - $start));
    

Extension Points

  1. Custom Logging Extend the middleware to support structured logging (e.g., ELK):

    protected function logDuration($duration, $request, $response)
    {
        $this->logger->info([
            'event' => 'http.request',
            'duration_ms' => $duration * 1000,
            'method' => $request->getMethod(),
            'uri' => (string) $request->getUri(),
            'status' => $response->getStatusCode(),
        ]);
    }
    
  2. Metrics Integration Publish durations to Prometheus or Datadog:

    use Prometheus\CollectorRegistry;
    
    protected function onResponse(ResponseInterface $response, $duration)
    {
        $registry->getOrRegisterCounter('http_requests_total', 'HTTP requests')
            ->inc();
        $registry->getOrRegisterGauge('http_request_duration_seconds', 'HTTP request duration')
            ->set($duration, ['uri' => $response->getRequest()->getUri()]);
    }
    
  3. Conditional Middleware Dynamically enable/disable based on environment:

    if (app()->environment('production')) {
        $stack->push(StopwatchMiddleware::class);
    }
    

Configuration Quirks

  • Default Log Level The middleware defaults to debug. Change it in the constructor or config:

    $stack->push(StopwatchMiddleware::class, ['log_level' => 'info']);
    
  • Time Unit Durations are logged in seconds. Multiply by 1000 for milliseconds in logs:

    $this->logger->info(sprintf("Duration: %.3fms", $duration * 1000));
    
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.
craftcms/url-validator
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