الكود الخاص بك يعمل. ولكن ماذا يحدث في الواقع؟
لقد كتبت خدمة رائعة بلغة Rust. المترجم سعيد، الاختبارات خضراء، السرعة مذهلة. تقوم بتشغيلها على الخادم، و... صمت. كيف تعرف ما يحدث في الداخل؟ هل تعطلت؟ هل هناك تسرب للذاكرة؟ هل معالجة الطلبات بطيئة؟
بدون المراقبة والتسجيل، الكود الخاص بك هو صندوق أسود. في عالم Rust، حيث كل بايت له أهميته والأداء حاسم، تصبح القدرة على النظر داخل العملية قوة خارقة. في هذه المقالة، سنشرح كيفية تحويل تطبيق Rust الخاص بك من منفذ صامت إلى مساعد ثرثار يخبرك بنفسه عن مشاكله.
لماذا تتطلب Rust نهجًا خاصًا لقابلية المراقبة؟
على عكس اللغات ذات وقت التشغيل (Java, Python, Go)، لا توفر Rust أدوات جاهزة "خارج الصندوق". لا يوجد ملف تعريف مدمج أو وكيل مقاييس. ولكن هناك مزايا: أنت تتحكم في كل شيء. نظام الأنواع والملكية يسمح بإنشاء أدوات مراقبة فعالة بشكل لا يصدق دون عبء إضافي. الشيء الرئيسي هو اختيار المكتبات والهندسة المعمارية المناسبة.
يعتمد مكدس قابلية المراقبة الحديث في Rust على ثلاثة أركان:
- التسجيل (Logging) — سجلات منظمة للأحداث
- المقاييس (Metrics) — مؤشرات رقمية (عدادات، رسوم بيانية)
- التتبع (Tracing) — سلاسل الاستدعاءات في الأنظمة الموزعة
وكل هذا يجب أن يعمل بأقل تأثير على الأداء. دعنا نرى كيفية تحقيق ذلك عمليًا.
1. التسجيل: من println! إلى البيانات المنظمة
لنبدأ من الأساسيات. انسَ println! في الإنتاج. بدلاً من ذلك، استخدم crate log — الواجهة القياسية للتسجيل في نظام Rust البيئي. إنه غير مرتبط بمحرك خلفي محدد، مما يوفر المرونة.
إضافة log وenv_logger
// Cargo.toml[dependencies]log = "0.4"env_logger = "0.11"مثال بسيط على الاستخدام:
use log::{info, warn, error};
fn main() { env_logger::init(); info!("تم تشغيل الخدمة على المنفذ {}", 8080); warn!("تجاوزت الذاكرة 80%"); error!("خطأ في الاتصال بقاعدة البيانات: {}", "timeout");}تحكم في مستوى التسجيل من خلال متغير البيئة RUST_LOG:
RUST_LOG=info cargo runRUST_LOG=warn,my_crate=debug cargo run # لـ crate معينولكن هذه مجرد البداية. للإنتاج، تحتاج إلى تسجيل منظم (JSON). استخدم log4rs أو slog للتنسيق المتقدم.
2. Tracing: التسجيل المعزز
crate tracing هو تطور لـ log. يضيف مفهوم الامتدادات (spans) — فترات زمنية مسماة يمكن تداخلها مع بعضها البعض. هذا مثالي لتتبع سلاسل الاستدعاءات.
تثبيت tracing
[dependencies]tracing = "0.1"tracing-subscriber = "0.3"مثال باستخدام الماكرو:
use tracing::{info, instrument, span, Level};use tracing_subscriber;
#[instrument] // ينشئ span تلقائيًا للدالةfn process_request(user_id: u64) { // span متداخل let validation_span = span!(Level::INFO, "validation", user_id); let _guard = validation_span.enter(); // بعض الإجراءات std::thread::sleep(std::time::Duration::from_millis(10)); info!("تم التحقق بنجاح"); // الخروج من span تلقائيًا عند إسقاط _guard}
fn main() { tracing_subscriber::fmt() .with_target(true) .init(); process_request(42);}سيحتوي الإخراج على معلومات منظمة حول وقت تنفيذ كل span والتداخل. هذا لا يقدر بثمن لتصحيح مشاكل زمن الوصول.
3. المقاييس: عد كل شيء
السجلات جيدة، ولكن لرؤية