# Production Logging Configuration Comprehensive logging configuration und best practices für Production Deployment des Custom PHP Frameworks. ## Logging Architecture Overview ``` Application Logs → Framework Logger → Log Handlers → Destinations ↓ ↓ ↓ Log Processors Formatters Files/Syslog/ (Metadata, (JSON, External Context, Line) Services) Performance) ``` ## Log Levels Das Framework verwendet PSR-3 Log Levels: | Level | Severity | Production Use | Description | |-----------|----------|----------------|-------------| | emergency | 0 | Always | System is unusable | | alert | 1 | Always | Action must be taken immediately | | critical | 2 | Always | Critical conditions | | error | 3 | Always | Error conditions | | warning | 4 | Recommended | Warning conditions | | notice | 5 | Optional | Normal but significant condition | | info | 6 | Minimal | Informational messages | | debug | 7 | Never | Debug-level messages | **Production Recommendation**: `LOG_LEVEL=warning` ## Framework Logger Configuration ### Core Logger Setup Das Framework nutzt ein hierarchisches Logging-System mit mehreren Kanälen: **Available Channels**: - `app` - Application-level logs - `security` - Security events (OWASP, authentication, authorization) - `performance` - Performance metrics and slow queries - `database` - Database queries and errors - `queue` - Background job processing - `cache` - Cache operations - `http` - HTTP requests and responses ### Log Configuration via Environment ```env # .env.production # Primary log level (emergency, alert, critical, error, warning, notice, info, debug) LOG_LEVEL=warning # Channel-specific log levels (optional) LOG_LEVEL_SECURITY=info LOG_LEVEL_PERFORMANCE=warning LOG_LEVEL_DATABASE=error LOG_LEVEL_QUEUE=warning # Log destination (file, syslog, stderr, or combination) LOG_DESTINATION=file,syslog # Log file path (relative to project root) LOG_FILE_PATH=storage/logs/application.log # Log rotation LOG_ROTATION_ENABLED=true LOG_ROTATION_MAX_FILES=14 LOG_ROTATION_MAX_SIZE=100M # JSON logging (recommended for production) LOG_FORMAT=json # Include stack traces in error logs LOG_INCLUDE_STACKTRACE=true # Sanitize sensitive data in logs LOG_SANITIZE_SENSITIVE=true ``` ## Log Handlers ### 1. File Handler (Default) **Location**: `storage/logs/` **Configuration**: ```php // src/Framework/Logging/LoggerInitializer.php use App\Framework\Logging\Handlers\JsonFileHandler; $fileHandler = new JsonFileHandler( filename: $logPath, level: LogLevel::WARNING, maxFiles: 14, maxSize: 100 * 1024 * 1024 // 100MB ); ``` **Log Files Structure**: ``` storage/logs/ ├── application.log # Current application log ├── application-2024-01-15.log ├── application-2024-01-14.log ├── security.log # Security events ├── performance.log # Performance metrics ├── database.log # Database queries └── error.log # Error-only log ``` ### 2. Syslog Handler **Integration with System Syslog**: ```php use App\Framework\Logging\Handlers\SyslogHandler; $syslogHandler = new SyslogHandler( ident: 'michaelschiemer-app', facility: LOG_USER, level: LogLevel::WARNING ); ``` **Syslog Configuration** (`/etc/rsyslog.d/50-app.conf`): ```conf # Application logs local0.* /var/log/michaelschiemer/app.log # Security logs local1.* /var/log/michaelschiemer/security.log # Performance logs local2.* /var/log/michaelschiemer/performance.log ``` ### 3. External Services (Optional) #### Sentry Integration ```env # .env.production SENTRY_DSN=https://xxx@sentry.io/xxx SENTRY_ENVIRONMENT=production SENTRY_TRACES_SAMPLE_RATE=0.1 ``` #### ELK Stack (Elasticsearch, Logstash, Kibana) **Logstash Configuration** (`/etc/logstash/conf.d/app.conf`): ```conf input { file { path => "/var/log/michaelschiemer/application.log" type => "app-logs" codec => "json" } } filter { if [type] == "app-logs" { json { source => "message" } date { match => ["timestamp", "ISO8601"] } } } output { elasticsearch { hosts => ["localhost:9200"] index => "app-logs-%{+YYYY.MM.dd}" } } ``` ## Log Processors Log Processors enrichen log entries mit zusätzlichem Context. ### Available Processors **1. Performance Processor**: ```php use App\Framework\Logging\Processors\PerformanceProcessor; // Adds execution time, memory usage, peak memory $performanceProcessor = new PerformanceProcessor(); ``` **2. Web Info Processor**: ```php use App\Framework\Logging\Processors\WebInfoProcessor; // Adds request ID, IP, user agent, URL $webInfoProcessor = new WebInfoProcessor($request); ``` **3. Exception Processor**: ```php use App\Framework\Logging\Processors\ExceptionProcessor; // Adds exception class, file, line, stack trace $exceptionProcessor = new ExceptionProcessor(); ``` **4. Interpolation Processor**: ```php use App\Framework\Logging\Processors\InterpolationProcessor; // Replaces placeholders in message with context values $interpolationProcessor = new InterpolationProcessor(); ``` ### Custom Processor Example ```php final readonly class TraceIdProcessor { public function __invoke(LogRecord $record): LogRecord { $traceId = $this->getDistributedTraceId(); return $record->withContext([ 'trace_id' => $traceId, 'span_id' => $this->getCurrentSpanId(), ]); } } ``` ## Log Formatters ### 1. JSON Formatter (Recommended for Production) **Output Format**: ```json { "timestamp": "2024-01-15T14:32:45.123456Z", "level": "error", "channel": "app", "message": "Database connection failed", "context": { "exception": "PDOException", "file": "/app/src/Database/Connection.php", "line": 42, "trace": "..." }, "extra": { "request_id": "req_abc123xyz", "ip": "203.0.113.42", "user_agent": "Mozilla/5.0...", "memory_usage": 12582912, "execution_time_ms": 234 } } ``` **Benefits**: - Structured data for log aggregation tools - Easy parsing and filtering - Machine-readable format - Standardized timestamps **Usage**: ```php use App\Framework\Logging\Formatter\JsonFormatter; $jsonFormatter = new JsonFormatter( prettyPrint: false, // Compact JSON for production includeContext: true, includeExtra: true ); ``` ### 2. Line Formatter (Human-Readable) **Output Format**: ``` [2024-01-15 14:32:45] app.ERROR: Database connection failed {"exception":"PDOException","file":"/app/src/Database/Connection.php","line":42} {"request_id":"req_abc123xyz","ip":"203.0.113.42"} ``` **Usage**: ```php use App\Framework\Logging\Formatter\LineFormatter; $lineFormatter = new LineFormatter( format: "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n", dateFormat: "Y-m-d H:i:s", allowInlineLineBreaks: false ); ``` ## Security Event Logging ### OWASP Security Events Das Framework nutzt OWASP Application Security Logging für Sicherheitsereignisse: **Available Event Types**: ```php use App\Framework\Security\OWASPEventIdentifier; // Authentication OWASPEventIdentifier::AUTHN_LOGIN_SUCCESS OWASPEventIdentifier::AUTHN_LOGIN_FAILURE OWASPEventIdentifier::AUTHN_LOGOUT_SUCCESS OWASPEventIdentifier::AUTHN_SESSION_EXPIRED // Authorization OWASPEventIdentifier::AUTHZ_PERMISSION_DENIED OWASPEventIdentifier::AUTHZ_PRIVILEGE_ESCALATION // Input Validation OWASPEventIdentifier::INPUT_VALIDATION_FAILURE OWASPEventIdentifier::INPUT_XSS_DETECTED OWASPEventIdentifier::INPUT_SQL_INJECTION_DETECTED // Security Events OWASPEventIdentifier::SECURITY_INTRUSION_DETECTED ``` **Usage**: ```php use App\Framework\Security\OWASPSecurityLogger; $this->owaspLogger->logSecurityEvent( new SecurityEventType(OWASPEventIdentifier::AUTHN_LOGIN_FAILURE), request: $request, context: [ 'username' => $credentials->username, 'ip_address' => $request->server->getRemoteAddr(), 'failure_reason' => 'Invalid credentials' ] ); ``` **Security Log Format**: ```json { "timestamp": "2024-01-15T14:32:45Z", "event_type": "authn_login_failure", "severity": "warning", "user": "john@example.com", "ip_address": "203.0.113.42", "user_agent": "Mozilla/5.0...", "request_id": "req_abc123xyz", "context": { "username": "john@example.com", "failure_reason": "Invalid credentials", "attempt_count": 3 } } ``` ## Performance Logging ### Slow Query Logging **Database Slow Queries**: ```php // Automatically logged via ProfilingConnection // Threshold: 500ms (configurable) // Log Entry: { "timestamp": "2024-01-15T14:32:45Z", "channel": "database", "level": "warning", "message": "Slow query detected", "context": { "query": "SELECT * FROM users WHERE ...", "execution_time_ms": 1234, "affected_rows": 15000, "trace": "..." } } ``` **N+1 Query Detection**: ```php // Automatically detected via N+1 detection system { "timestamp": "2024-01-15T14:32:45Z", "channel": "performance", "level": "warning", "message": "N+1 query pattern detected", "context": { "parent_query": "SELECT * FROM posts", "repeated_query": "SELECT * FROM comments WHERE post_id = ?", "repetition_count": 50, "total_time_ms": 2500, "suggestion": "Use eager loading or JOIN" } } ``` ### Request Performance Logging ```php // Automatically logged via PerformanceMiddleware // Threshold: 500ms (configurable) { "timestamp": "2024-01-15T14:32:45Z", "channel": "performance", "level": "warning", "message": "Slow request detected", "context": { "method": "GET", "path": "/api/users", "execution_time_ms": 1234, "memory_usage_mb": 45, "peak_memory_mb": 52, "db_queries": 23, "cache_hits": 15, "cache_misses": 8 } } ``` ## Log Rotation ### File-Based Rotation **Configuration**: ```php // Automatic rotation via JsonFileHandler new JsonFileHandler( filename: 'application.log', maxFiles: 14, // Keep 14 days of logs maxSize: 100 * 1024 * 1024 // 100MB per file ); ``` **Rotation Trigger**: - Daily at midnight (new date in filename) - When file size exceeds maxSize **Filename Pattern**: `application-YYYY-MM-DD.log` ### Logrotate Configuration (System-Level) **`/etc/logrotate.d/michaelschiemer`**: ```conf /var/log/michaelschiemer/*.log { daily rotate 14 compress delaycompress missingok notifempty create 0640 www-data www-data sharedscripts postrotate # Reload application if needed # docker-compose -f /path/to/docker-compose.yml kill -s USR1 php endscript } ``` ## Log Monitoring & Alerting ### Real-Time Log Monitoring **1. Docker Logs**: ```bash # Follow all logs docker-compose logs -f --tail=100 # Filter by service docker-compose logs -f php # Filter by log level docker-compose logs -f | grep -E "(ERROR|CRITICAL|ALERT|EMERGENCY)" # Search for specific pattern docker-compose logs -f | grep "Database connection failed" ``` **2. File-Based Monitoring (tail)**: ```bash # Follow application log tail -f storage/logs/application.log | jq '.' # Filter errors only tail -f storage/logs/application.log | jq 'select(.level == "error")' # Filter security events tail -f storage/logs/security.log | jq 'select(.event_type | startswith("authn"))' ``` ### Alerting Configuration **1. Log-Based Alerts (with Logstash/Elasticsearch)**: **Watcher Alert Example** (Elasticsearch): ```json { "trigger": { "schedule": { "interval": "1m" } }, "input": { "search": { "request": { "indices": ["app-logs-*"], "body": { "query": { "bool": { "must": [ { "range": { "@timestamp": { "gte": "now-5m" } } }, { "terms": { "level": ["error", "critical", "alert", "emergency"] } } ] } } } } } }, "condition": { "compare": { "ctx.payload.hits.total": { "gt": 10 } } }, "actions": { "send_email": { "email": { "to": "ops@example.com", "subject": "High Error Rate Detected", "body": "More than 10 errors in the last 5 minutes" } } } } ``` **2. Custom Alert Script**: ```bash #!/bin/bash # /usr/local/bin/log-alert-check.sh LOG_FILE="/var/log/michaelschiemer/application.log" ALERT_EMAIL="ops@example.com" ERROR_THRESHOLD=10 # Count errors in last 5 minutes ERROR_COUNT=$(grep -c '"level":"error"' <(tail -n 1000 "$LOG_FILE")) if [ "$ERROR_COUNT" -gt "$ERROR_THRESHOLD" ]; then echo "High error rate detected: $ERROR_COUNT errors" | \ mail -s "ALERT: High Error Rate" "$ALERT_EMAIL" fi ``` **Cron Job**: ```cron # Check every 5 minutes */5 * * * * /usr/local/bin/log-alert-check.sh ``` ## Log Sanitization ### Sensitive Data Protection Das Framework sanitiert automatisch sensitive Daten in Logs: **Automatically Redacted Keys**: - `password`, `passwd`, `pwd` - `token`, `access_token`, `refresh_token` - `secret`, `api_key`, `api_secret` - `authorization`, `auth` - `credit_card`, `cc_number`, `cvv` - `ssn`, `social_security` **Example**: ```php $logger->info('User authentication', [ 'username' => 'john@example.com', 'password' => 'secret123', // Will be logged as '[REDACTED]' 'api_key' => 'sk_live_xxx' // Will be logged as '[REDACTED]' ]); ``` **Custom Sanitization**: ```php use App\Framework\Logging\LogSanitizer; $sanitizer = new LogSanitizer( sensitiveKeys: ['custom_secret', 'internal_token'] ); $sanitizedContext = $sanitizer->sanitize($context); ``` ## Production Logging Best Practices ### 1. Log Level Management **DO**: - Use `warning` level in production for operational awareness - Use `error` for failures that need investigation - Use `critical`/`alert`/`emergency` for immediate action needed **DON'T**: - Never use `debug` level in production (performance impact) - Avoid `info` level for high-frequency events - Don't log sensitive data (passwords, tokens, PII) ### 2. Structured Logging **DO**: - Use JSON format for machine parsing - Include consistent context (request_id, user_id, ip) - Add relevant metadata (execution_time, memory_usage) - Use consistent field names across all logs **DON'T**: - Avoid unstructured string concatenation - Don't mix log formats (JSON vs plain text) - Avoid dynamic field names ### 3. Performance Considerations **DO**: - Implement asynchronous logging for high-traffic systems - Use log rotation to prevent disk space issues - Monitor log volume and adjust thresholds - Cache logger instances **DON'T**: - Never perform expensive operations in log statements - Avoid logging in tight loops - Don't log entire objects (use specific fields) ### 4. Security **DO**: - Log all authentication and authorization events - Log security exceptions (SQL injection, XSS attempts) - Include request context for security analysis - Implement log integrity checks **DON'T**: - Never log passwords, tokens, or sensitive PII - Avoid logging entire request/response bodies - Don't expose internal system paths in production logs ## Troubleshooting ### Problem: Logs not being written ```bash # Check log directory permissions ls -la storage/logs/ # Fix permissions chmod 755 storage/logs/ chmod 644 storage/logs/*.log # Verify logger configuration php console.php config:check --key=LOG_LEVEL # Test logging php console.php log:test --level=error ``` ### Problem: Log files growing too large ```bash # Check log file sizes du -sh storage/logs/* # Manually rotate logs mv storage/logs/application.log storage/logs/application-$(date +%Y%m%d).log # Compress old logs gzip storage/logs/application-2024*.log # Force logrotate logrotate -f /etc/logrotate.d/michaelschiemer ``` ### Problem: Performance degradation due to logging ```bash # Increase log level to reduce volume # .env: LOG_LEVEL=error # Disable verbose processors # Remove WebInfoProcessor, PerformanceProcessor in production # Use asynchronous logging # Implement QueuedLogHandler for high-traffic scenarios ``` ## See Also - **Security Patterns**: `docs/claude/security-patterns.md` - **Performance Monitoring**: `docs/claude/performance-monitoring.md` - **Error Handling**: `docs/claude/error-handling.md` - **Deployment Guide**: `docs/deployment/production-prerequisites.md`