تحية ، أيها الأصدقاء الأعزاء!
أريد أن أشارك أفكاري حول موضوع التسجيل وما أدى إليه.
ربما بسبب بعض البحوث النظرية ، كان قطع الأشجار دائمًا منطقة من الاضطرابات في عالم جافا. بمرور الوقت ، أدى ذلك إلى ظهور عدة مكتبات للتسجيل ، مثل:
- Log4j
- Java Util Logging
- تسجيل العموم
- Logback
- Log4j2
في محاولة لتضييق بقية البقية ، قدم كل منهم للأسف عيوبه الخاصة.
وإذا تم تحسين الوضع بعد ظهور Slf4j - من وجهة نظر توحيد الرموز - كطبقة تجريدية للتسجيل ، فلا تزال المشكلات التي لم يتم حلها موجودة في التطبيقات الحالية.
كمجتمع مفتوح المصدر ، نحن نأخذ زمام المبادرة للتوصل إلى مقاربة ثورية جديدة - وإنشاء مسجل خفيف الوزن (ولكن في نفس الوقت غني وظيفيًا) ، باستخدام أحدث التطورات ، مثل البرمجة النصية.
المشاكل
- توفر التطبيقات الحالية دعما جزئيا فقط للبرامج النصية في الإعدادات
يؤدي ذلك إلى البرمجة التصريحية في ملفات تكوين المسجل (XML ، JSON ، YAML) ، على الرغم من أنه سيكون من الأسهل بكثير تفسير قيم التكوين ديناميكيًا في وقت التشغيل باستخدام البرمجة النصية الضرورية.
دعنا نأخذ مثالا على تكوين مرشح في Logback ، لتسجيل الرسائل فقط مع INFO مستوى التسجيل:
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter>
هذا مثال نموذجي لبرمجة XML التعريفي.
(نعم ، يدعم Logback مرشحًا يستخدم Groovy ، لكنه ينطبق فقط على مُقرضين محددين ، وليس على المُسجل)
لكن دعم البرمجة النصية لتنسيق السلسلة مفقود تمامًا.
- التكوين معقدة وطويلة
خذ Logback و Log4j2:
لا توجد طريقة لتكوين مستوى التسجيل لمقدم محدد.
يتم تكوين الملحقة بشكل منفصل عن قطع الاشجار والاشجار تشير إلى الملاحين باستخدام السمة "AppenderRef" - بينما يدعم المسجلون فقط تحديد مستوى التسجيل وأسماء الفئات.
افترض أننا بحاجة إلى استبعاد رسائل Debug من فئة Foo واحدة من ملف سجل معين دون التأثير على ملفات السجل والفئات الأخرى.
في Logback ، يكون ذلك ممكنًا باستخدام عامل تصفية Groovy Script على المُلاحق - ولكن إذا كان لدينا العديد من المُقَدِّسين ، فإن حجم التكوين يزداد باطراد.
- إلى كل مستوى من مستويات التسجيل - ملف منفصل!
لم نتمكن من العثور على إمكانية مثل هذا الإعداد الذي يتم فيه تجميع الرسائل في ملفات حسب مستوى الرسالة (تصحيح ، معلومات ، إلخ.)
تتطلب الميزات الحالية ازدواجية في الأجهزة عند كل مستوى من مستويات التسجيل.
- إعداد التصفية حسب اسم الفئة في مسجل الجذر نفسه
يدعم أداة تسجيل الجذر تحديد مستوى التسجيل فقط ، ولكن لا توجد إمكانية للتحكم المركزي في الفئات التي يجب تسجيلها.
- هناك انفصال مفاهيمي بين كيفية إنشاء بيانات السجل في التطبيق وكيف يتم استهلاك هذه البيانات بواسطة المسجل
الممارسة التاريخية هي أن قطع الأشجار (وتكوينها) أكثر مركزية من الطبقة إذا كانت تتمحور حول الملف.
هذا يتناقض مع الإدراك البشري ، الذي يتصور منطقياً التوقعات حول المحتويات النهائية لملفات السجل ، بدلاً من القلق حول إعداد كل فئة على حدة.
في الممارسة العملية ، هذه المفارقة هي سبب القيود الوظيفية للتطبيقات الحالية:
- تكوين اسم ملف معقد
- التكوين غير المنطقي للمسجل ، على سبيل المثال:
يدعم Logback الحد الأقصى 1 "تمييز" في "SiftingAppender".
لدى SiftingAppender قيود في إعدادات السياسة للأرشفة
إعادة تصميم RoutingAppender في Log4j2
الحل
- دعم البرمجة النصية الكاملة في التكوين
يستخدم Bobbin التكوين بمثابة محدد موقع للبرامج النصية Groovy التي تحدد سلوك المسجل في وقت تشغيل التطبيق.
هذا ما يبدو عليه مثال "المرشح":
{ "levels": "['info'].contains(level)" }
يدعم كل جانب من جوانب التخصيص التخصيص باستخدام البرامج النصية:
- مستويات التسجيل
- أسماء الصف
- تنسيق الرسالة
- أسماء الملفات
- الإعداد بسيطة وموجزة
لا يتطلب Bobbin تشفيرًا وأنماطًا ومرشحات ومميزات والعديد من الأشياء الأخرى غير الضرورية.
تم تكوينه مع بعض المعلمات الأساسية فقط:
- المستويات
- فصول
- الملفات
- تنسيق الخط
ملفات منفصلة لكل مستوى تسجيل: فقط ضع "$ {level}" في قناع اسم الملف في Bobbin.json (ملف التكوين).
مثال ملف التكوين:
{ "levels": "['debug', 'info', 'warn', 'error'].contains(level)", "destinations": [ { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/PLUGINS/INPUT/${className}/${level}/${className}_${level}.log\"" }, "classes": "className.contains('conf.plugins.input')" }, { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/PLUGINS/OUTPUT/${className}/${level}/${threadName}_${level}_${date}.log\"" }, "classes": "className.contains('conf.plugins.output')" }, { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/THREADS/${threadGroupName}/${threadName}/${level}/${threadName}_${level}_${date}.log\"" }, "classes": "className.contains('io.infinite.')" }, { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/ALL/WARNINGS_AND_ERRORS_${date}.log\"" }, "levels": "['warn', 'error'].contains(level)" }, { "name": "io.infinite.bobbin.destinations.ConsoleDestination", "levels": "['warn', 'error'].contains(level)" } ] }
جرب Bobbin الآن:
Gradle: compile "io.infinite:bobbin:2.0.0"
* Bobbin هو مشروع مفتوح المصدر مرخص تحت Apache.