Eclair - مكتبة التسجيل الربيعي جافا



هناك الكثير من الأسئلة حول عمل الخدمات في مراحل التطوير والاختبار والدعم ، وكلها للوهلة الأولى على عكس: "ماذا حدث؟" "هل كان هناك طلب؟" ، "ما هو تنسيق التاريخ؟" ، "لماذا لا تستجيب الخدمة؟" الخ.

سيتمكن السجل الذي تم تجميعه بشكل صحيح من الإجابة بالتفصيل على هذه الأسئلة والعديد من الأسئلة الأخرى بشكل مستقل تمامًا دون مشاركة المطورين. سعيًا لتحقيق هذا الهدف المغري ، ولدت مكتبة تسجيل Eclair ، المصممة للدخول في حوار مع جميع المشاركين في العملية دون سحب الكثير من البطانيات.

حول بطانية وميزات الحل - أدناه.

ما هي مشكلة التسجيل


إذا لم تكن مهتمًا جدًا بفهم المبنى ، يمكنك المتابعة فورًا إلى وصف حلنا .

  • سجل التطبيق هو غيابه.
    في معظم الأحيان ، هو الوحيد الذي يمكنه إثبات نجاح التطبيق. لا توجد حالة في الخدمات المصغرة ؛ الأنظمة المجاورة متحركة ومتحركة. "كرر" ، "أعيد إنشاء" ، "فحص مزدوج" - كل هذا صعب و / أو مستحيل. يجب أن يكون السجل بالمعلومات الكافية للإجابة على السؤال: "ماذا حدث؟" في أي وقت . . يجب أن يكون السجل واضحًا للجميع: المطور ، والاختبار ، وأحيانًا المحلل ، وأحيانًا المسؤول ، وأحيانًا السطر الأول من الدعم - يحدث أي شيء.
  • الخدمات الدقيقة تدور حول تعدد الخيوط.
    غالبًا ما تتم معالجة الطلبات الواردة إلى الخدمة (أو البيانات التي تطلبها الخدمة) من خلال عدة سلاسل رسائل. عادة ما يتم خلط سجل كافة مؤشرات الترابط. هل تريد التمييز بين الخيوط المتوازية والتمييز بين الخيوط "المتسلسلة"؟ يتم إعادة استخدام الدفق نفسه للمعالجة التسلسلية للطلبات ، مرارًا وتكرارًا تنفيذ منطقه الخاص لمجموعات البيانات المختلفة. هذه التدفقات "المتسلسلة" تتدفق من مستوى آخر ، لكن حدودها يجب أن تكون واضحة للقارئ.
  • يجب أن يحفظ السجل تنسيق البيانات الأصلي.
    إذا تم تبادل الخدمات في الواقع بواسطة XML ، فيجب على السجل المقابل تخزين XML. إنه ليس مضغوطًا دائمًا وليس جميلًا دائمًا (ولكنه مريح). من السهل أن ترى النجاح ، أسهل في تحليل الفشل. في بعض الحالات ، يمكن استخدام السجل لتشغيل الطلب أو إعادة معالجته يدويًا.
  • يتطلب جزء من البيانات في السجل علاقة خاصة.
    غالبًا ما يلزم تخزين البيانات الواردة (الطلبات) والبيانات الصادرة (الإجابات) والطلبات المقدمة إلى أنظمة الجهات الخارجية والاستجابات منها بشكل منفصل. تخضع لمتطلبات خاصة: من خلال مدة الصلاحية أو الموثوقية. بالإضافة إلى ذلك ، يمكن أن تحتوي هذه البيانات على مبلغ مثير للإعجاب مقارنة بخط سجل نموذجي.
  • جزء من البيانات ليس للسجل.
    يجب استبعاد ما يلي عادة من السجل العادي: البيانات الثنائية (صفائف البايت ، base64 ، ..) ، البيانات الشخصية للعملاء / الشركاء / الأفراد الآخرين والكيانات القانونية. إنها دائمًا قصة فردية ، ولكنها منهجية ولا تخضع للتحكم اليدوي.

لماذا لا اليدين


