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
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace Tests\Integration;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\ProcessorManager;
use App\Framework\Logging\Processors\ExceptionEnrichmentProcessor;
use App\Framework\Logging\ValueObjects\LogContext;
use Exception;
use RuntimeException;
/**
* Integration test demonstrating Exception Enrichment Processor usage
*/
it('demonstrates exception enrichment in real logging scenario', function () {
// Setup logger with exception processor
$processorManager = new ProcessorManager();
$processorManager = $processorManager->addProcessor(new ExceptionEnrichmentProcessor());
$handler = new class () implements \App\Framework\Logging\LogHandler {
public array $handledRecords = [];
public function isHandling(\App\Framework\Logging\LogRecord $record): bool
{
return true;
}
public function handle(\App\Framework\Logging\LogRecord $record): void
{
$this->handledRecords[] = $record;
}
};
$logger = new DefaultLogger(
minLevel: LogLevel::DEBUG,
handlers: [$handler],
processorManager: $processorManager
);
// Simulate real application scenario with nested exceptions
try {
try {
throw new RuntimeException('Database connection failed');
} catch (RuntimeException $e) {
throw new Exception('User registration failed', 500, $e);
}
} catch (Exception $exception) {
// Log the exception with context
$context = LogContext::withData([
'user_email' => 'test@example.com',
'action' => 'registration',
'exception' => $exception,
])->addTags('error', 'registration');
$logger->error('User registration failed with exception', $context);
}
// Verify enrichment worked
expect($handler->handledRecords)->toHaveCount(1);
$record = $handler->handledRecords[0];
$extras = $record->getExtras();
// Verify basic exception info
expect($extras['exception_class'])->toBe(Exception::class);
expect($extras['exception_message'])->toBe('User registration failed');
expect($extras['exception_code'])->toBe(500);
expect($extras['exception_severity'])->toBe('unknown');
// Verify stack trace
expect($extras['stack_trace'])->toBeArray();
expect($extras['stack_trace_short'])->toBeString();
// Verify previous exceptions
expect($extras['previous_exceptions'])->toBeArray();
expect($extras['previous_exceptions'])->toHaveCount(1);
expect($extras['previous_exceptions'][0]['class'])->toBe(RuntimeException::class);
expect($extras['previous_exceptions'][0]['message'])->toBe('Database connection failed');
// Verify exception hash for grouping
expect($extras['exception_hash'])->toBeString();
expect($extras['exception_hash'])->toHaveLength(8);
echo "\n🎉 Exception Enrichment Working! Enhanced exception details:\n";
echo " Exception: {$extras['exception_class']}\n";
echo " Severity: {$extras['exception_severity']}\n";
echo " Hash: {$extras['exception_hash']}\n";
echo " Previous: " . count($extras['previous_exceptions']) . " exception(s)\n";
echo " Short trace: {$extras['stack_trace_short']}\n";
});
/**
* Demonstrates processor integration with channel logging
*/
it('enriches exceptions in channel logging', function () {
$processorManager = new ProcessorManager();
$processorManager = $processorManager->addProcessor(new ExceptionEnrichmentProcessor());
$handler = new class () implements \App\Framework\Logging\LogHandler {
public array $handledRecords = [];
public function isHandling(\App\Framework\Logging\LogRecord $record): bool
{
return true;
}
public function handle(\App\Framework\Logging\LogRecord $record): void
{
$this->handledRecords[] = $record;
}
};
$logger = new DefaultLogger(
handlers: [$handler],
processorManager: $processorManager
);
// Use security channel with exception
$securityException = new RuntimeException('Authentication failed');
$context = LogContext::withData([
'ip_address' => '192.168.1.100',
'user_agent' => 'Mozilla/5.0...',
'exception' => $securityException,
])->addTags('security', 'auth_failure');
$logger->security->warning('Security breach detected', $context);
// Verify channel and enrichment
$record = $handler->handledRecords[0];
expect($record->getChannel())->toBe('security');
expect($record->getExtra('exception_class'))->toBe(RuntimeException::class);
expect($record->getExtra('exception_severity'))->toBe('medium');
});

View File

