CI / changes (push) Successful in 9s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 26s
CI / web (push) Successful in 33s
CI / go (push) Successful in 56s
CI / bird2 (push) Successful in 14s
CI / release (push) Successful in 20s
Added new endpoints for estimating and executing runtime log auto-cleanup based on tenant settings. Introduced configuration options for auto-cleanup policies, including scheduling and file size limits. Updated the API documentation and UI components to reflect these changes, improving user interaction with runtime log management. Enhanced error handling and added new UI elements for better visibility of audit logs and cleanup actions.
148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
package runtimelogs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
const autoSchedulerActor = "auto:scheduler"
|
|
|
|
// FileEstimate describes whether a log file would be cleaned by auto policy.
|
|
type FileEstimate struct {
|
|
Filename string `json:"filename"`
|
|
SizeBytes int64 `json:"size_bytes"`
|
|
WouldCleanup bool `json:"would_cleanup"`
|
|
SkipReason string `json:"skip_reason,omitempty"`
|
|
}
|
|
|
|
// AutoCleanupDeps wires FS cleanup with audit persistence.
|
|
type AutoCleanupDeps struct {
|
|
Service *Service
|
|
Store store.Backend
|
|
TenantID string
|
|
}
|
|
|
|
// EstimateAutoCleanup lists files that exceed the configured size threshold.
|
|
func EstimateAutoCleanup(svc *Service, policy AutoPolicy) ([]FileEstimate, error) {
|
|
if svc == nil || !svc.Available() {
|
|
return nil, ErrUnavailable
|
|
}
|
|
files, err := svc.ListFiles()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]FileEstimate, 0, len(files))
|
|
for _, f := range files {
|
|
est := FileEstimate{
|
|
Filename: f.Name,
|
|
SizeBytes: f.SizeBytes,
|
|
}
|
|
if f.SizeBytes <= policy.MaxFileBytes {
|
|
est.SkipReason = "under_threshold"
|
|
} else if f.SizeBytes > MaxCleanupBytes {
|
|
est.SkipReason = "too_large"
|
|
} else {
|
|
est.WouldCleanup = true
|
|
}
|
|
out = append(out, est)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// RunAutoCleanup applies auto policy to eligible files and writes audit rows.
|
|
func RunAutoCleanup(ctx context.Context, deps AutoCleanupDeps, policy AutoPolicy, dryRun bool, trigger string) (map[string]any, error) {
|
|
if deps.Service == nil || !deps.Service.Available() {
|
|
return nil, ErrUnavailable
|
|
}
|
|
if deps.Store == nil || deps.TenantID == "" {
|
|
return nil, fmt.Errorf("runtimelogs: store tenant required for auto cleanup")
|
|
}
|
|
_ = ctx
|
|
|
|
estimates, err := EstimateAutoCleanup(deps.Service, policy)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
detailBase := map[string]any{
|
|
"trigger": trigger,
|
|
"dry_run": dryRun,
|
|
"max_file_bytes": policy.MaxFileBytes,
|
|
"mode": policy.Mode,
|
|
"files_processed": 0,
|
|
}
|
|
cleaned := make([]map[string]any, 0)
|
|
skipped := make([]map[string]any, 0)
|
|
|
|
for _, est := range estimates {
|
|
if !est.WouldCleanup {
|
|
if est.SkipReason != "" {
|
|
skipped = append(skipped, map[string]any{
|
|
"filename": est.Filename,
|
|
"reason": est.SkipReason,
|
|
"size": est.SizeBytes,
|
|
})
|
|
}
|
|
continue
|
|
}
|
|
if dryRun {
|
|
cleaned = append(cleaned, map[string]any{
|
|
"filename": est.Filename,
|
|
"size": est.SizeBytes,
|
|
"dry_run": true,
|
|
})
|
|
continue
|
|
}
|
|
sizeBefore, sizeAfter, err := deps.Service.Cleanup(est.Filename, policy.Mode)
|
|
if err != nil {
|
|
skipped = append(skipped, map[string]any{
|
|
"filename": est.Filename,
|
|
"reason": err.Error(),
|
|
"size": est.SizeBytes,
|
|
})
|
|
continue
|
|
}
|
|
auditDetail := map[string]any{
|
|
"trigger": trigger,
|
|
"mode": policy.Mode,
|
|
}
|
|
auditID, err := deps.Store.AppendRuntimeLogCleanupAudit(
|
|
deps.TenantID, autoSchedulerActor, est.Filename, policy.Mode, sizeBefore, sizeAfter, auditDetail)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("runtimelogs: audit: %w", err)
|
|
}
|
|
entry := map[string]any{
|
|
"filename": est.Filename,
|
|
"audit_id": auditID,
|
|
"size_before": sizeBefore,
|
|
"action": policy.Mode,
|
|
}
|
|
if sizeAfter != nil {
|
|
entry["size_after"] = *sizeAfter
|
|
}
|
|
cleaned = append(cleaned, entry)
|
|
}
|
|
|
|
detailBase["files_processed"] = len(cleaned)
|
|
return map[string]any{
|
|
"dry_run": dryRun,
|
|
"trigger": trigger,
|
|
"policy": policySnapshot(policy),
|
|
"cleaned": cleaned,
|
|
"skipped": skipped,
|
|
"cleaned_count": len(cleaned),
|
|
"skipped_count": len(skipped),
|
|
}, nil
|
|
}
|
|
|
|
func policySnapshot(p AutoPolicy) map[string]any {
|
|
return map[string]any{
|
|
"enabled": p.Enabled,
|
|
"max_file_bytes": p.MaxFileBytes,
|
|
"schedule": p.Schedule,
|
|
"mode": p.Mode,
|
|
}
|
|
}
|