1 Commits
main ... dev

Author SHA1 Message Date
357790ac34 wip 2025-07-30 22:13:58 +02:00
21 changed files with 4257 additions and 110 deletions

36
.gitignore vendored
View File

@@ -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

File diff suppressed because it is too large Load Diff

28
Cargo.toml Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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);
}

View 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);
}
}

View 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);
}

View 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(())
}

View File

@@ -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"

View File

@@ -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
View 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

View File

@@ -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
View 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",
]

View 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
View 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
View 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()
}
}