
هل ما زلت تعبت من كتابة logger.info('ServiceName.methodName.')
و logger.info('ServiceName.methodName -> done.')
لكل عطس؟ ربما كنت مثلي قد فكرت مرارًا وتكرارًا في أتمتة هذا العمل؟ في هذه المقالة ، سنتحدث عن أداة تسجيل الفصل الدراسي كواحد من الحلول للمشكلة مع اثنين فقط من المصممين.
لماذا هو ضروري حتى؟
ليس كل كمال الكمال هم مهندسون ، ولكن كل المهندسين هم كمال الكمال (جيدًا ، تقريبًا). نحن نحب التجريد laconic جميلة. نرى الجمال في مجموعة التماسيح ، وهو شخص غير مستعد ولا يستطيع القراءة. نود أن نشعر وكأننا آلهة ، وخلق الأكوان الصغيرة التي تعيش وفقا لقواعدنا. من المفترض أن كل هذه الصفات تنبع من كسلنا الهائل. لا ، المهندس لا يخاف من العمل كصف ، لكنه يكره بشدة الإجراءات المتكررة الروتينية التي تحاول ذراعيه أتمتة.
بعد أن كتب عدة آلاف من سطور التعليمات البرمجية المخصصة للتسجيل فقط ، عادة ما نأتي إلى إنشاء بعض الأنماط والمعايير الخاصة بنا لكتابة الرسائل. لسوء الحظ ، لا يزال يتعين علينا تطبيق هذه الأنماط يدويًا. يتمثل السبب الرئيسي وراء "السبب" لبرنامج تسجيل الفصل الدراسي في توفير طريقة تعريفية قابلة للتكوين لتسجيل رسائل القوالب بسهولة عند إنشاء فئة واستدعاء طرقها.
لعبة الداما العارية!
دون التجول ، دعنا نذهب مباشرة إلى الكود.
import { LogClass, Log } from 'class-logger' @LogClass() class ServiceCats { @Log() eat(food: string) { return 'purr' } }
ستنشئ هذه الخدمة ثلاث إدخالات سجل:
- قبل إنشائه مع قائمة الوسائط التي تم تمريرها إلى المنشئ.
- قبل الاتصال ،
eat
مع قائمة الحجج. - بعد الاتصال ،
eat
مع قائمة الحجج ونتيجة التنفيذ.
بكلمات الكود:
// ServiceCats // `ServiceCats.construct. Args: [].` const serviceCats = new ServiceCats() // `eat` // `ServiceCats.eat. Args: [milk].` serviceCats.eat('milk') // `eat` // `ServiceCats.eat -> done. Args: [milk]. Res: purr.`
كزة ذلك العيش .
قائمة كاملة بالأحداث التي يمكن التعهد بها:
- قبل إنشاء فئة.
- قبل استدعاء الأساليب والخصائص الوظيفية المتزامنة وغير المتزامنة ، سواء الثابتة أو غير الساكنة.
- بعد استدعاء الأساليب والخصائص الوظيفية المتزامنة وغير المتزامنة ، الثابتة وغير الثابتة على حد سواء.
- أخطاء في استدعاء الأساليب المتزامنة وغير المتزامنة والخصائص الوظيفية ، سواء الثابتة أو غير الساكنة.
الخاصية الوظيفية هي وظيفة سهم تم تخصيصها لخاصية ما ( class ServiceCats { private meow = () => null }
). غالبا ما تستخدم للحفاظ على سياق التنفيذ ( this
).
لقد وعدنا بطريقة "شكلي" للتسجيل
ثلاثة مستويات المسجل من التسلسل الهرمي التكوين:
عند استدعاء كل طريقة ، يتم دمج المستويات الثلاثة معًا. توفر المكتبة تهيئة افتراضية عامة معقولة ، بحيث يمكن استخدامها دون أي تكوين مسبق.
التكوين العالمي
يتم إنشاؤه للتطبيق بأكمله. يمكن تجاوزه مع setConfig
.
import { setConfig } from 'class-logger' setConfig({ log: console.info, })
التكوين الطبقة
انها فريدة من نوعها لكل فئة ويتم تطبيقها على جميع أساليب هذا الفصل. قد يتجاوز التكوين العام.
import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // log: console.debug, }) class ServiceCats {}
طريقة التكوين
يعمل فقط للطريقة نفسها. يستغرق الأسبقية على التكوين الطبقة والتكوين العالمي.
import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // log: console.debug, }) class ServiceCats { private energy = 100 @Log({ // log: console.warn, }) eat(food: string) { return 'purr' } // `console.debug` sleep() { this.energy += 100 } }
كزة العيش
ما يمكن تكوينه؟
لقد درسنا كيفية تغيير التكوين ، لكننا لم نكتشف بعد ما الذي يمكن تغييره بالضبط.
هنا يمكنك العثور على العديد من أمثلة كائنات التكوين لحالات مختلفة .
اربط بالواجهة إذا فهمت TypeScript بشكل أفضل. من الروسية :)
يحتوي كائن التكوين على الخصائص التالية:
سجل
هذه هي الوظيفة التي تتعامل في الواقع مع قطع الأشجار. تتلقى رسالة منسقة كسلسلة عند الإدخال. يستخدم لتسجيل الأحداث التالية:
- قبل إنشاء فئة.
- قبل استدعاء الأساليب والخصائص الوظيفية المتزامنة وغير المتزامنة ، سواء الثابتة أو غير الساكنة.
- بعد استدعاء الأساليب والخصائص الوظيفية المتزامنة وغير المتزامنة ، الثابتة وغير الثابتة على حد سواء.
بشكل افتراضي ، console.log
.
logError
هذه وظيفة تتعامل أيضًا مع التسجيل ، لكن رسائل الخطأ فقط. كما تتلقى رسالة منسقة كسلسلة. يستخدم لتسجيل الأحداث التالية:
- أخطاء في استدعاء الأساليب المتزامنة وغير المتزامنة والخصائص الوظيفية ، سواء الثابتة أو غير الساكنة.
الافتراضي هو console.error
.
هذا كائن له طريقتان: start
end
. هذه الطرق إنشاء نفس الرسالة المنسقة.
start
رسائل للأحداث التالية:
- قبل إنشاء فئة.
- قبل استدعاء الأساليب والخصائص الوظيفية المتزامنة وغير المتزامنة ، سواء الثابتة أو غير الساكنة.
end
ينشئ رسائل للأحداث التالية:
- بعد استدعاء الأساليب والخصائص الوظيفية المتزامنة وغير المتزامنة ، الثابتة وغير الثابتة على حد سواء.
- أخطاء في استدعاء الأساليب المتزامنة وغير المتزامنة والخصائص الوظيفية ، سواء الثابتة أو غير الساكنة.
بشكل افتراضي ، new ClassLoggerFormatterService()
.
تضمن
تكوين ما يجب تضمينه في الرسالة النهائية.
وسائط
يمكن أن يكون إما قيمة منطقية أو كائن.
إذا كان boolean
، boolean
ما إذا كان يجب تضمين قائمة الوسائط (تذكر Args: [milk]
؟) في جميع الرسائل ( start
end
).
إذا كان كائنًا ، فيجب أن يحتوي على خاصيتين منطقيتين: start
end
. تحدد start
ما إذا كنت تريد تضمين قائمة وسيطة لرسائل start
end
للرسائل end
.
الافتراضي هو true
.
بناء
هذا boolean
يتحكم في تسجيل إنشاء الفصل أم لا.
الافتراضي هو true
.
يؤدي
هذا boolean
يتحكم في تسجيل قيمة الإرجاع من الطريقة. تجدر الإشارة إلى أن قيمة الإرجاع ليست فقط ما تُرجعه الطريقة عند التنفيذ الناجح ، ولكن أيضًا الخطأ الذي تحدثه في حالة التنفيذ غير الناجح. تذكر Res: purr
؟ إذا كانت هذه العلامة false
، فلن يكون هناك Res: purr
.
الافتراضي هو true
.
classInstance
يمكن أن يكون إما قيمة منطقية أو كائن. المفهوم هو نفسه include.args
.
يضيف طريقة عرض متسلسلة لفصلك إلى المشاركات. بمعنى آخر ، إذا كان لفصلك أي خصائص ، فسيتم تسلسلها في JSON وإضافتها إلى السجلات.
ليست كل الخصائص قابلة للتسلسل. يستخدم class-logger المنطق التالي:
- نحن نأخذ كل خصائصنا (تلك التي ليست في النموذج الأولي) من intansa.
- لماذا؟ نادرًا ما يتغير النموذج الأولي ديناميكيًا ، لذلك ليس من المنطقي تسجيل محتوياته.
- نرمي كل منهم من نوع
function
.
- لماذا؟ في أكثر الأحيان ، تكون خصائص
function
الكتابة مجرد وظائف سهم لا يتم إجراؤها بواسطة الطرق التقليدية من أجل الحفاظ على السياق ( this
). نادرا ما يكون لديهم طبيعة ديناميكية ، لذلك لا يوجد أي نقطة في تسجيلهم.
- نطرد منهم كل الأشياء التي ليست أشياء بسيطة.
- ما هي هذه الأشياء البسيطة؟ يعتبر
ClassLoggerFormatterService
كائنًا بسيطًا إذا كان النموذج الأولي الخاص به هو Object.prototype
. - لماذا؟ غالبًا ما تكون مثيلات فئات الخدمة الأخرى بمثابة خصائص. سوف تنتفخ سجلاتنا بأكثر الطرق قبيحة إذا بدأنا في إجراء تسلسل.
- تسلسل في سلسلة JSON كل ما تبقى.
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 } } // `Test` // 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' const test = new Test() // `method2` // 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' test.method2() // `method2` // 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.'
الافتراضي هو false
.
ماذا لو كنت تحب الفكرة ، لكن فكرتك عن الجميل تصر على تنسيق مختلف لخط الرسالة؟ يمكنك التحكم الكامل في تنسيق الرسائل عن طريق تمرير التنسيق الخاص بك.
يمكنك كتابة المنسق الخاص بك من الصفر. بالطبع ولكن لن يتم تغطية هذا الخيار في هذا الإطار (إذا كنت مهتمًا بهذا الخيار المحدد ، فقم بإلقاء نظرة على قسم "التنسيق" في README).
الطريقة الأسرع والأسهل لتجاوز التنسيق هي توريث المنسق من المنسق الافتراضي - ClassLoggerFormatterService
.
يحتوي ClassLoggerFormatterService
على الأساليب protected
التالية التي تنشئ كتل صغيرة من الرسائل النهائية:
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}}
. إذا قمت بتضمين include.classInstance
في التكوين ، لكن لسبب ما لم يكن المثيل نفسه متاحًا في وقت التسجيل (على سبيل المثال ، للطرق الثابتة أو قبل إنشاء فئة) ، بإرجاع 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
يمكنك تجاوز الأساليب الأساسية التي تحتاجها فقط.
دعونا نلقي نظرة على كيفية إضافة طابع زمني لجميع المشاركات.
أنا لا أقول أنه ينبغي القيام بذلك في مشاريع حقيقية. يمكن لـ pino و winston ومعظم الحطابين القيام بذلك بمفردهم. هذا المثال هو لأغراض تعليمية فقط.
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(), })
كزة العيش
استنتاج
تذكر اتباع إرشادات التثبيت والتعرف على المتطلبات قبل استخدام هذه المكتبة في مشروعك.
أرجو ألا تضيع الوقت دون جدوى ، وكانت المقالة مفيدة لك على الأقل. يرجى ركلة وانتقاد. سوف نتعلم أن نرمز معا بشكل أفضل.