Total Requests
+—
+All requests received by the server
+Average Response Time
+—
+Mean duration in seconds
+Application Status
+—
+Based on health check
+diff --git a/Dockerfile b/Dockerfile index 6edafc1..25e757c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:16.3.0-alpine -COPY q1-deploy-monitor /app +COPY app.js dashboard.js index.html package.json /app/ RUN mkdir -p /logs && touch /logs/logs.txt diff --git a/app.js b/app.js new file mode 100644 index 0000000..b1a44e7 --- /dev/null +++ b/app.js @@ -0,0 +1,82 @@ +const express = require('express') +const fs = require('fs') +const path = require('path') + +const app = express() +const port = 5000 +const logDir = '/logs' +const logFile = path.join(logDir, 'logs.txt') + +if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }) +} + +function getStatsFromLogFile() { + if (!fs.existsSync(logFile)) { + return { requestCount: 0, averageResponseTime: 0 } + } + + const content = fs.readFileSync(logFile, 'utf8') + const lines = content.split('\n').filter(Boolean) + const requestCount = lines.length + + if (requestCount === 0) { + return { requestCount: 0, averageResponseTime: 0 } + } + + const totalResponseTime = lines.reduce((total, line) => { + const parts = line.trim().split(' ') + const duration = parseFloat(parts[1]) + return total + (Number.isNaN(duration) ? 0 : duration) + }, 0) + + return { + requestCount, + averageResponseTime: totalResponseTime / requestCount + } +} + +app.use('/dashboard', express.static(path.join(__dirname, 'dashboard'), { index: false })) + +app.use((req, res, next) => { + const startTime = Date.now() + const requestTime = new Date(startTime).toISOString() + + res.on('finish', () => { + const durationSeconds = (Date.now() - startTime) / 1000 + const line = `${req.path} ${durationSeconds.toFixed(3)} ${requestTime} ${res.statusCode}\n` + + fs.appendFile(logFile, line, (err) => { + if (err) console.error('Failed to write request log:', err) + }) + }) + + next() +}) + +app.get('/dashboard', (req, res) => { + res.sendFile(path.join(__dirname, 'dashboard', 'index.html')) +}) + +app.get('/api/stats', (req, res) => { + const { requestCount, averageResponseTime } = getStatsFromLogFile() + + res.json({ + requestCount, + averageResponseTime, + status: 'up' + }) +}) + +app.get('/', (req, res) => { + res.send('Hello World from GHAYMAH') +}) + +app.get('/health', (req, res) => { + res.send('The app up and runing') +}) + +app.listen(port, () => { + console.log(`Example app listening at http://localhost:${port}`) + console.log(`Dashboard available at http://localhost:${port}/dashboard`) +}) diff --git a/dashboard.js b/dashboard.js new file mode 100644 index 0000000..f3478d5 --- /dev/null +++ b/dashboard.js @@ -0,0 +1,49 @@ +const requestCountEl = document.getElementById('requestCount') +const averageTimeEl = document.getElementById('averageTime') +const appStatusEl = document.getElementById('appStatus') +const statusBadgeEl = document.getElementById('statusBadge') +const statusTextEl = document.getElementById('statusText') +const lastUpdatedEl = document.getElementById('lastUpdated') + +function setStatus(isUp) { + statusBadgeEl.classList.remove('up', 'down') + appStatusEl.classList.remove('up', 'down') + + if (isUp) { + statusBadgeEl.classList.add('up') + appStatusEl.classList.add('up') + statusTextEl.textContent = 'Online' + appStatusEl.textContent = 'Up' + return + } + + statusBadgeEl.classList.add('down') + appStatusEl.classList.add('down') + statusTextEl.textContent = 'Offline' + appStatusEl.textContent = 'Down' +} + +async function fetchStats() { + try { + const response = await fetch('/api/stats') + + if (!response.ok) { + throw new Error('Stats request failed') + } + + const data = await response.json() + + requestCountEl.textContent = data.requestCount.toLocaleString() + averageTimeEl.textContent = `${data.averageResponseTime.toFixed(3)}s` + setStatus(data.status === 'up') + lastUpdatedEl.textContent = `Last updated: ${new Date().toLocaleTimeString()}` + } catch (error) { + requestCountEl.textContent = '—' + averageTimeEl.textContent = '—' + setStatus(false) + lastUpdatedEl.textContent = 'Last updated: failed to connect' + } +} + +fetchStats() +setInterval(fetchStats, 5000) diff --git a/index.html b/index.html new file mode 100644 index 0000000..0331e20 --- /dev/null +++ b/index.html @@ -0,0 +1,50 @@ + + +
+ + +Live monitoring for your Node.js application
+Total Requests
+—
+All requests received by the server
+Average Response Time
+—
+Mean duration in seconds
+Application Status
+—
+Based on health check
+