PHP code example of andydefer / laravel-jsonl

1. Go to this page and download the library: Download andydefer/laravel-jsonl library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

andydefer / laravel-jsonl example snippets


use AndyDefer\LaravelJsonl\JsonlService;
use AndyDefer\LaravelJsonl\Contexts\JsonlContext;
use AndyDefer\LaravelJsonl\Strategies\TemporalPathStrategy;
use AndyDefer\PhpServices\Services\FileSystemService;

$strategy = new TemporalPathStrategy('/var/logs');
$fileSystem = new FileSystemService();
$context = new JsonlContext();
$service = new JsonlService($strategy, $fileSystem, $context);

use AndyDefer\LaravelJsonl\Records\LogJsonlRecord;
use AndyDefer\PhpVo\ValueObjects\DateTimeVO;
use AndyDefer\DomainStructures\Structures\StrictDataObject;

$log = new LogJsonlRecord(
    time: new DateTimeVO(),                    // Date/heure de l'événement
    level: 'info',                             // Niveau (info, warning, error, debug)
    type: 'user_login',                        // Type d'événement
    payload: new StrictDataObject([            // Données métier
        'user_id' => 123,
        'ip' => '192.168.1.100',
    ]),
);

use AndyDefer\LaravelJsonl\Records\CacheJsonlRecord;

$cache = new CacheJsonlRecord(
    key: 'user_profile_123',                  // Clé unique
    value: json_encode(['name' => 'John']),   // Valeur (JSON string)
    expires_at: new DateTimeVO('+1 hour'),    // Expiration (optionnel)
);

// Le service demande à la stratégie : "Où dois-je mettre ce log ?"
$filePath = $strategy->getFilePath($logRecord);

$strategy = new TemporalPathStrategy('/var/logs');

$log = new LogJsonlRecord(
    time: new DateTimeVO('2026-01-15T14:35:00Z'),
    // ...
);

$path = $strategy->getFilePath($log);
// Résultat: /var/logs/2026-01-15/14.jsonl

$strategy = new KeyBasedPathStrategy('/cache', hashLevels: 2);

$cache = new CacheJsonlRecord(key: 'user_123', ...);

$path = $strategy->getFilePath($cache);
// Résultat: /cache/e/1/user_123.jsonl

use AndyDefer\LaravelJsonl\Contracts\JsonlPathStrategyInterface;

/**
 * Organisation par tenant (multi-entreprise)
 * Structure: /{tenant_id}/{YYYY-MM-DD}/{HH}.jsonl
 */
class TenantBasedPathStrategy implements JsonlPathStrategyInterface
{
    public function __construct(
        private string $basePath,
        private string $tenantId,
    ) {}

    public function getFilePath(AbstractRecord $entity): string
    {
        if (!$entity instanceof LogJsonlRecord) {
            throw new InvalidArgumentException('Expected LogJsonlRecord');
        }

        $date = $entity->time->format('Y-m-d');
        $hour = $entity->time->format('H');

        return implode(DIRECTORY_SEPARATOR, [
            rtrim($this->basePath, DIRECTORY_SEPARATOR),
            $this->tenantId,
            $date,
            $hour . '.jsonl',
        ]);
    }

    public function getFilesToScan(AbstractRecord $query): array
    {
        // Logique pour scanner tous les fichiers du tenant sur la plage demandée
        // ...
    }

    public function getBaseDirectory(): string
    {
        return $this->basePath;
    }
}

use AndyDefer\LaravelJsonl\JsonlService;
use AndyDefer\LaravelJsonl\Contexts\JsonlContext;
use AndyDefer\LaravelJsonl\Records\LogJsonlRecord;
use AndyDefer\LaravelJsonl\Strategies\TemporalPathStrategy;
use AndyDefer\PhpServices\Services\FileSystemService;
use AndyDefer\PhpVo\ValueObjects\DateTimeVO;

$strategy = new TemporalPathStrategy(storage_path('logs/structured'));
$fs = new FileSystemService();
$context = new JsonlContext();
$service = new JsonlService($strategy, $fs, $context);

$log = new LogJsonlRecord(
    time: new DateTimeVO(),
    level: 'info',
    type: 'user_login',
    payload: new StrictDataObject([
        'user_id' => 123,
        'username' => 'john_doe',
        'ip' => '192.168.1.100',
    ]),
);

