Weave 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

Http Logger Bundle Laravel Package

forci/http-logger-bundle

A Symfony bundle that captures and logs HTTP requests and responses, helping you debug APIs, trace outgoing calls, and inspect traffic in your application. Configurable handlers let you control what gets recorded and where the logs are written.

View on GitHub
Deep Wiki
Context7

Getting Started

First, confirm what this package actually is. forci/http-logger-bundle is a Symfony bundle — the -bundle suffix is structural, not cosmetic. It registers through Kernel::registerBundles() and a DI Extension class. Laravel has no equivalent lifecycle, no auto-discovery hook for it, and no facade or config-publish surface. So "getting started" for a Laravel developer means one of two things:

  1. Extract the pattern (the recommended path) into a Guzzle middleware + Monolog channel you own.
  2. Fork and shim it behind a hand-written ServiceProvider if a stakeholder forces reuse — accepting full maintenance ownership.

Read in this order before writing a line of code:

File Why
composer.json PHP constraint, license field, actual dependencies
src/DependencyInjection/Configuration.php This is the documentation — the full config surface lives here
The middleware/decorator class The interception seam: Symfony HttpClient middleware vs. PSR-18 decorator vs. Monolog handler. Only the last two are portable to Laravel's Guzzle-backed client.
Resources/config/*.yaml Service wiring and default channel names

License first. The metadata says NOASSERTION. No recognized LICENSE file means default all-rights-reserved. Do not vendor, fork, or ship this before legal review — that gate comes before any spike.

Your first shippable use case is outbound third-party API logging (payments, shipping, CRM). That's the highest-value slice and the one where the field set from this package — method, URI, status, latency, correlation ID, redacted headers/body — is directly reusable as a spec.

Laravel-native skeleton you can ship today, no dependency:

// config/logging.php
'channels' => [
    'http' => [
        'driver'  => 'daily',
        'path'    => storage_path('logs/http.log'),
        'level'   => 'info',
        'days'    => 14,
        'tap'     => [App\Logging\RedactSecrets::class],
        'formatter' => Monolog\Formatter\JsonFormatter::class,
    ],
],
// app/Providers/HttpLoggingServiceProvider.php
use Illuminate\Support\Facades\Http;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

public function boot(): void
{
    Http::globalMiddleware(Middleware::tap(
        function (RequestInterface $request) {
            // start timer, attach correlation id header
            $request = $request->withHeader('X-Request-Id', Correlation::id());
        },
        function (RequestInterface $request, array $options, ?ResponseInterface $response) {
            Log::channel('http')->info('http.out', [
                'method'  => $request->getMethod(),
                'uri'     => (string) $request->getUri(),
                'status'  => $response?->getStatusCode(),
                'ms'      => $options['on_stats'] ? null : null, // use on_stats below for timing
                'request_id' => Correlation::id(),
            ]);
        }
    ));
}

The real timing hook is Guzzle's on_stats option; wire it in the middleware's mapRequest. Start with method/URI/status/request_id — add bodies and timing after redaction is reviewed.

Implementation Patterns

1. Correlation ID as the backbone. Generate (or accept) a request ID in inbound middleware, push it into the log context, propagate it on every outbound call, and carry it across queue boundaries.

// app/Http/Middleware/CorrelationId.php
public function handle($request, Closure $next)
{
    $id = $request->header('X-Request-Id') ?: (string) Str::uuid();
    Log::withContext(['request_id' => $id]);
    $request->attributes->set('request_id', $id);

    $response = $next($request);
    return $response->header('X-Request-Id', $id);
}

Then in an outbound middleware, read Log::getLogger()->getHandlers() context — or simpler, a small Correlation singleton — and stamp the header on outbound requests.

2. Dedicated channel, structured JSON. Never write HTTP logs to the app channel. One channel, JSON formatter, own retention. This is what makes the logs queryable in CloudWatch/Datadog/Loki without regex.

3. Redaction tap. The package does not encode a policy you can defend in a review. Own it:

class RedactSecrets
{
    private array $deny = ['authorization', 'cookie', 'set-cookie', 'x-api-key', 'password', 'card_number'];

    public function __invoke($logger): void
    {
        foreach ($logger->getHandlers() as $handler) {
            $handler->pushProcessor(function (array $record) {
                $ctx = $record['context'] ?? [];
                foreach (['headers', 'body'] as $field) {
                    foreach ($ctx[$field] ?? [] as $k => $_) {
                        if (in_array(strtolower($k), $this->deny, true)) {
                            $ctx[$field][$k] = '[REDACTED]';
                        }
                    }
                }
                $record['context'] = $ctx;
                return $record;
            });
        }
    }
}

4. Sampling and size caps. Log every 4xx/5xx and every call over a latency threshold. Sample 2xx at a low rate (e.g. 1-in-100). Hash or truncate bodies over ~4 KB.

$shouldLog = $response->getStatusCode() >= 400
    || $durationMs > 1000
    || random_int(1, 100) === 1;

5. Inbound middleware vs. outbound middleware — keep them separate. Inbound captures method/path/status/duration/request-id. Outbound captures method/URI/status/duration and which integration was called. Two channels or one channel with a direction field — pick one and be consistent, because mixing makes dashboards painful.

6. Queue/worker context propagation. Log::withContext() does not survive serialization into a queued job. Pass the request ID explicitly in the job payload and re-push it into context at the start of handle().

7. Testing pattern. Assert on log output, not on the package:

Log::spy();
Http::fake(['api.stripe.com/*' => Http::response([], 200)]);
Http::get('https://api.stripe.com/v1/charges');

Log::shouldHaveReceived('channel')->with('http');
// or capture via a test handler and assert on fields

8. If you do use the Symfony bundle (in a Symfony service, not Laravel): register it in Kernel::registerBundles(), configure the Monolog channel in config/packages/monolog.yaml, and confirm whether the interception happens at the Symfony HttpClient middleware layer — because if it does, it will not touch Laravel's Guzzle client in the same monorepo.

Gotchas and Tips

  • NOASSERTION is the hard blocker, not a footnote. Absence of a LICENSE file defaults to all-rights-reserved. You cannot confidently redistribute or fork into a commercial product until counsel rules. Everything else in this list is secondary to that.

  • Silent no-op is the worst failure mode. If the bundle intercepts symfony/http-client, it will register cleanly in a mixed repo and log nothing for Laravel's outbound calls, because those go through Guzzle. You find out during an incident, exactly when you needed the logs. Verify interception by firing one real outbound call and inspecting the file before you trust it.

  • Log::withContext() bleeds across requests in Octane and queue workers. Long-lived workers reuse the logger; context from request N is still there in request N+1. Reset it — in Octane via a RequestReceived listener, in jobs at the top of handle().

  • Logger exceptions can fail the outbound call. A disk-full or bad-formatter exception inside Guzzle middleware propagates. Wrap logging in try/catch (\Throwable) and swallow — logging must never break the thing it observes.

  • PII and credential leakage is a reportable exposure, not a bug. Auth headers, Set-Cookie, request bodies with card or token data. Redaction must be yours, tested, and reviewed — do not inherit it from a zero-adoption dependency.

  • Body capture is a memory bomb. Unbounded response buffering on large downloads can exhaust PHP worker memory. Check Content-Length and stream-type first; cap or skip bodies over N KB.

  • High-cardinality URIs wreck log-index cost. GET /users/12345/orders/98765 in a hosted aggregator is a billing incident. Normalize to /users/{id}/orders/{id} before writing.

  • Synchronous file writes in the request path add latency. Use a buffered/queued handler (whatfailuregroup, or a Monolog BufferHandler flushed on terminate) for anything other than low-volume debugging.

  • Guzzle version alignment. Laravel pins Guzzle 7. Any middleware code you borrow from the bundle must be checked against that constraint; a PSR-18 decorator is version-safer than a Guzzle-specific one.

  • Debugging checklist when nothing logs: confirm the channel name matches (Log::channel('http')), confirm the handler level (info swallows nothing, but a misconfigured level on daily will), confirm the listener actually registered (Event::getRawListeners()), and confirm storage/logs is writable by the worker user.

  • Extension points you actually want: a Monolog processor for redaction, a custom formatter for JSON, and a Log::withContext push in inbound middleware — all first-party, all testable. That's the whole surface area of what this package provides, rebuilt in ~150 lines you own.

  • Do not adopt this package in a Laravel codebase. Use it as a field-selection reference, ship the native equivalent on Illuminate\Http\Client events plus Guzzle middleware, and revisit OpenTelemetry PHP if tracing or sampling requirements appear — this package covers neither.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package