تسجيل الدخول في تطبيقات Rust: أفضل الممارسات عملياً
يُعد تسجيل الدخول أحد العناصر الأساسية لأي تطبيق إنتاج. فهو يساعد في تشخيص الأخطاء، وتتبع سلوك النظام، وجمع المقاييس. في نظام Rust البيئي، توجد عدة حلول ناضجة لذلك. في هذه المقالة، سنستعرض أفضل ممارسات تسجيل الدخول مع أمثلة كود واقعية.
1. الأساسيات: crate log والهندسة الواجهية
في Rust، المعيار الفعلي هو crate log. فهو يوفر واجهة مجردة غير مرتبطة بتنفيذ محدد. هذا يعني أنك تكتب السجلات عبر وحدات الماكرو log، بينما يتم تحديد أي backend سيعالجها (الإخراج إلى وحدة التحكم، إلى ملف، الإرسال إلى ELK) في مرحلة ربط crate آخر.
ربط تسجيل الدخول الأساسي
// Cargo.toml[dependencies]log = "0.4"env_logger = "0.11"// main.rsuse log::{info, warn, error, debug};
fn main() { // تهيئة env_logger (يقرأ متغير RUST_LOG) env_logger::init();
info!("تم تشغيل التطبيق"); warn!("يتم استخدام التكوين الافتراضي"); error!("خطأ حرج: {}", "الملف غير موجود");}قم بتشغيل التطبيق مع متغير البيئة:
RUST_LOG=info cargo runسترى على الشاشة:
[2025-01-15T10:30:00Z INFO my_app] تم تشغيل التطبيق[2025-01-15T10:30:00Z WARN my_app] يتم استخدام التكوين الافتراضي[2025-01-15T10:30:00Z ERROR my_app] خطأ حرج: الملف غير موجود2. ضبط مرن لمستويات تسجيل الدخول
يسمح المتغير RUST_LOG بتصفية الرسائل بمرونة حسب الوحدات والمستويات. هذا أمر بالغ الأهمية للمشاريع الكبيرة، حيث يمكن للضوضاء الناتجة عن سجلات التصحيح أن تخفي الأحداث الهامة.
أمثلة على التصفية
# الأخطاء والتحذيرات فقط من التطبيق بأكملهRUST_LOG=warn
# التصحيح فقط في وحدة database، والباقي infoRUST_LOG=info,my_crate::database=debug
# جميع المستويات، بما في ذلك traceRUST_LOG=traceعملياً، استخدم هذا الأسلوب:
use std::env;
fn setup_logging() { if env::var("RUST_LOG").is_err() { // القيمة الافتراضية للتطوير env::set_var("RUST_LOG", "info,my_crate=debug"); } env_logger::init();}3. تسجيل الدخول المنظم باستخدام tracing
للأنظمة المعقدة (الخدمات المصغرة، التطبيقات غير المتزامنة)، من الأفضل استخدام crate tracing. فهو يدعم النطاقات (spans) والحقول المنظمة، مما يسمح بربط السجلات في سلاسل الطلبات.
مقارنة بين log و tracing
- log — بسيط، خفيف، مناسب للمشاريع الصغيرة.
- tracing — قوي، يدعم النطاقات، الأحداث، الحقول المنظمة؛ مثالي للكود غير المتزامن والأنظمة ذات التجهيز بالتتبع.
مثال مع tracing و tracing-subscriber
// Cargo.toml[dependencies]tracing = "0.1"tracing-subscriber = "0.3"use tracing::{info, error, span, Level};use tracing_subscriber::FmtSubscriber;
#[tokio::main]async fn main() { // إعداد subscriber للإخراج إلى stdout let subscriber = FmtSubscriber::builder() .with_max_level(Level::TRACE) .finish(); tracing::subscriber::set_global_default(subscriber) .expect("فشل في تعيين subscriber");
// إنشاء نطاق لطلب HTTP let request_span = span!(Level::INFO, "http_request", method = "GET", path = "/api/users"); let _guard = request_span.enter();
info!("بدأت معالجة الطلب");
// نطاق متداخل للعمل مع قاعدة البيانات let db_span = span!(Level::DEBUG, "db_query", query = "SELECT * FROM users"); let _db_gu