mongoose-studio/phobos-framework-logger

Phobos Framework Logger - Structured JSON logging with trace context (PSR-3)

Maintainers

Package info

github.com/mongoose-studio/phobos-framework-logger

pkg:composer/mongoose-studio/phobos-framework-logger

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-07-18 04:45 UTC

This package is auto-updated.

Last update: 2026-07-18 04:51:16 UTC


README

PHP Version License PSR-3 Phobos Framework

Phobos Framework

Librería satélite opt-in de logging estructurado para Phobos Framework. Logs JSON line-delimited con contexto de traza automático (trace_id/request_id), redacción de datos sensibles y writers intercambiables. Implementa PSR-3 sin arrastrar Monolog ni ninguna otra dependencia pesada — fiel al minimalismo del núcleo, que a propósito no trae logger.

Características

  • 📋 PSR-3 completo - Psr\Log\LoggerInterface, interpola placeholders {clave}, valida niveles
  • 🧱 JSON estructurado - una línea = un objeto JSON, listo para Loki/agentes de logs (patrón K8s)
  • 🔗 Contexto de traza - trace_id/request_id inyectados en cada registro desde un LogContext request-scoped
  • 🌐 W3C Trace Context - adopta el traceparent entrante (Kong/OTel) y lo propaga saliente con traceParent()
  • 🕵️ Redacción de secretos - enmascara claves sensibles (password, token, authorization, ...) y patrones sueltos (JWT, PAN)
  • 🚦 Access-log integrado - request.handled con método, path, status y duración; nivel según status (4xx=warning, 5xx=error)
  • 🖥️ Writers intercambiables - stdout (prod), pretty (dev, coloreado), file, memory (tests), null
  • 💥 Excepciones expandidas - context['exception'] con un Throwable se expande a {class, message, file, line, trace}
  • 🧪 Testeable por diseño - DI por constructor en todo; MemoryWriter para afirmar logs en tests
  • 🚫 Cero magia - sin facades, sin estado estático: la forma canónica de Phobos

Instalación

composer require mongoose-studio/phobos-framework-logger

Dependencias

  • mongoose-studio/phobos-framework ^3.1
  • psr/log ^3.0
  • PHP >= 8.4

Uso

1. Registrar el provider (módulo raíz)

public function providers(): array {
    return [AppServiceProvider::class, LoggerServiceProvider::class];
}

2. Middleware global (contexto de traza + access-log)

Primero en la cadena, para que todo lo demás ya loguee con contexto:

Phobos::init(ROOT, APPLICATION)
    ->loadEnvironment()->loadConfig()
    ->middleware(RequestLogContextMiddleware::class)
    ->middleware(CorsMiddleware::class)
    ->bootstrap(ApiModule::class)
    ->run()->send();

3. Loguear desde un service (inyección por constructor)

use Psr\Log\LoggerInterface;

class PostService {
    public function __construct(private LoggerInterface $log) {}   // autowiring

    public function publish(object $data): array {
        $this->log->info('post.publishing', ['kind' => $data->kind]);

        try {
            // ...
        } catch (\Throwable $e) {
            $this->log->error('post.publish_failed', ['exception' => $e]);
            throw $e;   // nunca tragarse la excepción
        }

        return ['status' => 'ok'];
    }
}

Depende de LoggerInterface (PSR-3), no de la clase concreta: testeable y reemplazable.

4. Excepciones no manejadas (entry point)

try {
    Phobos::init(ROOT, APPLICATION)->/* ... */->run()->send();
} catch (\Throwable $e) {
    inject(Psr\Log\LoggerInterface::class)->critical('unhandled.exception', ['exception' => $e]);
    // ... luego la traducción a JSON del template
}

5. Propagar la traza hacia afuera

// A una llamada HTTP saliente (Guzzle) o al sobre de un evento del bus:
$headers['traceparent'] = logContext()->traceParent();

Formato del registro

{
  "timestamp":  "2026-07-15T12:00:00.123Z",
  "level":      "info",
  "service":    "core-social",
  "message":    "request.handled",
  "trace_id":   "4bf92f3577b34da6a3ce929d0e0e4736",
  "request_id": "0af7651916cd43dd8448eb211c80319c",
  "tenant_id":  null,
  "actor_id":   null,
  "context":    { "method": "GET", "path": "/v1/feed", "status": 200, "duration_ms": 42.1 }
}

tenant_id/actor_id se promueven a la raíz: se fijan una vez en el LogContext (p. ej. en el middleware de auth) y salen en todos los registros del request.

logContext()->setActorId($userId);
logContext()->setTenantId($clientId);

Configuración (config/logging.php)

Todo es opcional; sin archivo, los defaults funcionan.

<?php
return [
    'service'    => env('SERVICE_NAME', 'app'),
    'level'      => env('LOG_LEVEL', 'info'),           // umbral mínimo PSR-3
    'writer'     => env('LOG_WRITER', is_prod() ? 'stdout' : 'pretty'),
    'access_log' => filter_var(env('LOG_ACCESS', true), FILTER_VALIDATE_BOOL),
    'redact'     => ['authorization', 'cookie', 'password', 'token',
                     'refresh_token', 'access_token', 'secret', 'pan', 'card'],
    'file_path'  => storage_path('logs/app.log'),       // solo si writer=file
];
Writer Uso Salida
stdout Producción (K8s) JSON line-delimited a stdout; el agente (Promtail/Grafana Agent) lo lleva a Loki
pretty Desarrollo Consola coloreada y legible
file VPS/scripts JSON append a archivo con lock (la rotación es de logrotate)
memory Tests Acumula en memoria (records(), last(), clear())
null Apagado Descarta todo

Redacción

Sin PII ni secretos en los logs, en ningún ambiente. Dos mecanismos:

  • Por clave (recursivo, insensible a mayúsculas, por contención): password, card_number, reset_token_hash... → ••••••
  • Por patrón (sobre strings sueltos): JWT y PAN de tarjeta se enmascaran aunque la clave no delate nada
$log->info('login.attempt', ['email' => $email, 'password' => $pass]);
// → "context": {"email":"a@b.c","password":"••••••"}

Testing (en tu proyecto)

Bindea el writer de memoria y afirma sobre lo logueado:

$writer = new MemoryWriter();
container()->instance(WriterInterface::class, $writer);

// ... ejercitar el código ...

$this->assertSame('post.publishing', $writer->last()['message']);

Relación con el Observer del core

El Observer (trace(), Observer::dumpFormatted()) es una línea de tiempo en memoria para depurar un request puntual; no persiste ni sale del proceso. Este Logger es para producción: estructurado, persistente (stdout → Loki) y correlacionado por trace_id. Son complementarios.

Tests de la librería

composer test

Licencia

Este proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para más detalles.

Autor

Marcel Rojas
marcelrojas16@gmail.com
Mongoose Studio

Contribuciones

Las contribuciones son bienvenidas. Por favor:

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/amazing-feature)
  3. Commit tus cambios (git commit -m 'Add amazing feature')
  4. Push a la rama (git push origin feature/amazing-feature)
  5. Abre un Pull Request

Phobos Framework by Mongoose Studio