لماذا المراقبة (Observability) هي القوة الخارقة لمطور Rust
\n\nتخيل: خدمتك على Rust تعمل في الإنتاج، ولكن فجأة يرتفع زمن الاستجابة (latency) إلى عنان السماء. السجلات صامتة، المقاييس تظهر 200 OK، والمستخدمون يشتكون. مألوف؟ هذه هي "الصندوق الأسود" الكلاسيكي. بدون المراقبة في Rust أنت أعمى.
\n\nالمراقبة (Observability) ليست مجرد "سجلات + مقاييس". إنها القدرة على طرح أي أسئلة على النظام دون الحاجة لكتابة كود جديد. في Rust، مع تركيزها على الأداء والموثوقية، تصبح المراقبة مكونًا إلزاميًا لتطبيقات جاهزة للإنتاج.
\n\nفي هذه المقالة، سنشرح الأركان الثلاثة لـ المراقبة في Rust: التتبع والمقاييس والسجلات. ستحصل على أمثلة كود جاهزة مع tracing وOpenTelemetry وPrometheus يمكنك تطبيقها فورًا في مشروعك.
التتبع في Rust: رحلة الطلب عبر النظام
\n\nالتتبع (tracing) هو قصة حياة طلب واحد. بدلاً من السجلات المتناثرة، تحصل على مسار مترابط: من الدخول إلى API إلى طلب قاعدة البيانات والرد على العميل. أفضل أداة لـ Rust هي crate tracing من مجتمع Tokio.
إعداد tracing في تطبيق Rust
\n\nلنبدأ بالإعداد الأساسي. أضف إلى Cargo.toml:
[dependencies]\ntracing = "0.1"\ntracing-subscriber = "0.3"\ntracing-opentelemetry = "0.23"\nopentelemetry = "0.22"\nopentelemetry-otlp = "0.15"\n\nالآن، لنقم بتهيئة المتتبع في main.rs:
use tracing_subscriber::{fmt, prelude::*, EnvFilter};\nuse opentelemetry::{global, KeyValue};\nuse opentelemetry_otlp::WithExportConfig;\n\nfn init_tracer() -> Result<(), Box<dyn std::error::Error>> {\n global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new());\n \n let tracer = opentelemetry_otlp::new_pipeline()\n .tracing()\n .with_exporter(\n opentelemetry_otlp::new_exporter()\n .tonic()\n .with_endpoint("http://localhost:4317")\n )\n .with_trace_config(\n opentelemetry::sdk::trace::config()\n .with_resource(opentelemetry::sdk::Resource::new(vec![\n KeyValue::new("service.name", "rust-observability-demo")\n ]))\n )\n .install_simple()?;\n\n // ربط tracing مع OpenTelemetry\n let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);\n let subscriber = fmt::Subscriber::builder()\n .with_env_filter(EnvFilter::from_default_env())\n .finish()\n .with(telemetry);\n\n tracing::subscriber::set_global_default(subscriber)?;\n Ok(())\n}\n\nإنشاء spans وtraces
\n\nالآن، لنضف التتبع إلى منطق الأعمال:
\n\nuse tracing::{span, Span, Level, instrument};\n\n#[instrument]\nasync fn handle_request(user_id: u64) -> Result<String, Box<dyn std::error::Error>> {\n let user_data = get_user_data(user_id).await?;\n let processed = process_data(&user_data).await?;\n Ok(processed)\n}\n\n#[instrument(skip(db_pool))]\nasync fn get_user_data(user_id: u64) -> Result<User, Box<dyn std::error::Error>> {\n // محاكاة طلب قاعدة بيانات\n let span = Span::current();\n span.record("db.query", "SELECT * FROM users WHERE id = $1");\n \n tokio::time::sleep(std::time::Duration::from_millis(50)).await;\n Ok(User { id: user_id, name: "Alice".into() })\n}\n\n#[instrument]\nasync fn process_data(user: &User) -> Result<String, Box<dyn std::error::Error>> {\n let result = format!("Processed user: {}", user.name);\n tracing::info!(user_id = user.id, "Data processed successfully");\n Ok(result)\n}\n\nالخاصية #[instrument] تنشئ تلقائيًا span للدالة وتسجل جميع الوسائط. في Jaege