PHPackages                             michael4d45/context-logging - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. michael4d45/context-logging

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

michael4d45/context-logging
===========================

Contextual logging for Laravel applications

v6.3.1(1mo ago)1790↓76.6%MITPHPPHP ^8.3

Since Jan 5Pushed 1mo agoCompare

[ Source](https://github.com/Michael4d45/Laravel-Context-Logging)[ Packagist](https://packagist.org/packages/michael4d45/context-logging)[ RSS](/packages/michael4d45-context-logging/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (23)Versions (47)Used By (0)

Contextual Logging for Laravel
==============================

[](#contextual-logging-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/769451c90eab87fe50404f1e997e7491e25ac182c6388333307db949b55c8be6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d69636861656c346434352f636f6e746578742d6c6f6767696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/michael4d45/context-logging)[![Tests](https://camo.githubusercontent.com/14df43a1ac9f6bc6a243384d9b141f62926831ddf2d478c66355a99b0d0301fd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4d69636861656c346434352f436f6e746578742d4c6f6767696e672f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/Michael4d45/Context-Logging/actions?query=workflow%3Arun-tests+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/064aacb5cd327948be723cbd367563ac1048fdca5365f8cfec5318d46f118e52/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d69636861656c346434352f636f6e746578742d6c6f6767696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/michael4d45/context-logging)

This package provides a structured, context-first logging model for Laravel applications.

Instead of emitting many unstructured log lines during execution, the system accumulates contextual information throughout a request's lifecycle and emits **a single structured log event** at completion. The resulting log data is high-signal, query-friendly, and compatible with modern observability workflows.

**Inspired by** ["Logging sucks"](https://loggingsucks.com) - a comprehensive exploration of wide events and modern observability practices.

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require michael4d45/context-logging
```

Laravel Middleware Registration
-------------------------------

[](#laravel-middleware-registration)

This package requires two global HTTP middleware to function correctly:

- One to initialize request-level context
- One to emit a single structured log entry after the request completes

Laravel 12+ registers middleware via `bootstrap/app.php`.

### Registering Global Middleware

[](#registering-global-middleware)

Open `bootstrap/app.php` and locate the `withMiddleware` section.

Append the package middleware to the global stack:

```
use Illuminate\Foundation\Configuration\Middleware;
use Michael4d45\ContextLogging\Middleware\RequestContextMiddleware;
use Michael4d45\ContextLogging\Middleware\EmitContextMiddleware;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->append(RequestContextMiddleware::class);
    $middleware->append(EmitContextMiddleware::class);
})
```

- `RequestContextMiddleware` runs early and seeds request metadata.
- `EmitContextMiddleware` implements both `handle` and `terminate` methods, ensuring it runs during request processing and emits the structured log entry after the response is sent.

Both middleware are appended to run after Laravel's core request processing.

### Ordering Considerations

[](#ordering-considerations)

Middleware execution order matters.

Recommended placement:

- `RequestContextMiddleware` **after** request normalization middleware (e.g. trimming, proxy handling)
- `EmitContextMiddleware` **at the end** of the global stack

Using `append()` achieves this safely.

If you need the request context earlier, you may use `prepend()` instead:

```
$middleware->prepend(RequestContextMiddleware::class);
```

### Manually Managing the Global Middleware Stack (Optional)

[](#manually-managing-the-global-middleware-stack-optional)

If your application explicitly defines Laravel's global middleware stack using `use()`, include the package middleware in that list.

Example:

```
use Illuminate\Foundation\Configuration\Middleware;
use Michael4d45\ContextLogging\Middleware\RequestContextMiddleware;
use Michael4d45\ContextLogging\Middleware\EmitContextMiddleware;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->use([
        \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
        \Illuminate\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Http\Middleware\ValidatePostSize::class,
        \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,

        // Contextual logging
        RequestContextMiddleware::class,
        EmitContextMiddleware::class,
    ]);
})
```

When using `use()`, Laravel **does not** automatically include defaults—you are responsible for the full stack. Both middleware should be included in the array.

### Notes

[](#notes)

- Middleware registration is intentionally **not** automatic.
- This avoids surprising behavior and allows explicit control.
- No changes to route middleware or middleware groups are required.

### Summary

[](#summary)

To enable contextual logging in Laravel:

1. Install the package via Composer
2. Register the two global middleware in `bootstrap/app.php`
3. Continue using `Log::info()`, `Log::error()`, etc. as usual

Once enabled, the package will emit **one structured log event per request** using Laravel's existing logging configuration.

How It Works
------------

[](#how-it-works)

### Traditional Logging

[](#traditional-logging)

```
Application code → Log::info() → formatter → handler → output

```

### Contextual Logging

[](#contextual-logging)

```
Application code → Log::info() → in-memory context only
Request termination → single wide event → Laravel logger → output

```

Log calls become **annotations** that are collected into comprehensive wide events.

### Bootstrap and early log calls

[](#bootstrap-and-early-log-calls)

Pre-lifecycle behavior is split by **how PHP is running**:

- **HTTP (not `runningInConsole()`)**: Instrumentation and `Log::` calls before `RequestContextMiddleware` (e.g. `routes/channels.php`, providers) are **buffered** and **merged** into the request-wide log when the middleware starts the lifecycle—same idea as before.
- **Console (queue workers, Artisan, PHPUnit, etc.)**: Pre-lifecycle events are **emitted immediately** as standalone structured lines so nothing piles up in memory or gets stapled onto the wrong job or command.

Once a lifecycle has started, annotations accumulate only until that request/job/command completes. Long-lived lifecycles clear the store after emit so completed runs and jobs do not leak into the next one.

### Interrupted requests and console runs

[](#interrupted-requests-and-console-runs)

If PHP exits before Laravel reaches its normal termination phase, the package now falls back to a shutdown handler. That means a request or console lifecycle can still emit its accumulated wide event when execution is interrupted by things like `dd()`, `exit`, or a fatal error.

Fatal shutdowns add a `PHP fatal error` event automatically. Non-fatal interruptions still emit a synthetic interruption event when nothing else was logged, so abrupt exits are visible instead of disappearing.

### Tinker behavior

[](#tinker-behavior)

`artisan tinker` is treated differently from normal console commands. Instead of buffering one giant context until you leave the shell, each evaluated statement gets its own lifecycle and emits immediately after execution. That keeps Tinker logging usable without waiting for shell exit.

Philosophy: Wide Events Only
----------------------------

[](#philosophy-wide-events-only)

This package embraces **Wide Events** exclusively - no more scattered logs! Instead of emitting individual log entries throughout request processing, all logging calls are accumulated and emitted as a single comprehensive event at request completion.

**Why Wide Events?**

- **Complete context**: Every log call from the entire request in one place
- **Request metadata**: Method, path, duration, status, user info
- **Temporal ordering**: All events with precise timestamps
- **Query-friendly**: Single event per request for analysis
- **Clean logs**: No scattered entries to grep through

**The transformation:**

- ❌ **Before**: Dozens of individual log lines per request
- ✅ **After**: One structured event containing everything

Your logs stop lying to you. They start telling the whole truth.

Usage
-----

[](#usage)

The package preserves the existing Laravel logging interface. No changes to your application code are required!

```
