جعل تسجيل NodeJS سهلة


كم مرة قمت بكتابة logger.info('ServiceName.methodName.') و logger.info('ServiceName.methodName -> done.') لكل طريقة من خدمتك التي تريد تسجيل الدخول؟ هل ترغب في أن تكون آلية ولديها نفس التوقيع الثابت عبر التطبيق بالكامل؟ إذا كان الأمر كذلك ، فنحن متشابهان للغاية ، فقد عانينا من الألم نفسه مرات عديدة ، والآن يمكننا أن نحاول حله أخيرًا. معا. سيداتي سادتي ، اسمح لي أن أعرض ... مسجّل الصف !


"السبب" من فئة المسجل


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


بعد كتابة عدة آلاف من أسطر رمز التسجيل فقط ، نأتي عادةً بأنماط معينة ، ونقوم بتوحيد ما نريد تسجيله. ومع ذلك ، لا يزال يتعين علينا تطبيق هذه الأنماط يدويًا. لذا فإن الفكرة الجوهرية لفئة التسجيل هي توفير طريقة معيارية عالية التكوين قابلة للتسجيل لتسجيل الرسائل قبل وبعد تنفيذ طريقة الفصل.


بداية سريعة


دعنا نصل إلى الأرض ونرى كيف يبدو الرمز الفعلي.


 import { LogClass, Log } from 'class-logger' @LogClass() class ServiceCats { @Log() eat(food: string) { return 'purr' } } 

هذه الخدمة سوف تسجل ثلاث مرات:


  • في إنشائها مع قائمة من الحجج التي تم تمريرها إلى المنشئ.
  • قبل أن يتم تنفيذ eat مع قائمة من الحجج.
  • بعد eat يتم تنفيذ مع قائمة الحجج والنتيجة.

بكلمات الكود:


 // Logs before the actual call to the constructor // `ServiceCats.construct. Args: [].` const serviceCats = new ServiceCats() // Logs before the actual call to `eat` // `ServiceCats.eat. Args: [milk].` serviceCats.eat('milk') // Logs after the actual call to `eat` // `ServiceCats.eat -> done. Args: [milk]. Res: purr.` 

عرض حي .


ماذا يمكن أن نسجل؟ إليك القائمة الكاملة للأحداث:


  • قبل البناء الطبقة.
  • قبل أساليب ثابتة وغير ثابتة متزامن وغير ثابت والخصائص الوظيفية.
  • بعد أساليب ثابتة وغير ثابتة متزامن وغير ثابت والخصائص الوظيفية.
  • أخطاء الطرق الثابتة وغير الثابتة والمتزامنة والخواص الوظيفية.

الخاصية الوظيفية هي وظيفة سهم تم تخصيصها لخاصية ما ( class ServiceCats { private meow = () => null } ).

تعديله حسب احتياجاتنا


جيد جدًا ، لكننا وعدنا "بالتخصيص" ، أليس كذلك؟ فكيف يمكننا أن يعدل ذلك؟


يوفر class-logger ثلاث طبقات من التكوين الهرمي:


  • عالمي
  • فئة
  • طريقة

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


التكوين العالمي


انها التكوين على نطاق التطبيق. يمكن تعيين مع دعوة setConfig .


 import { setConfig } from 'class-logger' setConfig({ log: console.info, }) 

التكوين الطبقة


له تأثير على كل طريقة من صفك. يمكن أن يتجاوز التكوين العالمي.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // It overrides global config for this service log: console.debug, }) class ServiceCats {} 

طريقة التكوين


يؤثر فقط على الطريقة نفسها. يتجاوز التكوين الطبقي ، وبالتالي ، التكوين العام.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // It overrides global config for this service log: console.debug, }) class ServiceCats { private energy = 100 @Log({ // It overrides class config for this method only log: console.warn, }) eat(food: string) { return 'purr' } // This method stil uses `console.debug` provided by class config sleep() { this.energy += 100 } } 

عرض حي


خيارات التكوين


