AppRunner: Automatically annotate logs
Change-Id: I5e614fc1e841e183ac649758972428ae55162a67
diff --git a/apps/app-runner/log.go b/apps/app-runner/log.go
index 2e5f524..084e3d1 100644
--- a/apps/app-runner/log.go
+++ b/apps/app-runner/log.go
@@ -1,24 +1,97 @@
package main
import (
+ "bytes"
"strings"
"sync"
+ "time"
)
-type Log struct {
- l sync.Mutex
- d strings.Builder
+type LogItem struct {
+ RunId string `json:"runId"`
+ TimestampMilli int64 `json:"timestampMilli"`
+ Commit string `json:"commit,omitempty"`
+ Contents []byte `json:"contents"`
}
-func (l *Log) Write(p []byte) (n int, err error) {
+type Logger struct {
+ l sync.Mutex
+ runId string
+ commitHash string
+ items []LogItem
+ curr LogItem
+}
+
+func NewLogger(runId string) *Logger {
+ return &Logger{
+ l: sync.Mutex{},
+ runId: runId,
+ items: []LogItem{},
+ curr: LogItem{
+ RunId: runId,
+ Contents: []byte{},
+ },
+ }
+}
+
+func (l *Logger) Write(p []byte) (n int, err error) {
l.l.Lock()
defer l.l.Unlock()
+ cnt := 0
// TODO(gio): Reset s.logs periodically
- return l.d.Write(p)
+ for len(p) > 0 {
+ pos := bytes.Index(p, []byte("\n"))
+ if pos != -1 {
+ if l.curr.TimestampMilli == 0 {
+ l.curr.TimestampMilli = time.Now().UnixMilli()
+ }
+ l.curr.Contents = append(l.curr.Contents, p[:pos]...)
+ l.items = append(l.items, l.curr)
+ l.curr = LogItem{
+ RunId: l.runId,
+ Contents: []byte{},
+ }
+ p = p[pos+len([]byte("\n")):]
+ cnt += pos + len([]byte("\n"))
+ } else {
+ if l.curr.TimestampMilli == 0 {
+ l.curr.TimestampMilli = time.Now().UnixMilli()
+ }
+ l.curr.Contents = append(l.curr.Contents, p...)
+ cnt += len(p)
+ p = []byte{}
+ }
+ }
+ return cnt, nil
}
-func (l *Log) Contents() string {
+func (l *Logger) Items() []LogItem {
l.l.Lock()
defer l.l.Unlock()
- return l.d.String()
+ ret := []LogItem{}
+ for _, i := range l.items {
+ ret = append(ret, i)
+ }
+ return ret
+}
+
+func (l *Logger) Trim(n int) {
+ l.l.Lock()
+ defer l.l.Unlock()
+ l.items = l.items[n:]
+}
+
+func (l *Logger) Contents() (string, error) {
+ l.l.Lock()
+ defer l.l.Unlock()
+ var ret strings.Builder
+ for _, i := range l.items {
+ if _, err := ret.Write(i.Contents); err != nil {
+ return "", err
+ }
+ if _, err := ret.WriteString("\n"); err != nil {
+ return "", err
+ }
+ }
+ return ret.String(), nil
}