workspaceLogs fetches logs for the given workspace build. If follow is true, the returned channel will stream new logs as they are emitted. Otherwise, the channel will be closed immediately. nolint: revive // control flag is appropriate here
(ctx context.Context, client *codersdk.Client, wb codersdk.WorkspaceBuild, follow bool)
| 105 | // the channel will be closed immediately. |
| 106 | // nolint: revive // control flag is appropriate here |
| 107 | func workspaceLogs(ctx context.Context, client *codersdk.Client, wb codersdk.WorkspaceBuild, follow bool) ([]logLine, <-chan logLine, error) { |
| 108 | logs := make([]logLine, 0) |
| 109 | logsCh := make(chan logLine) |
| 110 | followCh := make(chan logLine) |
| 111 | |
| 112 | var fetchGroup, followGroup errgroup.Group |
| 113 | |
| 114 | buildLogsAfterCh := make(chan int64) |
| 115 | fetchGroup.Go(func() error { |
| 116 | var afterID int64 |
| 117 | defer func() { |
| 118 | if !follow { |
| 119 | return |
| 120 | } |
| 121 | buildLogsAfterCh <- afterID |
| 122 | }() |
| 123 | buildLogsC, closer, err := client.WorkspaceBuildLogsAfter(ctx, wb.ID, 0) |
| 124 | if err != nil { |
| 125 | return xerrors.Errorf("failed to get build logs: %w", err) |
| 126 | } |
| 127 | defer closer.Close() |
| 128 | for log := range buildLogsC { |
| 129 | afterID = log.ID |
| 130 | logsCh <- logLine{ |
| 131 | ts: log.CreatedAt, |
| 132 | text: log.Text(), |
| 133 | } |
| 134 | } |
| 135 | return nil |
| 136 | }) |
| 137 | |
| 138 | if follow { |
| 139 | followGroup.Go(func() error { |
| 140 | afterID := <-buildLogsAfterCh |
| 141 | buildLogsC, closer, err := client.WorkspaceBuildLogsAfter(ctx, wb.ID, afterID) |
| 142 | if err != nil { |
| 143 | return xerrors.Errorf("failed to follow build logs: %w", err) |
| 144 | } |
| 145 | defer closer.Close() |
| 146 | for log := range buildLogsC { |
| 147 | followCh <- logLine{ |
| 148 | ts: log.CreatedAt, |
| 149 | text: log.Text(), |
| 150 | } |
| 151 | } |
| 152 | return nil |
| 153 | }) |
| 154 | } |
| 155 | |
| 156 | for _, res := range wb.Resources { |
| 157 | for _, agt := range res.Agents { |
| 158 | logSrcNames := make(map[uuid.UUID]string) |
| 159 | for _, src := range agt.LogSources { |
| 160 | logSrcNames[src.ID] = src.DisplayName |
| 161 | } |
| 162 | agentLogsAfterCh := make(chan int64) |
| 163 | var afterID int64 |
| 164 | fetchGroup.Go(func() error { |
no test coverage detected