415bf0cce1
Single Go binary for the ClickHouse log store (logs.raw): chlog with cat/tail/grep subcommands, plus chcat/chtail/chgrep argv[0]-dispatched symlink entrypoints. Every query is time-bounded and fully parameterized; chgrep guards wide unfiltered scans. Ships nfpm RPM with completions and woodpecker PR/tag pipelines mirroring node-lookup.
39 lines
988 B
Go
39 lines
988 B
Go
package chlog
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
var durationRe = regexp.MustCompile(`^(\d+)([smhdw])$`)
|
|
|
|
var durationUnits = map[string]time.Duration{
|
|
"s": time.Second,
|
|
"m": time.Minute,
|
|
"h": time.Hour,
|
|
"d": 24 * time.Hour,
|
|
"w": 7 * 24 * time.Hour,
|
|
}
|
|
|
|
// ParseTimeSpec accepts a relative duration (15m, 1h, 2d, 1w) meaning "that
|
|
// long before now", or an absolute RFC3339 timestamp.
|
|
func ParseTimeSpec(spec string, now time.Time) (time.Time, error) {
|
|
if spec == "" {
|
|
return time.Time{}, fmt.Errorf("empty time spec")
|
|
}
|
|
if m := durationRe.FindStringSubmatch(spec); m != nil {
|
|
n, err := strconv.ParseInt(m[1], 10, 64)
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("invalid duration %q: %w", spec, err)
|
|
}
|
|
return now.Add(-time.Duration(n) * durationUnits[m[2]]), nil
|
|
}
|
|
t, err := time.Parse(time.RFC3339, spec)
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("invalid time %q: use a duration (15m, 1h, 2d) or RFC3339", spec)
|
|
}
|
|
return t.UTC(), nil
|
|
}
|