Introduction
After a considerable break from Python Flask development, I decided to refresh my skills by creating a practical project - a real-time laptop monitoring system. This project combines the simplicity of Flask with the power of Redis for real-time data handling, creating a responsive dashboard that displays crucial system metrics like CPU, memory, and disk usage.
Why Redis?
Redis, an in-memory data structure store, plays a crucial role in this project. It serves as a perfect solution for:
- Real-time Data Storage: Storing rapidly changing system metrics efficiently
- Fast Data Retrieval: Providing instant access to historical performance data
- Time-Series Data: Managing temporal data for trend analysis and visualization
- Lightweight: Minimal overhead compared to traditional databases
- Built-in Data Structures: Utilizing Redis lists for storing time-series metrics
Prerequisites
Before diving into the project, ensure you have the following installed:
- Docker and Docker Compose
- Python 3.9 (included in the Docker image)
- Modern web browser for viewing the dashboard
- Basic understanding of Python, Flask, and Redis
Project Structure
laptop-monitor/
├── app/
│ ├── __init__.py
│ ├── monitor.py
│ ├── redis_client.py
│ ├── routes.py
│ └── templates/
│ └── index.html
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── script.js
├── Dockerfile
├── docker-compose.yml
└── requirements.txt
Code Implementation
1. Flask Application Initialization (app/init.py)
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from flask import Flask
from routes import init_routes
app = Flask(__name__,
static_folder='../static',
static_url_path='')
init_routes(app)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
2. System Monitoring (app/monitor.py)
import psutil
def get_metrics():
return {
'cpu_percent': psutil.cpu_percent(),
'memory_percent': psutil.virtual_memory().percent,
'disk_percent': psutil.disk_usage('/').percent
}
3. Redis Client (app/redis_client.py)
import redis
import json
from datetime import datetime
redis_client = redis.Redis(host='redis', port=6379, db=0)
def store_metrics(metrics):
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
metrics['timestamp'] = timestamp
redis_client.lpush('metrics', json.dumps(metrics))
redis_client.ltrim('metrics', 0, 99) # Keep last 100 entries
def get_metrics_history():
metrics_history = redis_client.lrange('metrics', 0, -1)
return [json.loads(m) for m in metrics_history]
4. Routes Configuration (app/routes.py)
from flask import render_template
from monitor import get_metrics
from redis_client import store_metrics, get_metrics_history
def init_routes(app):
@app.route('/')
def dashboard():
metrics = get_metrics()
store_metrics(metrics)
history = get_metrics_history()
return render_template('index.html', metrics=metrics, history=history)
@app.route('/api/metrics')
def api_metrics():
return get_metrics()
5. Frontend Template (app/templates/index.html)
</span>
lang="en">
charset="UTF-8">
name="viewport" content="width=device-width, initial-scale=1.0">
Laptop Monitor
<span class="na">src="https://cdn.jsdelivr.net/npm/chart.js">
rel="stylesheet" type="text/css" href="/css/style.css">
class="container">
Laptop Performance Dashboard
class="metrics-grid">
class="metric-card">
CPU Usage
class="metric-value">{{ metrics.cpu_percent }}%
class="metric-card">
Memory Usage
class="metric-value">{{ metrics.memory_percent }}%
class="metric-card">
Disk Usage
class="metric-value">{{ metrics.disk_percent }}%
class="chart-container">
id="performanceChart">
<span class="na">src="/js/script.js">
Enter fullscreen mode
Exit fullscreen mode
6. Frontend Styling (static/css/style.css)
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f4f7fa;
margin: 0;
padding: 0;
color: #333;
}
.container {
max-width: 1200px;
margin: 40px auto;
padding: 20px;
}
h1 {
text-align: center;
color: #2c3e50;
margin-bottom: 30px;
font-size: 2.5em;
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.metric-card {
background: #ffffff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 20px;
text-align: center;
transition: transform 0.2s;
}
.metric-card:hover {
transform: translateY(-5px);
}
.metric-card h3 {
margin: 0 0 10px;
color: #34495e;
font-size: 1.2em;
}
.metric-value {
font-size: 2em;
font-weight: bold;
color: #2980b9;
margin: 0;
}
.chart-container {
background: #ffffff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 20px;
max-width: 100%;
}
canvas {
width: 100% !important;
height: auto !important;
}
Enter fullscreen mode
Exit fullscreen mode
7. Frontend JavaScript (static/js/script.js)
let chart;
const maxDataPoints = 10;
function initChart() {
const ctx = document.getElementById('performanceChart').getContext('2d');
chart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'CPU Usage',
borderColor: '#2980b9',
data: [],
fill: false
},
{
label: 'Memory Usage',
borderColor: '#27ae60',
data: [],
fill: false
},
{
label: 'Disk Usage',
borderColor: '#c0392b',
data: [],
fill: false
}
]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
max: 100
}
},
animation: {
duration: 0
}
}
});
}
function updateMetrics() {
fetch('/api/metrics')
.then(response => response.json())
.then(data => {
// Update metric values
document.querySelector('.metric-card:nth-child(1) .metric-value').textContent = `${data.cpu_percent}%`;
document.querySelector('.metric-card:nth-child(2) .metric-value').textContent = `${data.memory_percent}%`;
document.querySelector('.metric-card:nth-child(3) .metric-value').textContent = `${data.disk_percent}%`;
// Update chart
const timestamp = new Date().toLocaleTimeString();
chart.data.labels.push(timestamp);
chart.data.datasets[0].data.push(data.cpu_percent);
chart.data.datasets[1].data.push(data.memory_percent);
chart.data.datasets[2].data.push(data.disk_percent);
// Remove old data points if exceeding maxDataPoints
if (chart.data.labels.length > maxDataPoints) {
chart.data.labels.shift();
chart.data.datasets.forEach(dataset => dataset.data.shift());
}
chart.update();
})
.catch(error => console.error('Error fetching metrics:', error));
}
// Initialize chart when page loads
document.addEventListener('DOMContentLoaded', () => {
initChart();
// Update metrics every 2 seconds
setInterval(updateMetrics, 2000);
});
Enter fullscreen mode
Exit fullscreen mode
8. Requirements (requirements.txt)
Flask==2.0.1
redis==4.0.2
psutil==5.8.0
Enter fullscreen mode
Exit fullscreen mode
9. Docker Configuration
Dockerfile
FROM python:3.9-slim
WORKDIR /app
# Copy requirements first to leverage Docker cache
COPY requirements.txt /app/
RUN pip install -r requirements.txt
# Create static directory structure explicitly
RUN mkdir -p /app/static/css /app/static/js
# Copy all application files
COPY . /app/
# Ensure proper permissions for static files
RUN chmod -R 755 /app/static
CMD ["python", "-m", "flask", "run", "--host=0.0.0.0"]
Enter fullscreen mode
Exit fullscreen mode
docker-compose.yml
services:
flask-app:
build: .
ports:
- "5001:5000"
depends_on:
- redis
environment:
- REDIS_HOST=redis
volumes:
- ./static:/app/static
redis:
image: redis:latest
ports:
- "6379:6379"
volumes:
- redis-data:/data
volumes:
redis-data:
Enter fullscreen mode
Exit fullscreen mode
Running the Application
To run the application, follow these steps:
Clone the repository:
cd laptop-monitor #at the root of you project where we have the docker compose file
Enter fullscreen mode
Exit fullscreen mode
Build and start the containers:
docker-compose up --build
Enter fullscreen mode
Exit fullscreen mode
Access the dashboard:
Open your web browser and navigate to http://localhost:5001
Conclusion
This laptop monitoring project serves as an excellent refresher for Python Flask development while incorporating modern development practices like containerization and real-time data handling. The combination of Flask's simplicity and Redis's powerful data handling capabilities creates a robust solution for system monitoring.The project demonstrates several key concepts:
Building a Flask web application
Real-time data handling with Redis
Docker containerization
Frontend development with Chart.js
System metrics monitoring with psutil
If you encounter any challenges while setting up or running the project, feel free to reach out in the comments section below. Your feedback and questions are valuable for improving this tutorial and helping others in the community.
Link to the code on github
References
Flask Documentation
Redis Documentation
Docker Documentation
Chart.js Documentation
psutil Documentation