حسنًا ، لقد تعلمنا كيفية تغيير الإعدادات الافتراضية ، ومع ذلك لن يضر تغطية ما هو موجود للتكوين ، أليس كذلك؟


هنا يمكنك العثور على الكثير من عمليات استبدال التكوين المفيدة .

إليك الرابط الخاص بواجهة كائن التكوين في حال تحدثت مع برنامج TypeScript أفضل من الإنجليزية :)

يحتوي كائن التكوين على هذه الخصائص:


سجل


إنها وظيفة تقوم بالتسجيل الفعلي للرسالة المنسقة النهائية. يستخدم لتسجيل هذه الأحداث:


  • قبل البناء الطبقة.
  • قبل أساليب ثابتة وغير ثابتة متزامن وغير ثابت والخصائص الوظيفية.
  • بعد أساليب ثابتة وغير ثابتة متزامن وغير ثابت والخصائص الوظيفية.

الافتراضي: console.log


logError


إنها وظيفة تقوم بالتسجيل الفعلي لرسالة الخطأ المنسقة النهائية. يتم استخدامه لتسجيل هذا الحدث والحدث الوحيد:


  • أخطاء الطرق الثابتة وغير الثابتة والمتزامنة والخواص الوظيفية.

الافتراضي: console.error


المنسق


إنه كائن ذو طريقتين: start end . يقوم بتنسيق تسجيل البيانات في السلسلة النهائية.


start تنسيق الرسائل لهذه الأحداث:


  • قبل البناء الطبقة.
  • قبل أساليب ثابتة وغير ثابتة متزامن وغير ثابت والخصائص الوظيفية.

end تنسيقات الرسائل لهذه الأحداث:


  • بعد أساليب ثابتة وغير ثابتة متزامن وغير ثابت والخصائص الوظيفية.
  • أخطاء الطرق الثابتة وغير الثابتة والمتزامنة والخواص الوظيفية.

الافتراضي: new ClassLoggerFormatterService()


تضمن


تكوين ما يجب تضمينه في الرسالة.


وسائط

يمكن أن يكون إما منطقية أو كائن.


إذا كان منطقيًا ، فإنه يحدد ما إذا كان يجب تضمين قائمة الوسائط (تذكر أن Args: [milk] ؟) في كليهما ، ابدأ (قبل الإنشاء وقبل استدعاء الطريقة) وتنتهي (بعد استدعاء الطريقة ، استدعاء طريقة الخطأ) ، الرسائل.


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


الافتراضي: true


بناء

إعداد علامة منطقية سواء لتسجيل إنشاء فئة أم لا.


الافتراضي: true


يؤدي

إعداد علامة منطقية أخرى سواء لتضمين قيمة إرجاع لاستدعاء الطريقة أو خطأ تم إلقاؤه به. تذكر Res: purr ؟ إذا قمت بتعيين هذه العلامة على " false فلن يكون هناك Res: purr .


الافتراضي: true


classInstance

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


لن يتم إضافة جميع الخصائص. يتبع class-logger هذا المنطق:


  • خذ الخصائص الخاصة (غير النموذج الأولي) للمثيل.
    • لماذا؟ إنها حالة نادرة عندما يتغير النموذج الأولي لديك بشكل ديناميكي ، لذلك يصعب تسجيله.
  • إسقاط أي منهم لديهم نوع function .
    • لماذا؟ معظم خصائص function الوقت هي فقط وظائف سهم غير قابلة للتغيير تستخدم بدلاً من أساليب الفصل العادية للحفاظ على this السياق. ليس من المنطقي أن تنتفخ سجلاتك بهيئات مقيدة لتلك الوظائف.
  • إسقاط أي منها ليست أشياء عادية.
    • ما هي الأشياء التي هي واضحة؟ يعتبر ClassLoggerFormatterService كائنًا كائنًا عاديًا إذا كان النموذج الأولي مساوٍ تمامًا لـ Object.prototype .
    • لماذا؟ غالبًا ما نقوم بتضمين مثيلات الفئات الأخرى كخصائص (حقنها كتبعيات). سوف تصبح سجلاتنا سمينًا للغاية إذا قمنا بتضمين نسخًا متشددة من هذه التبعيات.
  • توتر ما تبقى.

 class ServiceA {} @LogClass({ include: { classInstance: true, }, }) class Test { private serviceA = new ServiceA() private prop1 = 42 private prop2 = { test: 42 } private method1 = () => null @Log() public method2() { return 42 } } // Logs to the console before the class' construction: // 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' const test = new Test() // Logs to the console before the method call: // 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' test.method2() // Logs to the console after the method call: // 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.' 

