* fix(style): resolve PHPCS line-length warnings in source files * fix(style): resolve PHPCS line-length warnings in test files * feat(audit): add structured audit logging with ErrorContext and AuditContext - ErrorContext: standardized error information with sensitive data sanitization - AuditContext: structured context for audit entries with operation types - StructuredAuditLogger: enhanced audit logger wrapper with timing support * feat(recovery): add recovery mechanism for failed masking operations - FailureMode enum: FAIL_OPEN, FAIL_CLOSED, FAIL_SAFE modes - RecoveryStrategy interface and RecoveryResult value object - RetryStrategy: exponential backoff with configurable attempts - FallbackMaskStrategy: type-aware fallback values * feat(strategies): add CallbackMaskingStrategy for custom masking logic - Wraps custom callbacks as MaskingStrategy implementations - Factory methods: constant(), hash(), partial() for common use cases - Supports exact match and prefix match for field paths * docs: add framework integration guides and examples - symfony-integration.md: Symfony service configuration and Monolog setup - psr3-decorator.md: PSR-3 logger decorator pattern implementation - framework-examples.md: CakePHP, CodeIgniter 4, Laminas, Yii2, PSR-15 - docker-development.md: Docker development environment guide * chore(docker): add Docker development environment - Dockerfile: PHP 8.2-cli-alpine with Xdebug for coverage - docker-compose.yml: development services with volume mounts * feat(demo): add interactive GDPR pattern tester playground - PatternTester.php: pattern testing utility with strategy support - index.php: web API endpoint with JSON response handling - playground.html: interactive web interface for testing patterns * docs(todo): update with completed medium priority items - Mark all PHPCS warnings as fixed (81 → 0) - Document new Audit and Recovery features - Update test count to 1,068 tests with 2,953 assertions - Move remaining items to low priority * feat: add advanced architecture, documentation, and coverage improvements - Add architecture improvements: - ArrayAccessorInterface and DotArrayAccessor for decoupled array access - MaskingOrchestrator for single-responsibility masking coordination - GdprProcessorBuilder for fluent configuration - MaskingPluginInterface and AbstractMaskingPlugin for plugin architecture - PluginAwareProcessor for plugin hook execution - AuditLoggerFactory for instance-based audit logger creation - Add advanced features: - SerializedDataProcessor for handling print_r/var_export/serialize output - KAnonymizer with GeneralizationStrategy for GDPR k-anonymity - RetentionPolicy for configurable data retention periods - StreamingProcessor for memory-efficient large log processing - Add comprehensive documentation: - docs/performance-tuning.md - benchmarking, optimization, caching - docs/troubleshooting.md - common issues and solutions - docs/logging-integrations.md - ELK, Graylog, Datadog, etc. - docs/plugin-development.md - complete plugin development guide - Improve test coverage (84.41% → 85.07%): - ConditionalRuleFactoryInstanceTest (100% coverage) - GdprProcessorBuilderEdgeCasesTest (100% coverage) - StrategyEdgeCasesTest for ReDoS detection and type parsing - 78 new tests, 119 new assertions - Update TODO.md with current statistics: - 141 PHP files, 1,346 tests, 85.07% line coverage * chore: tests, update actions, sonarcloud issues * chore: rector * fix: more sonarcloud fixes * chore: more fixes * refactor: copilot review fix * chore: rector
10 KiB
Troubleshooting Guide
This guide helps diagnose and resolve common issues with the Monolog GDPR Filter library.
Table of Contents
- Installation Issues
- Pattern Matching Problems
- Performance Issues
- Memory Problems
- Integration Issues
- Audit Logging Issues
- Error Messages Reference
Installation Issues
Composer Installation Fails
Symptom: composer require fails with dependency conflicts.
Solution:
# Check PHP version
php -v # Must be 8.2 or higher
# Clear Composer cache
composer clear-cache
# Update Composer
composer self-update
# Try again with verbose output
composer require ivuorinen/monolog-gdpr-filter -vvv
Class Not Found Errors
Symptom: Class 'Ivuorinen\MonologGdprFilter\GdprProcessor' not found
Solutions:
- Regenerate autoloader:
composer dump-autoload
- Verify installation:
composer show ivuorinen/monolog-gdpr-filter
- Check namespace in your code:
<?php
// Correct
use Ivuorinen\MonologGdprFilter\GdprProcessor;
// Wrong
use MonologGdprFilter\GdprProcessor;
Pattern Matching Problems
Pattern Not Matching Expected Data
Symptom: Sensitive data is not being masked.
Diagnostic steps:
<?php
use Ivuorinen\MonologGdprFilter\PatternValidator;
$validator = new PatternValidator();
$pattern = '/your-pattern-here/';
// Test 1: Validate pattern syntax
$result = $validator->validate($pattern);
if (!$result['valid']) {
echo "Invalid pattern: " . $result['error'] . "\n";
}
// Test 2: Test pattern directly
$testData = 'your test data with sensitive@email.com';
if (preg_match($pattern, $testData, $matches)) {
echo "Pattern matches: " . print_r($matches, true);
} else {
echo "Pattern does not match\n";
}
// Test 3: Test with processor
$processor = new GdprProcessor([$pattern => '[MASKED]']);
$record = [
'message' => $testData,
'context' => [],
'level' => 200,
'level_name' => 'INFO',
'channel' => 'app',
'datetime' => new DateTimeImmutable(),
'extra' => [],
];
$result = $processor($record);
echo "Result: " . $result['message'] . "\n";
Pattern Matches Too Much
Symptom: Non-sensitive data is being masked.
Solutions:
- Add word boundaries:
<?php
// Too broad
$pattern = '/\d{4}/'; // Matches any 4 digits
// Better - with boundaries
$pattern = '/\b\d{4}\b/'; // Matches standalone 4-digit numbers
- Use more specific patterns:
<?php
// Too broad for credit cards
$pattern = '/\d{16}/';
// Better - credit card format
$pattern = '/\b(?:\d{4}[-\s]?){3}\d{4}\b/';
- Add negative lookahead/lookbehind:
<?php
// Avoid matching dates that look like years
$pattern = '/(?<!\d{2}\/)\b\d{4}\b(?!\/\d{2})/';
Special Characters in Patterns
Symptom: Pattern with special characters fails.
Solution: Escape special regex characters:
<?php
// Wrong - unescaped special chars
$pattern = '/user.name@domain.com/';
// Correct - escaped dots
$pattern = '/user\.name@domain\.com/';
// Using preg_quote for dynamic patterns
$email = 'user.name@domain.com';
$pattern = '/' . preg_quote($email, '/') . '/';
Performance Issues
Slow Processing
Symptom: Log processing is slower than expected.
Diagnostic:
<?php
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
$processor($record);
}
$elapsed = microtime(true) - $start;
echo "1000 records: {$elapsed}s\n";
Solutions:
- Reduce pattern count:
<?php
// Only include patterns you need
$patterns = DefaultPatterns::emails() + DefaultPatterns::creditCards();
- Simplify complex patterns:
<?php
// Slow: Complex pattern with many alternatives
$slow = '/(january|february|march|april|may|june|july|august|september|october|november|december)/i';
// Faster: Simpler pattern
$fast = '/\b[A-Z][a-z]{2,8}\b/';
- Limit recursion depth:
<?php
$processor = new GdprProcessor($patterns, [], [], null, 5); // Max depth 5
See Performance Tuning Guide for detailed optimization strategies.
High CPU Usage
Symptom: Processing causes CPU spikes.
Solutions:
- Check for catastrophic backtracking:
<?php
// Problematic pattern
$bad = '/.*@.*\..*/'; // Can cause backtracking
// Fixed pattern
$good = '/[^@]+@[^.]+\.[a-z]+/i';
- Add pattern timeout (PHP 7.3+):
<?php
// Set PCRE backtrack limit
ini_set('pcre.backtrack_limit', '100000');
Memory Problems
Out of Memory Errors
Symptom: Allowed memory size exhausted
Solutions:
- Use streaming for large files:
<?php
use Ivuorinen\MonologGdprFilter\Streaming\StreamingProcessor;
use Ivuorinen\MonologGdprFilter\MaskingOrchestrator;
$orchestrator = new MaskingOrchestrator($patterns);
$streaming = new StreamingProcessor($orchestrator, chunkSize: 100);
// Process file without loading entirely into memory
$lineParser = fn(string $line): array => ['message' => $line, 'context' => []];
foreach ($streaming->processFile($largefile, $lineParser) as $record) {
// Process one record at a time
}
- Reduce recursion depth:
<?php
$processor = new GdprProcessor($patterns, [], [], null, 3);
- Disable audit logging:
<?php
$processor = new GdprProcessor($patterns, [], [], null); // No audit logger
Memory Leaks
Symptom: Memory usage grows over time in long-running processes.
Solutions:
- Clear caches periodically:
<?php
// In long-running workers
if ($processedCount % 10000 === 0) {
gc_collect_cycles();
}
- Use fresh processor instances for batch jobs:
<?php
foreach ($batches as $batch) {
$processor = new GdprProcessor($patterns); // Fresh instance
foreach ($batch as $record) {
$processor($record);
}
unset($processor); // Release memory
}
Integration Issues
Laravel Integration
Symptom: Processor not being applied to logs.
Solutions:
- Verify service provider registration:
<?php
// config/app.php
'providers' => [
Ivuorinen\MonologGdprFilter\Laravel\GdprServiceProvider::class,
],
- Check logging configuration:
<?php
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['gdpr'],
],
'gdpr' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'tap' => [GdprLogTap::class],
],
],
- Clear config cache:
php artisan config:clear
php artisan cache:clear
Monolog Integration
Symptom: Processor not working with Monolog logger.
Solution: Ensure processor is pushed to logger:
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Ivuorinen\MonologGdprFilter\GdprProcessor;
$logger = new Logger('app');
$logger->pushHandler(new StreamHandler('app.log'));
$logger->pushProcessor(new GdprProcessor(DefaultPatterns::all()));
// Test it
$logger->info('User email: test@example.com');
Symfony Integration
See Symfony Integration Guide for detailed setup.
Audit Logging Issues
Audit Logger Not Receiving Events
Symptom: Audit callback never called.
Solutions:
- Verify audit logger is set:
<?php
$auditLogs = [];
$auditLogger = function (string $path, mixed $original, mixed $masked) use (&$auditLogs): void {
$auditLogs[] = compact('path', 'original', 'masked');
};
$processor = new GdprProcessor(
patterns: $patterns,
auditLogger: $auditLogger
);
- Verify masking is actually occurring:
<?php
// Audit is only called when data is actually masked
$record = ['message' => 'No sensitive data here', 'context' => []];
// This won't trigger audit because nothing is masked
Rate-Limited Audit Missing Events
Symptom: Some audit events are being dropped.
Solution: Adjust rate limit settings:
<?php
use Ivuorinen\MonologGdprFilter\RateLimiter;
use Ivuorinen\MonologGdprFilter\RateLimitedAuditLogger;
$rateLimiter = new RateLimiter(
maxEvents: 1000, // Increase limit
windowSeconds: 60,
burstLimit: 100 // Increase burst
);
$rateLimitedLogger = new RateLimitedAuditLogger($baseLogger, $rateLimiter);
Error Messages Reference
InvalidRegexPatternException
Message: Invalid regex pattern: [pattern]
Cause: The pattern has invalid regex syntax.
Solution:
<?php
// Test pattern before using
$pattern = '/[invalid/';
if (@preg_match($pattern, '') === false) {
echo "Invalid pattern: " . preg_last_error_msg();
}
RecursionDepthExceededException
Message: Maximum recursion depth exceeded
Cause: Nested data structure exceeds max depth.
Solutions:
<?php
// Increase max depth
$processor = new GdprProcessor($patterns, [], [], null, 20);
// Or flatten your data before processing
$flatContext = iterator_to_array(
new RecursiveIteratorIterator(
new RecursiveArrayIterator($context)
),
false
);
MaskingOperationFailedException
Message: Masking operation failed: [details]
Cause: An error occurred during masking.
Solution: Enable recovery mode:
<?php
use Ivuorinen\MonologGdprFilter\Recovery\FallbackMaskStrategy;
use Ivuorinen\MonologGdprFilter\Recovery\FailureMode;
$fallback = new FallbackMaskStrategy(FailureMode::FAIL_SAFE);
// Use with your processor
InvalidConfigurationException
Message: Invalid configuration: [details]
Cause: Invalid processor configuration.
Solution: Validate configuration:
<?php
use Ivuorinen\MonologGdprFilter\Builder\GdprProcessorBuilder;
try {
$processor = (new GdprProcessorBuilder())
->addPattern('/valid-pattern/', '[MASKED]')
->build();
} catch (InvalidConfigurationException $e) {
echo "Configuration error: " . $e->getMessage();
}
Getting Help
If you're still experiencing issues:
-
Check the tests: The test suite contains many usage examples:
ls tests/ -
Enable debug mode: Add verbose logging:
<?php $auditLogger = function ($path, $original, $masked): void { error_log("GDPR Mask: $path | $original -> $masked"); }; -
Report issues: Open an issue on GitHub with:
- PHP version (
php -v) - Library version (
composer show ivuorinen/monolog-gdpr-filter) - Minimal reproduction code
- Expected vs actual behavior
- PHP version (