mirror of
https://github.com/kopia/kopia.git
synced 2025-12-23 22:57:50 -05:00
150 lines
3.5 KiB
Go
150 lines
3.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/alecthomas/kingpin/v2"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/internal/clock"
|
|
"github.com/kopia/kopia/internal/repodiag"
|
|
"github.com/kopia/kopia/repo/blob"
|
|
)
|
|
|
|
type logSessionInfo struct {
|
|
id string
|
|
startTime time.Time
|
|
endTime time.Time
|
|
segments []blob.Metadata
|
|
totalSize int64
|
|
}
|
|
|
|
type logSelectionCriteria struct {
|
|
all bool
|
|
latest int
|
|
youngerThan time.Duration
|
|
olderThan time.Duration
|
|
}
|
|
|
|
func (c *logSelectionCriteria) setup(cmd *kingpin.CmdClause) {
|
|
cmd.Flag("all", "Show all logs").BoolVar(&c.all)
|
|
cmd.Flag("latest", "Include last N logs, by default the last one is shown").Short('n').IntVar(&c.latest)
|
|
cmd.Flag("younger-than", "Include logs younger than X (e.g. '1h')").DurationVar(&c.youngerThan)
|
|
cmd.Flag("older-than", "Include logs older than X (e.g. '1h')").DurationVar(&c.olderThan)
|
|
}
|
|
|
|
func (c *logSelectionCriteria) any() bool {
|
|
return c.all || c.latest > 0 || c.youngerThan > 0 || c.olderThan > 0
|
|
}
|
|
|
|
func (c *logSelectionCriteria) filterLogSessions(allSessions []*logSessionInfo) []*logSessionInfo {
|
|
if c.all {
|
|
return allSessions
|
|
}
|
|
|
|
if c.youngerThan > 0 {
|
|
allSessions = filterLogSessions(allSessions, func(ls *logSessionInfo) bool {
|
|
return clock.Now().Sub(ls.startTime) < c.youngerThan
|
|
})
|
|
}
|
|
|
|
if c.olderThan > 0 {
|
|
allSessions = filterLogSessions(allSessions, func(ls *logSessionInfo) bool {
|
|
return clock.Now().Sub(ls.startTime) > c.olderThan
|
|
})
|
|
}
|
|
|
|
if c.latest > 0 && len(allSessions) > c.latest {
|
|
allSessions = allSessions[len(allSessions)-c.latest:]
|
|
}
|
|
|
|
return allSessions
|
|
}
|
|
|
|
func getLogSessions(ctx context.Context, st blob.Reader) ([]*logSessionInfo, error) {
|
|
sessions := map[string]*logSessionInfo{}
|
|
|
|
var allSessions []*logSessionInfo
|
|
|
|
if err := st.ListBlobs(ctx, repodiag.LogBlobPrefix, func(bm blob.Metadata) error {
|
|
parts := strings.Split(string(bm.BlobID), "_")
|
|
|
|
//nolint:mnd
|
|
if len(parts) < 8 {
|
|
log(ctx).Errorf("invalid part count: %v skipping unrecognized log: %v", len(parts), bm.BlobID)
|
|
return nil
|
|
}
|
|
|
|
id := parts[2] + "_" + parts[3]
|
|
|
|
startTime, err := strconv.ParseInt(parts[4], 10, 64)
|
|
if err != nil {
|
|
log(ctx).Errorf("invalid start time - skipping unrecognized log: %v", bm.BlobID)
|
|
|
|
//nolint:nilerr
|
|
return nil
|
|
}
|
|
|
|
endTime, err := strconv.ParseInt(parts[5], 10, 64)
|
|
if err != nil {
|
|
log(ctx).Errorf("invalid end time - skipping unrecognized log: %v", bm.BlobID)
|
|
|
|
//nolint:nilerr
|
|
return nil
|
|
}
|
|
|
|
s := sessions[id]
|
|
if s == nil {
|
|
s = &logSessionInfo{
|
|
id: id,
|
|
}
|
|
sessions[id] = s
|
|
allSessions = append(allSessions, s)
|
|
}
|
|
|
|
if t := time.Unix(startTime, 0); s.startTime.IsZero() || t.Before(s.startTime) {
|
|
s.startTime = t
|
|
}
|
|
|
|
if t := time.Unix(endTime, 0); t.After(s.endTime) {
|
|
s.endTime = t
|
|
}
|
|
|
|
s.segments = append(s.segments, bm)
|
|
s.totalSize += bm.Length
|
|
|
|
return nil
|
|
}); err != nil {
|
|
return nil, errors.Wrap(err, "error listing logs")
|
|
}
|
|
|
|
for _, s := range allSessions {
|
|
sort.Slice(s.segments, func(i, j int) bool {
|
|
return s.segments[i].Timestamp.Before(s.segments[j].Timestamp)
|
|
})
|
|
}
|
|
|
|
// sort sessions by start time
|
|
sort.Slice(allSessions, func(i, j int) bool {
|
|
return allSessions[i].segments[0].Timestamp.Before(allSessions[j].segments[0].Timestamp)
|
|
})
|
|
|
|
return allSessions, nil
|
|
}
|
|
|
|
func filterLogSessions(logs []*logSessionInfo, predicate func(l *logSessionInfo) bool) []*logSessionInfo {
|
|
var result []*logSessionInfo
|
|
|
|
for _, l := range logs {
|
|
if predicate(l) {
|
|
result = append(result, l)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|