Stream live server logs over SSE

This commit is contained in:
npc0-hue
2026-08-03 22:28:54 +08:00
parent 5d4fca14f9
commit 7eac1926dd
48 changed files with 1526 additions and 263 deletions
+43
View File
@@ -1,7 +1,10 @@
package api
import (
"bufio"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
@@ -35,6 +38,32 @@ func TestLogIngestAPIWorkflow(t *testing.T) {
}
}
func TestLogEventsSSEReplaysHistory(t *testing.T) {
router := newTestRouter()
hello := createLogIngestAPIFixtures(t, router)
batch := validLogBatchRequest(t, hello.SessionToken, 1, 2)
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", batch), http.StatusOK)
server := httptest.NewServer(router)
defer server.Close()
client := server.Client()
client.Timeout = 2 * time.Second
response, err := client.Get(server.URL + "/api/v1/server-instances/server-1/logs/events?historyLimit=2")
if err != nil {
t.Fatalf("open log event stream: %v", err)
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK || !strings.HasPrefix(response.Header.Get("Content-Type"), "text/event-stream") {
t.Fatalf("unexpected event stream response: status=%d content-type=%q", response.StatusCode, response.Header.Get("Content-Type"))
}
body := readSSEUntil(t, response, "event: ready")
for _, fragment := range []string{"event: stream", "event: log", `"streamId":"log-1"`, `"seq":1`, `"seq":2`} {
if !strings.Contains(body, fragment) {
t.Fatalf("expected SSE body to contain %q, got:\n%s", fragment, body)
}
}
}
func TestLogIngestAPIDuplicateAndErrors(t *testing.T) {
router := newTestRouter()
hello := createLogIngestAPIFixtures(t, router)
@@ -77,6 +106,20 @@ func createLogIngestAPIFixtures(t *testing.T, router http.Handler) dto.RunContro
return hello
}
func readSSEUntil(t *testing.T, response *http.Response, marker string) string {
t.Helper()
reader := bufio.NewReader(response.Body)
var body strings.Builder
for !strings.Contains(body.String(), marker) {
line, err := reader.ReadString('\n')
if err != nil {
t.Fatalf("read event stream: %v\n%s", err, body.String())
}
body.WriteString(line)
}
return body.String()
}
func validLogBatchRequest(t *testing.T, sessionToken string, firstSeq uint64, lastSeq uint64) dto.LogBatchIngestRequest {
t.Helper()
entries := make([]dto.LogEntryBody, 0, lastSeq-firstSeq+1)