feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready
This commit is contained in:
231
src/Framework/ErrorReporting/ErrorReportingConfig.php
Normal file
231
src/Framework/ErrorReporting/ErrorReportingConfig.php
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\ErrorReporting;
|
||||
|
||||
use App\Framework\Config\Environment;
|
||||
use App\Framework\Config\EnvironmentType;
|
||||
use App\Framework\Config\EnvKey;
|
||||
|
||||
/**
|
||||
* Error Reporting Configuration that adapts to environment
|
||||
*
|
||||
* Provides type-safe configuration for error reporting system
|
||||
* with environment-specific defaults (dev/staging/production).
|
||||
*
|
||||
* Features:
|
||||
* - Environment-based factory methods
|
||||
* - Type-safe configuration properties
|
||||
* - Framework-compliant readonly Value Object
|
||||
* - Sensible defaults per environment
|
||||
*/
|
||||
final readonly class ErrorReportingConfig
|
||||
{
|
||||
/**
|
||||
* @param bool $enabled Enable/disable error reporting globally
|
||||
* @param bool $asyncProcessing Process error reports asynchronously via queue
|
||||
* @param string[] $filterLevels Only report these log levels (empty = all levels)
|
||||
* @param string[] $excludedExceptionTypes Exception types to exclude from reporting
|
||||
* @param bool $captureRequestContext Capture HTTP request context
|
||||
* @param bool $captureUserContext Capture user/session context
|
||||
* @param bool $captureStackTraces Include full stack traces in reports
|
||||
* @param int $maxStackTraceDepth Maximum stack trace depth
|
||||
* @param bool $sanitizeSensitiveData Sanitize passwords, tokens, etc.
|
||||
* @param int $samplingRate Sampling rate for high-volume errors (1-100, 100 = all)
|
||||
* @param int $maxReportsPerMinute Rate limit for error reports
|
||||
* @param bool $enableAnalytics Enable error analytics and anomaly detection
|
||||
* @param int $analyticsRetentionDays Days to retain error reports for analytics
|
||||
*/
|
||||
public function __construct(
|
||||
public bool $enabled = true,
|
||||
public bool $asyncProcessing = true,
|
||||
public array $filterLevels = [],
|
||||
public array $excludedExceptionTypes = [],
|
||||
public bool $captureRequestContext = true,
|
||||
public bool $captureUserContext = true,
|
||||
public bool $captureStackTraces = true,
|
||||
public int $maxStackTraceDepth = 20,
|
||||
public bool $sanitizeSensitiveData = true,
|
||||
public int $samplingRate = 100,
|
||||
public int $maxReportsPerMinute = 60,
|
||||
public bool $enableAnalytics = true,
|
||||
public int $analyticsRetentionDays = 30
|
||||
) {
|
||||
// Validation
|
||||
if ($samplingRate < 1 || $samplingRate > 100) {
|
||||
throw new \InvalidArgumentException('Sampling rate must be between 1 and 100');
|
||||
}
|
||||
|
||||
if ($maxStackTraceDepth < 1) {
|
||||
throw new \InvalidArgumentException('Max stack trace depth must be at least 1');
|
||||
}
|
||||
|
||||
if ($analyticsRetentionDays < 1) {
|
||||
throw new \InvalidArgumentException('Analytics retention days must be at least 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create configuration from environment
|
||||
*/
|
||||
public static function fromEnvironment(Environment $env): self
|
||||
{
|
||||
$appEnv = $env->getString(EnvKey::APP_ENV, 'production');
|
||||
|
||||
$environmentType = match (strtolower($appEnv)) {
|
||||
'production', 'prod' => EnvironmentType::PROD,
|
||||
'staging', 'stage' => EnvironmentType::STAGING,
|
||||
'development', 'dev', 'local' => EnvironmentType::DEV,
|
||||
default => EnvironmentType::PROD
|
||||
};
|
||||
|
||||
return self::forEnvironment($environmentType, $env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create environment-specific configuration
|
||||
*/
|
||||
public static function forEnvironment(EnvironmentType $environment, Environment $env): self
|
||||
{
|
||||
return match ($environment) {
|
||||
EnvironmentType::PROD => self::production($env),
|
||||
EnvironmentType::STAGING => self::staging($env),
|
||||
EnvironmentType::DEV => self::development($env),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Production configuration - strict, sanitized, sampled
|
||||
*/
|
||||
private static function production(Environment $env): self
|
||||
{
|
||||
$filterLevels = $env->getString('ERROR_REPORTING_FILTER_LEVELS', '');
|
||||
$excludedTypes = $env->getString('ERROR_REPORTING_EXCLUDED_TYPES', '');
|
||||
|
||||
// Production default: only error, critical, alert, emergency
|
||||
$defaultFilterLevels = ['error', 'critical', 'alert', 'emergency'];
|
||||
|
||||
return new self(
|
||||
enabled: $env->getBool('ERROR_REPORTING_ENABLED', true),
|
||||
asyncProcessing: $env->getBool('ERROR_REPORTING_ASYNC', true),
|
||||
filterLevels: $filterLevels !== ''
|
||||
? explode(',', $filterLevels)
|
||||
: $defaultFilterLevels,
|
||||
excludedExceptionTypes: $excludedTypes !== ''
|
||||
? explode(',', $excludedTypes)
|
||||
: [],
|
||||
captureRequestContext: $env->getBool('ERROR_REPORTING_CAPTURE_REQUEST', true),
|
||||
captureUserContext: $env->getBool('ERROR_REPORTING_CAPTURE_USER', true),
|
||||
captureStackTraces: $env->getBool('ERROR_REPORTING_CAPTURE_STACK_TRACES', true),
|
||||
maxStackTraceDepth: $env->getInt('ERROR_REPORTING_MAX_STACK_DEPTH', 15),
|
||||
sanitizeSensitiveData: $env->getBool('ERROR_REPORTING_SANITIZE', true),
|
||||
samplingRate: $env->getInt('ERROR_REPORTING_SAMPLING_RATE', 100),
|
||||
maxReportsPerMinute: $env->getInt('ERROR_REPORTING_MAX_PER_MINUTE', 30),
|
||||
enableAnalytics: $env->getBool('ERROR_REPORTING_ANALYTICS', true),
|
||||
analyticsRetentionDays: $env->getInt('ERROR_REPORTING_RETENTION_DAYS', 30)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Staging configuration - more verbose, less restrictive
|
||||
*/
|
||||
private static function staging(Environment $env): self
|
||||
{
|
||||
$filterLevels = $env->getString('ERROR_REPORTING_FILTER_LEVELS', '');
|
||||
$excludedTypes = $env->getString('ERROR_REPORTING_EXCLUDED_TYPES', '');
|
||||
|
||||
// Staging default: warning and above
|
||||
$defaultFilterLevels = ['warning', 'error', 'critical', 'alert', 'emergency'];
|
||||
|
||||
return new self(
|
||||
enabled: $env->getBool('ERROR_REPORTING_ENABLED', true),
|
||||
asyncProcessing: $env->getBool('ERROR_REPORTING_ASYNC', true),
|
||||
filterLevels: $filterLevels !== ''
|
||||
? explode(',', $filterLevels)
|
||||
: $defaultFilterLevels,
|
||||
excludedExceptionTypes: $excludedTypes !== ''
|
||||
? explode(',', $excludedTypes)
|
||||
: [],
|
||||
captureRequestContext: $env->getBool('ERROR_REPORTING_CAPTURE_REQUEST', true),
|
||||
captureUserContext: $env->getBool('ERROR_REPORTING_CAPTURE_USER', true),
|
||||
captureStackTraces: $env->getBool('ERROR_REPORTING_CAPTURE_STACK_TRACES', true),
|
||||
maxStackTraceDepth: $env->getInt('ERROR_REPORTING_MAX_STACK_DEPTH', 20),
|
||||
sanitizeSensitiveData: $env->getBool('ERROR_REPORTING_SANITIZE', true),
|
||||
samplingRate: $env->getInt('ERROR_REPORTING_SAMPLING_RATE', 100),
|
||||
maxReportsPerMinute: $env->getInt('ERROR_REPORTING_MAX_PER_MINUTE', 60),
|
||||
enableAnalytics: $env->getBool('ERROR_REPORTING_ANALYTICS', true),
|
||||
analyticsRetentionDays: $env->getInt('ERROR_REPORTING_RETENTION_DAYS', 14)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Development configuration - verbose, everything enabled
|
||||
*/
|
||||
private static function development(Environment $env): self
|
||||
{
|
||||
$filterLevels = $env->getString('ERROR_REPORTING_FILTER_LEVELS', '');
|
||||
$excludedTypes = $env->getString('ERROR_REPORTING_EXCLUDED_TYPES', '');
|
||||
|
||||
return new self(
|
||||
enabled: $env->getBool('ERROR_REPORTING_ENABLED', true),
|
||||
asyncProcessing: $env->getBool('ERROR_REPORTING_ASYNC', false), // Sync in dev for debugging
|
||||
filterLevels: $filterLevels !== '' ? explode(',', $filterLevels) : [], // All levels
|
||||
excludedExceptionTypes: $excludedTypes !== '' ? explode(',', $excludedTypes) : [],
|
||||
captureRequestContext: $env->getBool('ERROR_REPORTING_CAPTURE_REQUEST', true),
|
||||
captureUserContext: $env->getBool('ERROR_REPORTING_CAPTURE_USER', true),
|
||||
captureStackTraces: $env->getBool('ERROR_REPORTING_CAPTURE_STACK_TRACES', true),
|
||||
maxStackTraceDepth: $env->getInt('ERROR_REPORTING_MAX_STACK_DEPTH', 30), // Deep traces
|
||||
sanitizeSensitiveData: $env->getBool('ERROR_REPORTING_SANITIZE', false), // See real data
|
||||
samplingRate: $env->getInt('ERROR_REPORTING_SAMPLING_RATE', 100), // All errors
|
||||
maxReportsPerMinute: $env->getInt('ERROR_REPORTING_MAX_PER_MINUTE', 1000), // No limit
|
||||
enableAnalytics: $env->getBool('ERROR_REPORTING_ANALYTICS', true),
|
||||
analyticsRetentionDays: $env->getInt('ERROR_REPORTING_RETENTION_DAYS', 7)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if error level should be reported
|
||||
*/
|
||||
public function shouldReportLevel(string $level): bool
|
||||
{
|
||||
// Empty filter = all levels
|
||||
if (empty($this->filterLevels)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array(strtolower($level), array_map('strtolower', $this->filterLevels), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if exception type should be reported
|
||||
*/
|
||||
public function shouldReportException(\Throwable $exception): bool
|
||||
{
|
||||
if (empty($this->excludedExceptionTypes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$exceptionClass = get_class($exception);
|
||||
|
||||
foreach ($this->excludedExceptionTypes as $excludedType) {
|
||||
if ($exceptionClass === $excludedType || is_subclass_of($exceptionClass, $excludedType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if error should be sampled (for high-volume scenarios)
|
||||
*/
|
||||
public function shouldSample(): bool
|
||||
{
|
||||
if ($this->samplingRate === 100) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return random_int(1, 100) <= $this->samplingRate;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user