Passer au contenu principal

Qu’est-ce que Context

Context est le mécanisme de Laravel pour enregistrer et partager des informations entre les requêtes, les jobs de file d’attente et les commandes. Les informations ajoutées via la façade Illuminate\Support\Facades\Context sont automatiquement ajoutées à chaque entrée de log écrite par l’application. Cela permet de distinguer clairement les informations passées à un appel de log spécifique et celles partagées via Context. Particulièrement utile pour le tracing dans les systèmes distribués ou basés sur des files d’attente.

Flux de propagation du contexte

Utilisation de base

Le cas le plus classique : définir un trace_id dans un middleware. Il apparaît alors automatiquement dans toutes les entrées de log qui suivent.
1

Créer le middleware

php artisan make:middleware AddContext
2

Ajouter un trace ID au Context

<?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);
    }
}
3

Enregistrer le middleware

Enregistrez-le comme middleware global dans bootstrap/app.php.
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\App\Http\Middleware\AddContext::class);
})
Après cette configuration, les logs écrits dans les contrôleurs ou services contiennent automatiquement url et trace_id.
Log::info('User authenticated.', ['auth_id' => Auth::id()]);
User authenticated. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}

Écriture dans le contexte

add — Ajouter une valeur

use Illuminate\Support\Facades\Context;

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

// Ajout multiple
Context::add([
    'first_key'  => 'value',
    'second_key' => 'value',
]);
add écrase les clés existantes. Pour n’ajouter que si la clé n’existe pas, utilisez addIf.
Context::add('key', 'first');
Context::addIf('key', 'second');

Context::get('key');
// "first" — non écrasée

increment / decrement — Compteurs

Méthodes dédiées à l’incrémentation/décrémentation. Le second argument précise le pas.
Context::increment('records_added');
Context::increment('records_added', 5);

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

when — Ajout conditionnel

when permet d’ajouter des données différentes selon un booléen.
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 — Empiler dans une pile

Context gère des piles pour des données de type liste. push empile dans l’ordre d’ajout.
Context::push('breadcrumbs', 'first_value');
Context::push('breadcrumbs', 'second_value', 'third_value');

Context::get('breadcrumbs');
// ['first_value', 'second_value', 'third_value']
Exemple : enregistrer l’historique des requêtes SQL dans une pile.
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\DB;

// Dans la méthode boot de AppServiceProvider.php
DB::listen(function ($event) {
    Context::push('queries', [$event->time, $event->sql]);
});

Lecture du contexte

get / all

$value = Context::get('key');

// Tout récupérer
$data = Context::all();

only / except — Sous-ensembles

$data = Context::only(['first_key', 'second_key']);

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

pull / pop — Lire et supprimer

pull lit la valeur puis la retire du contexte.
$value = Context::pull('key');
Pour dépiler la dernière valeur d’une pile, utilisez pop.
Context::push('breadcrumbs', 'first_value', 'second_value');

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

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

remember — Définir si absent

$permissions = Context::remember(
    'user-permissions',
    fn () => $user->permissions,
);

has / missing — Existence d’une clé

if (Context::has('key')) {
    // ...
}

if (Context::missing('key')) {
    // ...
}
has retourne true même si la valeur stockée est null. Elle vérifie uniquement la présence de la clé.

Suppression

Utilisez forget pour supprimer une clé.
Context::add(['first_key' => 1, 'second_key' => 2]);

Context::forget('first_key');

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

// Suppression multiple
Context::forget(['first_key', 'second_key']);

Contexte scopé

scope modifie temporairement le contexte pour la durée d’une closure, puis restaure l’état initial. Pratique pour joindre des informations locales à vos logs.
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],
);

// Après le scope, l'état d'origine est restauré
Context::all();
// ['trace_id' => 'abc-999']

Context::allHidden();
// ['user_id' => 123]
Si vous modifiez un objet dans le scope, la modification persiste au-delà du scope. Ce n’est pas un problème pour les valeurs primitives.

Hidden Context

Les données à ne pas exposer dans les logs (mots de passe, clés d’API, données personnelles) doivent être placées dans le Hidden Context. get normal ne les retourne pas ; utilisez les méthodes dédiées getHidden.
use Illuminate\Support\Facades\Context;

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

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

Context::get('key');
// null — invisible via get normal
Le Hidden Context propose les mêmes méthodes que le contexte standard.
Context::addHidden(/* ... */);
Context::addHiddenIf(/* ... */);
Context::pushHidden(/* ... */);
Context::getHidden(/* ... */);
Context::pullHidden(/* ... */);
Context::popHidden(/* ... */);
Context::onlyHidden(/* ... */);
Context::exceptHidden(/* ... */);
Context::allHidden(/* ... */);
Context::hasHidden(/* ... */);
Context::missingHidden(/* ... */);
Context::forgetHidden(/* ... */);

Transmission aux jobs de queue

Quand un job est dispatché, le contexte est automatiquement sérialisé dans le payload. Il est restauré à l’exécution du job, ce qui permet au trace_id défini pendant la requête d’être présent dans les logs de la queue.
// Définition dans le middleware
Context::add('trace_id', Str::uuid()->toString());

// Dispatch dans le contrôleur
ProcessPodcast::dispatch($podcast);
class ProcessPodcast implements ShouldQueue
{
    use Queueable;

    public function handle(): void
    {
        Log::info('Processing podcast.', ['podcast_id' => $this->podcast->id]);
    }
}
Processing podcast. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}
Le trace_id de la requête est bien présent dans les logs du job.

Dehydrating — Personnaliser l’envoi du job

Context::dehydrating permet de modifier le contexte juste avant l’envoi du job. Par exemple, pour transmettre la locale déterminée par l’en-tête Accept-Language.
// 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'));
    });
}
Dans un callback dehydrating, n’utilisez pas la façade Context. Manipulez uniquement le $context passé en argument. L’utilisation de la façade modifierait le contexte du processus courant.

Hydrated — Restauration lors de l’exécution du job

Context::hydrated s’exécute juste avant que le job ne démarre, une fois le contexte restauré. Par exemple, pour réappliquer la locale stockée dans la configuration.
// 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'));
        }
    });
}
Dans un callback hydrated aussi, n’utilisez pas la façade Context : manipulez uniquement le $context fourni.

Récapitulatif

ContextLog::withContext
CibleTous les channels de logUn channel spécifique
Transmission aux jobsAutomatique (Dehydrate/Hydrate)Aucune
Données cachéesPrises en chargeNon
UsageTracing, systèmes distribuésMétadonnées propres à un channel
Le Hidden Context n’est pas écrit dans les logs, il convient donc pour :
  • Identifiants de session ou utilisateur (à ne pas exposer)
  • Clés d’API et tokens d’authentification
  • Locale et valeurs de configuration à transmettre aux jobs sans polluer les logs
  • Flags internes et états
  1. Transmission de locale : sauvegarder app.locale en Hidden Context via dehydrating, puis restaurer via Config::set dans hydrated.
  2. Propagation d’authentification : partager les informations de l’utilisateur authentifié entre la requête et le job.
  3. ID de tenant : partager l’identifiant de tenant entre requête et job dans une application multi-tenant.
Dernière modification le 13 juillet 2026