tty logger: show multi-line messages

Support multi-line log messages rather than truncating and correctly
account terminal height.

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi 2021-10-06 11:51:10 -07:00
parent 92a04fe001
commit 82fc36c76b

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"sync" "sync"
@ -12,6 +13,7 @@ import (
"github.com/containerd/console" "github.com/containerd/console"
"github.com/morikuni/aec" "github.com/morikuni/aec"
"github.com/tonistiigi/vt100"
"go.dagger.io/dagger/environment" "go.dagger.io/dagger/environment"
) )
@ -187,26 +189,13 @@ func (c *TTYOutput) print() {
} }
fmt.Fprint(c.cons, b.ANSI) fmt.Fprint(c.cons, b.ANSI)
runningGroups := 0 linesPerGroup := c.linesPerGroup(width, height)
for _, message := range c.logs.Messages {
group := message.Group
if group == nil || group.State != environment.StateComputing {
continue
}
runningGroups++
}
linesPerGroup := 5
if freeLines := (height - len(c.logs.Messages)); freeLines > 0 && runningGroups > 0 {
linesPerGroup = (freeLines - 2) / runningGroups
}
lineCount := 0 lineCount := 0
for _, message := range c.logs.Messages { for _, message := range c.logs.Messages {
if group := message.Group; group != nil { if group := message.Group; group != nil {
lineCount += c.printGroup(group, width, linesPerGroup) lineCount += c.printGroup(group, width, linesPerGroup)
} else { } else {
lineCount += c.printLine(message.Event, width) lineCount += c.printLine(c.cons, message.Event, width)
} }
} }
@ -220,7 +209,32 @@ func (c *TTYOutput) print() {
c.lineCount = lineCount c.lineCount = lineCount
} }
func (c *TTYOutput) printLine(event Event, width int) int { func (c *TTYOutput) linesPerGroup(width, height int) int {
usedLines := 0
for _, message := range c.logs.Messages {
if group := message.Group; group != nil {
usedLines++
continue
}
usedLines += c.printLine(io.Discard, message.Event, width)
}
runningGroups := 0
for _, message := range c.logs.Messages {
if group := message.Group; group != nil && group.State == environment.StateComputing {
runningGroups++
}
}
linesPerGroup := 5
if freeLines := (height - usedLines); freeLines > 0 && runningGroups > 0 {
linesPerGroup = (freeLines - 2) / runningGroups
}
return linesPerGroup
}
func (c *TTYOutput) printLine(w io.Writer, event Event, width int) int {
message := colorize.Color(fmt.Sprintf("%s %s %s%s", message := colorize.Color(fmt.Sprintf("%s %s %s%s",
formatTimestamp(event), formatTimestamp(event),
formatLevel(event), formatLevel(event),
@ -228,11 +242,6 @@ func (c *TTYOutput) printLine(event Event, width int) int {
formatFields(event), formatFields(event),
)) ))
// trim
for utf8.RuneCountInString(message) > width {
message = message[0:len(message)-4] + "…"
}
// pad // pad
if delta := width - utf8.RuneCountInString(message); delta > 0 { if delta := width - utf8.RuneCountInString(message); delta > 0 {
message += strings.Repeat(" ", delta) message += strings.Repeat(" ", delta)
@ -240,9 +249,11 @@ func (c *TTYOutput) printLine(event Event, width int) int {
message += "\n" message += "\n"
// print // print
fmt.Fprint(c.cons, message) fmt.Fprint(w, message)
return 1 t := vt100.NewVT100(100, width)
t.Write([]byte(message))
return t.UsedHeight()
} }
func (c *TTYOutput) printGroup(group *Group, width, maxLines int) int { func (c *TTYOutput) printGroup(group *Group, width, maxLines int) int {
@ -294,23 +305,26 @@ func (c *TTYOutput) printGroup(group *Group, width, maxLines int) int {
fmt.Fprint(c.cons, out) fmt.Fprint(c.cons, out)
lineCount++ lineCount++
if group.State == environment.StateCompleted { printEvents := []Event{}
// for completed tasks, don't show any logs switch group.State {
return lineCount case environment.StateComputing:
} printEvents = group.Events
events := group.Events
if group.State == environment.StateComputing {
// for computing tasks, show only last N // for computing tasks, show only last N
if len(events) > maxLines { if len(printEvents) > maxLines {
events = events[len(events)-maxLines:] printEvents = printEvents[len(printEvents)-maxLines:]
} }
case environment.StateCanceled:
// for completed tasks, don't show any logs
printEvents = []Event{}
case environment.StateFailed:
// for failed, show all logs
printEvents = group.Events
case environment.StateCompleted:
// for completed tasks, don't show any logs
printEvents = []Event{}
} }
// for everything else (error, canceled), show all logs for _, event := range printEvents {
for _, event := range events {
lineCount += c.printGroupLine(event, width) lineCount += c.printGroupLine(event, width)
} }