feat: setup logs retention by max file size

This commit is contained in:
fsylvain committed 2026-10-07 22:02:13 +02:00
1 parent 70c8492f4f
commit f914eae3c5
44 files changed
+2316 -161

No files matched your search

@@ -83,6 +83,7 @@ func encodeJSON(value any, empty string) (string, error) {
func AuditSettingsToDomain(m models.AuditSettings) audit.Settings {
return audit.Settings{
RetentionDays: m.RetentionDays,
RetentionMaxGB: m.RetentionMaxGB,
LastCleanupAt: m.LastCleanupAt,
LastCleanupRemoved: m.LastCleanupRemoved,
}
@@ -91,6 +92,7 @@ func AuditSettingsToDomain(m models.AuditSettings) audit.Settings {
func AuditSettingsToModel(s audit.Settings) models.AuditSettings {
return models.AuditSettings{
RetentionDays: s.RetentionDays,
RetentionMaxGB: s.RetentionMaxGB,
LastCleanupAt: s.LastCleanupAt,
LastCleanupRemoved: s.LastCleanupRemoved,
}
@@ -5,6 +5,7 @@ import "time"
type AuditSettings struct {
ID uint32 `gorm:"primaryKey"`
RetentionDays int `gorm:"not null"`
RetentionMaxGB int `gorm:"not null;default:0"`
LastCleanupAt *time.Time
LastCleanupRemoved int `gorm:"not null"`
}
@@ -4,6 +4,7 @@ import (
"RenewCMS/internal/domain/audit"
"RenewCMS/internal/infrastructure/persistence/mappers"
entity "RenewCMS/internal/infrastructure/persistence/models"
"slices"
"strings"
"time"
@@ -186,3 +187,49 @@ func (a *AuditEventRepository) DeleteOlderThan(cutoff time.Time) (int, error) {
result := a.db.Where("created_at < ?", cutoff.UTC()).Delete(&entity.AuditEvent{})
return int(result.RowsAffected), result.Error
}
const (
deleteChunkSize = 500
payloadSizeSQL = `COALESCE(SUM(
OCTET_LENGTH(action) + OCTET_LENGTH(actor_name) + OCTET_LENGTH(target_type) +
OCTET_LENGTH(target_id) + OCTET_LENGTH(target_name) + COALESCE(OCTET_LENGTH(failure_reason), 0) +
COALESCE(OCTET_LENGTH(ip), 0) + COALESCE(OCTET_LENGTH(user_agent), 0) +
OCTET_LENGTH(params) + OCTET_LENGTH(changes)), 0) AS payload, COUNT(*) AS total`
)
type payloadRow struct {
Payload int64
Total int64
}
func (a *AuditEventRepository) SizeBytes() (int64, error) {
var row payloadRow
if err := a.db.Model(&entity.AuditEvent{}).Select(payloadSizeSQL).Scan(&row).Error; err != nil {
return 0, err
}
return row.Payload + row.Total*audit.RowOverheadBytes, nil
}
func (a *AuditEventRepository) DeleteOldest(limit int, before time.Time) (int, error) {
if limit < 1 {
return 0, nil
}
var ids []uint64
err := a.db.Model(&entity.AuditEvent{}).
Where("created_at < ?", before.UTC()).
Order("created_at ASC, id ASC").
Limit(limit).
Pluck("id", &ids).Error
if err != nil {
return 0, err
}
removed := 0
for chunk := range slices.Chunk(ids, deleteChunkSize) {
result := a.db.Where("id IN ?", chunk).Delete(&entity.AuditEvent{})
if result.Error != nil {
return removed, result.Error
}
removed += int(result.RowsAffected)
}
return removed, nil
}
@@ -34,9 +34,10 @@ func (a *AuditSettingsRepository) Get() (audit.Settings, error) {
return mappers.AuditSettingsToDomain(model), nil
}
func (a *AuditSettingsRepository) SaveRetention(days int) error {
return a.update(map[string]any{"retention_days": days}, func(settings *audit.Settings) {
func (a *AuditSettingsRepository) SaveRetention(days int, maxGB int) error {
return a.update(map[string]any{"retention_days": days, "retention_max_gb": maxGB}, func(settings *audit.Settings) {
settings.RetentionDays = days
settings.RetentionMaxGB = maxGB
})
}