Skip to content

Commit 56f3aba

Browse files
author
farhadzand
committed
feat: implement audit log retention system with configurable strategies
- Introduced a comprehensive retention system for managing audit logs, supporting delete, anonymize, and archive strategies. - Added configuration options for global and per-entity retention settings in `config/audit-logger.php`. - Implemented `CleanupAuditLogsCommand` for manual cleanup operations and `RetentionCleanupJob` for queued processing. - Enhanced `RetentionService` to handle retention logic and integrate with various strategies. - Updated database schema to include `anonymized_at` timestamp and added relevant indexes. - Expanded documentation in README.md to cover new retention features and usage examples. - Added tests to validate retention functionality and ensure correct behavior across different scenarios.
1 parent 437398e commit 56f3aba

22 files changed

+1995
-10
lines changed

README.md

Lines changed: 276 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The package uses a high-performance direct logging architecture with optional qu
3535
- [Performance Optimization](#performance-optimization)
3636
- [Testing](#testing)
3737
- [Security Best Practices](#security-best-practices)
38+
- [Audit Log Retention](#audit-log-retention)
3839
- [Migration & Upgrade Guide](#migration--upgrade-guide)
3940
- [Troubleshooting](#troubleshooting)
4041
- [Contributing](#contributing)
@@ -54,6 +55,7 @@ The package uses a high-performance direct logging architecture with optional qu
5455
- **Enhanced Query Scopes**: Comprehensive filtering capabilities with dedicated scopes for different query patterns.
5556
- **Extensible Drivers**: Supports multiple storage drivers (currently MySQL) with the ability to implement custom drivers.
5657
- **Automatic Migration**: Seamlessly creates audit tables for new models when enabled.
58+
- **Smart Retention System**: Comprehensive retention policies with delete, anonymize, and archive strategies for compliance and performance.
5759
- **Static Analysis**: Level 5 PHPStan compliance for maximum code quality and reliability.
5860

5961
## Requirements
@@ -137,13 +139,29 @@ return [
137139
'resolver' => null, // custom resolver class
138140
],
139141

142+
// Retention configuration for automatic cleanup
143+
'retention' => [
144+
'enabled' => env('AUDIT_RETENTION_ENABLED', false),
145+
'days' => env('AUDIT_RETENTION_DAYS', 365),
146+
'strategy' => env('AUDIT_RETENTION_STRATEGY', 'delete'), // 'delete', 'archive', 'anonymize'
147+
'batch_size' => env('AUDIT_RETENTION_BATCH_SIZE', 1000),
148+
'anonymize_after_days' => env('AUDIT_ANONYMIZE_DAYS', 180),
149+
'archive_connection' => env('AUDIT_ARCHIVE_CONNECTION', null),
150+
'run_cleanup_automatically' => env('AUDIT_AUTO_CLEANUP', false),
151+
],
152+
140153
// Registered entities for centralized configuration
141154
'entities' => [
142155
// Example entity configuration:
143156
// \App\Models\User::class => [
144157
// 'table' => 'users',
145158
// 'exclude' => ['password'],
146159
// 'include' => ['*'],
160+
// 'retention' => [
161+
// 'days' => 730,
162+
// 'strategy' => 'anonymize',
163+
// 'anonymize_after_days' => 365,
164+
// ],
147165
// ],
148166
],
149167
];
@@ -168,6 +186,15 @@ AUDIT_QUEUE_DELAY=0
168186

169187
# Migration Configuration
170188
AUDIT_AUTO_MIGRATION=true
189+
190+
# Retention Configuration
191+
AUDIT_RETENTION_ENABLED=false
192+
AUDIT_RETENTION_DAYS=365
193+
AUDIT_RETENTION_STRATEGY=delete
194+
AUDIT_RETENTION_BATCH_SIZE=1000
195+
AUDIT_ANONYMIZE_DAYS=180
196+
AUDIT_ARCHIVE_CONNECTION=null
197+
AUDIT_AUTO_CLEANUP=false
171198
```
172199

173200
### Database Schema
@@ -186,12 +213,14 @@ CREATE TABLE audit_{model_name}_logs (
186213
metadata JSON NULL,
187214
source VARCHAR(255) NULL,
188215
created_at TIMESTAMP NOT NULL,
216+
anonymized_at TIMESTAMP NULL,
189217

190218
INDEX idx_entity_id (entity_id),
191219
INDEX idx_causer (causer_type, causer_id),
192220
INDEX idx_created_at (created_at),
193221
INDEX idx_source (source),
194-
INDEX idx_action (action)
222+
INDEX idx_action (action),
223+
INDEX idx_anonymized_at (anonymized_at)
195224
);
196225
```
197226

@@ -897,6 +926,252 @@ final class AuditLogPolicy
897926
}
898927
```
899928

929+
## Audit Log Retention
930+
931+
The Laravel Audit Logger package includes a powerful retention system that helps you manage the lifecycle of your audit logs. This system supports automatic cleanup, anonymization, and archiving of old audit data to help with compliance requirements and database performance.
932+
933+
### Features
934+
935+
- **Multiple Retention Strategies**: Delete, anonymize, or archive old audit logs
936+
- **Per-Model Configuration**: Override global settings for specific models
937+
- **Batch Processing**: Efficient processing of large datasets
938+
- **Smart Anonymization**: Automatically detect and anonymize sensitive fields
939+
- **Flexible Scheduling**: Run manually or via scheduled jobs
940+
- **Dry Run Mode**: Preview changes before execution
941+
942+
### Configuration
943+
944+
#### Global Configuration
945+
946+
Configure retention settings in your `config/audit-logger.php`:
947+
948+
```php
949+
'retention' => [
950+
'enabled' => env('AUDIT_RETENTION_ENABLED', false),
951+
'days' => env('AUDIT_RETENTION_DAYS', 365),
952+
'strategy' => env('AUDIT_RETENTION_STRATEGY', 'delete'), // 'delete', 'archive', 'anonymize'
953+
'batch_size' => env('AUDIT_RETENTION_BATCH_SIZE', 1000),
954+
'anonymize_after_days' => env('AUDIT_ANONYMIZE_DAYS', 180),
955+
'archive_connection' => env('AUDIT_ARCHIVE_CONNECTION', null),
956+
'run_cleanup_automatically' => env('AUDIT_AUTO_CLEANUP', false),
957+
],
958+
```
959+
960+
#### Environment Variables
961+
962+
```env
963+
AUDIT_RETENTION_ENABLED=true
964+
AUDIT_RETENTION_DAYS=365
965+
AUDIT_RETENTION_STRATEGY=delete
966+
AUDIT_RETENTION_BATCH_SIZE=1000
967+
AUDIT_ANONYMIZE_DAYS=180
968+
AUDIT_ARCHIVE_CONNECTION=archive_db
969+
AUDIT_AUTO_CLEANUP=false
970+
```
971+
972+
#### Per-Entity Configuration
973+
974+
Configure retention settings for specific entities:
975+
976+
```php
977+
'entities' => [
978+
\App\Models\User::class => [
979+
'table' => 'users',
980+
'retention' => [
981+
'enabled' => true,
982+
'days' => 730, // Keep for 2 years
983+
'strategy' => 'anonymize',
984+
'anonymize_after_days' => 365, // Anonymize after 1 year
985+
],
986+
],
987+
\App\Models\Order::class => [
988+
'table' => 'orders',
989+
'retention' => [
990+
'days' => 2555, // Keep for 7 years (compliance)
991+
'strategy' => 'archive',
992+
],
993+
],
994+
],
995+
```
996+
997+
#### Per-Model Configuration
998+
999+
Configure retention directly in your model:
1000+
1001+
```php
1002+
<?php
1003+
1004+
declare(strict_types=1);
1005+
1006+
namespace App\Models;
1007+
1008+
use Illuminate\Database\Eloquent\Model;
1009+
use iamfarhad\LaravelAuditLog\Traits\Auditable;
1010+
1011+
final class User extends Model
1012+
{
1013+
use Auditable;
1014+
1015+
// Per-model retention configuration
1016+
protected array $auditRetention = [
1017+
'enabled' => true,
1018+
'days' => 730,
1019+
'strategy' => 'anonymize',
1020+
'anonymize_after_days' => 365,
1021+
];
1022+
}
1023+
```
1024+
1025+
### Retention Strategies
1026+
1027+
#### 1. Delete Strategy
1028+
1029+
Permanently removes old audit logs from the database.
1030+
1031+
```php
1032+
'strategy' => 'delete',
1033+
'days' => 365, // Delete records older than 1 year
1034+
```
1035+
1036+
**Use Case**: When you want to minimize storage usage and don't need historical data beyond a certain period.
1037+
1038+
#### 2. Anonymize Strategy
1039+
1040+
Replaces sensitive information with anonymized values while keeping the audit structure intact.
1041+
1042+
```php
1043+
'strategy' => 'anonymize',
1044+
'days' => 180, // Anonymize records older than 6 months
1045+
```
1046+
1047+
**Anonymized Fields**: email, phone, address, ip_address, user_agent, name, first_name, last_name, full_name
1048+
1049+
**Use Case**: Compliance requirements where you need to maintain audit trail structure but remove personally identifiable information.
1050+
1051+
#### 3. Archive Strategy
1052+
1053+
Moves old audit logs to a separate database connection for long-term storage.
1054+
1055+
```php
1056+
'strategy' => 'archive',
1057+
'days' => 365, // Archive records older than 1 year
1058+
'archive_connection' => 'archive_db',
1059+
```
1060+
1061+
**Use Case**: Long-term compliance requirements where you need to maintain historical data but keep the primary database lean.
1062+
1063+
#### 4. Combined Strategy
1064+
1065+
You can combine anonymization with deletion or archiving:
1066+
1067+
```php
1068+
'strategy' => 'delete', // or 'archive'
1069+
'days' => 730, // Final action after 2 years
1070+
'anonymize_after_days' => 365, // Anonymize after 1 year
1071+
```
1072+
1073+
### Usage
1074+
1075+
#### Manual Execution
1076+
1077+
```bash
1078+
# Run cleanup for all entities
1079+
php artisan audit:cleanup --force
1080+
1081+
# Run cleanup for specific entity
1082+
php artisan audit:cleanup --entity="App\Models\User" --force
1083+
1084+
# Dry run to see what would be processed
1085+
php artisan audit:cleanup --dry-run
1086+
1087+
# Process specific number of records
1088+
php artisan audit:cleanup --limit=1000 --force
1089+
```
1090+
1091+
#### Queue Processing
1092+
1093+
```php
1094+
use iamfarhad\LaravelAuditLog\Jobs\RetentionCleanupJob;
1095+
1096+
// Dispatch cleanup job for all entities
1097+
RetentionCleanupJob::dispatch();
1098+
1099+
// Dispatch cleanup job for specific entity
1100+
RetentionCleanupJob::dispatch('App\Models\User');
1101+
```
1102+
1103+
#### Scheduled Execution
1104+
1105+
Add to your `app/Console/Kernel.php`:
1106+
1107+
```php
1108+
protected function schedule(Schedule $schedule): void
1109+
{
1110+
// Run retention cleanup weekly
1111+
$schedule->command('audit:cleanup --force')
1112+
->weekly()
1113+
->environments(['production']);
1114+
1115+
// Or run for specific entities
1116+
$schedule->command('audit:cleanup --entity="App\Models\User" --force')
1117+
->daily()
1118+
->at('02:00');
1119+
}
1120+
```
1121+
1122+
#### Service Usage
1123+
1124+
```php
1125+
<?php
1126+
1127+
declare(strict_types=1);
1128+
1129+
use iamfarhad\LaravelAuditLog\Contracts\RetentionServiceInterface;
1130+
use iamfarhad\LaravelAuditLog\DTOs\RetentionConfig;
1131+
1132+
final class AuditMaintenanceService
1133+
{
1134+
public function __construct(
1135+
private readonly RetentionServiceInterface $retentionService
1136+
) {}
1137+
1138+
public function cleanupUserAudits(): void
1139+
{
1140+
$config = new RetentionConfig(
1141+
enabled: true,
1142+
days: 365,
1143+
strategy: 'anonymize',
1144+
batchSize: 1000,
1145+
anonymizeAfterDays: 180
1146+
);
1147+
1148+
$result = $this->retentionService->processRetention(
1149+
entityClass: 'App\Models\User',
1150+
config: $config,
1151+
dryRun: false
1152+
);
1153+
1154+
// Handle results...
1155+
}
1156+
}
1157+
```
1158+
1159+
### Best Practices
1160+
1161+
1. **Start with Dry Runs**: Always test your retention policies with `--dry-run` before applying
1162+
2. **Monitor Performance**: Use appropriate batch sizes for your database performance
1163+
3. **Backup Strategy**: Ensure you have backups before running retention operations
1164+
4. **Compliance Review**: Review your retention policies with legal/compliance teams
1165+
5. **Gradual Implementation**: Start with longer retention periods and adjust based on requirements
1166+
6. **Queue Processing**: Use queued processing for large datasets to avoid timeouts
1167+
1168+
### Security Considerations
1169+
1170+
- **Access Control**: Ensure only authorized users can execute retention commands
1171+
- **Audit the Audit**: Consider logging retention operations themselves
1172+
- **Data Recovery**: Have procedures for data recovery in case of mistakes
1173+
- **Anonymization Verification**: Test that anonymized data meets your privacy requirements
1174+
9001175
## Migration & Upgrade Guide
9011176

9021177
### Upgrading from Version 1.2.x to 1.3.x

config/audit-logger.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,32 @@
106106
'resolver' => null, // custom resolver class
107107
],
108108

109+
/*
110+
|--------------------------------------------------------------------------
111+
| Retention Configuration
112+
|--------------------------------------------------------------------------
113+
|
114+
| Configure automatic cleanup and retention policies for audit logs.
115+
| You can set global defaults and override them per entity.
116+
|
117+
*/
118+
'retention' => [
119+
'enabled' => env('AUDIT_RETENTION_ENABLED', false),
120+
'days' => env('AUDIT_RETENTION_DAYS', 365),
121+
'strategy' => env('AUDIT_RETENTION_STRATEGY', 'delete'), // 'delete', 'archive', 'anonymize'
122+
'batch_size' => env('AUDIT_RETENTION_BATCH_SIZE', 1000),
123+
'anonymize_after_days' => env('AUDIT_ANONYMIZE_DAYS', 180),
124+
'archive_connection' => env('AUDIT_ARCHIVE_CONNECTION', null),
125+
'run_cleanup_automatically' => env('AUDIT_AUTO_CLEANUP', false),
126+
],
127+
109128
/*
110129
|--------------------------------------------------------------------------
111130
| Registered Entities
112131
|--------------------------------------------------------------------------
113132
|
114-
| List of entities that should be audited.
133+
| List of entities that should be audited. You can override retention
134+
| settings per entity by adding a 'retention' key.
115135
|
116136
*/
117137
'entities' => [
@@ -120,6 +140,11 @@
120140
// 'table' => 'users',
121141
// 'exclude' => ['password'],
122142
// 'include' => ['*'],
143+
// 'retention' => [
144+
// 'days' => 730,
145+
// 'strategy' => 'anonymize',
146+
// 'anonymize_after_days' => 365,
147+
// ],
123148
// ],
124149
],
125150
];

0 commit comments

Comments
 (0)