مقدمة: لماذا تسجيل السجلات ليس مجرد إخراج نص؟
في تطوير C++ الحديث، تجاوز تسجيل السجلات حدود std::cout البسيط. إنها أداة بالغة الأهمية لتصحيح الأخطاء والمراقبة والتدقيق في التطبيقات. ومع ذلك، يمكن أن يؤدي التنفيذ غير الصحيح إلى انخفاض الأداء أو فقدان البيانات أو ازدحام مساحة القرص. في هذه المقالة، سنستعرض أفضل ممارسات تسجيل السجلات في C++ بأمثلة محددة يمكن تطبيقها فورًا في المشاريع الحقيقية.
1. اختيار مكتبة تسجيل السجلات: spdlog والبدائل
لا توفر مكتبة C++ القياسية أدوات مدمجة لتسجيل السجلات بشكل مرن. يُوصى باستخدام حلول خارجية موثوقة. Spdlog هي واحدة من أشهر المكتبات بفضل أدائها العالي وسهولة تكاملها.
تثبيت spdlog (vcpkg أو header-only)
// التثبيت عبر vcpkgvcpkg install spdlog
// أو استخدم الإصدار header-only#include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"#include "spdlog/sinks/rotating_file_sink.h"مثال أساسي لإعداد المسجل
#include <memory>#include "spdlog/spdlog.h"#include "spdlog/sinks/stdout_color_sinks.h"
int main() { // إنشاء مسجل وحدة التحكم مع إخراج ملون auto console = spdlog::stdout_color_mt("console"); // تعيين مستوى التسجيل spdlog::set_level(spdlog::level::debug); // أمثلة على الرسائل console->info("تم تشغيل التطبيق"); console->warn("تم اكتشاف مشكلة محتملة"); console->error("خطأ حرج: {}", errno); return 0;}البدائل: Boost.Log (للمشاريع المؤسسية)، Google Glog (إذا كنت بحاجة إلى التكامل مع Google Test). للأنظمة المضمنة، استخدم EasyLogger.
2. مستويات التسجيل والتصفية
الاستخدام الصحيح لمستويات التسجيل هو مفتاح قابلية قراءة السجلات. في C++، يُعتمد التسلسل الهرمي التالي (من الأكثر حرجًا إلى الأقل):
- TRACE — تتبع تفصيلي للتنفيذ (لتصحيح الأخطاء فقط)
- DEBUG — معلومات تصحيح الأخطاء
- INFO — رسائل إعلامية حول عمل التطبيق
- WARN — تحذيرات حول المشكلات المحتملة
- ERROR — أخطاء لا تعطل عمل التطبيق
- CRITICAL — أخطاء قاتلة تتطلب تدخلًا فوريًا
إعداد التصفية في spdlog
// تفعيل WARN وما فوق فقطspdlog::set_level(spdlog::level::warn);
// لتصحيح الأخطاء، تفعيل جميع المستويات#ifdef _DEBUG spdlog::set_level(spdlog::level::trace);#endif
// مثال على الاستخدام مع الماكرو#define LOG_TRACE(...) spdlog::trace(__VA_ARGS__)#define LOG_ERROR(...) spdlog::error(__VA_ARGS__)
LOG_TRACE("دخول إلى الدالة processData()");LOG_ERROR("فشل فتح الملف: {}", filename);نصيحة: لا تستخدم أبدًا TRACE في إصدارات الإنتاج — فقد يؤدي ذلك إلى تقليل الأداء بمقدار 10-100 مرة بسبب تنسيق السلاسل النصية.
3. أداء تسجيل السجلات: الوضع المتزامن مقابل غير المتزامن
في الأنظمة عالية التحميل، يمكن أن يصبح التسجيل المتزامن عنق زجاجة. كل استدعاء للمسجل يحظر الخيط حتى اكتمال الكتابة. الحل هو التسجيل غير المتزامن.
إعداد المسجل غير المتزامن في spdlog
#include "spdlog/async.h"#include "spdlog/sinks/rotating_file_sink.h"
int main() { // إنشاء مسجل غير متزامن بحجم طابور 8192 spdlog::init_thread_pool(8192, 1); // خيط واحد للمعالجة auto async_file = spdlog::rotating_logger_mt<spdlog::async_factory>( "async_logger", "logs/app.log", 102