$service->write($log);

use AndyDefer\LaravelJsonl\Records\CacheJsonlRecord;
use AndyDefer\LaravelJsonl\Strategies\KeyBasedPathStrategy;

$strategy = new KeyBasedPathStrategy(storage_path('cache'), 2);
$service = new JsonlService($strategy, $fs, $context);

$cache = new CacheJsonlRecord(
    key: 'user_123',
    value: json_encode(['name' => 'John', 'email' => '[email protected]']),
    expires_at: new DateTimeVO('+1 hour'),
);

$service->write($cache);

// Lire toutes les lignes
$lines = $service->readAll('/var/logs/2026-01-15/14.jsonl');
foreach ($lines as $line) {
    echo $line['type'] . "\n";
}

// Lire ligne par ligne (streaming - économique en mémoire)
$service->readLineByLine('/var/logs/2026-01-15/14.jsonl', function ($line) {
    echo $line['time'] . ': ' . $line['level'] . "\n";
});

// Lire uniquement la première ou dernière ligne
$first = $service->getFirstLine('/var/logs/2026-01-15/14.jsonl');
$last = $service->getLastLine('/var/logs/2026-01-15/14.jsonl');

// Trouver toutes les erreurs
$errors = $service->search('/var/logs/2026-01-15/14.jsonl', function ($line) {
    return $line['level'] === 'error';
});

// Trouver les logs d'un utilisateur spécifique
$userLogs = $service->search('/var/logs/2026-01-15/14.jsonl', function ($line) {
    return isset($line['payload']['user_id']) && $line['payload']['user_id'] === 123;
});

$logs = [
    new LogJsonlRecord(/* ... */),
    new LogJsonlRecord(/* ... */),
    // ...
];

$service->writeBatch($logs); // Une seule opération I/O

// Pas de verrou (plus rapide mais pas sûr en concurrence)
$service->write($record, lock: false);

$files = [
    '/var/logs/2026-01-15/14.jsonl',
    '/var/logs/2026-01-15/15.jsonl',
];

$errors = $service->searchMultiple($files, function ($line) {
    return $line['level'] === 'error';
});

$context = new JsonlProcessingContext();

try {
    $service->write($log, context: $context);
    
    if ($context->isCompleted()) {
        echo "Files processed: " . $context->getProcessedFiles()->count();
        echo "Lines written: " . $context->getTotalLinesProcessed();
        echo "Duration: " . $context->getDuration() . " seconds";
    }
} catch (JsonlException $e) {
    echo "Failed: " . $context->getLastError();
}

// Buffer de 100 lignes avant écriture automatique
$service->enableBuffer(100);

$service->enableBuffer(50);

for ($i = 0; $i < 100; $i++) {
    $service->writeBuffered($log);  // Stocké en mémoire
    
    // Toutes les 50 écritures, flush automatique
}

$service->flushBuffer();  // Flush manuel des derniers

$service->enableBuffer(100);
$service->onFlush(function ($filePath, $count) {
    echo "Flushed {$count} lines to {$filePath}\n";
});

$service->disableBuffer();  // Flush puis désactive

// Verrou automatique (par défaut)
$service->write($record);  // lock = true

// Sans verrou (dangereux en concurrence)
$service->write($record, lock: false);

// Acquérir un verrou
if ($service->acquire('/var/logs/app.jsonl', timeout: 5)) {
    try {
        // Opérations exclusives
        $this->fileSystem->append('/var/logs/app.jsonl', $data);
    } finally {
        $service->release('/var/logs/app.jsonl');
    }
}

$result = $service->executeWithLock('/var/logs/app.jsonl', function () use ($service) {
    $content = $service->readAll('/var/logs/app.jsonl');
    $content[] = ['time' => date('c'), 'event' => 'processed'];
    return count($content);
});

// Supprimer les fichiers de plus de 30 jours
$deleted = $service->cleanOlderThan(30, '/var/logs');
echo "Deleted {$deleted} old log files";

// Supprimer tous les fichiers du 15 janvier
$pattern = '/var/logs/2026-01-15/*.jsonl';
$deleted = $service->cleanByPattern($pattern);

