مراقبة تطبيقات Rust: المقاييس والتنبيهات من الألف إلى الياء
\n\nعندما يخرج تطبيق Rust الخاص بك إلى الإنتاج، يتوقف الكود عن كونه مجرد كود. يصبح خدمة يعتمد عليها المستخدمون والأعمال. بدون مراقبة، أنت تطير بشكل أعمى: يتم اكتشاف الأعطال من خلال الشكاوى، ويتم تقييم الأداء "بالتخمين". في هذه المقالة، سنشرح كيفية بناء نظام مراقبة كامل لـ Rust - من جمع المقاييس الأساسية إلى إعداد التنبيهات في Prometheus والتصور في Grafana.
\n\n1. لماذا تعتبر المراقبة مهمة لمشاريع Rust
\nتشتهر Rust بالأداء العالي وأمان الذاكرة، لكن هذا لا يلغي الحاجة إلى مراقبة التطبيق. المشاكل النموذجية في الإنتاج:
\n- \n
- تسرب الذاكرة (على سبيل المثال، عبر
Arcأو المراجع الدائرية). \n - الانسدادات في الكود غير المتزامن (deadlocks أو starvation في tokio). \n
- الانهيارات غير المتوقعة (حتى مع
catch_unwind). \n - تدهور الأداء بسبب الخوارزميات غير المثلى. \n
توفر المراقبة إجابات على الأسئلة: "ماذا يحدث الآن؟"، "لماذا حدث هذا؟" و"كيف نمنعه؟".
\n\n2. جمع المقاييس: مكتبة metrics والتصدير إلى Prometheus
\nالمكدس القياسي لـ Rust هو crate metrics (أو prometheus). نحن نستخدم metrics لأنه يجرد عملية التصدير ويسمح بتغيير الخلفية بسهولة.
2.1. التثبيت والإعداد الأساسي
\nأضف التبعيات إلى Cargo.toml:
[dependencies]\nmetrics = "0.21"\nmetrics-exporter-prometheus = "0.12"\ntokio = { version = "1", features = ["full"] }\n\nنقوم بإنشاء خادم HTTP بسيط لنقطة النهاية /metrics:
use metrics_exporter_prometheus::PrometheusBuilder;\nuse std::net::SocketAddr;\nuse tokio::net::TcpListener;\n\n#[tokio::main]\nasync fn main() {\n // تهيئة مصدر Prometheus\n let (recorder, exporter) = PrometheusBuilder::new()\n .build()\n .expect("فشل إنشاء مصدر Prometheus");\n\n metrics::set_boxed_recorder(Box::new(recorder))\n .expect("فشل تعيين المسجل العام");\n\n // تشغيل خادم المقاييس\n let addr: SocketAddr = "0.0.0.0:9090".parse().unwrap();\n let listener = TcpListener::bind(addr).await.unwrap();\n println!("المقاييس متاحة على http://{}", addr);\n\n loop {\n let (mut stream, _) = listener.accept().await.unwrap();\n let body = exporter.encode().unwrap();\n let response = format!(\n "HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: {}\\r\\n\\r\\n{}",\n body.len(),\n body\n );\n tokio::io::AsyncWriteExt::write_all(&mut stream, response.as_bytes())\n .await\n .unwrap();\n }\n}\n\n2.2. تسجيل المقاييس المخصصة
\nنضيف عداد الطلبات ومدرج تكراري لوقت الاستجابة:
\nuse metrics::{counter, histogram, increment_counter};\nuse std::time::Instant;\n\nasync fn handle_request() {\n let start = Instant::now();\n\n // منطق عملك\n tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;\n\n // تسجيل المقاييس\n increment_counter!("http_requests_total", "method" => "GET");\n histogram!("http_request_duration_seconds", start.elapsed().as_secs_f64(), "method" => "GET");\n}\nالآن على /metrics سترى:
# HELP http_requests_total إجمالي طلبات HTTP\n# TYPE http_requests_total counter\nhttp_requests_total{method="GET"} 1\n# HELP http_request_duration_seconds مدة طلب HTTP\n# TYPE http_request_duration_seconds histogram\nhttp_request_duration_seconds_bucket{method="GET",le="0.005"} 0\nhttp_request_duration_seconds_bucket{method="GET",le="0.01"} 0\nhttp_request_duration_seconds_bucket{method="GET",le="0.025"} 0\n