wip
This commit is contained in:
36
.gitignore
vendored
36
.gitignore
vendored
@@ -1,2 +1,34 @@
|
||||
target
|
||||
.env
|
||||
# Rust
|
||||
target/
|
||||
Cargo.lock
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
monitoring.log
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Backup files
|
||||
*.bak
|
||||
*.backup
|
||||
|
||||
# Documentation builds
|
||||
docs/book/
|
||||
|
||||
# Coverage reports
|
||||
lcov.info
|
||||
coverage/
|
||||
2064
Cargo.lock
generated
Normal file
2064
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
Cargo.toml
Normal file
28
Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"node_notifier",
|
||||
"discord_client",
|
||||
"system_monitor"
|
||||
]
|
||||
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Your Name <your.email@example.com>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/your-username/node-notifier"
|
||||
description = "Système de surveillance de ressources système avec notifications Discord"
|
||||
|
||||
[workspace.dependencies]
|
||||
# Dépendances communes
|
||||
sysinfo = "0.35.2"
|
||||
reqwest = { version = "0.12.22", features = ["blocking", "json"] }
|
||||
serde = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
dotenvy = "0.15.7"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
# Dépendances de test
|
||||
mockito = "1.7.0"
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Node Notifier Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
173
README.md
Normal file
173
README.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Node Notifier
|
||||
|
||||
Un système de surveillance de ressources système avec notifications Discord, développé en Rust.
|
||||
|
||||
## 📋 Description
|
||||
|
||||
Node Notifier est un outil de monitoring système qui surveille en temps réel les ressources (CPU, mémoire, swap, disque) et envoie des notifications Discord lorsque des seuils sont dépassés. Le projet est architecturé de manière modulaire avec trois crates spécialisées.
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
Le projet est organisé en trois crates indépendantes :
|
||||
|
||||
```
|
||||
node-notifier/
|
||||
├── node_notifier/ # Application principale
|
||||
├── discord_client/ # Client de notifications Discord
|
||||
├── system_monitor/ # Surveillance des ressources système
|
||||
└── README.md # Ce fichier
|
||||
```
|
||||
|
||||
### Crates
|
||||
|
||||
- **`node_notifier`** : Point d'entrée principal qui orchestre la surveillance et les notifications
|
||||
- **`discord_client`** : Gère l'envoi de notifications vers Discord via webhooks
|
||||
- **`system_monitor`** : Fournit les fonctionnalités de surveillance des ressources système
|
||||
|
||||
## 🚀 Installation et Usage
|
||||
|
||||
### Prérequis
|
||||
|
||||
- Rust 1.70+
|
||||
- Un webhook Discord configuré
|
||||
|
||||
### Configuration
|
||||
|
||||
1. Clonez le repository :
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd node-notifier
|
||||
```
|
||||
|
||||
2. Créez un fichier `.env` à la racine du projet :
|
||||
```env
|
||||
DISCORD_WEBHOOK=https://discord.com/api/webhooks/your/webhook/url
|
||||
```
|
||||
|
||||
3. Compilez le projet :
|
||||
```bash
|
||||
cd node_notifier
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
4. Exécutez le monitoring :
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
### Exemples d'utilisation
|
||||
|
||||
Le projet inclut plusieurs exemples pratiques :
|
||||
|
||||
```bash
|
||||
# Surveillance basique
|
||||
cd node_notifier && cargo run --example basic_monitoring
|
||||
|
||||
# Seuils personnalisés
|
||||
cd node_notifier && cargo run --example custom_thresholds
|
||||
|
||||
# Test des notifications Discord (nécessite DISCORD_WEBHOOK)
|
||||
cd node_notifier && DISCORD_WEBHOOK="your-webhook-url" cargo run --example discord_notifications
|
||||
|
||||
# Surveillance continue (nécessite DISCORD_WEBHOOK)
|
||||
cd node_notifier && DISCORD_WEBHOOK="your-webhook-url" cargo run --example continuous_monitoring
|
||||
```
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Seuils par défaut
|
||||
|
||||
- **CPU** : 80%
|
||||
- **Mémoire** : 80%
|
||||
- **Swap** : 80%
|
||||
- **Disque** : 80% (surveille `/` et `/home`)
|
||||
|
||||
### Personnalisation
|
||||
|
||||
Pour modifier les seuils ou ajouter de nouveaux types de surveillance, consultez la documentation de la crate `system_monitor`.
|
||||
|
||||
## 📊 Fonctionnalités
|
||||
|
||||
- ✅ Surveillance en temps réel du CPU, mémoire, swap et disque
|
||||
- ✅ Notifications Discord automatiques en cas de dépassement de seuils
|
||||
- ✅ Rapport de synthèse des ressources système
|
||||
- ✅ Architecture modulaire et extensible
|
||||
- ✅ Configuration via variables d'environnement
|
||||
- ✅ Gestion d'erreurs robuste
|
||||
|
||||
## 🛠️ Développement
|
||||
|
||||
### Structure du workspace
|
||||
|
||||
Le projet utilise un workspace Cargo avec trois crates :
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
members = [
|
||||
"node_notifier",
|
||||
"discord_client",
|
||||
"system_monitor"
|
||||
]
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
```bash
|
||||
# Tests pour toutes les crates
|
||||
cargo test --workspace
|
||||
|
||||
# Tests pour une crate spécifique
|
||||
cargo test -p system_monitor
|
||||
```
|
||||
|
||||
### Compilation
|
||||
|
||||
```bash
|
||||
# Compilation de tout le workspace
|
||||
cargo build --workspace
|
||||
|
||||
# Compilation release
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
## 📦 Crates individuelles
|
||||
|
||||
- [discord_client](./discord_client/README.md) - Client de notifications Discord
|
||||
- [system_monitor](./system_monitor/README.md) - Surveillance des ressources système
|
||||
|
||||
## 🤝 Contribution
|
||||
|
||||
1. Forkez le projet
|
||||
2. Créez une branche pour votre fonctionnalité (`git checkout -b feature/nouvelle-fonctionnalite`)
|
||||
3. Committez vos changements (`git commit -am 'Ajout d'une nouvelle fonctionnalité'`)
|
||||
4. Poussez vers la branche (`git push origin feature/nouvelle-fonctionnalite`)
|
||||
5. Créez une Pull Request
|
||||
|
||||
## 📄 Licence
|
||||
|
||||
Ce projet est sous licence MIT. Voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
||||
|
||||
## 🔧 Dépannage
|
||||
|
||||
### Problèmes courants
|
||||
|
||||
**Erreur "DISCORD_WEBHOOK environment variable not set"**
|
||||
- Vérifiez que le fichier `.env` existe et contient la variable `DISCORD_WEBHOOK`
|
||||
- Assurez-vous que l'URL du webhook Discord est valide
|
||||
|
||||
**Permissions insuffisantes pour lire les informations système**
|
||||
- Sur certains systèmes, des permissions root peuvent être nécessaires
|
||||
- Utilisez `sudo` si nécessaire
|
||||
|
||||
**Erreur de compilation**
|
||||
- Vérifiez que vous utilisez Rust 1.70+
|
||||
- Nettoyez le cache avec `cargo clean` puis recompilez
|
||||
|
||||
## 📈 Roadmap
|
||||
|
||||
- [ ] Interface web de monitoring
|
||||
- [ ] Support de multiples canaux de notification (Slack, email, etc.)
|
||||
- [ ] Historique des métriques
|
||||
- [ ] Configuration via fichier YAML/TOML
|
||||
- [ ] Surveillance de processus spécifiques
|
||||
- [ ] Alertes configurables par plage horaire
|
||||
135
REFACTORING.md
Normal file
135
REFACTORING.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Refactorisation : Meilleure délégation des responsabilités
|
||||
|
||||
## 🎯 Problèmes identifiés
|
||||
|
||||
### 1. **Mélange des responsabilités dans `system_monitor`**
|
||||
- ❌ Formatage des messages d'alerte dans la crate de monitoring
|
||||
- ❌ Messages hardcodés limitant la flexibilité
|
||||
- ❌ Couplage fort entre données et présentation
|
||||
|
||||
### 2. **Logique d'orchestration insuffisante dans `node_notifier`**
|
||||
- ❌ Simple "collage" de crates sans valeur ajoutée
|
||||
- ❌ Pas de traitement intelligent des données
|
||||
|
||||
## ✅ Solutions apportées
|
||||
|
||||
### 1. **Séparation claire des responsabilités**
|
||||
|
||||
#### `system_monitor` (Crate de données)
|
||||
- ✅ **Responsabilité unique** : Collecte et analyse des métriques système
|
||||
- ✅ **Pas de formatage** : Retourne des structures de données pures
|
||||
- ✅ **API riche** : `SystemMetrics`, `ThresholdViolation`, `Severity`
|
||||
|
||||
#### `node_notifier` (Crate d'orchestration)
|
||||
- ✅ **Formatage intelligent** : `AlertFormatter` avec logique métier
|
||||
- ✅ **Messages contextuels** : Différents messages selon métrique et gravité
|
||||
- ✅ **Orchestration** : Coordination entre monitoring et notifications
|
||||
|
||||
#### `discord_client` (Crate de transport)
|
||||
- ✅ **Transport pur** : Envoi de messages sans logique métier
|
||||
- ✅ **Réutilisable** : Peut être utilisé par d'autres applications
|
||||
|
||||
### 2. **Nouvelles structures de données**
|
||||
|
||||
```rust
|
||||
// Métriques pures sans formatage
|
||||
pub struct SystemMetrics {
|
||||
pub cpu_usage: f32,
|
||||
pub memory_usage: f32,
|
||||
pub swap_usage: f32,
|
||||
pub disk_usage: f32,
|
||||
}
|
||||
|
||||
// Violation avec contexte riche
|
||||
pub struct ThresholdViolation {
|
||||
pub metric_name: String,
|
||||
pub current_value: f32,
|
||||
pub threshold: f32,
|
||||
pub severity: Severity, // Warning | Critical
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **Formatage intelligent dans `node_notifier`**
|
||||
|
||||
```rust
|
||||
struct AlertFormatter;
|
||||
|
||||
impl AlertFormatter {
|
||||
fn format_violation(&self, violation: &ThresholdViolation) -> String {
|
||||
// Logique de formatage contextuelle
|
||||
// Emojis, niveaux, messages spécifiques
|
||||
}
|
||||
|
||||
fn get_message_for_metric(&self, metric: &str, severity: &Severity) -> String {
|
||||
// Messages personnalisés par métrique et gravité
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🏗️ Architecture finale
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ system_monitor │ │ node_notifier │ │ discord_client │
|
||||
│ │ │ │ │ │
|
||||
│ • Collecte │───▶│ • Orchestration │───▶│ • Transport │
|
||||
│ • Analyse │ │ • Formatage │ │ • Envoi │
|
||||
│ • Seuils │ │ • Logique métier │ │ │
|
||||
│ │ │ │ │ │
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
DONNÉES BUSINESS TRANSPORT
|
||||
```
|
||||
|
||||
## 📈 Avantages obtenus
|
||||
|
||||
### 1. **Réutilisabilité**
|
||||
- `system_monitor` peut être utilisé dans d'autres contextes (CLI, web, etc.)
|
||||
- `discord_client` réutilisable pour tout type de notification
|
||||
- `node_notifier` concentre la logique spécifique à cette application
|
||||
|
||||
### 2. **Maintenabilité**
|
||||
- Responsabilités claires et séparées
|
||||
- Changement de formatage sans impact sur le monitoring
|
||||
- Tests plus simples et ciblés
|
||||
|
||||
### 3. **Extensibilité**
|
||||
- Facile d'ajouter de nouveaux formats (email, Slack, etc.)
|
||||
- Nouveaux types de métriques sans casser l'existant
|
||||
- Seuils configurables avec niveaux multiples
|
||||
|
||||
### 4. **Qualité du code**
|
||||
- Structures de données typées et riches
|
||||
- Gestion d'erreurs centralisée
|
||||
- API claire et documentée
|
||||
|
||||
## 🔄 Migration des exemples
|
||||
|
||||
Tous les exemples ont été mis à jour pour utiliser la nouvelle API :
|
||||
|
||||
- ✅ `basic_monitoring` : Affichage structuré des métriques
|
||||
- ✅ `custom_thresholds` : Seuils avec niveaux critique/warning
|
||||
- ✅ `continuous_monitoring` : Formatage riche des alertes
|
||||
- ✅ `discord_notifications` : Inchangé (transport pur)
|
||||
|
||||
## 🎯 Résultat
|
||||
|
||||
### Avant (couplage fort)
|
||||
```rust
|
||||
// system_monitor retournait des strings formatées
|
||||
let alerts = monitor.check_thresholds(); // Vec<String>
|
||||
for alert in alerts {
|
||||
notifier.send_notification(&alert); // Pas de contrôle du format
|
||||
}
|
||||
```
|
||||
|
||||
### Après (séparation claire)
|
||||
```rust
|
||||
// system_monitor retourne des données structurées
|
||||
let violations = monitor.check_thresholds(); // Vec<ThresholdViolation>
|
||||
for violation in violations {
|
||||
let message = formatter.format_violation(&violation); // Formatage intelligent
|
||||
notifier.send_notification(&message); // Transport pur
|
||||
}
|
||||
```
|
||||
|
||||
Cette refactorisation respecte maintenant parfaitement les principes SOLID et offre une architecture robuste et évolutive ! 🚀
|
||||
277
discord_client/README.md
Normal file
277
discord_client/README.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Discord Client
|
||||
|
||||
Une crate Rust pour envoyer des notifications vers Discord via webhooks.
|
||||
|
||||
## 📋 Description
|
||||
|
||||
Cette crate fournit un client simple et robuste pour envoyer des notifications vers Discord en utilisant les webhooks. Elle permet de personnaliser l'apparence du bot (nom d'utilisateur, avatar) et gère les erreurs de manière appropriée.
|
||||
|
||||
## 🚀 Fonctionnalités
|
||||
|
||||
- ✅ Envoi de messages vers Discord via webhooks
|
||||
- ✅ Personnalisation du nom d'utilisateur et avatar du bot
|
||||
- ✅ Gestion d'erreurs robuste
|
||||
- ✅ API simple et intuitive
|
||||
- ✅ Tests unitaires avec mocking
|
||||
- ✅ Support des messages formatés (Markdown Discord)
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
Ajoutez cette dépendance à votre `Cargo.toml` :
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
discord_client = { path = "path/to/discord_client" }
|
||||
```
|
||||
|
||||
Ou pour un usage externe :
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
reqwest = { version = "0.12", features = ["blocking", "json"] }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
```
|
||||
|
||||
## 🔧 Utilisation
|
||||
|
||||
### Utilisation basique
|
||||
|
||||
```rust
|
||||
use discord_client::DiscordNotifier;
|
||||
|
||||
// Créer un client Discord
|
||||
let notifier = DiscordNotifier::new(
|
||||
"https://discord.com/api/webhooks/your/webhook/url".to_string(),
|
||||
"System Monitor".to_string(),
|
||||
"https://example.com/bot-avatar.png".to_string(),
|
||||
);
|
||||
|
||||
// Envoyer une notification
|
||||
match notifier.send_notification("🚨 Alerte système détectée !") {
|
||||
Ok(_) => println!("Notification envoyée avec succès"),
|
||||
Err(e) => eprintln!("Erreur lors de l'envoi : {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
### Messages formatés
|
||||
|
||||
Discord supporte le formatage Markdown :
|
||||
|
||||
```rust
|
||||
let message = r#"
|
||||
🖥️ **Rapport Système**
|
||||
|
||||
**CPU:** 85% 🔥
|
||||
**Mémoire:** 76% 📊
|
||||
**Disque:** 45% 💾
|
||||
|
||||
*Surveillance automatique*
|
||||
"#;
|
||||
|
||||
notifier.send_notification(message)?;
|
||||
```
|
||||
|
||||
### Gestion d'erreurs avancée
|
||||
|
||||
```rust
|
||||
use discord_client::DiscordNotifier;
|
||||
|
||||
fn send_alert(notifier: &DiscordNotifier, message: &str) {
|
||||
match notifier.send_notification(message) {
|
||||
Ok(_) => {
|
||||
println!("✅ Notification envoyée");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Échec d'envoi de notification : {}", e);
|
||||
|
||||
// Vous pourriez implémenter une logique de retry ici
|
||||
// ou utiliser un système de backup (logs, email, etc.)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 Personnalisation
|
||||
|
||||
### Avatar et nom personnalisés
|
||||
|
||||
```rust
|
||||
let notifier = DiscordNotifier::new(
|
||||
webhook_url,
|
||||
"🤖 MonBot Perso".to_string(),
|
||||
"https://mon-site.com/avatar-custom.png".to_string(),
|
||||
);
|
||||
```
|
||||
|
||||
### Messages avec emojis et formatage
|
||||
|
||||
```rust
|
||||
// Message d'alerte
|
||||
let alert = "🚨 **ALERTE CRITIQUE** 🚨\n\n CPU: 95% - Intervention requise !";
|
||||
|
||||
// Message d'information
|
||||
let info = "ℹ️ **Info Système**\n\n Surveillance normale en cours...";
|
||||
|
||||
// Message de succès
|
||||
let success = "✅ **Système OK**\n\n Toutes les métriques sont normales.";
|
||||
```
|
||||
|
||||
## 🔗 Configuration Discord
|
||||
|
||||
### Création d'un webhook
|
||||
|
||||
1. **Accédez à votre serveur Discord**
|
||||
2. **Paramètres du canal** → Intégrations → Webhooks
|
||||
3. **Créer un webhook**
|
||||
4. **Copiez l'URL du webhook**
|
||||
5. **Optionnel** : Personnalisez le nom et l'avatar par défaut
|
||||
|
||||
### Permissions requises
|
||||
|
||||
Le webhook nécessite les permissions suivantes sur le canal :
|
||||
- `Envoyer des messages`
|
||||
- `Intégrer des liens` (pour les embeds)
|
||||
- `Utiliser des emojis externes` (si utilisés)
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
La crate inclut des tests unitaires avec mocking :
|
||||
|
||||
```bash
|
||||
# Exécuter tous les tests
|
||||
cargo test
|
||||
|
||||
# Tests avec logs détaillés
|
||||
cargo test -- --nocapture
|
||||
|
||||
# Test spécifique
|
||||
cargo test test_send_notification
|
||||
```
|
||||
|
||||
### Exemple de test
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use mockito::Server;
|
||||
|
||||
#[test]
|
||||
fn test_custom_notification() {
|
||||
let mut server = Server::new();
|
||||
let mock = server.mock("POST", "/")
|
||||
.with_status(204)
|
||||
.create();
|
||||
|
||||
let notifier = DiscordNotifier::new(
|
||||
server.url(),
|
||||
"Test Bot".to_string(),
|
||||
"https://example.com/avatar.png".to_string(),
|
||||
);
|
||||
|
||||
assert!(notifier.send_notification("Test message").is_ok());
|
||||
mock.assert();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 API Reference
|
||||
|
||||
### `DiscordNotifier`
|
||||
|
||||
#### Constructeur
|
||||
|
||||
```rust
|
||||
pub fn new(webhook_url: String, username: String, avatar_url: String) -> DiscordNotifier
|
||||
```
|
||||
|
||||
Crée une nouvelle instance du notifier Discord.
|
||||
|
||||
**Paramètres :**
|
||||
- `webhook_url` : URL du webhook Discord
|
||||
- `username` : Nom d'utilisateur affiché pour le bot
|
||||
- `avatar_url` : URL de l'avatar du bot
|
||||
|
||||
#### Méthodes
|
||||
|
||||
```rust
|
||||
pub fn send_notification(&self, message: &str) -> Result<(), Box<dyn std::error::Error>>
|
||||
```
|
||||
|
||||
Envoie un message vers Discord.
|
||||
|
||||
**Paramètres :**
|
||||
- `message` : Contenu du message à envoyer
|
||||
|
||||
**Retour :**
|
||||
- `Ok(())` si l'envoi réussit
|
||||
- `Err(...)` en cas d'erreur (réseau, webhook invalide, etc.)
|
||||
|
||||
## 🔧 Dépannage
|
||||
|
||||
### Erreurs courantes
|
||||
|
||||
**"Invalid webhook URL"**
|
||||
- Vérifiez que l'URL commence par `https://discord.com/api/webhooks/`
|
||||
- Assurez-vous que le webhook n'a pas été supprimé
|
||||
|
||||
**Timeout de réseau**
|
||||
- Vérifiez votre connexion internet
|
||||
- Le webhook Discord peut être temporairement indisponible
|
||||
|
||||
**Erreur 404**
|
||||
- Le webhook a été supprimé ou l'URL est incorrecte
|
||||
- Recréez un nouveau webhook si nécessaire
|
||||
|
||||
**Erreur 429 (Rate Limit)**
|
||||
- Vous envoyez trop de messages trop rapidement
|
||||
- Implémentez une logique de retry avec délai
|
||||
|
||||
### Debug
|
||||
|
||||
Activez les logs pour plus d'informations :
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug cargo test
|
||||
```
|
||||
|
||||
## 🚀 Fonctionnalités avancées
|
||||
|
||||
### Retry automatique
|
||||
|
||||
```rust
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
|
||||
fn send_with_retry(notifier: &DiscordNotifier, message: &str, max_retries: u32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
for attempt in 1..=max_retries {
|
||||
match notifier.send_notification(message) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(e) => {
|
||||
if attempt == max_retries {
|
||||
return Err(e);
|
||||
}
|
||||
println!("Tentative {} échouée, retry dans 2s...", attempt);
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
```
|
||||
|
||||
### Messages avec embeds (extension future)
|
||||
|
||||
Cette crate se concentre sur les messages simples, mais peut être étendue pour supporter les embeds Discord :
|
||||
|
||||
```rust
|
||||
// Exemple d'extension possible
|
||||
pub fn send_embed(&self, embed: DiscordEmbed) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Implémentation future
|
||||
}
|
||||
```
|
||||
|
||||
## 📄 Licence
|
||||
|
||||
Cette crate est sous licence MIT.
|
||||
25
examples/Cargo.toml
Normal file
25
examples/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "node_notifier_examples"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[example]]
|
||||
name = "basic_monitoring"
|
||||
path = "basic_monitoring.rs"
|
||||
|
||||
[[example]]
|
||||
name = "custom_thresholds"
|
||||
path = "custom_thresholds.rs"
|
||||
|
||||
[[example]]
|
||||
name = "discord_notifications"
|
||||
path = "discord_notifications.rs"
|
||||
|
||||
[[example]]
|
||||
name = "continuous_monitoring"
|
||||
path = "continuous_monitoring.rs"
|
||||
|
||||
[dependencies]
|
||||
discord_client = { path = "../discord_client" }
|
||||
system_monitor = { path = "../system_monitor" }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
166
examples/README.md
Normal file
166
examples/README.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Exemples d'utilisation
|
||||
|
||||
Ce dossier contient des exemples pratiques d'utilisation des différentes crates du projet Node Notifier.
|
||||
|
||||
## 📁 Structure
|
||||
|
||||
- `basic_monitoring.rs` - Exemple basique de surveillance système
|
||||
- `custom_thresholds.rs` - Configuration de seuils personnalisés
|
||||
- `discord_notifications.rs` - Test du client Discord
|
||||
- `continuous_monitoring.rs` - Surveillance continue avec boucle
|
||||
|
||||
## 🚀 Exécution des exemples
|
||||
|
||||
Depuis le répertoire `node_notifier` :
|
||||
|
||||
### 1. Surveillance basique
|
||||
```bash
|
||||
cargo run --example basic_monitoring
|
||||
```
|
||||
Démontre l'utilisation basique de `SystemMonitor` avec les seuils par défaut.
|
||||
|
||||
**Sortie exemple :**
|
||||
```
|
||||
🖥️ Surveillance système basique
|
||||
|
||||
✅ Aucune alerte - Système OK
|
||||
|
||||
📊 Résumé des ressources:
|
||||
CPU usage: 14.72%
|
||||
Memory usage: 23.10%
|
||||
Swap usage: 0.00%
|
||||
Disk usage: 79.17%
|
||||
```
|
||||
|
||||
### 2. Seuils personnalisés
|
||||
```bash
|
||||
cargo run --example custom_thresholds
|
||||
```
|
||||
Montre comment configurer des seuils personnalisés et créer des messages d'alerte sur mesure.
|
||||
|
||||
**Fonctionnalités :**
|
||||
- Seuils CPU, mémoire et disque personnalisés
|
||||
- Messages d'alerte personnalisés
|
||||
- Configuration flexible
|
||||
|
||||
### 3. Test Discord
|
||||
```bash
|
||||
DISCORD_WEBHOOK="your-webhook-url" cargo run --example discord_notifications
|
||||
```
|
||||
Teste la fonctionnalité de notification Discord avec différents types de messages.
|
||||
|
||||
**Prérequis :**
|
||||
- Variable d'environnement `DISCORD_WEBHOOK` configurée
|
||||
- Webhook Discord valide
|
||||
|
||||
**Messages testés :**
|
||||
- Message de test de connexion
|
||||
- Alerte simulée avec formatage
|
||||
- Rapport système formaté
|
||||
|
||||
### 4. Surveillance continue
|
||||
```bash
|
||||
DISCORD_WEBHOOK="your-webhook-url" cargo run --example continuous_monitoring
|
||||
```
|
||||
Démontre une surveillance continue avec notifications automatiques.
|
||||
|
||||
**Fonctionnalités :**
|
||||
- Vérifications toutes les 30 secondes
|
||||
- Alertes instantanées en cas de dépassement
|
||||
- Rapport périodique toutes les 5 minutes
|
||||
- Gestion des timestamps
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
Pour les exemples Discord, définissez votre webhook :
|
||||
|
||||
```bash
|
||||
export DISCORD_WEBHOOK="https://discord.com/api/webhooks/your/webhook/url"
|
||||
```
|
||||
|
||||
Ou créez un fichier `.env` dans le répertoire racine :
|
||||
|
||||
```env
|
||||
DISCORD_WEBHOOK=https://discord.com/api/webhooks/your/webhook/url
|
||||
```
|
||||
|
||||
### Configuration Discord
|
||||
|
||||
1. **Créer un webhook :**
|
||||
- Accédez à votre serveur Discord
|
||||
- Paramètres du canal → Intégrations → Webhooks
|
||||
- Créez un nouveau webhook
|
||||
- Copiez l'URL
|
||||
|
||||
2. **Permissions requises :**
|
||||
- Envoyer des messages
|
||||
- Intégrer des liens
|
||||
- Utiliser des emojis externes
|
||||
|
||||
## 📊 Cas d'usage
|
||||
|
||||
### Surveillance ponctuelle
|
||||
Utilisez `basic_monitoring` ou `custom_thresholds` pour une vérification unique des ressources système.
|
||||
|
||||
### Monitoring de production
|
||||
Utilisez `continuous_monitoring` comme base pour un système de surveillance permanent.
|
||||
|
||||
### Tests d'intégration
|
||||
Utilisez `discord_notifications` pour valider votre configuration Discord.
|
||||
|
||||
## 🛠️ Personnalisation
|
||||
|
||||
### Modifier les seuils
|
||||
Dans `custom_thresholds.rs`, adaptez les valeurs selon vos besoins :
|
||||
|
||||
```rust
|
||||
ResourceThreshold::new(
|
||||
"CPU".to_string(),
|
||||
get_cpu_usage,
|
||||
95.0, // Seuil critique à 95%
|
||||
"🔥 CPU en surchauffe !".to_string(),
|
||||
),
|
||||
```
|
||||
|
||||
### Ajouter des métriques
|
||||
Créez vos propres fonctions de surveillance :
|
||||
|
||||
```rust
|
||||
fn get_custom_metric(sys: &System, disks: &Disks) -> f32 {
|
||||
// Votre logique personnalisée
|
||||
42.0
|
||||
}
|
||||
```
|
||||
|
||||
### Personnaliser les messages Discord
|
||||
Modifiez le formatage des messages dans `continuous_monitoring.rs` :
|
||||
|
||||
```rust
|
||||
let message = format!(
|
||||
"🚨 **ALERTE CRITIQUE**\n\n{}\n\n⏰ {}",
|
||||
alert,
|
||||
chrono::Utc::now().format("%H:%M:%S")
|
||||
);
|
||||
```
|
||||
|
||||
## 🔍 Debugging
|
||||
|
||||
### Activer les logs
|
||||
```bash
|
||||
RUST_LOG=debug cargo run --example basic_monitoring
|
||||
```
|
||||
|
||||
### Tester sans Discord
|
||||
Les exemples `basic_monitoring` et `custom_thresholds` fonctionnent sans configuration Discord.
|
||||
|
||||
### Vérifier les métriques
|
||||
Consultez les valeurs retournées pour identifier les problèmes de seuils.
|
||||
|
||||
## 💡 Conseils
|
||||
|
||||
- **Production** : Adaptez `continuous_monitoring` pour vos besoins spécifiques
|
||||
- **Seuils** : Commencez avec des valeurs conservatrices et ajustez progressivement
|
||||
- **Rate limiting** : Attention aux limites Discord (30 req/min par webhook)
|
||||
- **Monitoring** : Surveillez aussi les performances de votre système de monitoring
|
||||
32
examples/basic_monitoring.rs
Normal file
32
examples/basic_monitoring.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
// Exemple basique d'utilisation de system_monitor
|
||||
use system_monitor::SystemMonitor;
|
||||
|
||||
fn main() {
|
||||
println!("🖥️ Surveillance système basique\n");
|
||||
|
||||
// Créer un moniteur avec les seuils par défaut
|
||||
let mut monitor = SystemMonitor::new();
|
||||
|
||||
// Vérifier les seuils
|
||||
let violations = monitor.check_thresholds();
|
||||
|
||||
if violations.is_empty() {
|
||||
println!("✅ Aucune alerte - Système OK");
|
||||
} else {
|
||||
println!("🚨 Alertes détectées:");
|
||||
for violation in violations {
|
||||
println!(" - {} : {:.2}% (seuil: {:.2}%)",
|
||||
violation.metric_name,
|
||||
violation.current_value,
|
||||
violation.threshold);
|
||||
}
|
||||
}
|
||||
|
||||
// Afficher les métriques système
|
||||
let metrics = monitor.get_metrics();
|
||||
println!("\n📊 Métriques système:");
|
||||
println!(" CPU: {:.1}%", metrics.cpu_usage);
|
||||
println!(" Mémoire: {:.1}%", metrics.memory_usage);
|
||||
println!(" Swap: {:.1}%", metrics.swap_usage);
|
||||
println!(" Disque: {:.1}%", metrics.disk_usage);
|
||||
}
|
||||
98
examples/continuous_monitoring.rs
Normal file
98
examples/continuous_monitoring.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
// Exemple de surveillance continue
|
||||
use discord_client::DiscordNotifier;
|
||||
use system_monitor::{SystemMonitor, Severity};
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
|
||||
fn format_violation_message(violation: &system_monitor::ThresholdViolation, check_count: u32) -> String {
|
||||
let emoji = match violation.severity {
|
||||
Severity::Warning => "⚠️",
|
||||
Severity::Critical => "🚨",
|
||||
};
|
||||
|
||||
let level = match violation.severity {
|
||||
Severity::Warning => "ALERTE",
|
||||
Severity::Critical => "CRITIQUE",
|
||||
};
|
||||
|
||||
format!(
|
||||
"{} **{}** - {}\n\n**Valeur:** {:.2}%\n**Seuil:** {:.2}%\n\n*Vérification #{} à {}*",
|
||||
emoji,
|
||||
level,
|
||||
violation.metric_name,
|
||||
violation.current_value,
|
||||
violation.threshold,
|
||||
check_count,
|
||||
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
|
||||
)
|
||||
}
|
||||
|
||||
fn format_report_message(metrics: &system_monitor::SystemMetrics, check_count: u32) -> String {
|
||||
format!(
|
||||
"📊 **Rapport système** (Vérification #{})\n\n```\nCPU: {:.1}%\nMémoire: {:.1}%\nSwap: {:.1}%\nDisque: {:.1}%\n```\n\n*Système OK - Surveillance continue*",
|
||||
check_count,
|
||||
metrics.cpu_usage,
|
||||
metrics.memory_usage,
|
||||
metrics.swap_usage,
|
||||
metrics.disk_usage
|
||||
)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🔄 Surveillance continue démarrée\n");
|
||||
|
||||
// Configuration
|
||||
let webhook_url = std::env::var("DISCORD_WEBHOOK")
|
||||
.expect("DISCORD_WEBHOOK environment variable not set");
|
||||
|
||||
let check_interval = Duration::from_secs(30); // Vérification toutes les 30 secondes
|
||||
let report_interval = 10; // Rapport complet toutes les 10 vérifications (5 minutes)
|
||||
|
||||
// Initialisation
|
||||
let notifier = DiscordNotifier::new(
|
||||
webhook_url,
|
||||
"🔍 System Monitor".to_string(),
|
||||
"https://cdn.shopify.com/s/files/1/0262/1423/6212/files/Lord_of_the_Rings_eye_of_Sauron_-_Ghtic.com_-_Blog.png?v=1579680018".to_string(),
|
||||
);
|
||||
|
||||
let mut monitor = SystemMonitor::new();
|
||||
let mut check_count = 0;
|
||||
|
||||
// Message de démarrage
|
||||
notifier.send_notification("🟢 **Surveillance système démarrée**\n\nMonitoring actif avec vérifications toutes les 30 secondes.")?;
|
||||
|
||||
println!("✅ Surveillance démarrée - Appuyez sur Ctrl+C pour arrêter");
|
||||
|
||||
// Boucle de surveillance
|
||||
loop {
|
||||
check_count += 1;
|
||||
|
||||
// Actualiser les données système
|
||||
monitor.refresh();
|
||||
|
||||
// Vérifier les seuils
|
||||
let violations = monitor.check_thresholds();
|
||||
|
||||
// Envoyer les alertes s'il y en a
|
||||
for violation in violations {
|
||||
let message = format_violation_message(&violation, check_count);
|
||||
|
||||
println!("🚨 Alerte envoyée: {} {:.2}%", violation.metric_name, violation.current_value);
|
||||
notifier.send_notification(&message)?;
|
||||
}
|
||||
|
||||
// Rapport périodique complet
|
||||
if check_count % report_interval == 0 {
|
||||
let metrics = monitor.get_metrics();
|
||||
let report = format_report_message(&metrics, check_count);
|
||||
|
||||
println!("📊 Envoi du rapport périodique (vérification #{})", check_count);
|
||||
notifier.send_notification(&report)?;
|
||||
} else {
|
||||
println!("✅ Vérification #{} - Système OK", check_count);
|
||||
}
|
||||
|
||||
// Attendre avant la prochaine vérification
|
||||
thread::sleep(check_interval);
|
||||
}
|
||||
}
|
||||
64
examples/custom_thresholds.rs
Normal file
64
examples/custom_thresholds.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
// Exemple avec seuils personnalisés
|
||||
use system_monitor::{SystemMonitor, ResourceThreshold, get_cpu_usage, get_memory_usage, get_disk_usage};
|
||||
|
||||
fn main() {
|
||||
println!("🎛️ Surveillance avec seuils personnalisés\n");
|
||||
|
||||
// Définir des seuils personnalisés
|
||||
let custom_thresholds = vec![
|
||||
ResourceThreshold::new(
|
||||
"CPU".to_string(),
|
||||
get_cpu_usage,
|
||||
90.0, // Seuil CPU plus élevé
|
||||
).with_critical_threshold(98.0),
|
||||
ResourceThreshold::new(
|
||||
"Memory".to_string(),
|
||||
get_memory_usage,
|
||||
75.0, // Seuil mémoire plus bas
|
||||
).with_critical_threshold(90.0),
|
||||
ResourceThreshold::new(
|
||||
"Disk".to_string(),
|
||||
get_disk_usage,
|
||||
85.0, // Seuil disque personnalisé
|
||||
).with_critical_threshold(95.0),
|
||||
];
|
||||
|
||||
// Créer le moniteur avec les seuils personnalisés
|
||||
let mut monitor = SystemMonitor::new_with_thresholds(custom_thresholds);
|
||||
|
||||
// Afficher la configuration
|
||||
println!("Configuration des seuils:");
|
||||
for threshold in &monitor.thresholds {
|
||||
let critical_info = threshold.critical_threshold
|
||||
.map(|c| format!(" (critique: {}%)", c))
|
||||
.unwrap_or_default();
|
||||
println!(" {} : {}%{}", threshold.name, threshold.threshold, critical_info);
|
||||
}
|
||||
println!();
|
||||
|
||||
// Vérifier les seuils
|
||||
let violations = monitor.check_thresholds();
|
||||
|
||||
if violations.is_empty() {
|
||||
println!("✅ Toutes les métriques sont dans les seuils acceptables");
|
||||
} else {
|
||||
println!("🚨 Violations avec seuils personnalisés:");
|
||||
for violation in violations {
|
||||
let severity_icon = match violation.severity {
|
||||
system_monitor::Severity::Warning => "⚠️",
|
||||
system_monitor::Severity::Critical => "🚨",
|
||||
};
|
||||
println!(" {} {} : {:.2}% (seuil: {:.2}%)",
|
||||
severity_icon,
|
||||
violation.metric_name,
|
||||
violation.current_value,
|
||||
violation.threshold);
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n📊 État détaillé:");
|
||||
let metrics = monitor.get_metrics();
|
||||
println!(" CPU: {:.1}%", metrics.cpu_usage);
|
||||
println!(" Mémoire: {:.1}%", metrics.memory_usage);
|
||||
println!(" Disque: {:.1}%", metrics.disk_usage);
|
||||
}
|
||||
52
examples/discord_notifications.rs
Normal file
52
examples/discord_notifications.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Exemple d'utilisation du client Discord
|
||||
use discord_client::DiscordNotifier;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("📢 Test du client Discord\n");
|
||||
|
||||
// Note: Remplacez par votre vraie URL de webhook pour tester
|
||||
let webhook_url = std::env::var("DISCORD_WEBHOOK")
|
||||
.unwrap_or_else(|_| "https://discord.com/api/webhooks/EXAMPLE/URL".to_string());
|
||||
|
||||
if webhook_url.contains("EXAMPLE") {
|
||||
println!("⚠️ Webhook d'exemple détecté - aucune notification ne sera envoyée");
|
||||
println!(" Définissez DISCORD_WEBHOOK pour tester réellement");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Créer le notifier
|
||||
let notifier = DiscordNotifier::new(
|
||||
webhook_url,
|
||||
"🤖 System Monitor Bot".to_string(),
|
||||
"https://cdn.shopify.com/s/files/1/0262/1423/6212/files/Lord_of_the_Rings_eye_of_Sauron_-_Ghtic.com_-_Blog.png?v=1579680018".to_string(),
|
||||
);
|
||||
|
||||
// Messages d'exemple
|
||||
let messages = vec![
|
||||
"✅ **Test de connexion**\n\nLe système de notification fonctionne correctement !",
|
||||
"🚨 **Alerte simulée**\n\n**CPU:** 95% - Utilisation critique\n**Mémoire:** 87% - Niveau élevé",
|
||||
"📊 **Rapport système**\n\n```\nCPU: 45%\nMémoire: 62%\nSwap: 12%\nDisque: 38%\n```\n\n*Surveillance automatique*",
|
||||
];
|
||||
|
||||
// Envoyer les messages de test
|
||||
for (i, message) in messages.iter().enumerate() {
|
||||
println!("Envoi du message {} de {}...", i + 1, messages.len());
|
||||
|
||||
match notifier.send_notification(message) {
|
||||
Ok(_) => println!("✅ Message {} envoyé avec succès", i + 1),
|
||||
Err(e) => {
|
||||
println!("❌ Erreur lors de l'envoi du message {} : {}", i + 1, e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Pause entre les messages pour éviter le rate limiting
|
||||
if i < messages.len() - 1 {
|
||||
println!(" Pause de 2 secondes...\n");
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n🎉 Tous les messages de test ont été envoyés !");
|
||||
Ok(())
|
||||
}
|
||||
9
node_notifier/Cargo.lock
generated
9
node_notifier/Cargo.lock
generated
@@ -644,7 +644,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sysinfo",
|
||||
"system_monitor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1093,6 +1093,13 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system_monitor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"sysinfo",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
|
||||
@@ -8,5 +8,22 @@ dotenvy = "0.15.7"
|
||||
reqwest = { version = "0.12.22", features = ["blocking", "json"] }
|
||||
serde = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
sysinfo = "0.35.2"
|
||||
discord_client = { path = "../discord_client" }
|
||||
system_monitor = { path = "../system_monitor" }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
[[example]]
|
||||
name = "basic_monitoring"
|
||||
path = "../examples/basic_monitoring.rs"
|
||||
|
||||
[[example]]
|
||||
name = "custom_thresholds"
|
||||
path = "../examples/custom_thresholds.rs"
|
||||
|
||||
[[example]]
|
||||
name = "discord_notifications"
|
||||
path = "../examples/discord_notifications.rs"
|
||||
|
||||
[[example]]
|
||||
name = "continuous_monitoring"
|
||||
path = "../examples/continuous_monitoring.rs"
|
||||
|
||||
226
node_notifier/README.md
Normal file
226
node_notifier/README.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# Node Notifier (Application principale)
|
||||
|
||||
Point d'entrée principal du système de surveillance avec notifications Discord.
|
||||
|
||||
## 📋 Description
|
||||
|
||||
Cette crate constitue l'application principale qui orchestre la surveillance des ressources système et l'envoi de notifications Discord. Elle utilise les crates `system_monitor` et `discord_client` pour fournir un système de monitoring complet.
|
||||
|
||||
## 🔧 Fonctionnalités
|
||||
|
||||
- Orchestration du monitoring système
|
||||
- Gestion des configurations via variables d'environnement
|
||||
- Coordination entre surveillance et notifications
|
||||
- Point d'entrée unique pour l'application
|
||||
|
||||
## 📦 Dépendances
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
dotenvy = "0.15.7" # Gestion des fichiers .env
|
||||
discord_client = { path = "../discord_client" } # Notifications Discord
|
||||
system_monitor = { path = "../system_monitor" } # Surveillance système
|
||||
```
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
Créez un fichier `.env` dans le répertoire racine du projet :
|
||||
|
||||
```env
|
||||
# Obligatoire : URL du webhook Discord
|
||||
DISCORD_WEBHOOK=https://discord.com/api/webhooks/your/webhook/url
|
||||
```
|
||||
|
||||
### Configuration Discord
|
||||
|
||||
1. Accédez à votre serveur Discord
|
||||
2. Allez dans les paramètres du canal où vous voulez recevoir les notifications
|
||||
3. Créez un webhook et copiez l'URL
|
||||
4. Ajoutez l'URL dans votre fichier `.env`
|
||||
|
||||
## 🚀 Utilisation
|
||||
|
||||
### Exécution simple
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
### Compilation et exécution
|
||||
|
||||
```bash
|
||||
# Mode debug
|
||||
cargo build
|
||||
./target/debug/node-notifier
|
||||
|
||||
# Mode release (optimisé)
|
||||
cargo build --release
|
||||
./target/release/node-notifier
|
||||
```
|
||||
|
||||
### Exécution en tâche de fond
|
||||
|
||||
```bash
|
||||
# Avec nohup
|
||||
nohup cargo run > monitoring.log 2>&1 &
|
||||
|
||||
# Avec systemd (recommandé pour la production)
|
||||
# Voir section "Déploiement" ci-dessous
|
||||
```
|
||||
|
||||
## 🔄 Flux d'exécution
|
||||
|
||||
1. **Initialisation** : Chargement de la configuration depuis `.env`
|
||||
2. **Création du notifier Discord** : Configuration du client avec webhook
|
||||
3. **Initialisation du monitor système** : Création avec seuils par défaut
|
||||
4. **Vérification des seuils** : Contrôle des ressources système
|
||||
5. **Envoi d'alertes** : Notifications pour les seuils dépassés
|
||||
6. **Rapport final** : Synthèse complète des ressources
|
||||
|
||||
## 🎛️ Personnalisation
|
||||
|
||||
### Modification des seuils
|
||||
|
||||
Pour personnaliser les seuils de surveillance, vous pouvez modifier le code pour utiliser `SystemMonitor::new_with_thresholds()` :
|
||||
|
||||
```rust
|
||||
use system_monitor::{SystemMonitor, ResourceThreshold, get_cpu_usage};
|
||||
|
||||
let custom_thresholds = vec![
|
||||
ResourceThreshold::new(
|
||||
"CPU".to_string(),
|
||||
get_cpu_usage,
|
||||
90.0, // Seuil à 90% au lieu de 80%
|
||||
"CPU critique !".to_string(),
|
||||
),
|
||||
// ... autres seuils
|
||||
];
|
||||
|
||||
let mut monitor = SystemMonitor::new_with_thresholds(custom_thresholds);
|
||||
```
|
||||
|
||||
### Avatar et nom du bot Discord
|
||||
|
||||
Modifiez ces valeurs dans `main.rs` :
|
||||
|
||||
```rust
|
||||
let notifier = DiscordNotifier::new(
|
||||
webhook_url,
|
||||
"Mon Monitor".to_string(), // Nom personnalisé
|
||||
"https://mon-site.com/avatar.png".to_string(), // Avatar personnalisé
|
||||
);
|
||||
```
|
||||
|
||||
## 🚀 Déploiement
|
||||
|
||||
### Service systemd (recommandé)
|
||||
|
||||
1. Créez un fichier de service `/etc/systemd/system/node-notifier.service` :
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Node Notifier System Monitor
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=your-user
|
||||
WorkingDirectory=/path/to/node-notifier/node_notifier
|
||||
ExecStart=/path/to/node-notifier/target/release/node-notifier
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=DISCORD_WEBHOOK=your-webhook-url
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
2. Activez et démarrez le service :
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable node-notifier
|
||||
sudo systemctl start node-notifier
|
||||
```
|
||||
|
||||
### Cron (alternative)
|
||||
|
||||
Pour une exécution périodique plutôt que continue :
|
||||
|
||||
```bash
|
||||
# Exécution toutes les 5 minutes
|
||||
*/5 * * * * cd /path/to/node-notifier/node_notifier && /path/to/cargo run
|
||||
```
|
||||
|
||||
## 📊 Logs et debugging
|
||||
|
||||
### Activation des logs
|
||||
|
||||
Ajoutez des logs pour le debugging :
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug cargo run
|
||||
```
|
||||
|
||||
### Redirection des logs
|
||||
|
||||
```bash
|
||||
# Logs dans un fichier
|
||||
cargo run > monitoring.log 2>&1
|
||||
|
||||
# Logs avec rotation
|
||||
cargo run 2>&1 | tee -a monitoring.log
|
||||
```
|
||||
|
||||
## 🔧 Dépannage
|
||||
|
||||
### Erreurs courantes
|
||||
|
||||
**"DISCORD_WEBHOOK environment variable not set"**
|
||||
```bash
|
||||
# Vérifiez votre fichier .env
|
||||
cat .env
|
||||
# Ou définissez directement
|
||||
export DISCORD_WEBHOOK="https://discord.com/api/webhooks/..."
|
||||
```
|
||||
|
||||
**"Failed to send notification"**
|
||||
- Vérifiez la connectivité réseau
|
||||
- Validez l'URL du webhook Discord
|
||||
- Consultez les logs Discord pour les erreurs
|
||||
|
||||
**Permissions système insuffisantes**
|
||||
- Certaines métriques peuvent nécessiter des privilèges élevés
|
||||
- Exécutez avec `sudo` si nécessaire
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
```bash
|
||||
# Tests unitaires
|
||||
cargo test
|
||||
|
||||
# Tests avec logs
|
||||
RUST_LOG=debug cargo test
|
||||
|
||||
# Tests spécifiques
|
||||
cargo test test_name
|
||||
```
|
||||
|
||||
## 📈 Métriques
|
||||
|
||||
L'application surveille par défaut :
|
||||
|
||||
- **CPU** : Utilisation globale en pourcentage
|
||||
- **Mémoire** : RAM utilisée vs totale
|
||||
- **Swap** : Espace swap utilisé vs total
|
||||
- **Disque** : Espace utilisé sur `/` et `/home`
|
||||
|
||||
## 🔮 Extensions possibles
|
||||
|
||||
- Configuration via fichier TOML/YAML
|
||||
- Support de multiples webhooks
|
||||
- Planification d'exécutions
|
||||
- Interface CLI avec arguments
|
||||
- Mode daemon avec rechargement de config
|
||||
@@ -1,100 +1,54 @@
|
||||
use discord_client::DiscordNotifier;
|
||||
use dotenvy::dotenv;
|
||||
use sysinfo::{Disks, System};
|
||||
use system_monitor::{SystemMonitor, ThresholdViolation, Severity};
|
||||
|
||||
struct ResourceThreshold {
|
||||
name: String,
|
||||
get_usage_fn: fn(&System, &Disks) -> f32,
|
||||
threshold: f32,
|
||||
value: f32,
|
||||
messagef: String,
|
||||
}
|
||||
struct AlertFormatter;
|
||||
|
||||
impl ResourceThreshold {
|
||||
fn check(&mut self, sys: &System, disks: &Disks) -> Option<String> {
|
||||
self.value = (self.get_usage_fn)(sys, disks);
|
||||
if self.value > self.threshold {
|
||||
Some(
|
||||
format!(
|
||||
"{}: {:.2}% - {}",
|
||||
self.name, self.value, self.messagef
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
impl AlertFormatter {
|
||||
fn format_violation(&self, violation: &ThresholdViolation) -> String {
|
||||
let emoji = match violation.severity {
|
||||
Severity::Warning => "⚠️",
|
||||
Severity::Critical => "🚨",
|
||||
};
|
||||
|
||||
let level = match violation.severity {
|
||||
Severity::Warning => "ALERTE",
|
||||
Severity::Critical => "CRITIQUE",
|
||||
};
|
||||
|
||||
format!(
|
||||
"{} **{}** - {}\n\n**Valeur actuelle:** {:.2}%\n**Seuil:** {:.2}%",
|
||||
emoji,
|
||||
level,
|
||||
self.get_message_for_metric(&violation.metric_name, &violation.severity),
|
||||
violation.current_value,
|
||||
violation.threshold
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cpu_usage(sys: &System, _: &Disks) -> f32 {
|
||||
sys.global_cpu_usage()
|
||||
}
|
||||
|
||||
fn get_memory_usage(sys: &System, _: &Disks) -> f32 {
|
||||
let total_memory = sys.total_memory() as f32;
|
||||
let used_memory = sys.used_memory() as f32;
|
||||
|
||||
used_memory / total_memory * 100.0
|
||||
}
|
||||
|
||||
fn get_swap_usage(sys: &System, _: &Disks) -> f32 {
|
||||
let total_swap = sys.total_swap() as f32;
|
||||
let used_swap = sys.used_swap() as f32;
|
||||
|
||||
used_swap / total_swap * 100.0
|
||||
}
|
||||
|
||||
fn get_disk_usage(_: &System, disks: &Disks) -> f32 {
|
||||
let mut total_used_space = 0.0;
|
||||
let mut total_space = 0.0;
|
||||
|
||||
for disk in disks {
|
||||
if let Some(mount_point) = disk.mount_point().to_str() {
|
||||
if mount_point == "/" || mount_point == "/home" {
|
||||
total_used_space += disk.total_space() as f32 - disk.available_space() as f32;
|
||||
total_space += disk.total_space() as f32;
|
||||
}
|
||||
fn get_message_for_metric(&self, metric: &str, severity: &Severity) -> String {
|
||||
match (metric, severity) {
|
||||
("CPU", Severity::Warning) => "Utilisation CPU élevée détectée".to_string(),
|
||||
("CPU", Severity::Critical) => "CPU en surchauffe - Intervention requise !".to_string(),
|
||||
("Memory", Severity::Warning) => "Utilisation mémoire élevée".to_string(),
|
||||
("Memory", Severity::Critical) => "Mémoire saturée - Risque de crash !".to_string(),
|
||||
("Swap", Severity::Warning) => "Utilisation swap élevée".to_string(),
|
||||
("Swap", Severity::Critical) => "Swap saturé - Performance dégradée !".to_string(),
|
||||
("Disk", Severity::Warning) => "Espace disque faible".to_string(),
|
||||
("Disk", Severity::Critical) => "Disque presque plein - Nettoyage urgent !".to_string(),
|
||||
(metric, _) => format!("Problème détecté sur {}", metric),
|
||||
}
|
||||
}
|
||||
|
||||
if total_space == 0.0 {
|
||||
return 0.0; // Avoid division by zero
|
||||
fn format_summary(&self, metrics: &system_monitor::SystemMetrics) -> String {
|
||||
format!(
|
||||
"📊 **Rapport Système**\n\n```\nCPU: {:.1}%\nMémoire: {:.1}%\nSwap: {:.1}%\nDisque: {:.1}%\n```\n\n*Surveillance automatique*",
|
||||
metrics.cpu_usage,
|
||||
metrics.memory_usage,
|
||||
metrics.swap_usage,
|
||||
metrics.disk_usage
|
||||
)
|
||||
}
|
||||
|
||||
(total_used_space / total_space) * 100.0
|
||||
}
|
||||
|
||||
fn get_resource_thresholds() -> Vec<ResourceThreshold> {
|
||||
vec![
|
||||
ResourceThreshold {
|
||||
name: "CPU".to_string(),
|
||||
get_usage_fn: get_cpu_usage,
|
||||
threshold: 80.0,
|
||||
value: 0.0,
|
||||
messagef: "High CPU usage detected".to_string(),
|
||||
},
|
||||
ResourceThreshold {
|
||||
name: "Memory".to_string(),
|
||||
get_usage_fn: get_memory_usage,
|
||||
threshold: 80.0,
|
||||
value: 0.0,
|
||||
messagef: "High memory usage detected".to_string(),
|
||||
},
|
||||
ResourceThreshold {
|
||||
name: "Swap".to_string(),
|
||||
get_usage_fn: get_swap_usage,
|
||||
threshold: 80.0,
|
||||
value: 0.0,
|
||||
messagef: "High swap usage detected".to_string(),
|
||||
},
|
||||
ResourceThreshold {
|
||||
name: "Disk".to_string(),
|
||||
get_usage_fn: get_disk_usage,
|
||||
threshold: 80.0,
|
||||
value: 0.0,
|
||||
messagef: "High disk usage detected".to_string(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -106,29 +60,20 @@ fn main() {
|
||||
"https://cdn.shopify.com/s/files/1/0262/1423/6212/files/Lord_of_the_Rings_eye_of_Sauron_-_Ghtic.com_-_Blog.png?v=1579680018".to_string(),
|
||||
);
|
||||
|
||||
let mut sys = System::new_all();
|
||||
let mut disks: Disks = Disks::new_with_refreshed_list();
|
||||
sys.refresh_all();
|
||||
disks.refresh(true);
|
||||
let mut monitor = SystemMonitor::new();
|
||||
let formatter = AlertFormatter;
|
||||
|
||||
let mut thresholds = get_resource_thresholds();
|
||||
|
||||
for threshold in &mut thresholds {
|
||||
if let Some(message) = threshold.check(&sys, &disks) {
|
||||
notifier.send_notification(&message).expect("Failed to send notification");
|
||||
}
|
||||
// Check for threshold violations and send alerts
|
||||
let violations = monitor.check_thresholds();
|
||||
for violation in violations {
|
||||
let message = formatter.format_violation(&violation);
|
||||
notifier.send_notification(&message).expect("Failed to send notification");
|
||||
}
|
||||
|
||||
// Send a final notification with all system information
|
||||
let mut final_message = String::new();
|
||||
for threshold in thresholds {
|
||||
final_message.push_str(&format!(
|
||||
"{} usage: {:.2}%\n",
|
||||
threshold.name, threshold.value
|
||||
));
|
||||
}
|
||||
|
||||
let metrics = monitor.get_metrics();
|
||||
let summary = formatter.format_summary(&metrics);
|
||||
notifier
|
||||
.send_notification(&final_message)
|
||||
.send_notification(&summary)
|
||||
.expect("Failed to send final notification");
|
||||
}
|
||||
|
||||
238
system_monitor/Cargo.lock
generated
Normal file
238
system_monitor/Cargo.lock
generated
Normal file
@@ -0,0 +1,238 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-kit"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.35.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-kit",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system_monitor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"sysinfo",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
7
system_monitor/Cargo.toml
Normal file
7
system_monitor/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "system_monitor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sysinfo = "0.35.2"
|
||||
329
system_monitor/README.md
Normal file
329
system_monitor/README.md
Normal file
@@ -0,0 +1,329 @@
|
||||
# System Monitor
|
||||
|
||||
Une crate Rust pour surveiller les ressources système (CPU, mémoire, swap, disque) avec des seuils configurables.
|
||||
|
||||
## 📋 Description
|
||||
|
||||
Cette crate fournit une API complète pour surveiller les ressources système et détecter les dépassements de seuils. Elle encapsule la complexité de `sysinfo` dans une interface simple et extensible, parfaite pour des systèmes d'alerte et de monitoring.
|
||||
|
||||
## 🚀 Fonctionnalités
|
||||
|
||||
- ✅ Surveillance du CPU, mémoire, swap et disque
|
||||
- ✅ Seuils configurables pour déclencher des alertes
|
||||
- ✅ API simple et extensible
|
||||
- ✅ Support pour des fonctions de surveillance personnalisées
|
||||
- ✅ Gestion automatique des cas d'erreur (division par zéro, etc.)
|
||||
- ✅ Structure orientée objet avec `SystemMonitor`
|
||||
- ✅ Surveillance spécifique des points de montage (`/` et `/home`)
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
Ajoutez cette dépendance à votre `Cargo.toml` :
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
system_monitor = { path = "path/to/system_monitor" }
|
||||
```
|
||||
|
||||
## 🔧 Utilisation
|
||||
|
||||
### Utilisation basique
|
||||
|
||||
```rust
|
||||
use system_monitor::SystemMonitor;
|
||||
|
||||
// Utilisation avec les seuils par défaut
|
||||
let mut monitor = SystemMonitor::new();
|
||||
|
||||
// Vérifier les seuils et obtenir les alertes
|
||||
let alerts = monitor.check_thresholds();
|
||||
for alert in alerts {
|
||||
println!("🚨 Alerte: {}", alert);
|
||||
}
|
||||
|
||||
// Obtenir un résumé du système
|
||||
let summary = monitor.get_system_summary();
|
||||
println!("📊 État système:\n{}", summary);
|
||||
```
|
||||
|
||||
### Seuils personnalisés
|
||||
|
||||
```rust
|
||||
use system_monitor::{SystemMonitor, ResourceThreshold, get_cpu_usage, get_memory_usage};
|
||||
|
||||
// Créer des seuils personnalisés
|
||||
let custom_thresholds = vec![
|
||||
ResourceThreshold::new(
|
||||
"CPU".to_string(),
|
||||
get_cpu_usage,
|
||||
90.0, // Seuil à 90% au lieu de 80%
|
||||
"CPU critique ! Intervention requise".to_string(),
|
||||
),
|
||||
ResourceThreshold::new(
|
||||
"Memory".to_string(),
|
||||
get_memory_usage,
|
||||
85.0, // Seuil mémoire à 85%
|
||||
"Mémoire élevée détectée".to_string(),
|
||||
),
|
||||
];
|
||||
|
||||
let mut monitor = SystemMonitor::new_with_thresholds(custom_thresholds);
|
||||
```
|
||||
|
||||
### Surveillance continue
|
||||
|
||||
```rust
|
||||
use system_monitor::SystemMonitor;
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
|
||||
let mut monitor = SystemMonitor::new();
|
||||
|
||||
loop {
|
||||
// Actualiser les données système
|
||||
monitor.refresh();
|
||||
|
||||
// Vérifier les seuils
|
||||
let alerts = monitor.check_thresholds();
|
||||
|
||||
if !alerts.is_empty() {
|
||||
for alert in alerts {
|
||||
println!("🚨 {}", alert);
|
||||
// Ici vous pourriez envoyer une notification
|
||||
}
|
||||
} else {
|
||||
println!("✅ Système OK");
|
||||
}
|
||||
|
||||
// Attendre 30 secondes avant la prochaine vérification
|
||||
thread::sleep(Duration::from_secs(30));
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Métriques surveillées
|
||||
|
||||
### Seuils par défaut
|
||||
|
||||
- **CPU**: 80% - Utilisation globale du processeur
|
||||
- **Mémoire**: 80% - RAM utilisée vs totale
|
||||
- **Swap**: 80% - Espace swap utilisé vs total
|
||||
- **Disque**: 80% - Espace utilisé sur les points de montage `/` et `/home`
|
||||
|
||||
### Détails des métriques
|
||||
|
||||
#### CPU
|
||||
- Mesure l'utilisation globale du CPU en pourcentage
|
||||
- Basé sur la moyenne de tous les cœurs
|
||||
- Actualisé à chaque appel de `refresh()`
|
||||
|
||||
#### Mémoire
|
||||
- Calcule le pourcentage de RAM utilisée
|
||||
- Inclut les buffers et cache système
|
||||
- Formule : `(mémoire_utilisée / mémoire_totale) * 100`
|
||||
|
||||
#### Swap
|
||||
- Surveille l'utilisation de l'espace swap
|
||||
- Important pour détecter la saturation mémoire
|
||||
- Retourne 0% si aucun swap n'est configuré
|
||||
|
||||
#### Disque
|
||||
- Surveille spécifiquement `/` (racine) et `/home`
|
||||
- Calcule l'espace utilisé combiné
|
||||
- Formule : `((total - disponible) / total) * 100`
|
||||
|
||||
## 🎛️ API Reference
|
||||
|
||||
### `SystemMonitor`
|
||||
|
||||
#### Constructeurs
|
||||
|
||||
```rust
|
||||
// Avec seuils par défaut
|
||||
pub fn new() -> Self
|
||||
|
||||
// Avec seuils personnalisés
|
||||
pub fn new_with_thresholds(thresholds: Vec<ResourceThreshold>) -> Self
|
||||
```
|
||||
|
||||
#### Méthodes
|
||||
|
||||
```rust
|
||||
// Actualise les données système
|
||||
pub fn refresh(&mut self)
|
||||
|
||||
// Vérifie les seuils et retourne les alertes
|
||||
pub fn check_thresholds(&mut self) -> Vec<String>
|
||||
|
||||
// Retourne un résumé des ressources système
|
||||
pub fn get_system_summary(&self) -> String
|
||||
```
|
||||
|
||||
### `ResourceThreshold`
|
||||
|
||||
Structure pour définir des seuils personnalisés :
|
||||
|
||||
```rust
|
||||
pub struct ResourceThreshold {
|
||||
pub name: String, // Nom de la ressource
|
||||
pub get_usage_fn: fn(&System, &Disks) -> f32, // Fonction de mesure
|
||||
pub threshold: f32, // Seuil (en %)
|
||||
pub value: f32, // Valeur actuelle
|
||||
pub message: String, // Message d'alerte
|
||||
}
|
||||
|
||||
impl ResourceThreshold {
|
||||
pub fn new(name: String, get_usage_fn: fn(&System, &Disks) -> f32, threshold: f32, message: String) -> Self
|
||||
pub fn check(&mut self, sys: &System, disks: &Disks) -> Option<String>
|
||||
}
|
||||
```
|
||||
|
||||
### Fonctions de surveillance
|
||||
|
||||
```rust
|
||||
// Utilisation du CPU (%)
|
||||
pub fn get_cpu_usage(sys: &System, _: &Disks) -> f32
|
||||
|
||||
// Utilisation de la mémoire (%)
|
||||
pub fn get_memory_usage(sys: &System, _: &Disks) -> f32
|
||||
|
||||
// Utilisation du swap (%)
|
||||
pub fn get_swap_usage(sys: &System, _: &Disks) -> f32
|
||||
|
||||
// Utilisation du disque (%)
|
||||
pub fn get_disk_usage(_: &System, disks: &Disks) -> f32
|
||||
|
||||
// Seuils par défaut
|
||||
pub fn get_default_resource_thresholds() -> Vec<ResourceThreshold>
|
||||
```
|
||||
|
||||
## 🔧 Surveillance personnalisée
|
||||
|
||||
### Créer une fonction de surveillance custom
|
||||
|
||||
```rust
|
||||
use system_monitor::{ResourceThreshold, SystemMonitor};
|
||||
use sysinfo::{System, Disks};
|
||||
|
||||
// Fonction personnalisée pour surveiller la température CPU (exemple)
|
||||
fn get_cpu_temperature(sys: &System, _: &Disks) -> f32 {
|
||||
// Implémentation exemple (nécessite une crate additionnelle)
|
||||
// Retourne la température en degrés Celsius
|
||||
65.0 // Valeur d'exemple
|
||||
}
|
||||
|
||||
// Utilisation
|
||||
let custom_threshold = ResourceThreshold::new(
|
||||
"CPU Temperature".to_string(),
|
||||
get_cpu_temperature,
|
||||
80.0, // Alerte si > 80°C
|
||||
"Température CPU élevée !".to_string(),
|
||||
);
|
||||
|
||||
let monitor = SystemMonitor::new_with_thresholds(vec![custom_threshold]);
|
||||
```
|
||||
|
||||
### Surveiller des processus spécifiques
|
||||
|
||||
```rust
|
||||
use sysinfo::{System, ProcessExt, Pid};
|
||||
|
||||
fn get_process_memory_usage(sys: &System, _: &Disks) -> f32 {
|
||||
if let Some(process) = sys.process(Pid::from(1234)) { // PID du processus
|
||||
let process_memory = process.memory() as f32;
|
||||
let total_memory = sys.total_memory() as f32;
|
||||
(process_memory / total_memory) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
```bash
|
||||
# Exécuter tous les tests
|
||||
cargo test
|
||||
|
||||
# Tests avec logs détaillés
|
||||
RUST_LOG=debug cargo test
|
||||
|
||||
# Tests de performance
|
||||
cargo test --release
|
||||
```
|
||||
|
||||
### Exemple de test
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cpu_usage() {
|
||||
let sys = System::new_all();
|
||||
let disks = Disks::new();
|
||||
|
||||
let usage = get_cpu_usage(&sys, &disks);
|
||||
assert!(usage >= 0.0 && usage <= 100.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_threshold_check() {
|
||||
let mut threshold = ResourceThreshold::new(
|
||||
"Test".to_string(),
|
||||
|_, _| 85.0, // Fonction qui retourne toujours 85%
|
||||
80.0, // Seuil à 80%
|
||||
"Test alert".to_string(),
|
||||
);
|
||||
|
||||
let sys = System::new();
|
||||
let disks = Disks::new();
|
||||
|
||||
// Devrait déclencher une alerte
|
||||
assert!(threshold.check(&sys, &disks).is_some());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Dépannage
|
||||
|
||||
### Problèmes courants
|
||||
|
||||
**Valeurs de métrique à 0**
|
||||
- Sur certains systèmes, l'initialisation peut prendre du temps
|
||||
- Appelez `refresh()` et attendez quelques secondes
|
||||
|
||||
**Permissions insuffisantes**
|
||||
- Certaines métriques peuvent nécessiter des privilèges élevés
|
||||
- Exécutez avec `sudo` si nécessaire
|
||||
|
||||
**Pas de disque détecté**
|
||||
- Vérifiez que `/` et `/home` sont montés
|
||||
- Adaptez la fonction `get_disk_usage()` pour vos points de montage
|
||||
|
||||
### Debug
|
||||
|
||||
```rust
|
||||
// Activer les logs sysinfo
|
||||
RUST_LOG=sysinfo=debug cargo run
|
||||
|
||||
// Inspecter les valeurs
|
||||
let monitor = SystemMonitor::new();
|
||||
println!("Disques détectés:");
|
||||
for disk in &monitor.disks {
|
||||
println!("- {}: {} bytes", disk.mount_point().display(), disk.total_space());
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 Extensions possibles
|
||||
|
||||
- Surveillance réseau (bande passante, latence)
|
||||
- Surveillance de la température
|
||||
- Métriques de processus spécifiques
|
||||
- Historique des valeurs
|
||||
- Export vers des formats standards (Prometheus, etc.)
|
||||
- Surveillance de services système
|
||||
|
||||
## 📄 Licence
|
||||
|
||||
Cette crate est sous licence MIT.
|
||||
211
system_monitor/src/lib.rs
Normal file
211
system_monitor/src/lib.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use sysinfo::{Disks, System};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SystemMetrics {
|
||||
pub cpu_usage: f32,
|
||||
pub memory_usage: f32,
|
||||
pub swap_usage: f32,
|
||||
pub disk_usage: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThresholdViolation {
|
||||
pub metric_name: String,
|
||||
pub current_value: f32,
|
||||
pub threshold: f32,
|
||||
pub severity: Severity,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Severity {
|
||||
Warning,
|
||||
Critical,
|
||||
}
|
||||
|
||||
pub struct ResourceThreshold {
|
||||
pub name: String,
|
||||
pub get_usage_fn: fn(&System, &Disks) -> f32,
|
||||
pub threshold: f32,
|
||||
pub critical_threshold: Option<f32>, // Nouveau: seuil critique
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
impl ResourceThreshold {
|
||||
pub fn new(name: String, get_usage_fn: fn(&System, &Disks) -> f32, threshold: f32) -> Self {
|
||||
Self {
|
||||
name,
|
||||
get_usage_fn,
|
||||
threshold,
|
||||
critical_threshold: None,
|
||||
value: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_critical_threshold(mut self, critical_threshold: f32) -> Self {
|
||||
self.critical_threshold = Some(critical_threshold);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn check(&mut self, sys: &System, disks: &Disks) -> Option<ThresholdViolation> {
|
||||
self.value = (self.get_usage_fn)(sys, disks);
|
||||
|
||||
if let Some(critical) = self.critical_threshold {
|
||||
if self.value > critical {
|
||||
return Some(ThresholdViolation {
|
||||
metric_name: self.name.clone(),
|
||||
current_value: self.value,
|
||||
threshold: critical,
|
||||
severity: Severity::Critical,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if self.value > self.threshold {
|
||||
Some(ThresholdViolation {
|
||||
metric_name: self.name.clone(),
|
||||
current_value: self.value,
|
||||
threshold: self.threshold,
|
||||
severity: Severity::Warning,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cpu_usage(sys: &System, _: &Disks) -> f32 {
|
||||
sys.global_cpu_usage()
|
||||
}
|
||||
|
||||
pub fn get_memory_usage(sys: &System, _: &Disks) -> f32 {
|
||||
let total_memory = sys.total_memory() as f32;
|
||||
let used_memory = sys.used_memory() as f32;
|
||||
|
||||
if total_memory == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
used_memory / total_memory * 100.0
|
||||
}
|
||||
|
||||
pub fn get_swap_usage(sys: &System, _: &Disks) -> f32 {
|
||||
let total_swap = sys.total_swap() as f32;
|
||||
let used_swap = sys.used_swap() as f32;
|
||||
|
||||
if total_swap == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
used_swap / total_swap * 100.0
|
||||
}
|
||||
|
||||
pub fn get_disk_usage(_: &System, disks: &Disks) -> f32 {
|
||||
let mut total_used_space = 0.0;
|
||||
let mut total_space = 0.0;
|
||||
|
||||
for disk in disks {
|
||||
if let Some(mount_point) = disk.mount_point().to_str() {
|
||||
if mount_point == "/" || mount_point == "/home" {
|
||||
total_used_space += disk.total_space() as f32 - disk.available_space() as f32;
|
||||
total_space += disk.total_space() as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if total_space == 0.0 {
|
||||
return 0.0; // Avoid division by zero
|
||||
}
|
||||
|
||||
(total_used_space / total_space) * 100.0
|
||||
}
|
||||
|
||||
pub fn get_default_resource_thresholds() -> Vec<ResourceThreshold> {
|
||||
vec![
|
||||
ResourceThreshold::new(
|
||||
"CPU".to_string(),
|
||||
get_cpu_usage,
|
||||
80.0,
|
||||
).with_critical_threshold(95.0),
|
||||
ResourceThreshold::new(
|
||||
"Memory".to_string(),
|
||||
get_memory_usage,
|
||||
80.0,
|
||||
).with_critical_threshold(95.0),
|
||||
ResourceThreshold::new(
|
||||
"Swap".to_string(),
|
||||
get_swap_usage,
|
||||
80.0,
|
||||
).with_critical_threshold(95.0),
|
||||
ResourceThreshold::new(
|
||||
"Disk".to_string(),
|
||||
get_disk_usage,
|
||||
80.0,
|
||||
).with_critical_threshold(90.0),
|
||||
]
|
||||
}
|
||||
|
||||
pub struct SystemMonitor {
|
||||
pub system: System,
|
||||
pub disks: Disks,
|
||||
pub thresholds: Vec<ResourceThreshold>,
|
||||
}
|
||||
|
||||
impl SystemMonitor {
|
||||
pub fn new() -> Self {
|
||||
let mut system = System::new_all();
|
||||
let mut disks = Disks::new_with_refreshed_list();
|
||||
system.refresh_all();
|
||||
disks.refresh(true);
|
||||
|
||||
Self {
|
||||
system,
|
||||
disks,
|
||||
thresholds: get_default_resource_thresholds(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_thresholds(thresholds: Vec<ResourceThreshold>) -> Self {
|
||||
let mut system = System::new_all();
|
||||
let mut disks = Disks::new_with_refreshed_list();
|
||||
system.refresh_all();
|
||||
disks.refresh(true);
|
||||
|
||||
Self {
|
||||
system,
|
||||
disks,
|
||||
thresholds,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self) {
|
||||
self.system.refresh_all();
|
||||
self.disks.refresh(true);
|
||||
}
|
||||
|
||||
pub fn check_thresholds(&mut self) -> Vec<ThresholdViolation> {
|
||||
let mut violations = Vec::new();
|
||||
|
||||
for threshold in &mut self.thresholds {
|
||||
if let Some(violation) = threshold.check(&self.system, &self.disks) {
|
||||
violations.push(violation);
|
||||
}
|
||||
}
|
||||
|
||||
violations
|
||||
}
|
||||
|
||||
pub fn get_metrics(&self) -> SystemMetrics {
|
||||
SystemMetrics {
|
||||
cpu_usage: get_cpu_usage(&self.system, &self.disks),
|
||||
memory_usage: get_memory_usage(&self.system, &self.disks),
|
||||
swap_usage: get_swap_usage(&self.system, &self.disks),
|
||||
disk_usage: get_disk_usage(&self.system, &self.disks),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SystemMonitor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user