$deleted = $service->cleanExpired('/cache', function ($line) {
    if (!isset($line['expires_at'])) {
        return false;
    }
    $expiresAt = new DateTimeVO($line['expires_at']);
    return $expiresAt->isBefore(new DateTimeVO());
});

// Voir ce qui serait supprimé sans rien effacer
$filesToDelete = $service->dryRun('/var/logs', function ($file) {
    return filemtime($file) < strtotime('-30 days');
});

foreach ($filesToDelete as $file) {
    echo "Would delete: {$file}\n";
}

if (count($filesToDelete) > 0) {
    $confirm = readline("Proceed with deletion? (y/n): ");
    if ($confirm === 'y') {
        $service->cleanOlderThan(30, '/var/logs');
    }
}

$deleted = $service->clear('/var/app/cache');
echo "Cleared {$deleted} cache files";

// config/jsonl.php
return [
    'base_path' => env('JSONL_BASE_PATH', storage_path('jsonl')),
    'buffer_size' => env('JSONL_BUFFER_SIZE', null),
    'directory_permission' => (int) env('JSONL_DIRECTORY_PERMISSION', 755),
];

$service->enableBuffer(100);
// Écritures...
$service->flushBuffer();

// Pour les logs
$query = new TemporalLogQueryRecord(
    from: new DateTimeVO('2026-01-15T00:00:00Z'),
    to: new DateTimeVO('2026-01-15T23:59:59Z'),
    type: 'user_login',      // Optionnel
    level: 'info',           // Optionnel
);

// Pour le cache
$query = new CacheKeyQueryRecord(key: 'user_123');

// ✅ BON - Données structurées
$payload = new StrictDataObject([
    'event' => 'user_login',
    'user_id' => 123,
    'ip' => '192.168.1.100',
]);

// ❌ MAUVAIS - Texte non structuré
$payload = new StrictDataObject(['message' => 'User 123 logged in from 192.168.1.100']);

try {
    $service->write($record);
} catch (JsonlException $e) {
    // Problème d'écriture
    Log::error('Failed to write JSONL: ' . $e->getMessage());
} catch (JsonlLockException $e) {
    // Timeout de verrou
    Log::warning('Could not acquire lock: ' . $e->getMessage());
}

// Cron daily
$service->cleanOlderThan(30, '/var/logs');
$service->cleanExpired('/cache', $isExpiredCallback);

class UserActivityLogger
{
    private JsonlService $service;

    public function __construct()
    {
        $strategy = new TemporalPathStrategy(storage_path('logs/activities'));
        $fs = new FileSystemService();
        $context = new JsonlContext();
        $this->service = new JsonlService($strategy, $fs, $context);
        $this->service->enableBuffer(50);
    }

    public function logLogin(int $userId, string $ip, bool $success): void
    {
        $log = new LogJsonlRecord(
            time: new DateTimeVO(),
            level: $success ? 'info' : 'warning',
            type: 'user_login',
            payload: new StrictDataObject([
                'user_id' => $userId,
                'ip' => $ip,
                'success' => $success,
                'timestamp' => time(),
            ]),
        );

        $this->service->writeBuffered($log);
    }

    public function getFailedLogins(DateTimeVO $date): array
    {
        $query = new TemporalLogQueryRecord(
            from: $date,
            to: $date,
            type: 'user_login',
            level: 'warning',
        );

        $files = $this->service->getFilesToScan($query);
        $failed = [];

        foreach ($files as $file) {
            $lines = $this->service->search($file, function ($line) {
                return $line['payload']['success'] === false;
            });
            $failed = array_merge($failed, $lines);
        }

        return $failed;
    }

    public function __destruct()
    {
        $this->service->flushBuffer();
    }
}

class PersistentCache
{
    private JsonlService $service;
    private int $ttl;

    public function __construct(int $defaultTtlSeconds = 3600)
    {
        $strategy = new KeyBasedPathStrategy(storage_path('cache/persistent'), 2);
        $fs = new FileSystemService();
        $context = new JsonlContext();
        $this->service = new JsonlService($strategy, $fs, $context);
        $this->ttl = $defaultTtlSeconds;
    }

