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).

v3.8.1(4w ago)01092MITPHPPHP &gt;=8.1

Since May 24Pushed 1mo 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 3w ago

READMEChangelogDependencies (35)Versions (35)Used By (2)

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\DomainStructures\Utils\StrictDataObject;
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 StrictDataObject([
            'event' => 'user_login',
            'user_id' => 123,
            'ip' => '127.0.0.1',
            'success' => 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":{"event":"user_login","user_id":123,"ip":"127.0.0.1","success":true}}}
```

> ⚠️ Le payload utilise `StrictDataObject` qui préserve exactement les noms de clés (camelCase ou snake\_case). Le timestamp est automatique.

---

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
```

---

Types de payload
----------------

[](#types-de-payload)

`StrictDataObject` accepte n'importe quelle structure clé-valeur :

TypeExemple`int``'user_id' => 123``float``'amount' => 99.99``string``'ip' => '127.0.0.1'``bool``'success' => true``null``'optional' => null``array``'tags' => ['premium', 'vip']``AbstractRecord``'user' => $userRecord``TypedCollection``'items' => $collection`---

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

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

### Lire des propriétés

[](#lire-des-propriétés)

```
$userId = $log->data->payload->user_id;      // Accès direct
$ip = $log->data->payload->ip;               // via propriété
$value = $log->data->payload->get('key');    // avec valeur par défaut
$hasKey = $log->data->payload->has('key');   // Vérifier existence
```

### Convertir en tableau

[](#convertir-en-tableau)

```
$array = $log->data->payload->toArray();
// ['event' => 'user_login', 'user_id' => 123, ...]
```

### Immuabilité - Créer une nouvelle version

[](#immuabilité---créer-une-nouvelle-version)

```
$newPayload = $payload->with('status', 'completed');  // Ajoute/modifie
$merged = $payload->merge(['new_key' => 'value']);    // Fusionne
$reduced = $payload->without('temp_key');              // Supprime
```

---

Requêter les logs
-----------------

[](#requêter-les-logs)

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

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

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

$query = new LogQueryRecord(
    from: new IsoZuluTime('2026-04-05T00:00:00Z'),
    to: new IsoZuluTime('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(
    from: new IsoZuluTime('2026-04-01T00:00:00Z'),
    to: new IsoZuluTime('2026-04-30T23:59:59Z'),
    level: LogLevel::ERROR,
);

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

### Query combinée

[](#query-combinée)

```
$from = new IsoZuluTime(now()->subDay()->toIso8601ZuluString());

$query = new LogQueryRecord(
    from: $from,
    to: new IsoZuluTime(now()->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->getValue() . "\n";
    echo $log->level->value . "\n";
    echo $log->data->type . "\n";
    echo $log->data->payload->user_id . "\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");
});
```

---

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/structured

Files to delete:
  - 2024-01-01/00-01 (1024 bytes)
  - 2024-01-01/01-02 (2048 bytes)
  - 2024-01-02/00-01 (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
```

---

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

[](#exemples-concrets)

### Authentification

[](#authentification)

```
// Connexion réussie
$payload = new StrictDataObject([
    'event' => 'user_login',
    'user_id' => $user->id,
    'ip' => request()->ip(),
    'success' => true,
]);

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

// Échec de connexion
$payload = new StrictDataObject([
    'event' => 'user_login_failed',
    'email' => request()->email,
    'ip' => request()->ip(),
    'reason' => 'invalid_password',
]);

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

### Paiement

[](#paiement)

```
// Paiement réussi
$payload = new StrictDataObject([
    'event' => 'payment_success',
    'order_id' => $order->id,
    'stripe_id' => $stripeId,
    'amount' => $order->total,
]);

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

// Paiement échoué
$payload = new StrictDataObject([
    'event' => 'payment_failed',
    'order_id' => $order->id,
    'error' => $exception->getMessage(),
]);

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

### Log avec un Record personnalisé

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

```
use AndyDefer\DomainStructures\Abstracts\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 StrictDataObject([
    'event' => 'user_created',
    'user' => $userRecord,
]);

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

---

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->user_id === 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->user_id === 123));

// ❌ MAUVAIS - Fragile
$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. Première propriété = type d'événement

[](#1-première-propriété--type-dévénement)

```
// ✅
$payload = new StrictDataObject([
    'event' => 'user_login',
    'user_id' => $userId,
    'ip' => $ip,
]);

// ❌
$payload = new StrictDataObject([
    'user_id' => $userId,
    'event' => 'user_login',
]);
```

### 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->user_id === 123

// ❌ Tester du texte
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 StrictDataObject([
    'event' => 'user_login',
    'user_id' => $userId,
    'ip' => $ip,
    'success' => 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->user_id === $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**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 avec `IsoZuluTime`**Tests robustes**On teste la structure (`$log->payload->user_id`), 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 StrictDataObject([
    'event' => 'user_login',
    'user_id' => $user->id,
    'ip' => $ip,
    'success' => true,
]);

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

---

Licence
-------

[](#licence)

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

```
---

```

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance93

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity54

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

34

Last Release

28d 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 (53 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

[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

135224.7k8](/packages/statamic-rad-pack-runway)[duncanmcclean/statamic-cargo

Comprehensive e-commerce addon for Statamic. Build bespoke e-commerce sites without the complexity.

3518.3k](/packages/duncanmcclean-statamic-cargo)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[nightowl/agent

NightOwl monitoring agent — collects telemetry from laravel/nightwatch and writes to PostgreSQL

771.7k](/packages/nightowl-agent)

PHPackages © 2026

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