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
+21 -1
View File
@@ -105,6 +105,8 @@ type RunJobExecutionInputBody struct {
LifecycleOperation string `json:"lifecycleOperation,omitempty"`
TargetVersion string `json:"targetVersion,omitempty"`
Inputs map[string]string `json:"inputs,omitempty"`
LogSource *RuntimeLogSourceBody `json:"logSource,omitempty"`
LogSources []RuntimeLogSourceBody `json:"logSources,omitempty"`
DLLExtensions []RuntimeDLLExtensionPlanBody `json:"dllExtensions,omitempty"`
SourceRCON *RuntimeSourceRCONPlanBody `json:"sourceRcon,omitempty"`
Deployment *ServerDeploymentExecutionBody `json:"deployment,omitempty"`
@@ -661,7 +663,7 @@ func RunJobAssignmentFromDomain(assignment domain.RunJobAssignment) RunJobAssign
State: assignment.State,
Progress: progressReportFromDomain(assignment.Progress),
ResultRef: assignment.ResultRef,
ExecutionInput: RunJobExecutionInputBody{WorkspaceScope: assignment.ExecutionInput.WorkspaceScope, Content: assignment.ExecutionInput.Content, ExpectedVersion: assignment.ExecutionInput.ExpectedVersion, ExpectedChecksum: assignment.ExecutionInput.ExpectedChecksum, MaxReadBytes: assignment.ExecutionInput.MaxReadBytes, RemoteAdapterKey: assignment.ExecutionInput.RemoteAdapterKey, RemoteAdapterKind: assignment.ExecutionInput.RemoteAdapterKind, TimeoutSeconds: assignment.ExecutionInput.TimeoutSeconds, PluginID: assignment.ExecutionInput.PluginID, LifecycleOperation: assignment.ExecutionInput.LifecycleOperation, TargetVersion: assignment.ExecutionInput.TargetVersion, Inputs: domain.CopyStringMap(assignment.ExecutionInput.Inputs), DLLExtensions: dllExtensionPlansFromDomain(assignment.ExecutionInput.DLLExtensions), SourceRCON: runtimeSourceRCONPlanFromDomain(assignment.ExecutionInput.SourceRCON), Deployment: deploymentExecutionFromDomain(assignment.ExecutionInput.Deployment), ServerDeploymentPlan: serverDeploymentPlanFromDomain(assignment.ExecutionInput.ServerDeploymentPlan)},
ExecutionInput: RunJobExecutionInputBody{WorkspaceScope: assignment.ExecutionInput.WorkspaceScope, Content: assignment.ExecutionInput.Content, ExpectedVersion: assignment.ExecutionInput.ExpectedVersion, ExpectedChecksum: assignment.ExecutionInput.ExpectedChecksum, MaxReadBytes: assignment.ExecutionInput.MaxReadBytes, RemoteAdapterKey: assignment.ExecutionInput.RemoteAdapterKey, RemoteAdapterKind: assignment.ExecutionInput.RemoteAdapterKind, TimeoutSeconds: assignment.ExecutionInput.TimeoutSeconds, PluginID: assignment.ExecutionInput.PluginID, LifecycleOperation: assignment.ExecutionInput.LifecycleOperation, TargetVersion: assignment.ExecutionInput.TargetVersion, Inputs: domain.CopyStringMap(assignment.ExecutionInput.Inputs), LogSource: runtimeLogSourceFromDomain(assignment.ExecutionInput.LogSource), LogSources: runtimeLogSourcesFromDomain(assignment.ExecutionInput.LogSources), DLLExtensions: dllExtensionPlansFromDomain(assignment.ExecutionInput.DLLExtensions), SourceRCON: runtimeSourceRCONPlanFromDomain(assignment.ExecutionInput.SourceRCON), Deployment: deploymentExecutionFromDomain(assignment.ExecutionInput.Deployment), ServerDeploymentPlan: serverDeploymentPlanFromDomain(assignment.ExecutionInput.ServerDeploymentPlan)},
LeaseToken: assignment.LeaseToken,
Attempt: assignment.Attempt,
FencingToken: assignment.FencingToken,
@@ -736,6 +738,24 @@ func runtimeSourceRCONPlanFromDomain(plan *domain.RuntimeSourceRCONPlan) *Runtim
return &RuntimeSourceRCONPlanBody{Protocol: plan.Protocol, ExtensionKey: plan.ExtensionKey, ModKey: plan.ModKey, ConfigRef: plan.ConfigRef, DeploymentStateRef: plan.DeploymentStateRef, Port: plan.Port}
}
func runtimeLogSourceFromDomain(source *domain.RuntimeLogSource) *RuntimeLogSourceBody {
if source == nil {
return nil
}
return &RuntimeLogSourceBody{Key: source.Key, Kind: source.Kind, TargetKey: source.TargetKey, StreamKey: source.StreamKey, CursorKind: source.CursorKind, RetentionDays: source.RetentionDays}
}
func runtimeLogSourcesFromDomain(sources []domain.RuntimeLogSource) []RuntimeLogSourceBody {
if len(sources) == 0 {
return nil
}
out := make([]RuntimeLogSourceBody, 0, len(sources))
for _, source := range sources {
out = append(out, RuntimeLogSourceBody{Key: source.Key, Kind: source.Kind, TargetKey: source.TargetKey, StreamKey: source.StreamKey, CursorKind: source.CursorKind, RetentionDays: source.RetentionDays})
}
return out
}
func progressReportToDomain(progress JobProgressBody) domain.RunJobProgressReport {
return domain.RunJobProgressReport{
Percent: progress.Percent,
+39 -8
View File
@@ -53,6 +53,21 @@ type LogStreamCursorResponse struct {
LatestSeq uint64 `json:"latestSeq"`
}
type LogStreamEventResponse struct {
ServerInstanceID string `json:"serverInstanceId"`
StreamID string `json:"streamId"`
Source domain.LogStreamSource `json:"source"`
StreamKey string `json:"streamKey"`
LatestSeq uint64 `json:"latestSeq"`
Entry LogEntryBody `json:"entry"`
}
type LogStreamEventsReadyResponse struct {
ServerInstanceID string `json:"serverInstanceId"`
StreamCount int `json:"streamCount"`
ServerTime time.Time `json:"serverTime"`
}
func (request LogBatchIngestRequest) ToDomain() domain.LogBatchIngest {
return domain.LogBatchIngest{
RunEndpointID: request.RunEndpointID,
@@ -99,6 +114,18 @@ func LogStreamCursorFromDomain(result domain.LogStreamCursorResult) LogStreamCur
}
}
func LogStreamEventFromDomain(event domain.LogStreamEvent) LogStreamEventResponse {
event = domain.CopyLogStreamEvent(event)
return LogStreamEventResponse{
ServerInstanceID: event.ServerInstanceID,
StreamID: event.Stream.ID,
Source: event.Stream.Source,
StreamKey: event.Stream.StreamKey,
LatestSeq: event.LatestSeq,
Entry: logEntryFromDomain(event.Entry),
}
}
func logEntriesToDomain(entries []LogEntryBody) []domain.LogEntry {
if entries == nil {
return nil
@@ -117,20 +144,24 @@ func logEntriesToDomain(entries []LogEntryBody) []domain.LogEntry {
return out
}
func logEntryFromDomain(entry domain.LogEntry) LogEntryBody {
return LogEntryBody{
Seq: entry.Seq,
Timestamp: entry.Timestamp,
Level: entry.Level,
Line: entry.Line,
Fields: copyStringMap(entry.Fields),
Redacted: entry.Redacted,
}
}
func logEntriesFromDomain(entries []domain.LogEntry) []LogEntryBody {
if entries == nil {
return nil
}
out := make([]LogEntryBody, len(entries))
for i, entry := range entries {
out[i] = LogEntryBody{
Seq: entry.Seq,
Timestamp: entry.Timestamp,
Level: entry.Level,
Line: entry.Line,
Fields: copyStringMap(entry.Fields),
Redacted: entry.Redacted,
}
out[i] = logEntryFromDomain(entry)
}
return out
}