Skip to content

Commit 523bbde

Browse files
committed
wip
1 parent 6a58940 commit 523bbde

File tree

6 files changed

+688
-562
lines changed

6 files changed

+688
-562
lines changed

src/cmd/cli/command/compose.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ func makeComposeLogsCmd() *cobra.Command {
423423
var utc, _ = cmd.Flags().GetBool("utc")
424424
var verbose, _ = cmd.Flags().GetBool("verbose")
425425
var filter, _ = cmd.Flags().GetString("filter")
426+
var until, _ = cmd.Flags().GetString("until")
426427

427428
if !cmd.Flags().Changed("verbose") {
428429
verbose = true // default verbose for explicit tail command
@@ -432,17 +433,27 @@ func makeComposeLogsCmd() *cobra.Command {
432433
os.Setenv("TZ", "") // used by Go's "time" package, see https://pkg.go.dev/time#Location
433434
}
434435

435-
ts, err := cli.ParseTimeOrDuration(since, time.Now())
436+
now := time.Now()
437+
sinceTs, err := cli.ParseTimeOrDuration(since, now)
436438
if err != nil {
437-
return fmt.Errorf("invalid duration or time: %w", err)
439+
return fmt.Errorf("invalid 'since' duration or time: %w", err)
438440
}
441+
sinceTs = sinceTs.UTC()
442+
untilTs, err := cli.ParseTimeOrDuration(until, now)
443+
if err != nil {
444+
return fmt.Errorf("invalid 'until' duration or time: %w", err)
445+
}
446+
untilTs = untilTs.UTC()
439447

440-
ts = ts.UTC()
441-
sinceStr := ""
442-
if pkg.IsValidTime(ts) {
443-
sinceStr = " since " + ts.Format(time.RFC3339Nano) + " "
448+
rangeStr := ""
449+
if pkg.IsValidTime(sinceTs) {
450+
rangeStr = " since " + sinceTs.Format(time.RFC3339Nano)
444451
}
445-
term.Infof("Showing logs%s; press Ctrl+C to stop:", sinceStr)
452+
if pkg.IsValidTime(untilTs) {
453+
rangeStr += " until " + untilTs.Format(time.RFC3339Nano)
454+
}
455+
term.Infof("Showing logs%s; press Ctrl+C to stop:", rangeStr)
456+
446457
services := args
447458
if len(name) > 0 {
448459
services = append(args, strings.Split(name, ",")...) // backwards compat
@@ -465,7 +476,8 @@ func makeComposeLogsCmd() *cobra.Command {
465476
LogType: logType,
466477
Raw: raw,
467478
Services: services,
468-
Since: ts,
479+
Since: sinceTs,
480+
Until: untilTs,
469481
Verbose: verbose,
470482
}
471483

@@ -478,7 +490,8 @@ func makeComposeLogsCmd() *cobra.Command {
478490
logsCmd.Flags().Bool("follow", false, "follow log output") // NOTE: -f is already used by --file
479491
logsCmd.Flags().MarkHidden("follow") // TODO: implement this
480492
logsCmd.Flags().BoolP("raw", "r", false, "show raw (unparsed) logs")
481-
logsCmd.Flags().StringP("since", "S", "", "show logs since duration/time")
493+
logsCmd.Flags().String("since", "", "show logs since duration/time")
494+
logsCmd.Flags().String("until", "", "show logs until duration/time")
482495
logsCmd.Flags().Bool("utc", false, "show logs in UTC timezone (ie. TZ=UTC)")
483496
logsCmd.Flags().Var(&logType, "type", fmt.Sprintf(`show logs of type; one of %v`, logs.AllLogTypes))
484497
logsCmd.Flags().String("filter", "", "only show logs containing given text; case-insensitive")

src/pkg/cli/client/byoc/aws/byoc.go

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ func (b *ByocAws) Query(ctx context.Context, req *defangv1.DebugRequest) error {
634634
}
635635
close(lgEvtChan)
636636
}(lgi)
637-
evtsChan = mergeLogEventChan(evtsChan, lgEvtChan) // Merge sort the log events base one timestamp
637+
evtsChan = mergeLogEventChan(evtsChan, lgEvtChan) // Merge sort the log events based on timestamp
638638
}
639639

640640
loop:
@@ -678,36 +678,40 @@ loop:
678678
}
679679

680680
// Inspired by https://dev.to/vinaygo/concurrency-merge-sort-using-channels-and-goroutines-in-golang-35f7
681-
func mergeLogEventChan(a, b chan ecs.LogEvent) chan ecs.LogEvent {
682-
if a == nil {
683-
return b
681+
func mergech[T any](left chan T, right chan T, c chan T, less func(T, T) bool) {
682+
defer close(c)
683+
val, ok := <-left
684+
val2, ok2 := <-right
685+
for ok && ok2 {
686+
if less(val, val2) {
687+
c <- val
688+
val, ok = <-left
689+
} else {
690+
c <- val2
691+
val2, ok2 = <-right
692+
}
693+
}
694+
for ok {
695+
c <- val
696+
val, ok = <-left
697+
}
698+
for ok2 {
699+
c <- val2
700+
val2, ok2 = <-right
701+
}
702+
}
703+
704+
func mergeLogEventChan(left, right chan ecs.LogEvent) chan ecs.LogEvent {
705+
if left == nil {
706+
return right
684707
}
685-
if b == nil {
686-
return a
708+
if right == nil {
709+
return left
687710
}
688711
out := make(chan ecs.LogEvent)
689-
go func() {
690-
defer close(out)
691-
valA, okA := <-a
692-
valB, okB := <-b
693-
for okA && okB {
694-
if *valA.Timestamp < *valB.Timestamp {
695-
out <- valA
696-
valA, okA = <-a
697-
} else {
698-
out <- valB
699-
valB, okB = <-b
700-
}
701-
}
702-
for okA {
703-
out <- valA
704-
valA, okA = <-a
705-
}
706-
for okB {
707-
out <- valB
708-
valB, okB = <-b
709-
}
710-
}()
712+
go mergech(left, right, out, func(i1, i2 ecs.LogEvent) bool {
713+
return *i1.Timestamp < *i2.Timestamp
714+
})
711715
return out
712716
}
713717

src/pkg/cli/debug.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/AlecAivazis/survey/v2"
12+
"github.com/DefangLabs/defang/src/pkg"
1213
"github.com/DefangLabs/defang/src/pkg/cli/client"
1314
"github.com/DefangLabs/defang/src/pkg/cli/compose"
1415
"github.com/DefangLabs/defang/src/pkg/term"
@@ -35,6 +36,7 @@ type DebugConfig struct {
3536
Project *compose.Project
3637
Provider client.Provider
3738
Since time.Time
39+
Until time.Time
3840
}
3941

4042
func InteractiveDebugDeployment(ctx context.Context, client client.FabricClient, debugConfig DebugConfig) error {
@@ -95,16 +97,20 @@ func DebugDeployment(ctx context.Context, client client.FabricClient, debugConfi
9597
return ErrDryRun
9698
}
9799

98-
var sinceTime *timestamppb.Timestamp = nil
99-
if !debugConfig.Since.IsZero() {
100+
var sinceTime, untilTime *timestamppb.Timestamp
101+
if pkg.IsValidTime(debugConfig.Since) {
100102
sinceTime = timestamppb.New(debugConfig.Since)
101103
}
104+
if pkg.IsValidTime(debugConfig.Until) {
105+
untilTime = timestamppb.New(debugConfig.Until)
106+
}
102107
req := defangv1.DebugRequest{
103108
Etag: debugConfig.Etag,
104109
Files: files,
105110
Project: debugConfig.Project.Name,
106111
Services: debugConfig.FailedServices,
107112
Since: sinceTime,
113+
Until: untilTime,
108114
}
109115
err := debugConfig.Provider.Query(ctx, &req)
110116
if err != nil {

src/pkg/cli/tail.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,25 @@ type EndLogConditional struct {
6767
type TailDetectStopEventFunc func(services []string, host string, eventlog string) bool
6868

6969
type TailOptions struct {
70-
Services []string
70+
EndEventDetectFunc TailDetectStopEventFunc // Deprecated: use Subscribe instead #851
7171
Etag types.ETag
72-
Since time.Time
72+
Filter string
73+
LogType logs.LogType
7374
Raw bool
74-
EndEventDetectFunc TailDetectStopEventFunc // Deprecated: use Subscribe instead #851
75+
Services []string
76+
Since time.Time
77+
Until time.Time
7578
Verbose bool
76-
LogType logs.LogType
77-
Filter string
7879
}
7980

8081
func (to TailOptions) String() string {
8182
cmd := "tail --since=" + to.Since.UTC().Format(time.RFC3339Nano)
8283
if len(to.Services) > 0 {
8384
cmd += " --name=" + strings.Join(to.Services, ",")
8485
}
86+
if !to.Until.IsZero() {
87+
cmd += " --until=" + to.Until.UTC().Format(time.RFC3339Nano)
88+
}
8589
if to.Etag != "" {
8690
cmd += " --etag=" + to.Etag
8791
}
@@ -208,11 +212,18 @@ func isTransientError(err error) bool {
208212
}
209213

210214
func tail(ctx context.Context, provider client.Provider, projectName string, options TailOptions) error {
211-
var since *timestamppb.Timestamp
215+
var since, until *timestamppb.Timestamp
212216
if pkg.IsValidTime(options.Since) {
213217
since = timestamppb.New(options.Since)
214-
} else {
215-
options.Since = time.Now() // this is used to continue from the last timestamp
218+
}
219+
if pkg.IsValidTime(options.Until) {
220+
until = timestamppb.New(options.Until)
221+
// If the user specifies a deadline in the future, we should respect it
222+
if options.Until.After(time.Now()) {
223+
var cancel context.CancelFunc
224+
ctx, cancel = context.WithDeadline(ctx, options.Until)
225+
defer cancel()
226+
}
216227
}
217228

218229
tailRequest := &defangv1.TailRequest{
@@ -221,7 +232,8 @@ func tail(ctx context.Context, provider client.Provider, projectName string, opt
221232
Pattern: options.Filter,
222233
Project: projectName,
223234
Services: options.Services,
224-
Since: since,
235+
Since: since, // this is also used to continue from the last timestamp
236+
Until: until,
225237
}
226238

227239
serverStream, err := provider.Follow(ctx, tailRequest)

0 commit comments

Comments
 (0)