What is the best way to stream my docker container log to my Webbrowser without refreshing using express
and node-docker-api
.
This is my current script.
app.get('/container/:id/log', async function(req, res){
await checkTokenHeader(req, res);
//todo: Check if Container exist
let container = await getDockerContainer(req.params.id);
await container.logs({
follow: true,
stdout: true,
stderr: true
}).then(async function(stream){
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.setHeader('Transfer-Encoding', 'chunked');
console.log(stream)
//stream.on('data', info => res.write(info.toString('utf-8').slice(8)))
//stream.on('data', info => console.log(info.toString('utf-8').slice(8)))
//I WANT TO STREAM THE OUTPUT TO Webbrowser or inside a div
})
});
I tried using res.write
or res.send
, but nothing works
One possible way to achieve what you described is:
Here's a very simple implementation:
// save this to "index.mjs" and run with "node ./index.mjs"
import http from "http";
import express from "express"; // npm i express
import {
Docker
} from "node-docker-api"; // npm i node-docker-api
import {
WebSocketServer
} from "ws"; // npm i ws
const app = express();
const docker = new Docker({
socketPath: "/var/run/docker.sock"
});
app.get("/container/:id/log", async function(req, res) {
res.setHeader("Content-Type", "text/html; charset=utf-8");
// This is a naïve way of building an HTML document.
// You most likely want to to build this from a template
res.end(`
<!DOCTYPE html>
<html>
<head>
<title>Container Logs</title>
</head>
<body>
<h1>Container Logs</h1>
<div id="logs"></div>
<script>
const ws = new WebSocket(\`ws://\${window.location.host}/${req.params.id}\`);
ws.onmessage = (event) => {
const log = document.createElement("p");
log.innerText = event.data;
document.getElementById("logs").appendChild(log);
window.scrollTo(0, document.body.scrollHeight);
};
</script>
</body>
</html>
`);
});
const server = http.createServer(app);
const wss = new WebSocketServer({
server
});
wss.on("connection", async(ws, req) => {
const containerId = req.url.split("/")[1];
// TODO: validate containerId
ws.on("error", console.error);
let container = docker.container.get(containerId);
const stream = await container.logs({
follow: true,
stdout: true,
stderr: true,
});
ws.once("close", () => {
stream.removeAllListeners();
stream.destroy();
});
stream.on("data", (info) => ws.send(info.toString("utf-8")));
});
server.listen(3000, () => {
console.log("Server running on port 3000");
});
Running the code above with the following docker-compose.yml
:
version: "3.8"
services:
counter:
image: busybox:latest
init: true
command: '/bin/sh -c "while true; do date; sleep 1; done"'
Appends the log lines to the page without refreshing: