Rust a été classé premier dans l'enquête Stack Overflow sur les développeurs comme « langage de programmation le plus apprécié » pendant plusieurs années consécutives, avec ses avantages principaux :
- Abstractions à coût zéro : Les fonctionnalités de langage de haut niveau n'entraînent aucune perte de performance à l'exécution
- Sécurité mémoire : Détection au moment de la compilation des courses de données et des fuites de mémoire
- Compilation multiplateforme : Un seul code source compile en binaires natifs pour Linux, macOS, Windows
- Excellent écosystème CLI : Les crates comme
clap,tokio,serderendent le développement CLI deux fois plus efficace avec la moitié de l'effort
Cet article vous guide de zéro pour construire un outil de gestion de serveurs Linux nommé sysmgr, couvrant les modules fonctionnels suivants :
- Collecte d'informations système (CPU, mémoire, disque, réseau)
- Gestion des processus (listage, terminaison, surveillance)
- Analyse de journaux (filtrage, statistiques, alertes)
- Opérations d'automatisation (tâches planifiées, opérations par lots)
1. Initialisation du projet et configuration des dépendances
1.1 Création du projet
cargo new sysmgr
cd sysmgr
1.2 Configuration de Cargo.toml
[package]
name = "sysmgr"
version = "0.1.0"
edition = "2021"
[dependencies]
# Framework CLI
clap = { version = "4.5", features = ["derive"] }
# Runtime asynchrone
tokio = { version = "1.40", features = ["full"] }
# Informations système
sysinfo = "0.31"
# Sérialisation
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# UI terminal
crossterm = "0.28"
# Expressions régulières
regex = "1.10"
# Gestion du temps
chrono = "0.4"
# Journalisation
tracing = "0.1"
tracing-subscriber = "0.3"
# Gestion des erreurs
anyhow = "1.0"
thiserror = "1.0"
1.3 Structure du projet
sysmgr/
├── Cargo.toml
├── src/
│ ├── main.rs # Point d'entrée et routage des commandes
│ ├── cli.rs # Définitions des arguments CLI
│ ├── commands/
│ │ ├── mod.rs
│ │ ├── info.rs # Informations système
│ │ ├── process.rs # Gestion des processus
│ │ ├── log.rs # Analyse de journaux
│ │ └── cron.rs # Tâches d'automatisation
│ └── utils/
│ ├── mod.rs
│ └── output.rs # Formatage de sortie
2. Construction du framework CLI
2.1 Définition de la structure des commandes
Utilisation des macros derive de clap pour définir les commandes :
// src/cli.rs
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "sysmgr")]
#[command(about = "Outil de gestion de serveurs Linux", version)]
pub struct Cli {
/// Activer la sortie détaillée
#[arg(short, long, global = true)]
pub verbose: bool,
/// Format de sortie
#[arg(short, long, default_value = "table", global = true)]
pub format: OutputFormat,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Afficher les informations système
Info {
/// Spécifier le type d'information
#[arg(short, long)]
section: Option<InfoSection>,
},
/// Gestion des processus
Proc {
#[command(subcommand)]
action: ProcAction,
},
/// Analyse de journaux
Log {
/// Chemin du fichier journal
#[arg(short, long)]
file: String,
/// Mot-clé de filtrage
#[arg(short, long)]
keyword: Option<String>,
/// Calculer les statistiques d'erreurs
#[arg(short, long)]
stats: bool,
},
/// Tâches d'automatisation
Cron {
#[command(subcommand)]
action: CronAction,
},
}
#[derive(Clone, Debug, clap::ValueEnum)]
pub enum InfoSection {
Cpu,
Memory,
Disk,
Network,
All,
}
#[derive(Clone, Debug, clap::ValueEnum)]
pub enum OutputFormat {
Table,
Json,
Csv,
}
#[derive(Subcommand)]
pub enum ProcAction {
/// Lister les processus
List {
/// Filtrer par nom
#[arg(short, long)]
name: Option<String>,
/// Trier par utilisation CPU
#[arg(long)]
sort_by_cpu: bool,
},
/// Terminer le processus
Kill {
/// ID du processus
pid: u32,
/// Terminaison forcée
#[arg(short, long)]
force: bool,
},
/// Surveiller le processus
Watch {
/// Nom du processus
name: String,
/// Intervalle de rafraîchissement (secondes)
#[arg(short, long, default_value = "2")]
interval: u64,
},
}
#[derive(Subcommand)]
pub enum CronAction {
/// Lister les tâches planifiées
List,
/// Ajouter une tâche planifiée
Add {
/// Expression Cron
schedule: String,
/// Commande à exécuter
command: String,
},
/// Supprimer une tâche planifiée
Remove {
/// ID de la tâche
id: u32,
},
}
2.2 Point d'entrée principal
// src/main.rs
mod cli;
mod commands;
mod utils;
use anyhow::Result;
use clap::Parser;
use cli::{Cli, Commands};
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
// Initialiser la journalisation
if cli.verbose {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
}
match cli.command {
Commands::Info { section } => {
commands::info::run(section, &cli.format).await?;
}
Commands::Proc { action } => {
commands::process::run(action, &cli.format).await?;
}
Commands::Log { file, keyword, stats } => {
commands::log::run(&file, keyword.as_deref(), stats, &cli.format).await?;
}
Commands::Cron { action } => {
commands::cron::run(action, &cli.format).await?;
}
}
Ok(())
}
3. Module d'informations système
3.1 Implémentation de la collecte d'informations système
// src/commands/info.rs
use crate::cli::{InfoSection, OutputFormat};
use anyhow::Result;
use sysinfo::System;
use serde::Serialize;
#[derive(Serialize)]
struct SystemInfo {
hostname: String,
os: String,
kernel: String,
uptime: u64,
cpu_count: usize,
cpu_usage: f32,
total_memory: u64,
used_memory: u64,
total_swap: u64,
used_swap: u64,
}
pub async fn run(section: Option<InfoSection>, format: &OutputFormat) -> Result<()> {
let mut sys = System::new_all();
sys.refresh_all();
let section = section.unwrap_or(InfoSection::All);
match section {
InfoSection::Cpu => print_cpu_info(&sys, format),
InfoSection::Memory => print_memory_info(&sys, format),
InfoSection::Disk => print_disk_info(format),
InfoSection::Network => print_network_info(format),
InfoSection::All => {
print_cpu_info(&sys, format);
println!();
print_memory_info(&sys, format);
println!();
print_disk_info(format);
println!();
print_network_info(format);
}
}
Ok(())
}
fn print_cpu_info(sys: &System, format: &OutputFormat) {
let cpu_count = sys.cpus().len();
let cpu_usage: f32 = sys.cpus().iter().map(|c| c.cpu_usage()).sum::<f32>() / cpu_count as f32;
match format {
OutputFormat::Json => {
let data = serde_json::json!({
"cpu_count": cpu_count,
"cpu_usage_percent": cpu_usage,
"cpu_brand": sys.cpus().first().map(|c| c.brand()).unwrap_or("Unknown"),
});
println!("{}", serde_json::to_string_pretty(&data).unwrap());
}
_ => {
println!("=== Informations CPU ===");
println!("Nombre de cœurs : {}", cpu_count);
println!("Utilisation : {:.1}%", cpu_usage);
if let Some(cpu) = sys.cpus().first() {
println!("Modèle : {}", cpu.brand());
}
}
}
}
fn print_memory_info(sys: &System, format: &OutputFormat) {
let total = sys.total_memory();
let used = sys.used_memory();
let total_swap = sys.total_swap();
let used_swap = sys.used_swap();
match format {
OutputFormat::Json => {
let data = serde_json::json!({
"total_memory_mb": total / 1024 / 1024,
"used_memory_mb": used / 1024 / 1024,
"memory_usage_percent": (used as f64 / total as f64) * 100.0,
"total_swap_mb": total_swap / 1024 / 1024,
"used_swap_mb": used_swap / 1024 / 1024,
});
println!("{}", serde_json::to_string_pretty(&data).unwrap());
}
_ => {
println!("=== Informations mémoire ===");
println!("Mémoire totale : {} MB", total / 1024 / 1024);
println!("Utilisée : {} MB ({:.1}%)", used / 1024 / 1024, (used as f64 / total as f64) * 100.0);
println!("Swap total : {} MB", total_swap / 1024 / 1024);
println!("Swap utilisé : {} MB", used_swap / 1024 / 1024);
}
}
}
fn print_disk_info(format: &OutputFormat) {
use sysinfo::Disks;
let disks = Disks::new_with_refreshed_list();
match format {
OutputFormat::Json => {
let data: Vec<_> = disks.iter().map(|d| {
serde_json::json!({
"mount_point": d.mount_point().to_string_lossy(),
"filesystem": d.file_system().to_string_lossy(),
"total_gb": d.total_space() / 1024 / 1024 / 1024,
"available_gb": d.available_space() / 1024 / 1024 / 1024,
})
}).collect();
println!("{}", serde_json::to_string_pretty(&data).unwrap());
}
_ => {
println!("=== Informations disque ===");
println!("{:<20} {:<10} {:<10} {:<10}", "Point de montage", "Système de fichiers", "Total", "Disponible");
for disk in disks.iter() {
println!("{:<20} {:<10} {:<8} GB {:<6} GB",
disk.mount_point().to_string_lossy(),
disk.file_system().to_string_lossy(),
disk.total_space() / 1024 / 1024 / 1024,
disk.available_space() / 1024 / 1024 / 1024);
}
}
}
}
fn print_network_info(format: &OutputFormat) {
use sysinfo::Networks;
let networks = Networks::new_with_refreshed_list();
match format {
OutputFormat::Json => {
let data: Vec<_> = networks.iter().map(|(name, data)| {
serde_json::json!({
"interface": name,
"received_mb": data.total_received() / 1024 / 1024,
"transmitted_mb": data.total_transmitted() / 1024 / 1024,
})
}).collect();
println!("{}", serde_json::to_string_pretty(&data).unwrap());
}
_ => {
println!("=== Informations réseau ===");
println!("{:<15} {:<15} {:<15}", "Interface", "Reçu", "Transmis");
for (name, data) in networks.iter() {
println!("{:<15} {:<10} MB {:<10} MB",
name,
data.total_received() / 1024 / 1024,
data.total_transmitted() / 1024 / 1024);
}
}
}
}
3.2 Test du module d'informations système
# Compiler
cargo build --release
# Voir toutes les informations système
./target/release/sysmgr info
# Voir uniquement les informations CPU
./target/release/sysmgr info --section cpu
# Sortie au format JSON
./target/release/sysmgr info --format json
# Voir les informations mémoire (JSON)
./target/release/sysmgr info --section memory --format json
4. Module de gestion des processus
4.1 Implémentation du listage des processus
// src/commands/process.rs
use crate::cli::{ProcAction, OutputFormat};
use anyhow::Result;
use sysinfo::{System, Process, Pid};
use serde::Serialize;
#[derive(Serialize)]
struct ProcessInfo {
pid: u32,
name: String,
cpu_usage: f32,
memory_mb: u64,
status: String,
user: String,
}
pub async fn run(action: ProcAction, format: &OutputFormat) -> Result<()> {
match action {
ProcAction::List { name, sort_by_cpu } => {
list_processes(name, sort_by_cpu, format).await?;
}
ProcAction::Kill { pid, force } => {
kill_process(pid, force).await?;
}
ProcAction::Watch { name, interval } => {
watch_process(name, interval).await?;
}
}
Ok(())
}
async fn list_processes(
name_filter: Option<String>,
sort_by_cpu: bool,
format: &OutputFormat,
) -> Result<()> {
let mut sys = System::new_all();
sys.refresh_all();
let mut processes: Vec<ProcessInfo> = sys.processes().iter().map(|(pid, proc_)| {
ProcessInfo {
pid: pid.as_u32(),
name: proc_.name().to_string_lossy().to_string(),
cpu_usage: proc_.cpu_usage(),
memory_mb: proc_.memory() / 1024 / 1024,
status: format!("{:?}", proc_.status()),
user: proc_.user_id().map(|u| u.to_string()).unwrap_or_default(),
}
}).collect();
// Filtrer
if let Some(name) = name_filter {
processes.retain(|p| p.name.to_lowercase().contains(&name.to_lowercase()));
}
// Trier
if sort_by_cpu {
processes.sort_by(|a, b| b.cpu_usage.partial_cmp(&a.cpu_usage).unwrap());
} else {
processes.sort_by_key(|p| p.pid);
}
// Sortie
match format {
OutputFormat::Json => {
println!("{}", serde_json::to_string_pretty(&processes)?);
}
OutputFormat::Csv => {
println!("PID,NAME,CPU%,MEMORY_MB,STATUS,USER");
for p in &processes {
println!("{},{},{:.1},{},{},{}", p.pid, p.name, p.cpu_usage, p.memory_mb, p.status, p.user);
}
}
OutputFormat::Table => {
println!("{:<8} {:<25} {:<8} {:<10} {:<10} {}", "PID", "Nom", "CPU%", "Mémoire(MB)", "Statut", "Utilisateur");
println!("{}", "-".repeat(80));
for p in processes.iter().take(50) {
println!("{:<8} {:<25} {:<8.1} {:<10} {:<10} {}",
p.pid, truncate(&p.name, 24), p.cpu_usage, p.memory_mb, p.status, p.user);
}
println!("\nTotal {} processus", processes.len());
}
}
Ok(())
}
fn truncate(s: &str, max_len: usize) -> String {
if s.len() > max_len {
format!("{}...", &s[..max_len - 3])
} else {
s.to_string()
}
}
async fn kill_process(pid: u32, force: bool) -> Result<()> {
let mut sys = System::new();
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
let pid = Pid::from(pid as usize);
if let Some(process) = sys.process(pid) {
if force {
process.kill();
println!("Processus {} terminé de force ({})", pid, process.name().to_string_lossy());
} else {
// Envoyer SIGTERM
#[cfg(unix)]
{
use std::process::Command;
Command::new("kill").arg(pid.to_string()).output()?;
println!("Signal TERM envoyé au processus {} ({})", pid, process.name().to_string_lossy());
}
}
} else {
println!("Processus {} non trouvé", pid);
}
Ok(())
}
async fn watch_process(name: String, interval: u64) -> Result<()> {
use std::time::Duration;
println!("Surveillance du processus : {} (rafraîchissement toutes les {} secondes, Ctrl+C pour arrêter)", name, interval);
loop {
let mut sys = System::new();
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
let matched: Vec<_> = sys.processes().iter()
.filter(|(_, p)| p.name().to_string_lossy().to_lowercase().contains(&name.to_lowercase()))
.collect();
// Effacer l'écran
print!("\x1B[2J\x1B[1;1H");
println!("=== Moniteur de processus : {} ===", name);
println!("Heure : {}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"));
println!();
if matched.is_empty() {
println!("Aucun processus correspondant à '{}' trouvé", name);
} else {
println!("{:<8} {:<25} {:<8} {:<10}", "PID", "Nom", "CPU%", "Mémoire(MB)");
println!("{}", "-".repeat(55));
for (pid, proc_) in &matched {
println!("{:<8} {:<25} {:<8.1} {:<10}",
pid, proc_.name().to_string_lossy(),
proc_.cpu_usage(),
proc_.memory() / 1024 / 1024);
}
}
tokio::time::sleep(Duration::from_secs(interval)).await;
}
}
4.2 Test de la gestion des processus
# Lister tous les processus
./target/release/sysmgr proc list
# Trier par utilisation CPU
./target/release/sysmgr proc list --sort-by-cpu
# Filtrer un processus spécifique
./target/release/sysmgr proc list --name nginx
# Sortie au format JSON
./target/release/sysmgr proc list --format json
# Terminer un processus
./target/release/sysmgr proc kill 1234
# Terminer de force
./target/release/sysmgr proc kill 1234 --force
# Surveiller un processus
./target/release/sysmgr proc watch nginx --interval 5
5. Module d'analyse de journaux
5.1 Implémentation de l'analyse de journaux
// src/commands/log.rs
use crate::cli::OutputFormat;
use anyhow::Result;
use regex::Regex;
use serde::Serialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
#[derive(Serialize)]
struct LogStats {
total_lines: usize,
error_count: usize,
warn_count: usize,
info_count: usize,
debug_count: usize,
errors_per_hour: HashMap<String, usize>,
}
pub async fn run(
file: &str,
keyword: Option<&str>,
stats: bool,
format: &OutputFormat,
) -> Result<()> {
let file = File::open(file)?;
let reader = BufReader::new(file);
if stats {
analyze_stats(reader, format)?;
} else if let Some(kw) = keyword {
filter_lines(reader, kw, format)?;
} else {
println!("Veuillez spécifier l'argument --keyword ou --stats");
}
Ok(())
}
fn analyze_stats(reader: BufReader<File>, format: &OutputFormat) -> Result<()> {
let mut stats = LogStats {
total_lines: 0,
error_count: 0,
warn_count: 0,
info_count: 0,
debug_count: 0,
errors_per_hour: HashMap::new(),
};
// Regex : correspondance des horodatages et niveaux de journal
let re = Regex::new(r"(?P<timestamp>\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}).*(?P<level>ERROR|WARN|INFO|DEBUG)")?;
for line in reader.lines() {
let line = line?;
stats.total_lines += 1;
if let Some(caps) = re.captures(&line) {
let level = caps.name("level").unwrap().as_str();
let timestamp = caps.name("timestamp").unwrap().as_str();
match level {
"ERROR" => {
stats.error_count += 1;
// Extraire l'heure
if let Some(hour) = timestamp.get(11..13) {
*stats.errors_per_hour.entry(hour.to_string()).or_insert(0) += 1;
}
}
"WARN" => stats.warn_count += 1,
"INFO" => stats.info_count += 1,
"DEBUG" => stats.debug_count += 1,
_ => {}
}
}
}
match format {
OutputFormat::Json => {
println!("{}", serde_json::to_string_pretty(&stats)?);
}
_ => {
println!("=== Statistiques de journal ===");
println!("Lignes totales : {}", stats.total_lines);
println!("ERROR : {}", stats.error_count);
println!("WARN : {}", stats.warn_count);
println!("INFO : {}", stats.info_count);
println!("DEBUG : {}", stats.debug_count);
if !stats.errors_per_hour.is_empty() {
println!("\n=== Distribution des erreurs par heure ===");
let mut hours: Vec<_> = stats.errors_per_hour.iter().collect();
hours.sort_by_key(|(h, _)| h.clone());
for (hour, count) in hours {
println!("{}:00 - {} erreurs", hour, count);
}
}
}
}
Ok(())
}
fn filter_lines(reader: BufReader<File>, keyword: &str, format: &OutputFormat) -> Result<()> {
let re = Regex::new(&format!("(?i){}", regex::escape(keyword)))?;
let mut count = 0;
for line in reader.lines() {
let line = line?;
if re.is_match(&line) {
count += 1;
match format {
OutputFormat::Json => {
println!("{}", serde_json::json!({"line": count, "content": line}));
}
_ => {
println!("{}", line);
}
}
}
}
eprintln!("\n{} lignes correspondantes trouvées", count);
Ok(())
}
5.2 Test de l'analyse de journaux
# Analyser le journal
./target/release/sysmgr log --file /var/log/syslog --stats
# Statistiques au format JSON
./target/release/sysmgr log --file /var/log/syslog --stats --format json
# Filtrer par mot-clé
./target/release/sysmgr log --file /var/log/syslog --keyword "error"
# Filtrer et sortir en JSON
./target/release/sysmgr log --file /var/log/syslog --keyword "failed" --format json
6. Module de tâches d'automatisation
6.1 Implémentation de la gestion Cron
// src/commands/cron.rs
use crate::cli::{CronAction, OutputFormat};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Debug)]
struct CronJob {
id: u32,
schedule: String,
command: String,
enabled: bool,
}
#[derive(Serialize, Deserialize, Debug, Default)]
struct CronConfig {
jobs: Vec<CronJob>,
}
fn config_path() -> PathBuf {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
PathBuf::from(home).join(".sysmgr").join("cron.json")
}
fn load_config() -> Result<CronConfig> {
let path = config_path();
if !path.exists() {
return Ok(CronConfig::default());
}
let content = fs::read_to_string(path)?;
Ok(serde_json::from_str(&content)?)
}
fn save_config(config: &CronConfig) -> Result<()> {
let path = config_path();
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(path, serde_json::to_string_pretty(config)?)?;
Ok(())
}
pub async fn run(action: CronAction, format: &OutputFormat) -> Result<()> {
match action {
CronAction::List => {
let config = load_config()?;
match format {
OutputFormat::Json => {
println!("{}", serde_json::to_string_pretty(&config.jobs)?);
}
_ => {
println!("{:<5} {:<20} {:<40} {:<8}", "ID", "Planification", "Commande", "Statut");
println!("{}", "-".repeat(75));
for job in &config.jobs {
let status = if job.enabled { "Activé" } else { "Désactivé" };
println!("{:<5} {:<20} {:<40} {:<8}", job.id, job.schedule, job.command, status);
}
}
}
}
CronAction::Add { schedule, command } => {
let mut config = load_config()?;
let new_id = config.jobs.iter().map(|j| j.id).max().unwrap_or(0) + 1;
config.jobs.push(CronJob {
id: new_id,
schedule,
command,
enabled: true,
});
save_config(&config)?;
println!("Tâche ID {} ajoutée", new_id);
}
CronAction::Remove { id } => {
let mut config = load_config()?;
let initial_len = config.jobs.len();
config.jobs.retain(|j| j.id != id);
if config.jobs.len() == initial_len {
println!("ID de tâche non trouvé : {}", id);
} else {
save_config(&config)?;
println!("Tâche ID {} supprimée", id);
}
}
}
Ok(())
}
6.2 Test des tâches d'automatisation
# Ajouter une tâche planifiée
./target/release/sysmgr cron add "0 */6 * * *" "/usr/bin/backup.sh"
# Lister toutes les tâches
./target/release/sysmgr cron list
# Liste au format JSON
./target/release/sysmgr cron list --format json
# Supprimer une tâche
./target/release/sysmgr cron remove 1
7. Fonctionnalités avancées
7.1 Compilation multiplateforme
# Compiler pour Linux x86_64
cargo build --release --target x86_64-unknown-linux-gnu
# Compiler pour Linux ARM64
cargo build --release --target aarch64-unknown-linux-gnu
# Compiler pour macOS
cargo build --release --target x86_64-apple-darwin
# Compiler pour Windows
cargo build --release --target x86_64-pc-windows-gnu
7.2 Liaison statique (éviter les problèmes de version de glibc)
# Utiliser musl pour la liaison statique
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
# Le binaire généré peut s'exécuter sur n'importe quelle distribution Linux
ls -lh target/x86_64-unknown-linux-musl/release/sysmgr
7.3 Empaquetage et distribution
# Empaqueter en paquet Debian avec cargo-deb
cargo install cargo-deb
cargo deb
# Empaqueter en RPM avec cargo-generate-rpm
cargo install cargo-generate-rpm
cargo generate-rpm
# Générer un paquet de distribution tar.gz
tar -czvf sysmgr-0.1.0-linux-x86_64.tar.gz \
-C target/release sysmgr
8. Optimisation des performances
8.1 Optimisation de la compilation
# Cargo.toml
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
strip = true
8.2 Optimisation du runtime
- Utiliser
mimallocau lieu de l'allocateur par défaut
// src/main.rs
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
- E/S asynchrones pour le traitement de grands journaux
use tokio::io::AsyncBufReadExt;
async fn process_large_log(path: &str) -> Result<()> {
let file = tokio::fs::File::open(path).await?;
let reader = tokio::io::BufReader::new(file);
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await? {
// Traiter chaque ligne
}
Ok(())
}
9. Conclusion
Cet article vous a guidé dans la construction d'un outil CLI de gestion de serveurs Linux entièrement fonctionnel en utilisant Rust, couvrant :
- Construction du framework CLI : Utilisation de
clappour définir une structure de commande claire - Collecte d'informations système : Surveillance en temps réel du CPU, de la mémoire, du disque, du réseau
- Gestion des processus : Lister, filtrer, terminer, surveiller les processus
- Analyse de journaux : Filtrage, statistiques, analyse de distribution des erreurs
- Tâches d'automatisation : Gestion CRUD des tâches Cron
- Compilation multiplateforme : Un seul code source compilant en binaires multiplateformes
- Optimisation des performances : Liaison statique, LTO, mimalloc, etc.
Les avantages de Rust dans le développement d'outils de gestion de serveurs sont pleinement démontrés :
- Sécurité : Prévention au moment de la compilation des courses de données et des problèmes de mémoire
- Performance : Efficacité d'exécution comparable à C/C++
- Portabilité : Cross-compilation facile et déploiement sur différentes architectures
- Écosystème : Crates riches accélérant le développement
Ressources associées : - Documentation sysinfo - Documentation clap - Documentation tokio - Guide d'application CLI Rust