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
+34 -13
View File
@@ -202,6 +202,8 @@ type Core interface {
GetLogStreamForSession(string, string) (domain.LogStream, error)
ListLogStreamsForSession(string, domain.LogStreamFilter) ([]domain.LogStream, error)
QueryLogStreamForSession(string, domain.LogStreamCursorQuery) (domain.LogStreamCursorResult, error)
SubscribeLogEvents(string) (LogEventSubscription, error)
SubscribeLogEventsForSession(string, string) (LogEventSubscription, error)
IngestLogBatch(domain.LogBatchIngest) (domain.LogBatchIngestResult, error)
QueryLogStream(domain.LogStreamCursorQuery) (domain.LogStreamCursorResult, error)
ListGamePlayersForSession(string, domain.GamePlayerFilter) ([]domain.GamePlayer, error)
@@ -236,6 +238,9 @@ type CoreService struct {
bridgeMu sync.Mutex
bridgeSeq uint64
logStore LogBodyStore
logEventMu sync.Mutex
logEventSubscribers map[uint64]logEventSubscriber
logEventSubscriberSeq uint64
artifactStore ArtifactBodyStore
artifactMu sync.Mutex
artifactTransfers map[string]domain.ArtifactTransferSession
@@ -279,6 +284,7 @@ func newCoreServiceWithLogStore(store repo.Store, logStore LogBodyStore, now fun
authSessions: map[string]string{},
runSessions: map[string]domain.RunControlSession{},
logStore: logStore,
logEventSubscribers: map[uint64]logEventSubscriber{},
artifactStore: artifactStore,
artifactTransfers: map[string]domain.ArtifactTransferSession{},
artifactPayloads: map[string][]byte{},
@@ -2329,21 +2335,36 @@ func (svc *CoreService) ensureJobLogStreams(job domain.Job, stamp time.Time) err
streams := []struct {
key string
source domain.LogStreamSource
}{
{key: "stdout", source: domain.LogStreamSourceProcess},
{key: "stderr", source: domain.LogStreamSourceProcess},
}{}
addStream := func(key string, source domain.LogStreamSource) {
key = strings.TrimSpace(key)
if key == "" {
return
}
for _, stream := range streams {
if stream.key == key {
return
}
}
streams = append(streams, struct {
key string
source domain.LogStreamSource
}{key: key, source: source})
}
addStream("stdout", domain.LogStreamSourceProcess)
addStream("stderr", domain.LogStreamSourceProcess)
for _, source := range job.ExecutionInput.LogSources {
if source.Kind != "process.stdout" && source.Kind != "process.stderr" {
continue
}
addStream(source.StreamKey, domain.LogStreamSourceProcess)
}
if job.Capability == domain.JobCapabilityRemoteRunProgram {
streams = append(streams,
struct {
key string
source domain.LogStreamSource
}{key: "management-program.stdout", source: domain.LogStreamSourceManagementProgram},
struct {
key string
source domain.LogStreamSource
}{key: "management-program.stderr", source: domain.LogStreamSourceManagementProgram},
)
addStream("management-program.stdout", domain.LogStreamSourceManagementProgram)
addStream("management-program.stderr", domain.LogStreamSourceManagementProgram)
}
if job.Capability == domain.JobCapabilityLogsBackfill && job.ExecutionInput.LogSource != nil && strings.TrimSpace(job.ExecutionInput.LogSource.StreamKey) != "" {
addStream(job.ExecutionInput.LogSource.StreamKey, domain.LogStreamSourceFile)
}
for _, item := range streams {
stream := domain.LogStream{