| package main |
| |
| import ( |
| "bytes" |
| "strings" |
| "sync" |
| "time" |
| ) |
| |
| type LogItem struct { |
| RunId string `json:"runId"` |
| TimestampMilli int64 `json:"timestampMilli"` |
| Commit string `json:"commit,omitempty"` |
| Contents []byte `json:"contents"` |
| } |
| |
| 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 |
| 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, |
| Commit: l.commitHash, |
| 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 *Logger) Items() []LogItem { |
| l.l.Lock() |
| defer l.l.Unlock() |
| 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 |
| } |