Files
unibots/shell/logger/query.go
T
agent fc644ecd6e feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)
Reemplaza el scaffold del echobot por la plataforma completa de bots traida
desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out:
los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms +
E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client).

- go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths
  relativos reajustados a la nueva ubicacion dentro de fn_registry).
- app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales.
- modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports).

agents_and_robots queda archivado como museo de la era Matrix.
2026-06-07 11:50:13 +02:00

150 lines
3.4 KiB
Go

package logger
import (
"bufio"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
// ReadLogs returns all log entries for agentID between from and to (inclusive).
func ReadLogs(baseDir, agentID string, from, to time.Time) ([]json.RawMessage, error) {
var result []json.RawMessage
for d := from; !d.After(to); d = d.AddDate(0, 0, 1) {
entries, err := ReadDayLogs(baseDir, agentID, d)
if err != nil {
continue // skip missing days
}
result = append(result, entries...)
}
return result, nil
}
// ReadDayLogs returns all log entries for a specific day.
func ReadDayLogs(baseDir, agentID string, date time.Time) ([]json.RawMessage, error) {
dir := filepath.Join(baseDir, agentID)
prefix := date.Format("2006-01-02")
entries, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("read dir %s: %w", dir, err)
}
var result []json.RawMessage
for _, e := range entries {
name := e.Name()
if !strings.HasPrefix(name, prefix) || !isLogFile(name) {
continue
}
lines, err := readLogFile(filepath.Join(dir, name))
if err != nil {
continue
}
result = append(result, lines...)
}
return result, nil
}
// SearchLogs returns log entries where field equals value, within the date range.
func SearchLogs(baseDir, agentID string, field, value string, from, to time.Time) ([]json.RawMessage, error) {
all, err := ReadLogs(baseDir, agentID, from, to)
if err != nil {
return nil, err
}
var matched []json.RawMessage
for _, raw := range all {
var m map[string]any
if json.Unmarshal(raw, &m) != nil {
continue
}
if v, ok := m[field]; ok && fmt.Sprint(v) == value {
matched = append(matched, raw)
}
}
return matched, nil
}
// ListAgents returns the agent IDs that have log directories under baseDir.
func ListAgents(baseDir string) ([]string, error) {
entries, err := os.ReadDir(baseDir)
if err != nil {
return nil, fmt.Errorf("read base dir %s: %w", baseDir, err)
}
var ids []string
for _, e := range entries {
if e.IsDir() {
ids = append(ids, e.Name())
}
}
sort.Strings(ids)
return ids, nil
}
// ListDates returns the dates for which logs exist for the given agent.
func ListDates(baseDir, agentID string) ([]time.Time, error) {
dir := filepath.Join(baseDir, agentID)
entries, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("read dir %s: %w", dir, err)
}
seen := make(map[string]bool)
var dates []time.Time
for _, e := range entries {
if e.IsDir() || !isLogFile(e.Name()) {
continue
}
d := parseDateFromFilename(e.Name())
if d.IsZero() {
continue
}
key := d.Format("2006-01-02")
if !seen[key] {
seen[key] = true
dates = append(dates, d)
}
}
sort.Slice(dates, func(i, j int) bool { return dates[i].Before(dates[j]) })
return dates, nil
}
// readLogFile reads all JSONL lines from a file (.jsonl or .jsonl.gz).
func readLogFile(path string) ([]json.RawMessage, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var r io.Reader = f
if strings.HasSuffix(path, ".gz") {
gz, err := gzip.NewReader(f)
if err != nil {
return nil, err
}
defer gz.Close()
r = gz
}
var lines []json.RawMessage
scanner := bufio.NewScanner(r)
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
for scanner.Scan() {
line := scanner.Bytes()
if len(line) == 0 {
continue
}
cp := make([]byte, len(line))
copy(cp, line)
lines = append(lines, json.RawMessage(cp))
}
return lines, scanner.Err()
}