@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace Tests\Integration;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\Formatter\DevelopmentFormatter;
use App\Framework\Logging\Formatter\JsonFormatter;
use App\Framework\Logging\Formatter\LineFormatter;
use App\Framework\Logging\Formatter\LogFormatter;
use App\Framework\Logging\Formatter\StructuredFormatter;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ProcessorManager;
use App\Framework\Logging\Processors\ExceptionEnrichmentProcessor;
use App\Framework\Logging\ValueObjects\LogContext;
use Exception;
use RuntimeException;
/**
* Integration test demonstrating Formatter usage with handlers
*/
// Enhanced handler that supports formatters
class FormattableHandler implements LogHandler
{
public array $handledOutputs = [];
public function __construct(
private readonly ?LogFormatter $formatter = null
) {
}
public function isHandling(LogRecord $record): bool
{
return true;
}
public function handle(LogRecord $record): void
{
if ($this->formatter) {
$output = ($this->formatter)($record);
$this->handledOutputs[] = $output;
} else {
$this->handledOutputs[] = $record->getMessage();
}
}
}
it('demonstrates different formatters with same log data', function () {
echo "\n🎨 Formatter Showcase:\n\n";
// Setup exception-enriched log record
$processorManager = new ProcessorManager();
$processorManager = $processorManager->addProcessor(new ExceptionEnrichmentProcessor());
try {
throw new RuntimeException('Database connection timeout');
} catch (RuntimeException $e) {
$exception = new Exception('User operation failed', 500, $e);
$context = LogContext::withData([
'user_id' => 12345,
'operation' => 'create_order',
'request_id' => 'req_abc123',
'exception' => $exception,
])->addTags('error', 'order_processing');
// Create handlers with different formatters
$lineHandler = new FormattableHandler(new LineFormatter());
$jsonHandler = new FormattableHandler(new JsonFormatter());
$devHandler = new FormattableHandler(new DevelopmentFormatter(colorOutput: false));
$structuredHandler = new FormattableHandler(new StructuredFormatter());
$logger = new DefaultLogger(
handlers: [$lineHandler, $jsonHandler, $devHandler, $structuredHandler],
processorManager: $processorManager
);
$logger->error('Order creation failed due to database issues', $context);
// Display different formatter outputs
echo "1. LINE FORMAT:\n";
echo $lineHandler->handledOutputs[0] . "\n\n";
echo "2. JSON FORMAT:\n";
echo $jsonHandler->handledOutputs[0] . "\n\n";
echo "3. DEVELOPMENT FORMAT:\n";
echo $devHandler->handledOutputs[0] . "\n";
echo "4. STRUCTURED FORMAT (logfmt):\n";
echo $structuredHandler->handledOutputs[0] . "\n\n";
// Verify all handlers received the log
expect($lineHandler->handledOutputs)->toHaveCount(1);
expect($jsonHandler->handledOutputs)->toHaveCount(1);
expect($devHandler->handledOutputs)->toHaveCount(1);
expect($structuredHandler->handledOutputs)->toHaveCount(1);
// Verify JSON is valid
$jsonData = json_decode($jsonHandler->handledOutputs[0], true);
expect($jsonData)->toBeArray();
expect($jsonData['extra']['exception_class'])->toBe(Exception::class);
}
});
it('demonstrates formatter customization options', function () {
echo "\n⚙️ Formatter Customization:\n\n";
$context = LogContext::withData(['api_key' => 'sk_test_123', 'response_time' => 245]);
// Custom line format
$customLineFormatter = new LineFormatter(
format: "{timestamp} | {level} | {message}",
timestampFormat: 'H:i:s'
);
// Pretty JSON
$prettyJsonFormatter = new JsonFormatter(prettyPrint: true);
// Structured as key-value
$kvFormatter = new StructuredFormatter(format: 'kv');
$handlers = [
'custom_line' => new FormattableHandler($customLineFormatter),
'pretty_json' => new FormattableHandler($prettyJsonFormatter),
'key_value' => new FormattableHandler($kvFormatter),
];
$logger = new DefaultLogger(handlers: array_values($handlers));
$logger->info('API request completed successfully', $context);
echo "CUSTOM LINE FORMAT:\n";
echo $handlers['custom_line']->handledOutputs[0] . "\n\n";
echo "PRETTY JSON FORMAT:\n";
echo $handlers['pretty_json']->handledOutputs[0] . "\n\n";
echo "KEY-VALUE FORMAT:\n";
echo $handlers['key_value']->handledOutputs[0] . "\n\n";
// Verify customizations
expect($handlers['custom_line']->handledOutputs[0])->toContain(' | INFO | ');
expect($handlers['pretty_json']->handledOutputs[0])->toContain(' '); // Pretty print spacing
expect($handlers['key_value']->handledOutputs[0])->toContain('level: INFO');
});
it('demonstrates formatter performance with batch logging', function () {
$jsonFormatter = new JsonFormatter();
$handler = new FormattableHandler($jsonFormatter);
$logger = new DefaultLogger(handlers: [$handler]);
// Batch log multiple entries
for ($i = 1; $i <= 5; $i++) {
$context = LogContext::withData(['batch_id' => "batch_$i", 'item_count' => $i * 10]);
$logger->info("Processed batch $i", $context);
}
expect($handler->handledOutputs)->toHaveCount(5);
// Verify all are valid JSON
foreach ($handler->handledOutputs as $output) {
$decoded = json_decode($output, true);
expect($decoded)->toBeArray();
expect($decoded['message'])->toContain('Processed batch');
}
echo "\n📊 Batch Logging Demo: " . count($handler->handledOutputs) . " entries formatted as JSON\n";
});