> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Context

> Learn how to use Laravel's Context feature to share information across requests, jobs, and commands, and automatically attach it to your logs.

## What is Context

Laravel's Context feature is a mechanism for recording and sharing information across requests, queue jobs, and command executions.
When you add information through the `Illuminate\Support\Facades\Context` facade, that information is automatically attached to every log entry your application writes.

This lets you clearly distinguish between data passed to individual log calls and shared information held by Context.
It's especially useful for tracing in distributed systems or queue-based architectures.

### Context propagation flow

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Middleware
    participant Controller
    participant Queue
    participant Job

    Client->>Middleware: HTTP request
    Middleware->>Middleware: Context::add('trace_id', uuid)
    Middleware->>Controller: next($request)
    Controller->>Controller: Log::info(...) — trace_id auto-attached
    Controller->>Queue: ProcessPodcast::dispatch()
    Queue->>Job: Context serialized and sent
    Job->>Job: Context restored (Hydrate)
    Job->>Job: Log::info(...) — trace_id auto-attached
```

## Basic usage

The most typical usage is to set a `trace_id` in middleware. It will automatically be included in every subsequent log entry.

<Steps>
  <Step title="Create the middleware">
    ```shell theme={null}
    php artisan make:middleware AddContext
    ```
  </Step>

  <Step title="Add a trace ID to Context">
    ```php theme={null}
    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Context;
    use Illuminate\Support\Str;
    use Symfony\Component\HttpFoundation\Response;

    class AddContext
    {
        public function handle(Request $request, Closure $next): Response
        {
            Context::add('url', $request->url());
            Context::add('trace_id', Str::uuid()->toString());

            return $next($request);
        }
    }
    ```
  </Step>

  <Step title="Register the middleware">
    Register it as global middleware in `bootstrap/app.php`.

    ```php theme={null}
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(\App\Http\Middleware\AddContext::class);
    })
    ```
  </Step>
</Steps>

After this setup, logs written from your controllers and services will automatically be tagged with `url` and `trace_id`.

```php theme={null}
Log::info('User authenticated.', ['auth_id' => Auth::id()]);
```

```text theme={null}
User authenticated. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}
```

## Writing to context

### add — add a value

```php theme={null}
use Illuminate\Support\Facades\Context;

Context::add('key', 'value');

// Add several at once
Context::add([
    'first_key'  => 'value',
    'second_key' => 'value',
]);
```

`add` overwrites existing keys. Use `addIf` to add only when the key does not already exist.

```php theme={null}
Context::add('key', 'first');
Context::addIf('key', 'second');

Context::get('key');
// "first" — not overwritten
```

### increment / decrement — manage counters

Dedicated methods for incrementing or decrementing numbers. Specify the change amount as the second argument.

```php theme={null}
Context::increment('records_added');
Context::increment('records_added', 5);

Context::decrement('records_added');
Context::decrement('records_added', 5);
```

### when — add conditionally

The `when` method lets you add different data depending on whether a condition is `true` or `false`.

```php theme={null}
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Context;

Context::when(
    Auth::user()->isAdmin(),
    fn ($context) => $context->add('permissions', Auth::user()->permissions),
    fn ($context) => $context->add('permissions', []),
);
```

### push — add to a stack

Context supports "stacks" that hold list-style data.
Using `push`, values are appended in the order they are added.

```php theme={null}
Context::push('breadcrumbs', 'first_value');
Context::push('breadcrumbs', 'second_value', 'third_value');

Context::get('breadcrumbs');
// ['first_value', 'second_value', 'third_value']
```

Here's an example of recording query execution history on a stack:

```php theme={null}
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\DB;

// Register in the boot method of AppServiceProvider.php
DB::listen(function ($event) {
    Context::push('queries', [$event->time, $event->sql]);
});
```

## Retrieving context

### get / all

```php theme={null}
$value = Context::get('key');

// Retrieve everything
$data = Context::all();
```

### only / except — retrieve a subset

```php theme={null}
$data = Context::only(['first_key', 'second_key']);

$data = Context::except(['first_key']);
```

### pull / pop — retrieve and remove

`pull` retrieves a key's value and removes it from the context at the same time.

```php theme={null}
$value = Context::pull('key');
```

To pop the last value off a stack, use `pop`.

```php theme={null}
Context::push('breadcrumbs', 'first_value', 'second_value');

Context::pop('breadcrumbs');
// 'second_value'

Context::get('breadcrumbs');
// ['first_value']
```

### remember — set and return if missing

```php theme={null}
$permissions = Context::remember(
    'user-permissions',
    fn () => $user->permissions,
);
```

### has / missing — check whether a key exists

```php theme={null}
if (Context::has('key')) {
    // ...
}

if (Context::missing('key')) {
    // ...
}
```

<Info>
  `has` returns `true` even if `null` is stored. It only checks whether the key is registered.
</Info>

## Removing context

Use `forget` to remove keys.

```php theme={null}
Context::add(['first_key' => 1, 'second_key' => 2]);

Context::forget('first_key');

Context::all();
// ['second_key' => 2]

// Remove multiple at once
Context::forget(['first_key', 'second_key']);
```

## Scoped context

The `scope` method lets you temporarily change context only while a closure runs, and automatically restores the previous state afterward.
It's useful for testing or when you want to include temporary additional information in logs for a specific local operation.

```php theme={null}
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\Log;

