146 lines
3.0 KiB
Vue
146 lines
3.0 KiB
Vue
|
|
<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,
|
||
|
|
step: Number,
|
||
|
|
stepphase: String
|
||
|
|
},
|
||
|
|
computed: {},
|
||
|
|
data() {
|
||
|
|
let formatter = new AnsiUp();
|
||
|
|
formatter.use_classes = true;
|
||
|
|
|
||
|
|
return {
|
||
|
|
items: [],
|
||
|
|
lines: [],
|
||
|
|
formatter: formatter,
|
||
|
|
es: null,
|
||
|
|
fetching: false
|
||
|
|
};
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
fetch: function() {
|
||
|
|
if (this.fetching) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
this.fetching = true;
|
||
|
|
if (this.stepphase == "running") {
|
||
|
|
this.streamLogs();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.stepphase == "success" || this.stepphase == "failed") {
|
||
|
|
this.getLogs();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
streamLogs: function() {
|
||
|
|
this.es = new EventSource(
|
||
|
|
apiurlwithtoken(
|
||
|
|
"/logs?runID=" +
|
||
|
|
this.runid +
|
||
|
|
"&taskID=" +
|
||
|
|
this.taskid +
|
||
|
|
"&step=" +
|
||
|
|
this.step +
|
||
|
|
"&follow"
|
||
|
|
)
|
||
|
|
);
|
||
|
|
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();
|
||
|
|
};
|
||
|
|
},
|
||
|
|
getLogs: function() {
|
||
|
|
fetch(
|
||
|
|
apiurl(
|
||
|
|
"/logs?runID=" +
|
||
|
|
this.runid +
|
||
|
|
"&taskID=" +
|
||
|
|
this.taskid +
|
||
|
|
"&step=" +
|
||
|
|
this.step
|
||
|
|
)
|
||
|
|
)
|
||
|
|
.then(r => {
|
||
|
|
if (r.status == 200) {
|
||
|
|
return r.text();
|
||
|
|
}
|
||
|
|
return "";
|
||
|
|
})
|
||
|
|
.then(data => {
|
||
|
|
this.items.push(this.formatter.ansi_to_html(data));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
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>
|