Files
agents_and_robots/shell/logger/cleanup.go
T
egutierrez 71079962ca feat: add structured JSONL logging package with rotation and query
Nuevo paquete shell/logger/ que implementa logging estructurado JSONL
para agentes. Incluye DailyRotatingWriter con rotación diaria y por
tamaño (50MB default), limpieza automática de archivos viejos (7 días),
compresión gzip de logs rotados, y funciones de consulta (ReadLogs,
SearchLogs, ListAgents, ListDates) para que agentes LLM puedan leer
logs de otros agentes. Basado en log/slog de stdlib, sin dependencias
externas. 18 tests unitarios cubren rotación, concurrencia y consultas.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 17:26:56 +00:00

85 lines
1.8 KiB
Go

package logger
import (
"context"
"os"
"path/filepath"
"strings"
"time"
)
// runCleanup periodically removes log files older than maxAgeDays for the
// given agent. It runs until ctx is cancelled.
func runCleanup(ctx context.Context, baseDir, agentID string, maxAgeDays int, interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
// Run once immediately at startup.
cleanOldLogs(baseDir, agentID, maxAgeDays)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
cleanOldLogs(baseDir, agentID, maxAgeDays)
}
}
}
// cleanOldLogs removes .jsonl and .jsonl.gz files older than maxAgeDays.
func cleanOldLogs(baseDir, agentID string, maxAgeDays int) {
dir := filepath.Join(baseDir, agentID)
entries, err := os.ReadDir(dir)
if err != nil {
return
}
cutoff := time.Now().UTC().AddDate(0, 0, -maxAgeDays)
for _, e := range entries {
if e.IsDir() {
continue
}
name := e.Name()
if !isLogFile(name) {
continue
}
date := parseDateFromFilename(name)
if date.IsZero() {
continue
}
if date.Before(cutoff) {
os.Remove(filepath.Join(dir, name))
}
}
}
// isLogFile returns true for .jsonl and .jsonl.gz files.
func isLogFile(name string) bool {
return strings.HasSuffix(name, ".jsonl") || strings.HasSuffix(name, ".jsonl.gz")
}
// parseDateFromFilename extracts YYYY-MM-DD from filenames like:
//
// 2026-03-06.jsonl
// 2026-03-06.1.jsonl
// 2026-03-06.jsonl.gz
func parseDateFromFilename(name string) time.Time {
// Strip extensions.
base := strings.TrimSuffix(name, ".gz")
base = strings.TrimSuffix(base, ".jsonl")
// Remove numeric suffix (e.g., ".1" from "2026-03-06.1").
if idx := strings.LastIndex(base, "."); idx >= 0 {
candidate := base[:idx]
if t, err := time.Parse("2006-01-02", candidate); err == nil {
return t
}
}
t, _ := time.Parse("2006-01-02", base)
return t
}