2018-12-09 13:21:20 +00:00
|
|
|
<template>
|
|
|
|
|
<div class="dark">
|
|
|
|
|
<div class="log">
|
|
|
|
|
<div class="stream-line" v-for="(item, index) in items" :key="index">
|
|
|
|
|
<div v-html="item"/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { apiurl, apiurlwithtoken, fetch } from "@/util/auth";
|
|
|
|
|
import AnsiUp from "ansi_up";
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: "Log",
|
|
|
|
|
props: {
|
|
|
|
|
show: Boolean,
|
|
|
|
|
runid: String,
|
|
|
|
|
taskid: String,
|
2019-03-13 14:42:58 +00:00
|
|
|
setup: Boolean,
|
2018-12-09 13:21:20 +00:00
|
|
|
step: Number,
|
|
|
|
|
stepphase: String
|
|
|
|
|
},
|
|
|
|
|
computed: {},
|
|
|
|
|
data() {
|
|
|
|
|
let formatter = new AnsiUp();
|
|
|
|
|
formatter.use_classes = true;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
items: [],
|
|
|
|
|
lines: [],
|
|
|
|
|
formatter: formatter,
|
|
|
|
|
es: null,
|
|
|
|
|
fetching: false
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
2019-03-29 17:19:07 +00:00
|
|
|
fetch() {
|
2018-12-09 13:21:20 +00:00
|
|
|
if (this.fetching) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.fetching = true;
|
|
|
|
|
if (this.stepphase == "running") {
|
|
|
|
|
this.streamLogs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.stepphase == "success" || this.stepphase == "failed") {
|
|
|
|
|
this.getLogs();
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-03-29 17:19:07 +00:00
|
|
|
streamLogs() {
|
2019-03-13 14:42:58 +00:00
|
|
|
let path = "/logs?runID=" + this.runid + "&taskID=" + this.taskid;
|
|
|
|
|
if (this.setup) {
|
|
|
|
|
path += "&setup";
|
|
|
|
|
} else {
|
|
|
|
|
path += "&step=" + this.step;
|
|
|
|
|
}
|
|
|
|
|
path += "&follow&stream";
|
|
|
|
|
|
|
|
|
|
this.es = new EventSource(apiurlwithtoken(path));
|
2018-12-09 13:21:20 +00:00
|
|
|
this.es.onmessage = event => {
|
|
|
|
|
var data = event.data;
|
|
|
|
|
// TODO(sgotti) ansi_up doesn't handle carriage return (\r), find a way to also handle it
|
|
|
|
|
this.items.push(this.formatter.ansi_to_html(data));
|
|
|
|
|
};
|
|
|
|
|
// don't reconnect on error
|
|
|
|
|
this.es.onerror = () => {
|
|
|
|
|
this.es.close();
|
|
|
|
|
};
|
|
|
|
|
},
|
2019-03-29 17:19:07 +00:00
|
|
|
async getLogs() {
|
2019-03-13 14:42:58 +00:00
|
|
|
let path = "/logs?runID=" + this.runid + "&taskID=" + this.taskid;
|
|
|
|
|
if (this.setup) {
|
|
|
|
|
path += "&setup";
|
|
|
|
|
} else {
|
|
|
|
|
path += "&step=" + this.step;
|
|
|
|
|
}
|
2019-04-02 16:27:28 +00:00
|
|
|
let res = await fetch(apiurl(path));
|
|
|
|
|
if (res.status == 200) {
|
2019-05-10 17:52:46 +00:00
|
|
|
const reader = res.body.getReader();
|
|
|
|
|
|
|
|
|
|
let items = this.items;
|
|
|
|
|
let formatter = this.formatter;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
let { done, value } = await reader.read();
|
|
|
|
|
if (done) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let data = String.fromCharCode.apply(null, value);
|
|
|
|
|
|
|
|
|
|
let lines = data.split("\n");
|
|
|
|
|
lines.forEach(line => {
|
|
|
|
|
items.push(formatter.ansi_to_html(line));
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-04-02 16:27:28 +00:00
|
|
|
}
|
2018-12-09 13:21:20 +00:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
show: function(post, pre) {
|
|
|
|
|
if (pre == false && post == true) {
|
|
|
|
|
this.fetch();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
stepphase: function(post, pre) {
|
|
|
|
|
if (pre == "notstarted" && post == "running") {
|
|
|
|
|
this.streamLogs();
|
|
|
|
|
}
|
|
|
|
|
if (pre == "notstarted" && (post == "success" || post == "failed")) {
|
|
|
|
|
this.getLogs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pre == "running" && (post == "success" || post == "failed")) {
|
|
|
|
|
// TODO(sgotti)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
created: function() {
|
|
|
|
|
if (this.show) {
|
|
|
|
|
this.fetch();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
if (this.es !== null) {
|
|
|
|
|
this.es.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.log {
|
|
|
|
|
background-color: #222;
|
|
|
|
|
color: #f1f1f1;
|
|
|
|
|
font-family: Cousine, monospace;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
line-height: 19px;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-wrap: break-word;
|
|
|
|
|
text-align: left;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
padding: 5px;
|
|
|
|
|
|
|
|
|
|
.stream-line {
|
|
|
|
|
pre {
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|