716 lines
16 KiB
Markdown
716 lines
16 KiB
Markdown
# 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 Enrichment Processor**:
|
|
```php
|
|
use App\Framework\Logging\Processors\ExceptionEnrichmentProcessor;
|
|
|
|
// Converts Throwables to ExceptionContext with enriched metadata
|
|
// - Exception hash for pattern recognition
|
|
// - Severity categorization
|
|
// - Short stack trace for quick overview
|
|
// - Exception chain length
|
|
$exceptionEnrichmentProcessor = new ExceptionEnrichmentProcessor();
|
|
```
|
|
|
|
**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`
|