Relay current process output without spool

This commit is contained in:
npc0-hue
2026-08-31 23:35:14 +08:00
parent cde99e19fa
commit c8f5213954
9 changed files with 239 additions and 90 deletions
+115
View File
@@ -359,6 +359,9 @@ func (worker *Worker) registerUnlocked(ctx context.Context) error {
}
sink.mu.Unlock()
}
if sink, ok := worker.executor.logSink.(*LiveLogSink); ok {
sink.SetSession(state.RunEndpointID, state.SessionToken)
}
if hook, ok := worker.executor.artifactHook.(*QueueArtifactHook); ok {
hook.RunEndpointID = state.RunEndpointID
hook.SessionToken = state.SessionToken
@@ -1438,6 +1441,118 @@ type SpoolLogSink struct {
Progress func(context.Context, string, string) (uint64, error)
}
type LiveLogClient interface {
RelayLiveLogBatch(context.Context, protocol.LogBatchIngestRequest) (protocol.LogBatchIngestResponse, error)
}
// LiveLogSink is process-local and best-effort. It has no disk spool, no
// resend backlog, and does not make lifecycle execution wait for the platform.
type LiveLogSink struct {
Client LiveLogClient
RunEndpointID string
SessionToken string
mu sync.Mutex
sequences map[string]uint64
queue chan protocol.LogBatchIngestRequest
closed bool
closeOnce sync.Once
}
func NewLiveLogSink(client LiveLogClient) *LiveLogSink {
sink := &LiveLogSink{Client: client, sequences: map[string]uint64{}, queue: make(chan protocol.LogBatchIngestRequest, 512)}
go sink.dispatch()
return sink
}
func (sink *LiveLogSink) SetSession(endpointID string, sessionToken string) {
sink.mu.Lock()
sink.RunEndpointID = endpointID
sink.SessionToken = sessionToken
sink.mu.Unlock()
}
func (sink *LiveLogSink) Append(ctx context.Context, assignment protocol.RunJobAssignment, stream string, line string) error {
return sink.append(assignment, stream, line)
}
func (sink *LiveLogSink) AppendWithCursor(ctx context.Context, assignment protocol.RunJobAssignment, stream string, line string, _ ProcessLogCursor) error {
return sink.append(assignment, stream, line)
}
func (sink *LiveLogSink) append(assignment protocol.RunJobAssignment, stream string, line string) error {
if sink == nil {
return nil
}
redactedLine := RedactText(line)
redacted := line != redactedLine || strings.HasPrefix(redactedLine, "[redacted protected ")
streamKey := declaredProcessStreamKey(assignment, stream)
logStreamID := logStreamIDForAssignment(assignment, streamKey)
sink.mu.Lock()
defer sink.mu.Unlock()
if sink.closed || sink.queue == nil {
return nil
}
endpointID := sink.RunEndpointID
if endpointID == "" {
endpointID = assignment.RunEndpointID
}
if endpointID == "" || sink.SessionToken == "" || assignment.ServerInstanceID == "" {
return nil
}
sink.sequences[logStreamID]++
sequence := sink.sequences[logStreamID]
batch := protocol.LogBatchIngestRequest{
RunEndpointID: endpointID,
SessionToken: sink.SessionToken,
LogStreamID: logStreamID,
ServerInstanceID: assignment.ServerInstanceID,
StreamKey: streamKey,
Source: "process",
LogSessionID: assignment.LogSessionID,
SessionStartedAt: assignment.SessionStartedAt,
FirstSeq: sequence,
LastSeq: sequence,
Compression: "none",
Entries: []protocol.LogEntry{{Seq: sequence, Timestamp: time.Now().UTC(), Level: "info", Line: redactedLine, Redacted: redacted}},
}
batch.Checksum, _ = checksumForLogEntries(batch.Entries)
select {
case sink.queue <- batch:
default:
log.Printf("RUN phase=live_log_relay status=dropped stream=%s sequence=%d reason=transient_queue_full", safeOptional(logStreamID), sequence)
}
return nil
}
func (sink *LiveLogSink) dispatch() {
for batch := range sink.queue {
if sink.Client == nil {
continue
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
_, err := sink.Client.RelayLiveLogBatch(ctx, batch)
cancel()
if err != nil {
log.Printf("RUN phase=live_log_relay status=dropped stream=%s sequence=%d error=%s", safeOptional(batch.LogStreamID), batch.FirstSeq, RedactText(err.Error()))
}
}
}
func (sink *LiveLogSink) Close() {
if sink == nil {
return
}
sink.closeOnce.Do(func() {
sink.mu.Lock()
sink.closed = true
if sink.queue != nil {
close(sink.queue)
}
sink.mu.Unlock()
})
}
func (sink *SpoolLogSink) Append(ctx context.Context, assignment protocol.RunJobAssignment, stream string, line string) error {
return sink.append(ctx, assignment, stream, line, nil)
}