Files
dozzle-monitoring/assets/components/LogEventSource.vue
James Cote 49b39fb3af Add clear logs functionality (#1584)
* Add clear logs functionality

Other Changes:
- Add actions toolbar for actions pertaining to container logs
- Move Download button to actions toolbar, remove text and margin to match clear logs button
- Move search box below actions toolbar
- Turn off pointer events for scroll progress component so that Download button can be clicked
- Adjust spacing of container bar to accomodate the new actions toolbar

* Change actions toolbar into a floating menu

Other Changes:
- Move actions toolbar into its own component
- Move clear hotkey from LogEventSource to LogActionsToolbar, where the button is
- Move search back to where it was before
- Add tooltip component from buefy to annotate action buttons
- Add CSS variable for action toolbar background color, changes based on theme
2021-11-04 18:48:51 -07:00

126 lines
3.3 KiB
Vue

<template>
<div>
<infinite-loader :onLoadMore="loadOlderLogs" :enabled="messages.length > 100"></infinite-loader>
<slot :messages="messages"></slot>
</div>
</template>
<script>
import debounce from "lodash.debounce";
import InfiniteLoader from "./InfiniteLoader";
import config from "../store/config";
import containerMixin from "./mixins/container";
export default {
props: ["id"],
mixins: [containerMixin],
name: "LogEventSource",
components: {
InfiniteLoader,
},
data() {
return {
messages: [],
buffer: [],
es: null,
lastEventId: null,
};
},
created() {
this.flushBuffer = debounce(this.flushNow, 250, { maxWait: 1000 });
this.loadLogs();
},
beforeDestroy() {
this.es.close();
},
methods: {
loadLogs() {
this.reset();
this.connect();
},
onContainerStopped() {
this.es.close();
this.buffer.push({ event: "container-stopped", message: "Container stopped", date: new Date(), key: new Date() });
this.flushBuffer();
this.flushBuffer.flush();
},
onMessage(e) {
this.lastEventId = e.lastEventId;
this.buffer.push(this.parseMessage(e.data));
this.flushBuffer();
},
onContainerStateChange(newValue, oldValue) {
if (newValue == "running" && newValue != oldValue) {
this.buffer.push({
event: "container-started",
message: "Container started",
date: new Date(),
key: new Date(),
});
this.connect();
}
},
connect() {
this.es = new EventSource(`${config.base}/api/logs/stream?id=${this.id}&lastEventId=${this.lastEventId ?? ""}`);
this.es.addEventListener("container-stopped", (e) => this.onContainerStopped());
this.es.addEventListener("error", (e) => console.error("EventSource failed: " + JSON.stringify(e)));
this.es.onmessage = (e) => this.onMessage(e);
},
flushNow() {
this.messages.push(...this.buffer);
this.buffer = [];
},
clear() {
this.messages = [];
},
reset() {
if (this.es) {
this.es.close();
}
this.flushBuffer.cancel();
this.es = null;
this.messages = [];
this.buffer = [];
this.lastEventId = null;
},
async loadOlderLogs() {
if (this.messages.length < 300) return;
this.$emit("loading-more", true);
const to = this.messages[0].date;
const last = this.messages[299].date;
const delta = to - last;
const from = new Date(to.getTime() + delta);
const logs = await (
await fetch(`${config.base}/api/logs?id=${this.id}&from=${from.toISOString()}&to=${to.toISOString()}`)
).text();
if (logs) {
const newMessages = logs
.trim()
.split("\n")
.map((line) => this.parseMessage(line));
this.messages.unshift(...newMessages);
}
this.$emit("loading-more", false);
},
parseMessage(data) {
let i = data.indexOf(" ");
if (i == -1) {
i = data.length;
}
const key = data.substring(0, i);
const date = new Date(key);
const message = data.substring(i + 1);
return { key, date, message };
},
},
watch: {
id(newValue, oldValue) {
if (oldValue !== newValue) {
this.loadLogs();
}
},
},
};
</script>