| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 4 | "bytes" |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 5 | "strings" |
| 6 | "sync" |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 7 | "time" |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 8 | ) |
| 9 | |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 10 | type LogItem struct { |
| 11 | RunId string `json:"runId"` |
| 12 | TimestampMilli int64 `json:"timestampMilli"` |
| 13 | Commit string `json:"commit,omitempty"` |
| 14 | Contents []byte `json:"contents"` |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 15 | } |
| 16 | |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 17 | type Logger struct { |
| 18 | l sync.Mutex |
| 19 | runId string |
| 20 | commitHash string |
| 21 | items []LogItem |
| 22 | curr LogItem |
| 23 | } |
| 24 | |
| 25 | func NewLogger(runId string) *Logger { |
| 26 | return &Logger{ |
| 27 | l: sync.Mutex{}, |
| 28 | runId: runId, |
| 29 | items: []LogItem{}, |
| 30 | curr: LogItem{ |
| 31 | RunId: runId, |
| 32 | Contents: []byte{}, |
| 33 | }, |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | func (l *Logger) Write(p []byte) (n int, err error) { |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 38 | l.l.Lock() |
| 39 | defer l.l.Unlock() |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 40 | cnt := 0 |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 41 | // TODO(gio): Reset s.logs periodically |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 42 | for len(p) > 0 { |
| 43 | pos := bytes.Index(p, []byte("\n")) |
| 44 | if pos != -1 { |
| 45 | if l.curr.TimestampMilli == 0 { |
| 46 | l.curr.TimestampMilli = time.Now().UnixMilli() |
| 47 | } |
| 48 | l.curr.Contents = append(l.curr.Contents, p[:pos]...) |
| 49 | l.items = append(l.items, l.curr) |
| 50 | l.curr = LogItem{ |
| 51 | RunId: l.runId, |
| 52 | Contents: []byte{}, |
| 53 | } |
| 54 | p = p[pos+len([]byte("\n")):] |
| 55 | cnt += pos + len([]byte("\n")) |
| 56 | } else { |
| 57 | if l.curr.TimestampMilli == 0 { |
| 58 | l.curr.TimestampMilli = time.Now().UnixMilli() |
| 59 | } |
| 60 | l.curr.Contents = append(l.curr.Contents, p...) |
| 61 | cnt += len(p) |
| 62 | p = []byte{} |
| 63 | } |
| 64 | } |
| 65 | return cnt, nil |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 66 | } |
| 67 | |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 68 | func (l *Logger) Items() []LogItem { |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 69 | l.l.Lock() |
| 70 | defer l.l.Unlock() |
| gio | aa6e27a | 2025-06-29 23:17:54 +0400 | [diff] [blame^] | 71 | ret := []LogItem{} |
| 72 | for _, i := range l.items { |
| 73 | ret = append(ret, i) |
| 74 | } |
| 75 | return ret |
| 76 | } |
| 77 | |
| 78 | func (l *Logger) Trim(n int) { |
| 79 | l.l.Lock() |
| 80 | defer l.l.Unlock() |
| 81 | l.items = l.items[n:] |
| 82 | } |
| 83 | |
| 84 | func (l *Logger) Contents() (string, error) { |
| 85 | l.l.Lock() |
| 86 | defer l.l.Unlock() |
| 87 | var ret strings.Builder |
| 88 | for _, i := range l.items { |
| 89 | if _, err := ret.Write(i.Contents); err != nil { |
| 90 | return "", err |
| 91 | } |
| 92 | if _, err := ret.WriteString("\n"); err != nil { |
| 93 | return "", err |
| 94 | } |
| 95 | } |
| 96 | return ret.String(), nil |
| gio | 183e834 | 2024-08-20 06:01:24 +0400 | [diff] [blame] | 97 | } |