الافتراضي: false


السيطرة على التنسيق


ماذا لو كنت تحب الفكرة العامة ، ولكن هل ترغب في ظهور رسائلك بشكل مختلف؟ يمكنك التحكم الكامل في التنسيق الذي يجتاز المنسق المخصص الخاص بك.


هل يمكن أن يكتب المنسق الخاص بك من الصفر. تماما. ومع ذلك ، لن نغطي هذا الخيار هنا (إذا كنت مهتمًا حقًا بذلك ، فقم بمعالجة قسم "التنسيق" في README).


أسرع شيء ، وربما هو أسهل شيء ، هو فئة فرعية منسق افتراضي ClassLoggerFormatterService - ClassLoggerFormatterService .


يحتوي ClassLoggerFormatterService على هذه الأساليب المحمية ، حيث يعمل ككتل بناء للرسالة النهائية:


  • base
    • إرجاع اسم الفئة باسم الأسلوب. مثال: ServiceCats.eat .
  • operation
    • إرجاع -> done أو -> error استنادًا إلى ما إذا كان التنفيذ ناجحًا لطريقة أو خطأ.
  • args
    • يُرجع قائمة متشددة من الوسائط. مثال: . Args: [milk] . Args: [milk] . ويستخدم سريع آمن سلسلة للكائنات تحت غطاء محرك السيارة.
  • classInstance
    • إرجاع مثيل فئة مصنفة. مثال: . Class instance: {"prop1":42,"prop2":{"test":42}} . Class instance: {"prop1":42,"prop2":{"test":42}} . إذا اخترت تضمين مثيل فئة ، لكنها غير متوفرة (هذه هي الطريقة بالنسبة للطرق الثابتة وإنشاء الفصل) ، فإنها تُرجع N/A
  • result
    • تُرجع نتيجة مصحوبة بالتنفيذ (حتى لو كان خطأ). يستخدم سلسلة سريعة آمنة لتسلسل الكائنات. سيتكون خطأ مقيد من الخصائص التالية:
    • اسم الفئة (وظيفة) تم إنشاء الخطأ مع ( error.constructor.name ).
    • رمز الخطأ ( error.code ).
    • رسالة الخطأ ( error.message ).
    • اسم الخطأ ( error.name ).
    • تتبع المكدس ( error.stack ).
  • final
    • العوائد . . فقط .

تتكون رسالة start من:


  • base
  • args
  • classInstance
  • final

تتكون الرسالة end من:


  • base
  • operation
  • args
  • classInstance
  • result
  • final

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


 import { ClassLoggerFormatterService, IClassLoggerFormatterStartData, setConfig, } from 'class-logger' class ClassLoggerTimestampFormatterService extends ClassLoggerFormatterService { protected base(data: IClassLoggerFormatterStartData) { const baseSuper = super.base(data) const timestamp = Date.now() const baseWithTimestamp = `${timestamp}:${baseSuper}` return baseWithTimestamp } } setConfig({ formatter: new ClassLoggerTimestampFormatterService(), }) 

عرض حي


استنتاج


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


نأمل أن تكون قد وجدت شيئًا مفيدًا لمشروعك. لا تتردد في توصيل ملاحظاتك لي! بالتأكيد أقدر أي نقد وأسئلة.

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


All Articles