diff --git a/server/api/stream.go b/server/api/stream.go
index eedc9285e..a4914860c 100644
--- a/server/api/stream.go
+++ b/server/api/stream.go
@@ -186,8 +186,8 @@ func LogStreamSSE(c *gin.Context) {
}
if step.State != model.StatusRunning {
- log.Debug().Msg("stream not found.")
- logWriteStringErr(io.WriteString(rw, "event: error\ndata: stream not found\n\n"))
+ log.Debug().Msg("step not running (anymore).")
+ logWriteStringErr(io.WriteString(rw, "event: error\ndata: step not running (anymore)\n\n"))
return
}
diff --git a/server/logging/log.go b/server/logging/log.go
index e29e498ce..6ea2e21ff 100644
--- a/server/logging/log.go
+++ b/server/logging/log.go
@@ -61,12 +61,12 @@ func (l *log) Open(_ context.Context, stepID int64) error {
return nil
}
-func (l *log) Write(_ context.Context, stepID int64, logEntry *model.LogEntry) error {
+func (l *log) Write(ctx context.Context, stepID int64, logEntry *model.LogEntry) error {
l.Lock()
s, ok := l.streams[stepID]
l.Unlock()
if !ok {
- return ErrNotFound
+ return l.Open(ctx, stepID)
}
s.Lock()
s.list = append(s.list, logEntry)
diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue
index 1deea6e8a..ad47f3f93 100644
--- a/web/src/components/repo/pipeline/PipelineLog.vue
+++ b/web/src/components/repo/pipeline/PipelineLog.vue
@@ -34,13 +34,41 @@
-
-
{{ line.index + 1 }}
-
-
-
{{ formatTime(line.time) }}
+
+
{{ line.index + 1 }}
+
+
+
+
{{ formatTime(line.time) }}
@@ -84,6 +112,7 @@ type LogLine = {
index: number;
text: string;
time?: number;
+ type: 'error' | 'warning' | null;
};
const props = defineProps<{
@@ -128,14 +157,26 @@ function formatTime(time?: number): string {
return time === undefined ? '' : `${time}s`;
}
-function writeLog(line: LogLine) {
+function writeLog(line: Partial
) {
logBuffer.value.push({
index: line.index ?? 0,
- text: ansiUp.value.ansi_to_html(line.text),
+ text: ansiUp.value.ansi_to_html(line.text ?? ''),
time: line.time ?? 0,
+ type: null, // TODO: implement way to detect errors and warnings
});
}
+// SOURCE: https://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings
+function b64DecodeUnicode(str: string) {
+ return decodeURIComponent(
+ window
+ .atob(str)
+ .split('')
+ .map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
+ .join(''),
+ );
+}
+
function scrollDown() {
nextTick(() => {
if (!consoleElement.value) {
@@ -198,7 +239,7 @@ async function download() {
downloadInProgress.value = false;
}
const fileURL = window.URL.createObjectURL(
- new Blob([logs.map((line) => atob(line.data)).join('')], {
+ new Blob([logs.map((line) => b64DecodeUnicode(line.data)).join('')], {
type: 'text/plain',
}),
);
@@ -240,13 +281,13 @@ async function loadLogs() {
if (isStepFinished(step.value)) {
const logs = await apiClient.getLogs(repo.value.id, pipeline.value.number, step.value.id);
- logs?.forEach((line) => writeLog({ index: line.line, text: atob(line.data), time: line.time }));
+ logs?.forEach((line) => writeLog({ index: line.line, text: b64DecodeUnicode(line.data), time: line.time }));
flushLogs(false);
}
if (isStepRunning(step.value)) {
stream.value = apiClient.streamLogs(repo.value.id, pipeline.value.number, step.value.id, (line) => {
- writeLog({ index: line.line, text: atob(line.data), time: line.time });
+ writeLog({ index: line.line, text: b64DecodeUnicode(line.data), time: line.time });
flushLogs(true);
});
}