PHPackages                             andydefer/laravel-logger - 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. andydefer/laravel-logger

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

andydefer/laravel-logger
========================

A structured logging package for Laravel that writes logs in JSONL format (JSON Lines).

3.1.1(1w ago)0431MITPHPPHP &gt;=8.1

Since May 24Pushed 2w agoCompare

[ Source](https://github.com/andydefer/laravel-logger)[ Packagist](https://packagist.org/packages/andydefer/laravel-logger)[ RSS](/packages/andydefer-laravel-logger/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (23)Versions (9)Used By (1)

Laravel Logger
==============

[](#laravel-logger)

**Un package de logging structuré pour Laravel qui écrit les logs au format JSONL (JSON Lines).**

[![PHP Version](https://camo.githubusercontent.com/83dd395020c37276225039739320f6c8e7e99963ab21ee3d09282cb48dad2a60/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e312532422d626c7565)](https://php.net)[![Laravel Version](https://camo.githubusercontent.com/ba69236eb9bfe25effcb7eb44086de41847364b41b47c2e3f6c3975cd2653974/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31322e7825323025374325323031332e7825323025374325323031342e7825323025374325323031352e782d626c7565)](https://laravel.com)[![License](https://camo.githubusercontent.com/bbd05f341c8cc2fef766a381c121830e990070db1c11179202f86be4fbfae318/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e63652d4d49542d677265656e)](LICENSE)

---

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

[](#installation)

```
composer require andydefer/laravel-logger
```

Le package s'enregistre automatiquement via Laravel.

---

Configuration
-------------

[](#configuration)

### Variables d'environnement (optionnel)

[](#variables-denvironnement-optionnel)

```
LOGGER_PATH=/custom/log/path
LOGGER_RETENTION_DAYS=60
```

### Publication du fichier de config (optionnel)

[](#publication-du-fichier-de-config-optionnel)

```
php artisan vendor:publish --tag=logger-config
```

---

Premier log
-----------

[](#premier-log)

```
use AndyDefer\Logger\Collections\MixedPayloadCollection;
use AndyDefer\Logger\Records\LogDataRecord;
use AndyDefer\Logger\Contracts\LoggerInterface;

class UserController extends Controller
{
    public function __construct(
        private readonly LoggerInterface $logger,
    ) {}

    public function login()
    {
        $payload = new MixedPayloadCollection();
        $payload->add('user_login', 123, '127.0.0.1', true);

        $logData = new LogDataRecord(type: 'auth', payload: $payload);

        $this->logger->info($logData);
    }
}
```

**Résultat dans le fichier de log :**

```
{"time":"2026-04-05T10:26:00Z","level":"info","data":{"type":"auth","payload":["user_login",123,"127.0.0.1",true]}}
```

---

Les 4 niveaux de log
--------------------

[](#les-4-niveaux-de-log)

```
$logger->debug($logData);   // DEBUG
$logger->info($logData);    // INFO
$logger->warning($logData); // WARNING
$logger->error($logData);   // ERROR
```

> Le timestamp est automatique.

---

Types acceptés dans un payload
------------------------------

[](#types-acceptés-dans-un-payload)

TypeExemple`int``$payload->add(123)``float``$payload->add(99.99)``string``$payload->add('hello')``bool``$payload->add(true)``null``$payload->add(null)``AbstractRecord``$payload->add($userRecord)``TypedCollection``$payload->add($tags)`> La méthode `add()` accepte plusieurs paramètres : `$payload->add('user_login', 123, '127.0.0.1', true)`

---

Travailler avec le payload
--------------------------

[](#travailler-avec-le-payload)

### Lire des éléments

[](#lire-des-éléments)

```
$first = $payload->firstItem();      // Premier élément
$last = $payload->lastItem();        // Dernier élément
$array = $payload->toArray();        // Tout en tableau
```

### Compter

[](#compter)

```
$count = $payload->count();           // Nombre d'éléments
$isEmpty = $payload->isEmpty();       // Collection vide ?
$isNotEmpty = $payload->isNotEmpty(); // Collection non vide ?

```

### Filtrer

[](#filtrer)

```
// Éléments > 3
$filtered = $payload->filter(fn($item) => $item > 3);

// Uniquement les strings
$strings = $payload->ofType('string');

// Uniquement les entiers
$ints = $payload->ofType('int');

// Uniquement les scalaires
$scalars = $payload->scalars();

// Uniquement les Records
$records = $payload->records();
```

### Transformer

[](#transformer)

```
// Doubler chaque élément
$doubles = $payload->map(fn($item) => $item * 2);

// Supprimer les doublons
$unique = $payload->unique();

// Mélanger
$shuffled = $payload->shuffle();
```

### Calculs (pour collections numériques)

[](#calculs-pour-collections-numériques)

```
$total = $payload->sum();     // Somme
$moyenne = $payload->avg();   // Moyenne
$max = $payload->max();       // Maximum
$min = $payload->min();       // Minimum
```

### Vérifications

[](#vérifications)

```
// Un élément existe ?
if ($payload->contains(123)) { ... }

// Tous sont du même type ?
if ($payload->isHomogeneous()) { ... }

// Tous sont des entiers ?
$payload->assertAllOfType('int');
```

---

Rechercher des logs
-------------------

[](#rechercher-des-logs)

### Query par type d'événement

[](#query-par-type-dévénement)

```
use AndyDefer\Logger\Records\LogQueryRecord;

$query = new LogQueryRecord(
    from: '2026-04-05T00:00:00Z',
    to: '2026-04-05T23:59:59Z',
    type: 'user_login',
);

$results = $logger->query($query);
```

### Query par niveau

[](#query-par-niveau)

```
use AndyDefer\Logger\Enums\LogLevel;

$query = new LogQueryRecord(
    level: LogLevel::ERROR,
);

$errors = $logger->query($query);
```

### Query combinée

[](#query-combinée)

```
$query = new LogQueryRecord(
    from: now()->subDay()->toIso8601ZuluString(),
    type: 'payment_failed',
    level: LogLevel::ERROR,
);

$failedPayments = $logger->query($query);
```

### Parcourir les résultats

[](#parcourir-les-résultats)

```
foreach ($results as $log) {
    echo $log->time . "\n";
    echo $log->level->value . "\n";
    echo $log->data->type . "\n";

    foreach ($log->data->payload as $item) {
        echo $item . "\n";
    }
}
```

### Streaming (tous les logs d'un jour)

[](#streaming-tous-les-logs-dun-jour)

```
// Jour spécifique
$logs = $logger->stream('2026-04-05');

// Aujourd'hui
$logs = $logger->stream();

foreach ($logs as $log) {
    // Traitement...
}
```

---

Buffer d'écriture (performance)
-------------------------------

[](#buffer-décriture-performance)

Le buffer regroupe les logs en mémoire avant de les écrire sur le disque.

### Activer le buffer

[](#activer-le-buffer)

```
$logger->enableBuffer(100);  // 100 logs avant écriture automatique
```

### Utilisation

[](#utilisation)

```
$logger->enableBuffer(50);

// Ces logs restent en mémoire
for ($i = 0; $i < 50; $i++) {
    $logger->info($logData);
}

// Déclenche l'écriture automatique
$logger->info($logData);

// Ou vider manuellement
$logger->flush();
```

### Désactiver

[](#désactiver)

```
$logger->disableBuffer();  // Vide automatiquement le buffer
```

### Callback à chaque flush

[](#callback-à-chaque-flush)

```
$logger->enableBuffer(100);
$logger->onFlush(function ($count) {
    \Log::info("{$count} logs écrits");
});
```

---

Exemples concrets
-----------------

[](#exemples-concrets)

### Authentification

[](#authentification)

```
// Connexion réussie
$payload = new MixedPayloadCollection();
$payload->add('user_login', $user->id, request()->ip(), true);

$logger->info(new LogDataRecord(type: 'auth', payload: $payload));

// Échec de connexion
$payload = new MixedPayloadCollection();
$payload->add('user_login_failed', request()->email, request()->ip(), 'invalid_password');

$logger->warning(new LogDataRecord(type: 'auth', payload: $payload));
```

### Paiement

[](#paiement)

```
// Paiement réussi
$payload = new MixedPayloadCollection();
$payload->add('payment_success', $order->id, $stripeId, $order->total);

$logger->info(new LogDataRecord(type: 'payment', payload: $payload));

// Paiement échoué
$payload = new MixedPayloadCollection();
$payload->add('payment_failed', $order->id, $exception->getMessage());

$logger->error(new LogDataRecord(type: 'payment', payload: $payload));
```

### Log avec un Record personnalisé

[](#log-avec-un-record-personnalisé)

```
use AndyDefer\Records\AbstractRecord;

final class UserRecord extends AbstractRecord
{
    public function __construct(
        public readonly int $id,
        public readonly string $email,
        public readonly string $role,
    ) {}
}

$userRecord = new UserRecord(id: 1, email: 'john@example.com', role: 'admin');

$payload = new MixedPayloadCollection();
$payload->add('user_created', $userRecord);

$logger->info(new LogDataRecord(type: 'user', payload: $payload));
```

### Log d'API externe

[](#log-dapi-externe)

```
$payload = new MixedPayloadCollection();
$payload->add('api_call', 'stripe', '/v1/customers', 'POST', json_encode($data));

$logger->info(new LogDataRecord(type: 'api', payload: $payload));
```

---

Commandes avec la directive
---------------------------

[](#commandes-avec-la-directive)

Le package intègre une directive pour nettoyer les vieux logs.

### Nettoyer les vieux logs

[](#nettoyer-les-vieux-logs)

```
# Nettoyer les logs de plus de 30 jours (valeur par défaut)
./vendor/bin/directive logger-clean

# Nettoyer les logs de plus de 60 jours
./vendor/bin/directive logger-clean --days=60

# Simulation (ne supprime rien)
./vendor/bin/directive logger-clean --dry-run

# Mode verbeux (affiche les fichiers à supprimer)
./vendor/bin/directive logger-clean --verbose

# Avec alias
./vendor/bin/directive clean-logs
./vendor/bin/directive log-clean

# Toutes les options combinées
./vendor/bin/directive logger-clean --days=90 --dry-run --verbose
```

### Exemple de sortie

[](#exemple-de-sortie)

```
$ ./vendor/bin/directive logger-clean --dry-run --verbose

Current statistics:
  Files: 45
  Size: 12.5 MB
  Lines: 15230
  Range: 2024-01-01 to 2024-01-31
  Path: storage/logs/directives

Files to delete:
  - 2024-01-01/00 (1024 bytes)
  - 2024-01-01/01 (2048 bytes)
  - 2024-01-02/00 (512 bytes)

⚠️ Dry run mode - no files will be deleted
Would delete files older than 2024-01-01
Would delete 15 file(s)
```

### Lister toutes les directives disponibles

[](#lister-toutes-les-directives-disponibles)

```
./vendor/bin/directive --list
```

---

Tests unitaires
---------------

[](#tests-unitaires)

### Mock du Logger

[](#mock-du-logger)

```
use AndyDefer\Logger\Contracts\LoggerInterface;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;

#[AllowMockObjectsWithoutExpectations]
class UserServiceTest extends TestCase
{
    public function test_login_logs_success(): void
    {
        $logger = $this->createMock(LoggerInterface::class);

        $logger->expects($this->once())
            ->method('info')
            ->with($this->callback(function ($logData) {
                return $logData->type === 'auth'
                    && $logData->payload->contains('user_login')
                    && $logData->payload->contains(123);
            }));

        $service = new UserService($logger);
        $service->login(123);
    }
}
```

### Tester la structure, pas le texte

[](#tester-la-structure-pas-le-texte)

```
// ✅ BON - Test robuste
$logger->expects($this->once())
    ->method('info')
    ->with($this->callback(fn($log) => $log->payload->contains(123)));

// ❌ MAUVAIS - Fragile (Laravel natif)
$logger->expects($this->once())
    ->method('info')
    ->with('User 123 logged in');
```

---

LogLevel - méthodes utilitaires
-------------------------------

[](#loglevel---méthodes-utilitaires)

```
use AndyDefer\Logger\Enums\LogLevel;

$level = LogLevel::INFO;

$level->getLabel();   // 'Info'
$level->isDebug();    // false
$level->isInfo();     // true
$level->isWarning();  // false
$level->isError();    // false

// Toutes les valeurs
LogLevel::values();   // ['debug', 'info', 'warning', 'error']

// Depuis une valeur
LogLevel::fromValue('info'); // LogLevel::INFO
```

---

Bonnes pratiques
----------------

[](#bonnes-pratiques)

### 1. Premier élément = type d'événement

[](#1-premier-élément--type-dévénement)

```
// ✅
$payload->add('user_login', $userId, $ip, $success);

// ❌
$payload->add($userId, 'user_login', $ip);
```

### 2. snake\_case pour les types

[](#2-snake_case-pour-les-types)

```
// ✅
'type' => 'user_login'
'type' => 'payment_failed'

// ❌
'type' => 'userLogin'
```

### 3. Injection uniquement, pas de facade

[](#3-injection-uniquement-pas-de-facade)

```
// ✅ Injection explicite
class MyService
{
    public function __construct(
        private readonly LoggerInterface $logger,
    ) {}
}

// ❌ Éviter les facades
\Log::info(...);
```

### 4. Tester la structure

[](#4-tester-la-structure)

```
// ✅ Tester la présence des données
$log->payload->contains(123)

// ❌ Tester du texte (Laravel natif)
str_contains($log, 'User 123')
```

---

Règle d'or
----------

[](#règle-dor)

> **ZÉRO appel statique. TOUTES les dépendances injectées. Le timestamp est automatique. Les tests vérifient la STRUCTURE, pas le TEXTE.**

```
// ✅ Le log parfait
$payload = new MixedPayloadCollection();
$payload->add('user_login', $userId, $ip, true);

$logger->info(new LogDataRecord(type: 'auth', payload: $payload));
```

```
// ✅ Le test parfait
$logger->expects($this->once())
    ->method('info')
    ->with($this->callback(fn($log) =>
        $log->type === 'auth'
        && $log->payload->contains($userId)
    ));
```

---

Pourquoi ce package ?
---------------------

[](#pourquoi-ce-package-)

### Les faiblesses du système de log natif de Laravel

[](#les-faiblesses-du-système-de-log-natif-de-laravel)

ProblèmeExplicationConséquence**Format non structuré**Les logs sont du texte libreImpossible de parser ou filtrer efficacement**Types non préservés**`Log::info('message', ['user' => $user])` → `"Array"`Perte d'information, données inexploitables**Pas de requêtage**On ne peut chercher que par texteImpossible de filtrer par type d'événement ou par niveau**Tests fragiles**`assertStringContainsString('User 123', $log)`Un simple changement de texte casse les tests**Pas de séparation sémantique**Message et contexte mélangésImpossible d'extraire proprement les données**Format non standard**Format propriétaire LaravelDifficile à intégrer avec des outils externes (ELK, Loki, Datadog)### Les avantages de ce package

[](#les-avantages-de-ce-package)

AvantageExplication**Format JSONL standard**Chaque ligne est un JSON valide, compatible avec tous les outils**Types préservés**Les entiers, booléens, objets restent typés**Requêtage puissant**Filtrage par type, niveau, plage de dates**Tests robustes**On teste la structure, pas le texte**Séparation claire**`type` = événement, `payload` = données**Performance**Buffer d'écriture, organisation par heure**Maintenance automatique**Nettoyage des vieux logs configurable**Directive intégrée**Nettoyage via CLI sans dépendre d'Artisan### Exemple comparatif

[](#exemple-comparatif)

```
// ❌ Laravel natif - Perte d'information
Log::info("Utilisateur {$user->id} connecté", ['ip' => $ip]);
// Sortie: [2024-01-15 14:30:00] local.INFO: Utilisateur 123 connecté {"ip":"127.0.0.1"}

// ✅ Ce package - Structure complète
$payload = new MixedPayloadCollection();
$payload->add('user_login', $user->id, $ip, true);

$logger->info(new LogDataRecord(type: 'auth', payload: $payload));
// Sortie: {"time":"2024-01-15T14:30:00Z","level":"info","data":{"type":"auth","payload":["user_login",123,"127.0.0.1",true]}}
```

Licence
-------

[](#licence)

MIT © [Andy Defer](https://github.com/andydefer)

```

```

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance98

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

8

Last Release

8d ago

Major Versions

2.0.4 → 3.0.02026-06-01

### Community

Maintainers

![](https://www.gravatar.com/avatar/2170ec3fbad9eb4b002661ab4f58b1cc374eae4293b92904c6a74bc2818bd570?d=identicon)[andydefer](/maintainers/andydefer)

---

Top Contributors

[![andydefer](https://avatars.githubusercontent.com/u/124321745?v=4)](https://github.com/andydefer "andydefer (13 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Psalm, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/andydefer-laravel-logger/health.svg)

```
[![Health](https://phpackages.com/badges/andydefer-laravel-logger/health.svg)](https://phpackages.com/packages/andydefer-laravel-logger)
```

###  Alternatives

[open-telemetry/opentelemetry-auto-laravel

OpenTelemetry auto-instrumentation for Laravel

582.4M8](/packages/open-telemetry-opentelemetry-auto-laravel)[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

135212.4k7](/packages/statamic-rad-pack-runway)[guanguans/laravel-exception-notify

Monitor exception and report to the notification channels(Log、Mail、AnPush、Bark、Chanify、DingTalk、Discord、Gitter、GoogleChat、IGot、Lark、Mattermost、MicrosoftTeams、NowPush、Ntfy、Push、Pushback、PushBullet、PushDeer、PushMe、Pushover、PushPlus、QQ、RocketChat、ServerChan、ShowdocPush、SimplePush、Slack、Telegram、WeWork、WPush、XiZhi、YiFengChuanHua、ZohoCliq、ZohoCliqWebHook、Zulip).

14844.4k1](/packages/guanguans-laravel-exception-notify)[ecotone/laravel

Ecotone for Laravel — CQRS, Event Sourcing, Sagas, Durable Workflows, and Outbox on top of Laravel Queue, via PHP attributes.

21313.7k3](/packages/ecotone-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