    public function set(string $key, mixed $value, ?int $ttl = null): void
    {
        $expiresAt = $ttl !== null
            ? new DateTimeVO("+{$ttl} seconds")
            : ($this->ttl ? new DateTimeVO("+{$this->ttl} seconds") : null);

        $cache = new CacheJsonlRecord(
            key: $key,
            value: json_encode($value),
            expires_at: $expiresAt,
        );

        $this->service->write($cache);
    }

    public function get(string $key): mixed
    {
        $query = new CacheKeyQueryRecord(key: $key);
        $files = $this->service->getFilesToScan($query);

        if (empty($files) || !$this->service->fileExists($files[0])) {
            return null;
        }

        $data = $this->service->readAll($files[0]);
        if (empty($data)) {
            return null;
        }

        $record = CacheJsonlRecord::fromArray($data[0]);

        if ($this->service->isExpired($record)) {
            unlink($files[0]);
            return null;
        }

        return json_decode($record->value, true);
    }

    public function delete(string $key): void
    {
        $query = new CacheKeyQueryRecord(key: $key);
        $files = $this->service->getFilesToScan($query);

        if (!empty($files) && $this->service->fileExists($files[0])) {
            unlink($files[0]);
        }
    }

    public function clear(): void
    {
        $this->service->clear(storage_path('cache/persistent'));
    }

    public function cleanExpired(): int
    {
        return $this->service->cleanExpired(storage_path('cache/persistent'), function ($line) {
            if (!isset($line['expires_at'])) {
                return false;
            }
            $expiresAt = new DateTimeVO($line['expires_at']);
            return $expiresAt->isBefore(new DateTimeVO());
        });
    }
}

// AppServiceProvider.php
use AndyDefer\LaravelJsonl\Contexts\JsonlContext;
use AndyDefer\LaravelJsonl\JsonlService;
use AndyDefer\LaravelJsonl\Strategies\TemporalPathStrategy;
use AndyDefer\LaravelJsonl\Strategies\KeyBasedPathStrategy;
use AndyDefer\PhpServices\Services\FileSystemService;

public function register(): void
{
    // Contexte partagé
    $this->app->singleton(JsonlContext::class);

    // Service pour les logs
    $this->app->singleton('jsonl.logs', function ($app) {
        $strategy = new TemporalPathStrategy(storage_path('logs/structured'));
        $fs = new FileSystemService();
        $context = $app->make(JsonlContext::class);
        return new JsonlService($strategy, $fs, $context);
    });

    // Service pour le cache
    $this->app->singleton('jsonl.cache', function ($app) {
        $strategy = new KeyBasedPathStrategy(storage_path('cache/jsonl'), 2);
        $fs = new FileSystemService();
        $context = $app->make(JsonlContext::class);
        $service = new JsonlService($strategy, $fs, $context);
        $service->enableBuffer(100);
        return $service;
    });
}

// Controller
class AnalyticsController extends Controller
{
    public function track(Request $request)
    {
        $jsonl = app('jsonl.logs');
        
        $log = new LogJsonlRecord(
            time: new DateTimeVO(),
            level: 'info',
            type: 'page_view',
            payload: new StrictDataObject([
                'page' => $request->path(),
                'user_id' => auth()->id(),
                'user_agent' => $request->userAgent(),
            ]),
        );
        
        $jsonl->writeBuffered($log);
        
        return response()->json(['status' => 'tracked']);
    }
}
bash
php artisan vendor:publish --tag=jsonl-config

storage/logs/
├── 2026-01-15/
│   ├── 00.jsonl   (00h00 - 00h59)
│   ├── 01.jsonl   (01h00 - 01h59)
│   └── 14.jsonl   (14h00 - 14h59)
├── 2026-01-16/
│   └── ...
json
{"time":"2026-01-15T14:35:00+00:00","level":"info","type":"user_login","payload":{"user_id":123,"username":"john_doe","ip":"192.168.1.100"}}
env
# Chemin de base pour les fichiers JSONL
JSONL_BASE_PATH=/custom/log/path

# Taille du buffer (null = désactivé)
JSONL_BUFFER_SIZE=100

# Permissions des dossiers (755, 775, 750, 700, etc.)
JSONL_DIRECTORY_PERMISSION=755