-
Notifications
You must be signed in to change notification settings - Fork 16
/
logs.go
109 lines (94 loc) · 2.69 KB
/
logs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package nomad
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/hashicorp/nomad/api"
"github.com/robinovitch61/wander/internal/tui/components/page"
"github.com/robinovitch61/wander/internal/tui/formatter"
"strings"
"time"
)
type LogType int8
const (
StdOut LogType = iota
StdErr
)
type LogsStreamMsg struct {
Value string // may include line breaks
Type LogType
}
func (p LogType) String() string {
switch p {
case StdOut:
return "Stdout Logs"
case StdErr:
return "Stderr Logs"
}
return "unknown"
}
func (p LogType) ShortString() string {
switch p {
case StdOut:
return "stdout"
case StdErr:
return "stderr"
}
return "unknown"
}
func FetchLogs(client api.Client, alloc api.Allocation, taskName string, logType LogType, logOffset int, logTail bool) tea.Cmd {
return func() tea.Msg {
// This is currently very important and strange. The logs api attempts to go through the node directly
// by default. The default timeout for this is 1 second. If it fails, it falls silently to going through
// the server. Since it always fails, at least in my Nomad setup, make it timeout immediately by setting
// the timeout to something tiny.
api.ClientConnTimeout = 1 * time.Microsecond
closeLogConn := make(chan struct{}) // never closed for now
logsChan, _ := client.AllocFS().Logs( // TODO: deal with error channel
&alloc,
logTail,
taskName,
logType.ShortString(),
"end",
int64(logOffset),
closeLogConn,
nil,
)
var logRows []string
var logsStream LogsStream
if !logTail {
allLogs := ""
for l := range logsChan {
allLogs += string(l.Data)
}
tabReplacedLogs := formatter.CleanLogs(allLogs)
logRows = strings.Split(tabReplacedLogs, "\n")
} else {
logsStream = LogsStream{logsChan, logType}
}
tableHeader, allPageData := logsAsTable(logRows, logType)
return PageLoadedMsg{Page: LogsPage, TableHeader: tableHeader, AllPageRows: allPageData, LogsStream: logsStream}
}
}
func logsAsTable(logs []string, logType LogType) ([]string, []page.Row) {
var logRows [][]string
var keys []string
for _, row := range logs {
if stripped := strings.TrimSpace(row); stripped != "" {
logRows = append(logRows, []string{row})
}
keys = append(keys, "")
}
columns := []string{logType.String()}
table := formatter.GetRenderedTableAsString(columns, logRows)
var rows []page.Row
for idx, row := range table.ContentRows {
rows = append(rows, page.Row{Key: keys[idx], Row: row})
}
return table.HeaderRows, rows
}
func ReadLogsStreamNextMessage(c LogsStream) tea.Cmd {
return func() tea.Msg {
line := <-c.Chan
cleanedData := formatter.CleanLogs(string(line.Data))
return LogsStreamMsg{Value: cleanedData, Type: c.LogType}
}
}