Skip to content

Commit

Permalink
FIX: export all logs in the log page (#1899)
Browse files Browse the repository at this point in the history
* FIX: export all logs in the log page

* REDO: export all logs for best practice

* fix: eslint error

* ADD: loafing for export all logs

* fix: the loading UI

* UPDATE: export all logs function to be faster and more clean

* ADD: 100000 Logs Limit

---------

Co-authored-by: NeoPlays <[email protected]>
  • Loading branch information
mabasian and NeoPlays authored Jun 3, 2024
1 parent 289ae2a commit 7adb04e
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 41 deletions.
20 changes: 8 additions & 12 deletions launcher/public/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,10 @@ video {
margin-top: 10rem;
}

.mt-5{
margin-top: 1.25rem;
}

.mt-6{
margin-top: 1.5rem;
}
Expand All @@ -1527,10 +1531,6 @@ video {
margin-top: 2rem;
}

.mt-5{
margin-top: 1.25rem;
}

.box-border{
-webkit-box-sizing: border-box;
box-sizing: border-box;
Expand Down Expand Up @@ -2098,6 +2098,10 @@ video {
width: 1.5rem;
}

.w-60{
width: 15rem;
}

.w-64{
width: 16rem;
}
Expand Down Expand Up @@ -2252,14 +2256,6 @@ video {
width: 100vw;
}

.w-40{
width: 10rem;
}

.w-60{
width: 15rem;
}

.min-w-\[100px\]{
min-width: 100px;
}
Expand Down
25 changes: 13 additions & 12 deletions launcher/src/backend/Monitoring.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,11 +436,11 @@ export class Monitoring {
var query =
rpc_method.trim().indexOf("{") < 0
? JSON.stringify({
jsonrpc: "2.0",
method: rpc_method.trim(),
params: rpc_params,
id: 1,
})
jsonrpc: "2.0",
method: rpc_method.trim(),
params: rpc_params,
id: 1,
})
: rpc_method;

// Define default response
Expand Down Expand Up @@ -1575,9 +1575,10 @@ export class Monitoring {
try {
//Nethermind returns the peers per client type (e.g. Geth, Erigon, Nethermind ...), therefore we need to sum them up
if (clt.service == "NethermindService") {
details[clientType]["numPeer"] = parseInt(xx
.filter((s) => s.metric.__name__ == services[clientType][clt.service][index])
.reduce((total, obj) => total + parseInt(obj.value.pop()), 0)
details[clientType]["numPeer"] = parseInt(
xx
.filter((s) => s.metric.__name__ == services[clientType][clt.service][index])
.reduce((total, obj) => total + parseInt(obj.value.pop()), 0)
);
} else {
details[clientType]["numPeer"] = parseInt(
Expand Down Expand Up @@ -2622,8 +2623,8 @@ export class Monitoring {
const addr_type = Array.isArray(addr)
? "arr"
: typeof addr === "string" && ["public", "local"].includes(addr)
? "str"
: "invalid";
? "str"
: "invalid";
addr = addr_type == "str" ? addr.toLowerCase().trim() : addr;
if (addr_type == "invalid") {
return {
Expand Down Expand Up @@ -2711,7 +2712,7 @@ export class Monitoring {
for (let i = 0; i < serviceInfos.length; i++) {
const hashDependencies =
serviceInfos[i].config.dependencies.consensusClients.length ||
serviceInfos[i].config.dependencies.executionClients.length
serviceInfos[i].config.dependencies.executionClients.length
? "yes"
: "no";
easyInfos.push({
Expand Down Expand Up @@ -3225,7 +3226,7 @@ rm -rf diskoutput
if (result.data === undefined) {
throw result;
}
const curlTag = await this.nodeConnection.ensureCurlImage()
const curlTag = await this.nodeConnection.ensureCurlImage();
const exitMsg = result.data;
const exitCommand = `docker run --rm --network=stereum curlimages/curl:${curlTag} curl 'http://stereum-${serviceId}:${beaconAPIPort}/eth/v1/beacon/pool/voluntary_exits' -H 'accept: */*' -H 'Content-Type: application/json' -d '${JSON.stringify(
exitMsg
Expand Down
30 changes: 26 additions & 4 deletions launcher/src/backend/NodeConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ export class NodeConnection {
"sudo -u root apt update &&\
sudo -u root apt install -y software-properties-common &&\
sudo -u root add-apt-repository --yes --update ppa:ansible/ansible &&\
sudo -u root apt install -y pip ansible tar gzip wget git", false
sudo -u root apt install -y pip ansible tar gzip wget git",
false
);
} catch (err) {
log.error(err);
Expand Down Expand Up @@ -2359,7 +2360,7 @@ export class NodeConnection {
throw new Error(SSHService.extractExecError(result));
}

return "latest"
return "latest";
} catch (error) {
// if pulling the latest image fails, try fetching the latest installed image
try {
Expand All @@ -2369,8 +2370,13 @@ export class NodeConnection {
throw new Error(SSHService.extractExecError(fetchedImages));
}

const images = fetchedImages.stdout.split(/\n/).slice(0, -1).map((json) => { return JSON.parse(json) });
log.info(`installed images: ${images}`)
const images = fetchedImages.stdout
.split(/\n/)
.slice(0, -1)
.map((json) => {
return JSON.parse(json);
});
log.info(`installed images: ${images}`);
if (images.length === 0) return "latest";

// get the latest installed image
Expand All @@ -2388,4 +2394,20 @@ export class NodeConnection {
}
}
}

async getAllServiceLogs(args) {
const containerName = `stereum-${args}`;
try {
const logResult = await this.sshService.exec(`docker logs ${containerName} --tail=100000 2>&1`);

if (logResult.rc || !logResult.stdout || logResult.stderr) {
throw new Error(logResult.stderr || "Error fetching logs");
}

return { containerId: containerName, logs: logResult.stdout.trim().split("\n") };
} catch (err) {
log.error(`Failed to get logs for container ${containerName}: `, err);
return { containerId: "ERROR", logs: err.message };
}
}
}
21 changes: 15 additions & 6 deletions launcher/src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const oneClickInstall = new OneClickInstall();
const serviceManager = new ServiceManager(nodeConnection);
const validatorAccountManager = new ValidatorAccountManager(nodeConnection, serviceManager);
const authenticationService = new AuthenticationService(nodeConnection);
const sshService = new SSHService;
const sshService = new SSHService();
const { globalShortcut } = require("electron");
const log = require("electron-log");
const stereumUpdater = new StereumUpdater(log, createWindow, isDevelopment);
Expand Down Expand Up @@ -220,6 +220,10 @@ ipcMain.handle("getServiceLogs", async (event, args) => {
return await monitoring.getServiceLogs(args);
});

ipcMain.handle("getAllServiceLogs", async (event, args) => {
return await nodeConnection.getAllServiceLogs(args);
});

ipcMain.handle("getServiceConfig", async (event, args) => {
return await nodeConnection.readServiceConfiguration(args);
});
Expand Down Expand Up @@ -497,15 +501,20 @@ ipcMain.handle("getCurrentEpochSlot", async (event, args) => {

ipcMain.handle("beginAuthSetup", async (event, args) => {
const current_window = event.sender;
return await authenticationService.beginAuthSetup(args.timeBased, args.increaseTimeLimit, args.enableRateLimit, current_window)
return await authenticationService.beginAuthSetup(
args.timeBased,
args.increaseTimeLimit,
args.enableRateLimit,
current_window
);
});

ipcMain.handle("finishAuthSetup", async () => {
return await authenticationService.finishAuthSetup()
return await authenticationService.finishAuthSetup();
});

ipcMain.handle("authenticatorVerification", async (event, args) => {
return await authenticationService.authenticatorVerification(args)
return await authenticationService.authenticatorVerification(args);
});

ipcMain.handle("removeAuthenticator", async (event, args) => {
Expand All @@ -517,7 +526,7 @@ ipcMain.handle("checkForAuthenticator", async (event, args) => {
});

ipcMain.handle("cancelVerification", async (event, args) => {
return await sshService.cancelVerification(args)
return await sshService.cancelVerification(args);
});

ipcMain.handle("changePassword", async (event, args) => {
Expand Down Expand Up @@ -676,7 +685,7 @@ ipcMain.handle("stopShell", async () => {

ipcMain.handle("create2FAQRCode", async (event, args) => {
return await authenticationService.create2FAQRCode(args.type, args.name, args.ip, args.secret);
})
});

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{ scheme: "app", privileges: { secure: true, standard: true } }]);
Expand Down
18 changes: 18 additions & 0 deletions launcher/src/components/UI/node-page/NodeScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
:client="nodeStore.clientToLogs"
@close-log="closeLogPage"
@export-log="exportLogs"
@export-all-log="updateAndExportAllLogs"
/>
</div>

Expand Down Expand Up @@ -112,6 +113,7 @@ onMounted(() => {
updateConnectionStats();
updateServiceLogs();
polling = setInterval(updateServiceLogs, 10000); // refresh logs
pollingVitals = setInterval(updateServerVitals, 1000); // refresh server vitals
pollingNodeStats = setInterval(updateNodeStats, 1000); // refresh server vitals
Expand Down Expand Up @@ -157,6 +159,21 @@ const updateServiceLogs = async () => {
nodeStore.serviceLogs = data;
}
};
const updateAndExportAllLogs = async (client) => {
nodeStore.isLogLoading = true;
nodeStore.allLogsForExp = await ControlService.getAllServiceLogs(client.config?.serviceID);
const fileName = `${client.name}_all_logs.txt`;
const data = [...nodeStore.allLogsForExp.logs].reverse();
const lineByLine = data.map((line, index) => `#${data.length - index}: ${line}`).join("\n\n");
const blob = new Blob([lineByLine], { type: "text/plain;charset=utf-8" });
saveAs(blob, fileName);
nodeStore.isLogLoading = false;
};
const updateServerVitals = async () => {
try {
if (serviceStore.installedServices && serviceStore.installedServices.length > 0 && headerStore.refresh) {
Expand All @@ -169,6 +186,7 @@ const updateServerVitals = async () => {
console.log("couldn't check server vitals");
}
};
const openExpertModal = (item) => {
nodeStore.isLineHidden = true;
expertModeClient.value = item;
Expand Down
19 changes: 15 additions & 4 deletions launcher/src/components/UI/node-page/components/logs/LogFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

<img
class="w-6 h-6 hover:scale-110 active:scale-95 transition-all ease-in-out duration-150 select-none"
src="/img/icon/service-log-icons/all-log-export-button.png"
:class="[isLoadingSpinning, { 'pointer-events-none opacity-50': nodeStore.isLogLoading }]"
:src="loadingIconsClass"
alt=""
@mousedown.prevent
@click="exportAllLogs"
Expand Down Expand Up @@ -73,7 +74,7 @@

<script setup>
import { useNodeStore } from "@/store/theNode";
import { ref } from "vue";
import { ref, computed } from "vue";

const props = defineProps({
client: {
Expand All @@ -82,17 +83,27 @@ const props = defineProps({
},
});

const emit = defineEmits(["export-log"]);
const emit = defineEmits(["export-log", "export-all-log"]);

const nodeStore = useNodeStore();

const isAllHovered = ref(false);
const is150Hovered = ref(false);

const loadingIconsClass = computed(() => {
return nodeStore.isLogLoading
? "/img/icon/loading-icons/loading-circle.png"
: "/img/icon/service-log-icons/all-log-export-button.png";
});

const isLoadingSpinning = computed(() => {
return nodeStore.isLogLoading ? "animate-spin" : "";
});

const exportAllLogs = () => {
nodeStore.exportLogs = false;
nodeStore.exportAllLogs = true;
emit("export-log", props.client);
emit("export-all-log", props.client);
};

const exportLogs = () => {
Expand Down
8 changes: 6 additions & 2 deletions launcher/src/components/UI/node-page/sections/LogsSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
>
<LogsHeader :client="client" @close-log="closeLog" />
<LogsBody :client="client" />
<LogsFooter :client="client" @export-log="exportLog" />
<LogsFooter :client="client" @export-log="exportLog" @export-all-log="exportAllLog" />
</div>
</template>

Expand All @@ -24,7 +24,7 @@ const { client } = defineProps({
},
});
const emit = defineEmits(["close-log", "export-log"]);
const emit = defineEmits(["close-log", "export-log", "export-all-log"]);
const closeLog = () => {
emit("close-log");
Expand All @@ -33,4 +33,8 @@ const closeLog = () => {
const exportLog = (item) => {
emit("export-log", item);
};
const exportAllLog = (item) => {
emit("export-all-log", item);
};
</script>
16 changes: 16 additions & 0 deletions launcher/src/components/UI/the-control/ControlScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
:client="nodeStore.clientToLogs"
@close-log="closeLogPage"
@export-log="exportLogs"
@export-all-log="updateAndExportAllLogs"
/>
<!-- End Control main layout -->
</base-layout>
Expand Down Expand Up @@ -226,6 +227,21 @@ const closeLogPage = () => {
isLogsPageActive.value = false;
controlStore.serviceLogs = null;
};
const updateAndExportAllLogs = async (client) => {
nodeStore.isLogLoading = true;
nodeStore.allLogsForExp = await ControlService.getAllServiceLogs(client.config?.serviceID);
const fileName = `${client.name}_all_logs.txt`;
const data = [...nodeStore.allLogsForExp.logs].reverse();
const lineByLine = data.map((line, index) => `#${data.length - index}: ${line}`).join("\n\n");
const blob = new Blob([lineByLine], { type: "text/plain;charset=utf-8" });
saveAs(blob, fileName);
nodeStore.isLogLoading = false;
};
const exportLogs = async (client) => {
const currentService = nodeStore.serviceLogs.find(
(service) => service.config?.serviceID === client.config?.serviceID
Expand Down
4 changes: 4 additions & 0 deletions launcher/src/store/ControlService.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ class ControlService extends EventEmitter {
return await this.promiseIpc.send("getServiceLogs", args);
}

async getAllServiceLogs(args) {
return await this.promiseIpc.send("getAllServiceLogs", args);
}

async getServiceConfig(args) {
return await this.promiseIpc.send("getServiceConfig", args);
}
Expand Down
3 changes: 2 additions & 1 deletion launcher/src/store/theNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export const useNodeStore = defineStore("theNode", {
runNodePowerModal: false,
clientToLogs: null,
logs: [],
exportAllLogs: false,
allLogsForExp: false,
isLogLoading: false,
exportLogs: false,
searchLogs: "",
serviceLogs: [],
Expand Down

0 comments on commit 7adb04e

Please sign in to comment.