خذ org.slf4j.Logger (قم org.slf4j.Logger إليه مع مقدمي الطلبات من أي مجموعة) واكتب كل ما هو مطلوب إلى السجل. مداخل الطرق الرئيسية ، مخارج ، إذا لزم الأمر ، تعكس أخطاء اشتعلت ، بعض البيانات. هل هذا ضروري؟ نعم ولكن:

  • كمية التعليمات البرمجية تنمو بشكل غير معقول (بشكل غير عادي). في البداية ، هذا ليس مدهشًا جدًا ، إذا قمت بتسجيل الدخول الأساسي فقط (الدعم الناجح ، بالمناسبة ، مع هذا النهج).
  • استدعاء المسجل بيديك يصبح كسولًا بسرعة. الإعلان عن حقل static باستخدام جهاز تسجيل كسول للغاية (حسنًا ، يمكن أن يفعل لومبوك هذا لنا). نحن المطورين كسالى. ونحن نستمع إلى كسلنا ، هذا كسل نبيل: إنه يغير العالم باستمرار للأفضل.
  • الخدمات الدقيقة ليست جيدة من جميع الجهات. نعم ، إنها صغيرة وجميلة ، ولكن هناك جانب آخر: هناك الكثير! غالبًا ما يكتب تطبيق واحد من البداية إلى النهاية بواسطة مطور واحد. الإرث لا يلوح أمام عينيه. سعيد ، غير مثقل بالقواعد المفروضة ، يعتبر المطور أنه من واجب اختراع تنسيق السجل الخاص به ، ومبدأه وقواعده الخاصة. ثم ينفذ الاختراع ببراعة. كل فئة مختلفة. هل هذه مشكلة؟ ضخم.
  • سيعيد تحطيم السجل الخاص بك. حتى الفكرة القادرة لن تنقذه. تحديث السجل مستحيل مثل تحديث Javadoc. في نفس الوقت ، على الأقل يقرأ Javadoc فقط من قبل المطورين (لا ، لا يقرأ أحد) ، لكن جمهور السجلات أوسع بكثير وفريق التطوير غير محدود.
  • يعد MDC (سياق التشخيص المعين) جزءًا لا يتجزأ من تطبيق متعدد الخيوط. يتطلب الملء اليدوي لـ MDC التنظيف في الوقت المناسب في نهاية العمل في التيار. وإلا ، فإنك تخاطر بربط ThreadLocal غير ذات صلة. اليدين والعيون للسيطرة على هذا ، أجرؤ على القول ، أمر مستحيل.

وبهذه الطريقة نحل هذه المشاكل في تطبيقاتنا.

ما هو Eclair وماذا يمكنه أن يفعل


Eclair هي أداة تبسط كتابة التعليمات البرمجية المسجلة. يساعد على جمع المعلومات الوصفية اللازمة حول شفرة المصدر ، وربطها بالبيانات التي تحلق في التطبيق في وقت التشغيل وإرسالها إلى مستودع السجل المعتاد ، مع توليد الحد الأدنى من التعليمات البرمجية.

الهدف الرئيسي هو جعل السجل واضحًا لجميع المشاركين في عملية التطوير. لذلك ، راحة كتابة التعليمات البرمجية ، لا تنتهي فوائد Eclair ، ولكنها تبدأ فقط.

تسجل Eclair الطرق والمعلمات المشروحة:

  • يسجل إدخال / خروج الأسلوب من الأسلوب / الاستثناءات / الوسيطات / القيم التي أرجعتها الطريقة
  • تصفية الاستثناءات لتسجيلها على وجه التحديد إلى الأنواع: فقط عند الضرورة
  • يغير "تفاصيل" السجل ، استنادًا إلى إعدادات التطبيق للموقع الحالي: على سبيل المثال ، في الحالة الأكثر تفصيلاً ، يطبع قيم الوسيطات (كلها أو بعضها) ، في أقصر نسخة - فقط حقيقة إدخال الطريقة
  • يطبع البيانات بتنسيق JSON / XML / بأي تنسيق آخر (جاهز للعمل مع جاكسون ، JAXB خارج الصندوق): يفهم التنسيق الأكثر ملاءمة لمعلمة معينة
  • يفهم SpEL (لغة تعبير الربيع) للتثبيت التعريفي والتنظيف التلقائي MDC
  • يكتب إلى N loggers ، "المسجل" في فهم Eclair هو حبة فول في السياق الذي يطبق واجهة EclairLogger : يمكنك تحديد المسجل الذي يجب أن يعالج التعليق التوضيحي بالاسم أو بالاسم المستعار أو بشكل افتراضي
  • يخبر المبرمج عن بعض الأخطاء في استخدام التعليقات التوضيحية: على سبيل المثال ، يعرف Eclair أنه يعمل على الوكلاء الديناميكيين (مع جميع الميزات التالية) ، لذلك ، يمكن أن يشير إلى أن التعليق التوضيحي على الطريقة private لن يعمل أبدًا
  • يقبل التعليقات التوضيحية الوصفية (كما يطلق عليها الربيع): يمكنك تحديد التعليقات التوضيحية الخاصة بك لتسجيل الدخول باستخدام بعض التعليقات التوضيحية الأساسية - لتقليل الشفرة
  • قادرة على إخفاء البيانات "الحساسة" عند الطباعة: من خارج منطقة الجزاء - تدريع XMLath
  • يكتب سجلاً في الوضع "اليدوي" ، ويعرف المستثمر و "يوسع" الحجج التي تنفذ Supplier : إعطاء الفرصة لتهيئة الحجج "كسول"

