mirror of
https://github.com/ivuorinen/monolog-gdpr-filter.git
synced 2026-01-26 03:34:00 +00:00
* 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
6.6 KiB
6.6 KiB
Symfony Integration Guide
This guide explains how to integrate the Monolog GDPR Filter with Symfony applications.
Installation
composer require ivuorinen/monolog-gdpr-filter
Basic Service Configuration
Add the GDPR processor as a service in config/services.yaml:
services:
App\Logging\GdprProcessor:
class: Ivuorinen\MonologGdprFilter\GdprProcessor
arguments:
$patterns: '%gdpr.patterns%'
$fieldPaths: '%gdpr.field_paths%'
$customCallbacks: []
$auditLogger: null
$maxDepth: 100
$dataTypeMasks: []
$conditionalRules: []
Parameters Configuration
Define GDPR patterns in config/services.yaml or a dedicated parameters file:
parameters:
gdpr.patterns:
'/\b\d{3}-\d{2}-\d{4}\b/': '***-**-****' # US SSN
'/\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}\b/': '****' # IBAN
'/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/': '[email]' # Email
'/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/': '****-****-****-****' # Credit Card
gdpr.field_paths:
'user.password': '***REMOVED***'
'user.ssn': '***-**-****'
'payment.card_number': '****-****-****-****'
Monolog Handler Configuration
Configure Monolog to use the GDPR processor in config/packages/monolog.yaml:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
formatter: monolog.formatter.json
processor: ['@App\Logging\GdprProcessor']
# For production with file rotation
production:
type: rotating_file
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: info
max_files: 14
processor: ['@App\Logging\GdprProcessor']
Environment-Specific Configuration
Create environment-specific configurations:
config/packages/dev/monolog.yaml
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
# In dev, you might want less aggressive masking
config/packages/prod/monolog.yaml
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
buffer_size: 50
nested:
type: rotating_file
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: info
max_files: 14
processor: ['@App\Logging\GdprProcessor']
Advanced Configuration with Audit Logging
Enable audit logging for compliance tracking:
services:
App\Logging\AuditLogger:
class: Ivuorinen\MonologGdprFilter\RateLimitedAuditLogger
arguments:
$auditLogger: '@App\Logging\AuditCallback'
$maxRequestsPerMinute: 100
$windowSeconds: 60
App\Logging\AuditCallback:
class: Closure
factory: ['App\Logging\AuditCallbackFactory', 'create']
arguments:
$logger: '@monolog.logger.audit'
App\Logging\GdprProcessor:
class: Ivuorinen\MonologGdprFilter\GdprProcessor
arguments:
$patterns: '%gdpr.patterns%'
$fieldPaths: '%gdpr.field_paths%'
$auditLogger: '@App\Logging\AuditLogger'
Create the factory class:
<?php
// src/Logging/AuditCallbackFactory.php
namespace App\Logging;
use Psr\Log\LoggerInterface;
class AuditCallbackFactory
{
public static function create(LoggerInterface $logger): callable
{
return function (string $path, mixed $original, mixed $masked) use ($logger): void {
$logger->info('GDPR masking applied', [
'path' => $path,
'original_type' => gettype($original),
'masked_preview' => substr((string) $masked, 0, 20) . '...',
]);
};
}
}
Conditional Masking by Environment
Apply different masking rules based on log level or channel:
services:
App\Logging\ConditionalRuleFactory:
class: App\Logging\ConditionalRuleFactory
App\Logging\GdprProcessor:
class: Ivuorinen\MonologGdprFilter\GdprProcessor
arguments:
$conditionalRules:
error_only: '@=service("App\\Logging\\ConditionalRuleFactory").createErrorOnlyRule()'
<?php
// src/Logging/ConditionalRuleFactory.php
namespace App\Logging;
use Monolog\Level;
use Monolog\LogRecord;
class ConditionalRuleFactory
{
public function createErrorOnlyRule(): callable
{
return fn(LogRecord $record): bool =>
$record->level->value >= Level::Error->value;
}
public function createChannelRule(array $channels): callable
{
return fn(LogRecord $record): bool =>
in_array($record->channel, $channels, true);
}
}
Testing in Symfony
Create a test to verify GDPR filtering works:
<?php
// tests/Logging/GdprProcessorTest.php
namespace App\Tests\Logging;
use Ivuorinen\MonologGdprFilter\GdprProcessor;
use Monolog\Level;
use Monolog\LogRecord;
use PHPUnit\Framework\TestCase;
use DateTimeImmutable;
class GdprProcessorTest extends TestCase
{
public function testEmailMasking(): void
{
$processor = new GdprProcessor([
'/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/' => '[email]',
]);
$record = new LogRecord(
datetime: new DateTimeImmutable(),
channel: 'test',
level: Level::Info,
message: 'User logged in: user@example.com',
context: []
);
$result = $processor($record);
$this->assertStringContainsString('[email]', $result->message);
$this->assertStringNotContainsString('user@example.com', $result->message);
}
}
Troubleshooting
Patterns Not Matching
- Verify regex patterns are valid:
preg_match('/your-pattern/', 'test-string') - Check pattern escaping in YAML (may need quotes)
- Enable debug mode to see which patterns are applied
Performance Issues
- Use the rate-limited audit logger
- Consider caching pattern validation results
- Profile with Symfony profiler
Memory Issues
- Set appropriate
maxDepthto prevent deep recursion - Monitor rate limiter statistics
- Use cleanup intervals for long-running processes