تسجيل الدخول في تطبيقات C++: أفضل الممارسات
تسجيل الدخول (Logging) هو جزء أساسي من تطوير أي برنامج جاد بلغة C++. بدون نظام تسجيل دخول موثوق، يصبح تصحيح الأخطاء المعقدة، ومراقبة الأداء، وتدقيق الأمان شبه مستحيل. في هذه المقالة، سنستعرض أفضل الممارسات لتسجيل الدخول في C++، بدءًا من اختيار المكتبة وصولاً إلى التقنيات المتقدمة لتنسيق الملفات وتدويرها.
1. اختيار مكتبة تسجيل الدخول
توجد في لغة C++ عدة مكتبات ناضجة لتسجيل الدخول. الاستغناء عن التنفيذ الخاص بك لصالح حل جاهز يوفر الوقت ويقلل من عدد الأخطاء.
- spdlog — المكتبة الأكثر شيوعًا. تتميز بأداء عالٍ (header-only، وضع غير متزامن)، وواجهة برمجة تطبيقات بسيطة، ودعم لتنسيقات متعددة (JSON، نص عادي).
- Boost.Log — مكتبة قوية ولكنها أثقل وزنًا من مجموعة Boost. مثالية للمشاريع المؤسسية التي تتطلب تصفية معقدة وإعدادًا مرنًا للواجهات الخلفية.
- Google Logging (glog) — مكتبة بسيطة من Google. مناسبة للمشاريع الصغيرة ولكنها أقل مرونة.
مثال على تهيئة spdlog:
#include <spdlog/spdlog.h>#include <spdlog/sinks/basic_file_sink.h>
int main() { // إنشاء مسجل يكتب إلى ملف "logs/app.log" auto logger = spdlog::basic_logger_mt("file_logger", "logs/app.log"); // تعيين مستوى التسجيل logger->set_level(spdlog::level::debug); // تسجيل رسالة logger->info("تم تشغيل التطبيق"); return 0;}2. مستويات التسجيل والتصفية
استخدام مستويات التسجيل (trace, debug, info, warning, error, critical) هو المعيار الفعلي. تسمح بضبط حجم المعلومات المعروضة بمرونة اعتمادًا على البيئة (تطوير، اختبار، إنتاج).
توصيات المستويات:
- Trace/Debug — للتطوير فقط. معلومات مفصلة عن تدفقات التنفيذ وقيم المتغيرات.
- Info — الأحداث القياسية: بدء/إيقاف الخدمة، العمليات الناجحة.
- Warning — حالات غير طبيعية لا تؤدي إلى خطأ (مثل استخدام API قديم).
- Error — أخطاء تتطلب الانتباه ولكنها ليست قاتلة.
- Critical — أخطاء قاتلة لا يمكن للتطبيق مواصلة العمل بعدها.
مثال على إعداد التصفية في spdlog:
// في الإنتاج، نكتب فقط warning وما فوقauto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();console_sink->set_level(spdlog::level::warn);
// إلى الملف، نكتب كل شيء بما في ذلك debugauto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/debug.log");file_sink->set_level(spdlog::level::debug);
auto logger = std::make_shared<spdlog::logger>("multi_sink", spdlog::sinks_init_list({console_sink, file_sink}));
spdlog::register_logger(logger);3. الأداء والتسجيل غير المتزامن
يمكن أن يصبح التسجيل المتزامن عنق زجاجة في الأداء، خاصة في الأنظمة عالية التحميل. كل استدعاء logger->info(...) يمنع الخيط حتى اكتمال الكتابة على القرص.
أفضل الممارسات للأداء:
- التسجيل غير المتزامن — استخدم خيطًا منفصلاً لكتابة السجلات. يدعم spdlog ذلك بشكل مدمج.
- تجنب العمليات المكلفة في أسطر السجلات — لا تستدعِ دوالًا ثقيلة داخل ماكرو التسجيل. على سبيل المثال،
logger->info("Result: {}", computeExpensiveValue());سوف يستدعيcomputeExpensiveValue()حتى لو كان مستوى السجل ليس