كيفية توصيل Eclair


يتم نشر شفرة المصدر على GitHub بموجب ترخيص Apache 2.0.

للاتصال ، تحتاج إلى Java 8 و Maven و Spring Boot 1.5+. قطعة أثرية استضافها مستودع Maven المركزي:

 <dependency> <groupId>ru.tinkoff</groupId> <artifactId>eclair-spring-boot-starter</artifactId> <version>0.8.3</version> </dependency> 

يحتوي المبتدئ على تنفيذ قياسي لـ EclairLogger ، والذي يستخدم نظام تسجيل تمت تهيئته بواسطة Spring Boot مع مجموعة من الإعدادات التي تم التحقق منها.

أمثلة


فيما يلي بعض الأمثلة على الاستخدام النموذجي للمكتبة. أولاً ، يتم إعطاء جزء من الكود ، ثم السجل المقابل ، اعتمادًا على توفر مستوى معين من التسجيل. يمكن العثور على مجموعة أكثر اكتمالاً من الأمثلة في ويكي المشروع في قسم الأمثلة .

أبسط مثال


المستوى الافتراضي هو DEBUG.

 @Log void simple() { } 
إذا كان المستوى متاحًا... ثم سيكون السجل هكذا
TRACE
DEBUG
DEBUG [] rteeExample.simple >
DEBUG [] rteeExample.simple <
INFO
WARN
ERROR
-

تعتمد تفاصيل السجل على مستوى التسجيل المتاح.


يؤثر مستوى التسجيل المتاح في الموقع الحالي على تفاصيل السجل. كلما انخفض المستوى المتاح (أي أقرب إلى TRACE) ، كلما كان السجل أكثر تفصيلاً.

 @Log(INFO) boolean verbose(String s, Integer i, Double d) { return false; } 
المستوىسجل
TRACE
DEBUG
INFO [] rteeExample.verbose > s="s", i=4, d=5.6
INFO [] rteeExample.verbose < false
INFOINFO [] rteeExample.verbose >
INFO [] rteeExample.verbose <
WARN
ERROR
-

صقل تسجيل الاستثناءات


يمكن تصفية أنواع الاستثناءات المسجلة. سيتم التعهد باستثناءات مختارة وذريتهم. في هذا المثال ، سيتم تسجيل NullPointerException على مستوى WARN ، Exception على مستوى الخطأ (افتراضيًا) ، ولن يتم تسجيل Error على الإطلاق (لأنه لم Error تضمين Error في فلتر التعليق التوضيحي الأول @Log.error ويتم استبعاده بشكل صريح من فلتر التعليق التوضيحي الثاني).

 @Log.error(level = WARN, ofType = {NullPointerException.class, IndexOutOfBoundsException.class}) @Log.error(exclude = Error.class) void filterErrors(Throwable throwable) throws Throwable { throw throwable; } //       filterErrors(new NullPointerException()); filterErrors(new Exception()); filterErrors(new Error()); 
