Integrate Monolog: replace four logging systems with single PSR-3 factory

- Add monolog/monolog dependency (^3.10)  
- Create app/Logger.php central factory with channels: app, admin, error, audit
- Each channel gets RotatingFileHandler (30-day retention) with pass-through LineFormatter
  preserving existing JSON format contracts
- Rewrite AppLogger as thin facade delegating to Logger::get('app')
- Rewrite ErrorHandler::log() to delegate to Logger::get('error')
- Rewrite AdminLogger file output to delegate to Logger::get('admin'), keep DB writes
- Add Monolog file shadow to Audit via Logger::get('audit') (Option A per monolog-plan)
- Log level controlled by LOG_LEVEL env var (defaults: DEBUG in cli-server, WARNING otherwise)
- Graceful NullHandler fallback when log directory is not writable
- Update SystemController LOG_FILES: remove php_error, add app/admin/error/audit
- JSON app logs parsed to readable one-liners in the log viewer
- Remove nginx config tab (parametres + fragment + template + css)
- Friendly empty-state message when app log files don't exist yet (notYet)
- PHP tail fallback when exec() unavailable
- All 228 PHPUnit tests pass, no call sites changed
This commit is contained in:
Pontoporeia
2026-05-20 02:16:17 +02:00
parent a6e0aa5887
commit ae66c2baad
19 changed files with 662 additions and 433 deletions

View File

@@ -4,7 +4,7 @@ use PHPUnit\Framework\TestCase;
/**
* SystemControllerHelpersTest — Pure logic tests for the static helper methods
* in SystemController (humanBytes, diskColor, logLineClass, nginxLineClass,
* in SystemController (humanBytes, diskColor, logLineClass,
* statusLabel, statusClass).
*
* These are stateless, no-IO functions.
@@ -116,32 +116,6 @@ class SystemControllerHelpersTest extends TestCase
$this->assertSame('', SystemController::logLineClass('GET / " 200 123 "'));
}
// ── nginxLineClass() ──────────────────────────────────────────────────────
public function testNginxLineClassComment(): void
{
$this->assertSame('nginx-comment', SystemController::nginxLineClass('# comment'));
$this->assertSame('nginx-comment', SystemController::nginxLineClass(' # indented comment'));
$this->assertSame('nginx-comment', SystemController::nginxLineClass(''));
$this->assertSame('nginx-comment', SystemController::nginxLineClass(' '));
}
public function testNginxLineClassBlock(): void
{
$this->assertSame('nginx-block', SystemController::nginxLineClass('server {'));
$this->assertSame('nginx-block', SystemController::nginxLineClass('location / {'));
$this->assertSame('nginx-block', SystemController::nginxLineClass(' upstream backend {'));
$this->assertSame('nginx-block', SystemController::nginxLineClass('events {'));
$this->assertSame('nginx-block', SystemController::nginxLineClass('http {'));
}
public function testNginxLineClassDirective(): void
{
$this->assertSame('nginx-directive', SystemController::nginxLineClass('listen 80;'));
$this->assertSame('nginx-directive', SystemController::nginxLineClass('root /var/www;'));
$this->assertSame('nginx-directive', SystemController::nginxLineClass(' server_name example.com;'));
}
// ── statusLabel() ─────────────────────────────────────────────────────────
public function testStatusLabelActive(): void