From 98a78b58e6961cfc6250872f5be07fcb38866505 Mon Sep 17 00:00:00 2001 From: Lennart Espe <3391295+lnsp@users.noreply.github.com> Date: Sun, 26 Jul 2026 14:13:52 +0200 Subject: [PATCH] Port batch log streaming onto streamOnce and follow This branch was cut from a stale local master, before #11 replaced streamRequest with streamOnce plus a reconnecting follow helper. The squash merge of #14 produced no textual conflict -- the removed symbol and the new call site are in different parts of the file -- so master built green in the branch and broken on trunk. Batch log streaming now uses the same path service logs do. A followed stream reconnects and resumes at an exact byte offset. It always starts at the beginning of the log rather than anchoring to the end: an execution's output is finite and usually short, so there is nothing to gain from skipping the start and a real chance of missing all of it. --- api/client.go | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/api/client.go b/api/client.go index e2d1a14..6536f28 100644 --- a/api/client.go +++ b/api/client.go @@ -590,12 +590,30 @@ func (client *Client) CancelBatchExecution(project, service, execution string) e // Batch logs are addressed per execution rather than per service: a batch // service has no deployment for the service-wide endpoint to derive a label // from. +// +// A followed stream reconnects and resumes at an exact byte offset, the same +// way service logs do. It always starts from the beginning of the log, since an +// execution's output is finite and usually short -- there is no reason to +// anchor to the end and risk missing the first lines. func (client *Client) StreamBatchExecutionLogs(project, service, execution string, w io.Writer, follow bool) error { - path := batchPath(project, service) + "/" + execution + "/logs" - if follow { - path += "?follow" + base := batchPath(project, service) + "/" + execution + "/logs" + logPath := func(params url.Values) string { + if follow { + params.Set("follow", "true") + params.Set("keepalive", keepaliveInterval.String()) + } + return base + "?" + params.Encode() } - return client.streamRequest(http.MethodGet, path, w) + start := url.Values{"seek": {"start"}} + if !follow { + return client.streamOnce(logPath(start), w) + } + return client.follow(followOptions{path: func(resumeAfter int) string { + if resumeAfter == 0 { + return logPath(start) + } + return logPath(url.Values{"seek": {"start"}, "offset": {strconv.Itoa(resumeAfter)}}) + }}, w) } // GetBatchConfig retrieves a batch service's configuration.