المستوىسجل
TRACE
DEBUG
INFO
WARN
WARN [] rteeExample.filterErrors ! java.lang.NullPointerException
java.lang.NullPointerException: null
at rteeExampleTest.filterErrors(ExampleTest.java:0)
..
ERROR [] rteeExample.filterErrors ! java.lang.Exception
java.lang.Exception: null
at rteeExampleTest.filterErrors(ExampleTest.java:0)
..
ERRORERROR [] rteeExample.filterErrors ! java.lang.Exception
java.lang.Exception: null
at rteeExampleTest.filterErrors(ExampleTest.java:0)
..

قم بتعيين كل معلمة على حدة


 @Log.in(INFO) void parameterLevels(@Log(INFO) Double d, @Log(DEBUG) String s, @Log(TRACE) Integer i) { } 
المستوىسجل
TRACEINFO [] rteeExample.parameterLevels > d=9.4, s="v", i=7
DEBUGINFO [] rteeExample.parameterLevels > d=9.4, s="v"
INFOINFO [] rteeExample.parameterLevels > 9.4
WARN
ERROR
-

حدد وتخصيص تنسيق الطباعة


يمكن تكوين "الطابعات" المسؤولة عن تنسيق الطباعة بواسطة معالجات ما قبل وما بعد. في المثال أعلاه ، maskJaxb2Printer تكوين maskJaxb2Printer بحيث maskJaxb2Printer العناصر التي تطابق تعبير XPath "//s" باستخدام "********" . في نفس الوقت ، تطبع jacksonPrinter "كما هي".

 @Log.out(printer = "maskJaxb2Printer") Dto printers(@Log(printer = "maskJaxb2Printer") Dto xml, @Log(printer = "jacksonPrinter") Dto json, Integer i) { return xml; } 
المستوىسجل
TRACE
DEBUG
DEBUG [] rteeExample.printers >
xml=<dto><i>5</i><s>********</s></dto>, json={"i":5,"s":"password"}
DEBUG [] rteeExample.printers <
<dto><i>5</i><s>********</s></dto>
INFO
WARN
ERROR
-

مسجلات متعددة في السياق


يتم تسجيل الطريقة باستخدام عدة برامج تسجيل في نفس الوقت: عن طريق المسجل الافتراضي (المشروح باستخدام @Primary ) auditLogger AuditLogger. يمكنك تحديد عدة برامج تسجيل إذا كنت تريد فصل الأحداث المسجلة ليس فقط حسب المستوى (TRACE - ERROR) ، ولكن أيضًا إرسالها إلى مخازن مختلفة. على سبيل المثال ، يمكن للمسجل الرئيسي كتابة سجل إلى ملف على القرص باستخدام slf4j ، ويمكن لـ auditLogger كتابة شريحة بيانات خاصة إلى تخزين ممتاز (على سبيل المثال ، في Kafka) بتنسيقه الخاص.

 @Log @Log(logger = "auditLogger") void twoLoggers() { } 

إدارة MDC


يتم حذف MDCs التي تم تعيينها باستخدام التعليق التوضيحي تلقائيًا بعد الخروج من الطريقة المُعلَّقة. يمكن حساب قيمة سجل MDC ديناميكيًا باستخدام SpEL. فيما يلي أمثلة: سلسلة ثابتة تدرك بواسطة ثابت ، تقييم التعبير 1 + 1 ، استدعاء jacksonPrinter ، استدعاء الأسلوب static randomUUID .
لا يتم حذف MDCs مع السمة global = true بعد الخروج من الطريقة: كما ترى ، فإن السجل الوحيد المتبقي في MDC حتى نهاية السجل هو sum .

 @Log void outer() { self.mdc(); } @Mdc(key = "static", value = "string") @Mdc(key = "sum", value = "1 + 1", global = true) @Mdc(key = "beanReference", value = "@jacksonPrinter.print(new ru.tinkoff.eclair.example.Dto())") @Mdc(key = "staticMethod", value = "T(java.util.UUID).randomUUID()") @Log void mdc() { self.inner(); } @Log.in void inner() { } 

سجل عند تنفيذ الرمز أعلاه:
DEBUG [] rteeExample.outer >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] rteeExample.mdc >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] rteeExample.inner >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] rteeExample.mdc <
DEBUG [sum=2] rteeExample.outer <


تثبيت MDC على أساس المعلمات


