Files
michaelschiemer/tests/Framework/Logging/DefaultLoggerTest.php
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

294 lines
9.2 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Framework\Logging;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ProcessorManager;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Logging\ValueObjects\RequestContext;
use App\Framework\Logging\ValueObjects\UserContext;
use App\Framework\Random\SecureRandomGenerator;
use App\Framework\Tracing\TraceContext;
describe('DefaultLogger', function () {
beforeEach(function () {
// Clear any existing trace context
TraceContext::clear();
});
afterEach(function () {
TraceContext::clear();
});
it('can log with array context (legacy)', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler]
);
$context = ['user_id' => 123, 'action' => 'login'];
$logger->info('User logged in', $context);
expect($handler->records)->toHaveCount(1);
$record = $handler->records[0];
expect($record->getMessage())->toBe('User logged in');
expect($record->getContext())->toBe($context);
expect($record->getLevel())->toBe(LogLevel::INFO);
});
it('can log with LogContext', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler]
);
$logContext = LogContext::withData(['user_id' => 123, 'action' => 'login'])
->addTags('authentication', 'user-action')
->addMetadata('source', 'test');
$logger->info('User logged in', $logContext);
expect($handler->records)->toHaveCount(1);
$record = $handler->records[0];
expect($record->getMessage())->toBe('User logged in');
expect($record->getLevel())->toBe(LogLevel::INFO);
// Context should be converted to array
$context = $record->getContext();
expect($context)->toHaveKey('user_id', 123);
expect($context)->toHaveKey('action', 'login');
expect($context)->toHaveKey('_tags', ['authentication', 'user-action']);
expect($context)->toHaveKey('source', 'test');
// Structured data should be in extras
expect($record->getExtra('structured_tags'))->toBe(['authentication', 'user-action']);
});
it('converts LogContext with trace to array context', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler]
);
$trace = TraceContext::start(new SecureRandomGenerator());
$span = $trace->startSpan('test-span');
$logContext = LogContext::withData(['key' => 'value'])
->withTrace($trace);
$logger->info('Test message', $logContext);
$record = $handler->records[0];
$context = $record->getContext();
expect($context)->toHaveKey('_trace_id', $trace->getTraceId());
expect($context)->toHaveKey('_span_id', $span->spanId);
// Trace should also be in extras
$traceExtra = $record->getExtra('trace_context');
expect($traceExtra)->toHaveKey('trace_id', $trace->getTraceId());
expect($traceExtra)->toHaveKey('active_span');
});
it('converts LogContext with user to array context', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler]
);
$userContext = UserContext::authenticated('123', 'john');
$logContext = LogContext::withData(['key' => 'value'])
->withUser($userContext);
$logger->info('Test message', $logContext);
$record = $handler->records[0];
$context = $record->getContext();
expect($context)->toHaveKey('_user_id', '123');
// User should also be in extras
$userExtra = $record->getExtra('user_context');
expect($userExtra)->toBeArray();
expect($userExtra)->toHaveKey('user_id', '123');
});
it('converts LogContext with request to array context', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler]
);
$requestContext = RequestContext::empty();
$logContext = LogContext::withData(['key' => 'value'])
->withRequest($requestContext);
$logger->info('Test message', $logContext);
$record = $handler->records[0];
// Request should be in extras
$requestExtra = $record->getExtra('request_context');
expect($requestExtra)->toBeArray();
});
it('logs at different levels with LogContext', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler]
);
$context = LogContext::withData(['test' => 'value']);
$logger->debug('Debug message', $context);
$logger->info('Info message', $context);
$logger->notice('Notice message', $context);
$logger->warning('Warning message', $context);
$logger->error('Error message', $context);
$logger->critical('Critical message', $context);
$logger->alert('Alert message', $context);
$logger->emergency('Emergency message', $context);
expect($handler->records)->toHaveCount(8);
$levels = array_map(fn ($record) => $record->getLevel(), $handler->records);
expect($levels)->toBe([
LogLevel::DEBUG,
LogLevel::INFO,
LogLevel::NOTICE,
LogLevel::WARNING,
LogLevel::ERROR,
LogLevel::CRITICAL,
LogLevel::ALERT,
LogLevel::EMERGENCY,
]);
});
it('respects minimum log level', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::WARNING,
handlers: [$handler]
);
$context = LogContext::withData(['test' => 'value']);
$logger->debug('Debug message', $context);
$logger->info('Info message', $context);
$logger->warning('Warning message', $context);
$logger->error('Error message', $context);
expect($handler->records)->toHaveCount(2);
expect($handler->records[0]->getLevel())->toBe(LogLevel::WARNING);
expect($handler->records[1]->getLevel())->toBe(LogLevel::ERROR);
});
it('calls processors on enriched record', function () {
$processor = new TestLogProcessor();
$processorManager = new ProcessorManager($processor);
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler],
processorManager: $processorManager
);
$context = LogContext::withData(['test' => 'value']);
$logger->info('Test message', $context);
expect($processor->processedRecords)->toHaveCount(1);
expect($handler->records)->toHaveCount(1);
// Processor should have seen the enriched record
$processedRecord = $processor->processedRecords[0];
expect($processedRecord->getExtra('structured_tags'))->toBeNull(); // No tags in this test
});
it('handles empty LogContext', function () {
$handler = new TestLogHandler();
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler]
);
$logger->info('Test message', LogContext::empty());
expect($handler->records)->toHaveCount(1);
$record = $handler->records[0];
expect($record->getContext())->toBe([]);
expect($record->getExtras())->toBe([]);
});
it('can get configuration', function () {
$handler = new TestLogHandler();
$processor = new TestLogProcessor();
$processorManager = new ProcessorManager($processor);
$logger = new DefaultLogger(
minLevel: LogLevel::INFO,
handlers: [$handler],
processorManager: $processorManager
);
$config = $logger->getConfiguration();
expect($config)->toHaveKey('minLevel', 200);
expect($config)->toHaveKey('handlers');
expect($config)->toHaveKey('processors');
expect($config['handlers'])->toBe([TestLogHandler::class]);
});
});
// Test helpers
class TestLogHandler implements LogHandler
{
public array $records = [];
public function isHandling(LogRecord $record): bool
{
return true;
}
public function handle(LogRecord $record): void
{
$this->records[] = $record;
}
}
class TestLogProcessor implements \App\Framework\Logging\LogProcessor
{
public array $processedRecords = [];
public function processRecord(LogRecord $record): LogRecord
{
$this->processedRecords[] = $record;
return $record->addExtra('processed_by', 'TestLogProcessor');
}
public function getPriority(): int
{
return 100;
}
public function getName(): string
{
return 'test-processor';
}
}