Context::add('trace_id', 'abc-999');
Context::addHidden('user_id', 123);

Context::scope(
    function () {
        Context::add('action', 'adding_friend');

        $userId = Context::getHidden('user_id');

        Log::debug("Adding user [{$userId}] to friends list.");
        // Adding user [987] to friends list.  {"trace_id":"abc-999","user_name":"taylor_otwell","action":"adding_friend"}
    },
    data: ['user_name' => 'taylor_otwell'],
    hidden: ['user_id' => 987],
);

// After the scope ends, values return to what they were
Context::all();
// ['trace_id' => 'abc-999']

Context::allHidden();
// ['user_id' => 123]
```

<Warning>
  If you modify an object inside the scope, that change is reflected outside the scope as well. There's no problem with primitive values.
</Warning>

## Hidden Context

Data you don't want to appear in logs (passwords, API keys, PII, etc.) should be stored in Hidden Context.
It cannot be retrieved via the ordinary `get` method—only via dedicated methods like `getHidden`.

```php theme={null}
use Illuminate\Support\Facades\Context;

Context::addHidden('key', 'value');

Context::getHidden('key');
// 'value'

Context::get('key');
// null — not retrievable via ordinary get
```

Hidden Context has the same set of methods as regular context.

```php theme={null}
Context::addHidden(/* ... */);
Context::addHiddenIf(/* ... */);
Context::pushHidden(/* ... */);
Context::getHidden(/* ... */);
Context::pullHidden(/* ... */);
Context::popHidden(/* ... */);
Context::onlyHidden(/* ... */);
Context::exceptHidden(/* ... */);
Context::allHidden(/* ... */);
Context::hasHidden(/* ... */);
Context::missingHidden(/* ... */);
Context::forgetHidden(/* ... */);
```

## Propagating to queue jobs

When you dispatch a job to a queue, the current context is automatically serialized into the job's payload.
When the job runs, the original context is restored, so the `trace_id` you attached during the request automatically flows through to queue-side logs.

```php theme={null}
// Set in middleware
Context::add('trace_id', Str::uuid()->toString());

// Dispatch the job in the controller
ProcessPodcast::dispatch($podcast);
```

```php theme={null}
class ProcessPodcast implements ShouldQueue
{
    use Queueable;

    public function handle(): void
    {
        Log::info('Processing podcast.', ['podcast_id' => $this->podcast->id]);
    }
}
```

```text theme={null}
Processing podcast. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}
```

You can see that the request-time `trace_id` also appears in the queue-side log.

### Dehydrating — customize on job send

Use `Context::dehydrating` to transform the context immediately before a job is sent.
For example, use it when you want to pass the locale determined by the `Accept-Language` header to the queue.

```php theme={null}
// AppServiceProvider.php

use Illuminate\Log\Context\Repository;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Context;

public function boot(): void
{
    Context::dehydrating(function (Repository $context) {
        $context->addHidden('locale', Config::get('app.locale'));
    });
}
```

<Warning>
  Inside a `dehydrating` callback, do not use the `Context` facade; only operate on the `$context` repository passed to the callback.
  Using the facade would modify the current process's context.
</Warning>

### Hydrated — restore on job execution

Use `Context::hydrated` to add processing right when the context is restored just before a job runs.
For example, apply a saved locale to your configuration.

```php theme={null}
// AppServiceProvider.php

use Illuminate\Log\Context\Repository;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Context;

public function boot(): void
{
    Context::hydrated(function (Repository $context) {
        if ($context->hasHidden('locale')) {
            Config::set('app.locale', $context->getHidden('locale'));
        }
    });
}
```

<Warning>
  Inside a `hydrated` callback, do not use the `Context` facade either; only operate on the `$context` repository passed in.
</Warning>

## Summary

<AccordionGroup>
  <Accordion title="Context vs Log::withContext">
    |                           | `Context`                     | `Log::withContext`        |
    | ------------------------- | ----------------------------- | ------------------------- |
    | Scope                     | All log channels              | Specific channels only    |
    | Propagation to queue jobs | Automatic (Dehydrate/Hydrate) | None                      |
    | Hidden data               | Supported                     | None                      |
    | Purpose                   | Tracing, distributed systems  | Channel-specific metadata |
  </Accordion>

  <Accordion title="Data that belongs in Hidden Context">
    Because Hidden Context is not written to logs, you can safely store the following types of data:

    * Session IDs or user IDs (when you don't want them in logs)
    * API keys or authentication tokens
    * Locales or configuration values (that you want to propagate to queues but not log)
    * Internal flags or state
  </Accordion>

  <Accordion title="Common Dehydrate/Hydrate use patterns">
    1. **Propagating locale**: Save `app.locale` into Hidden Context in `dehydrating`, then `Config::set` in `hydrated` to restore.
    2. **Propagating authentication info**: Make the authenticated user's information from the request available inside queue jobs.
    3. **Tenant ID**: In multi-tenant applications, share the tenant identifier across queues.
  </Accordion>
</AccordionGroup>


## Related topics

- [Session context and filtering](/en/packages/laravel-copilot-sdk/session-context.md)
- [Logging](/en/logging.md)
- [Streaming events](/en/packages/laravel-copilot-sdk/streaming-events.md)
- [Telemetry](/en/packages/laravel-copilot-sdk/telemetry.md)
- [Laravel AI SDK](/en/ai-sdk.md)