إذا قمت بتحديد MDC باستخدام التعليق التوضيحي على المعلمة ، فإن المعلمة التي تم التعليق عليها متاحة ككائن الجذر لسياق التقييم. هنا "s" هو حقل من الفئة Dto بنوع String .

 @Log.in void mdcByArgument(@Mdc(key = "dto", value = "#this") @Mdc(key = "length", value = "s.length()") Dto dto) { } 

سجل عند تنفيذ الرمز أعلاه:
DEBUG [length=8, dto=Dto{i=12, s='password'}] rteeExample.mdcByArgument > dto=Dto{i=12, s='password'}

تسجيل يدوي


بالنسبة للتسجيل "اليدوي" ، يكفي تنفيذ تطبيق ManualLogger . الحجج التي تم تمريرها والتي تنفذ Supplier الواجهة سيتم "توسيعها" فقط إذا لزم الأمر.

 @Autowired private ManualLogger logger; @Log void manual() { logger.info("Eager logging: {}", Math.PI); logger.debug("Lazy logging: {}", (Supplier) () -> Math.PI); } 
المستوىسجل
TRACE
DEBUG
DEBUG [] rteeExample.manual >
INFO [] rteeExample.manual - Eager logging: 3.141592653589793
DEBUG [] rteeExample.manual - Lazy logging: 3.141592653589793
DEBUG [] rteeExample.manual <
INFOINFO [] rteeExample.manual - Eager logging: 3.141592653589793
WARN
ERROR
-

ماذا لا تفعل ايكلير


لا تعرف Eclair المكان الذي ستخزن فيه سجلاتك ، إلى متى وإلى التفاصيل. لا تعرف Eclair كيف تخطط لاستخدام السجل الخاص بك. يستخرج Eclair بعناية من تطبيقك جميع المعلومات التي تحتاجها ويعيد توجيهها إلى التخزين الذي قمت بتكوينه.

مثال على تهيئة EclairLogger لتوجيه سجل إلى مسجل Logback مع ملحق معين:

 @Bean public EclairLogger eclairLogger() { LoggerFacadeFactory factory = loggerName -> { ch.qos.logback.classic.LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); ch.qos.logback.classic.Logger logger = context.getLogger(loggerName); // Appender<ILoggingEvent> appender = ? // logger.addAppender(appender); return new Slf4JLoggerFacade(logger); }; return new SimpleLogger(factory, LoggingSystem.get(SimpleLogger.class.getClassLoader())); } 

هذا الحل ليس للجميع.


قبل البدء في استخدام Eclair كأداة رئيسية للتسجيل ، يجب أن تتعرف على عدد من ميزات هذا الحل. ترجع هذه "الميزات" إلى حقيقة أن Eclair يعتمد على آلية الوكيل القياسية لـ Spring.

- إن سرعة تنفيذ الكود الملفوف في البروكسي التالي غير مهمة ، لكنها ستنخفض. بالنسبة لنا ، نادرا ما تكون هذه الخسائر كبيرة. إذا ظهر السؤال المتعلق بتقليل المهلة الزمنية ، فهناك العديد من إجراءات التحسين الفعالة. يمكن اعتبار رفض سجل إعلامي مناسب كأحد الإجراءات ، ولكن ليس في المقام الأول.

- StackTrace "منتفخ" أكثر قليلاً. إذا لم تكن معتادًا على المكدس الطويل من وكلاء الربيع ، فقد يكون هذا مصدر إزعاج لك. لسبب واضح بنفس القدر ، سيكون تصحيح الطبقات بالوكالة أمرًا صعبًا.

- لا يمكن ربط كل فئة وكل طريقة: لا يمكن إجراء بروكسي للطرق private ، ستحتاج إلى تسجيل الدخول في سلسلة الطرق في حبة واحدة ، ولا يمكنك استخدام وكيل أي شيء غير حبة ، وما إلى ذلك.

في النهاية


من الواضح تمامًا أنه يجب استخدام هذه الأداة ، مثل أي أداة أخرى ، للاستفادة منها. وهذه المادة تضيء بشكل سطحي فقط الجانب الذي قررنا فيه التحرك بحثًا عن الحل المثالي.

النقد والأفكار والتلميحات والروابط - أرحب بحرارة بأي من مشاركتك في حياة المشروع! سأكون سعيدًا إذا وجدت Eclair مفيدًا لمشاريعك.

Source: https://habr.com/ru/post/ar412871/


All Articles