LogRock: اختبار من خلال التسجيل
منذ أكثر من عامين ونحن نعمل على مشروع
Cleverbrush لدينا. هذا هو برنامج للعمل مع الرسومات المتجهة. ينطوي العمل مع محرر رسومي على عدد كبير من حالات استخدام التطبيق. نحن نحاول توفير المال والوقت ، لذلك نحن نحسن كل شيء ، بما في ذلك الاختبار. إن تغطية حالات الاختبار مع كل خيار يعد مكلفًا للغاية وغير منطقي ، خاصة أنه من المستحيل تغطية جميع الخيارات.
أثناء التطوير ، تم إنشاء وحدة نمطية لتطبيقات
React JS -
LogRock (github) .
تسمح لك هذه الوحدة بتنظيم تطبيقات التسجيل الحديثة. بناءً على السجلات ، نقوم بإجراء الاختبار. في هذه المقالة سوف أخبرك عن تعقيدات استخدام هذه الوحدة وكيفية تنظيم الاختبار من خلال التسجيل.
ما هي المشكلة؟
إذا قارنت البرنامج مع كائن حي ، فإن الخلل فيه مرض. قد يكون سبب هذا "المرض" عددًا من العوامل ، بما في ذلك بيئة مستخدم معين. هذا صحيح بشكل خاص إذا كنا نفكر في منصة على شبكة الإنترنت. في بعض الأحيان تكون العلاقة السببية معقدة للغاية ، ويكون الخطأ الذي تم العثور عليه أثناء الاختبار نتيجة لعدد من الأحداث.
كما هو الحال مع الأمراض التي تصيب الإنسان ، لا يمكن لأحد أن يشرح أعراضه بشكل أفضل من المريض ، ولا يستطيع أي شخص اختبار ما حدث ، وأفضل من البرنامج نفسه.
ماذا تفعل؟
لفهم ما يحدث ، نحتاج إلى قائمة بالإجراءات التي قام بها المستخدم في تطبيقنا.
حتى يتمكن برنامجنا نفسه من إخبارنا بما "يؤلمنا" ، سوف نأخذ وحدة
LogRock (github) وربطها بـ ElasticSearch و LogStash و Kibana.
ElasticSearch هو محرك بحث كامل النص الكامل. يمكنك مشاهدة البرنامج التعليمي ElasticSearch
هنا .
LogStash هو نظام لجمع السجلات من مصادر مختلفة ، والتي يمكن أن ترسل السجلات ، بما في ذلك إلى ElasticSearch.
Kibana هي واجهة ويب لـ ElasticSearch مع العديد من الإضافات.
كيف يعمل؟
في حالة وجود خطأ (أو فقط عند الطلب) ، يرسل التطبيق سجلات إلى الخادم حيث يتم حفظها في ملف. Logstash يحفظ بشكل متزايد البيانات في ElasticSearch - إلى قاعدة البيانات. يقوم المستخدم بتسجيل الدخول إلى Kibana ويرى السجلات المحفوظة.
يبدو وكأنه Kibana ضبطها جيدا. يعرض البيانات من ElasticSearch. يمكن لـ Kibana عرض البيانات في شكل جداول ورسوم بيانية وخرائط وما إلى ذلك ، وهو مناسب جدًا لتحليل وفهم ما يحدث في طلبنا.
في هذه المقالة ، لن أناقش إعداد ElasticStack!إنشاء نظام تسجيل
على سبيل المثال ، سنقوم بدمج نظام التسجيل في تطبيق JS من صفحة واحدة مكتوب في React. لا يهم حقًا الإطار الذي سيتم كتابة طلبك عليه. سأحاول وصف طريقة بناء نظام السجل نفسه.
1. العملاء
1.0 LogRock. تركيب
رابط لوغروكلتثبيت ، يجب إجراء:
npm install logrock yarn add logrock
1.1 LogRock. إعداد التطبيق
للبدء ، لف تطبيقنا في مكون
import { LoggerContainer } from "logrock"; <LoggerContainer> <App /> </LoggerContainer>
LoggerContainer هو مكون يستجيب لأخطاء التطبيق الخاص بك ويشكل مكدس.
المكدس هو كائن يحتوي على معلومات حول نظام تشغيل المستخدم أو المستعرض أو زر الماوس أو لوحة المفاتيح الذي تم الضغط عليه ، وبالطبع صفيف الإجراءات الفرعي ، حيث يتم تسجيل جميع إجراءات المستخدم التي قام بها في نظامنا.
يحتوي LoggerContainer على عدد من الإعدادات ، فكر في بعضها
<LoggerContainer active={true|false} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
نشط - تمكين أو تعطيل المسجل
limit - يحدد حدًا لعدد الإجراءات الحديثة المحفوظة بواسطة المستخدم. إذا كان المستخدم ينفذ 21 إجراء ، فسيتم تلقائيًا حذف الإجراء الأول في هذه المجموعة. وبالتالي ، سيكون لدينا الإجراءات العشرين الأخيرة التي سبقت الخطأ.
onError - رد الاتصال الذي يتم استدعاء عند حدوث خطأ. يأتي كائن Stack فيه ، حيث يتم تخزين جميع المعلومات حول البيئة ، وإجراءات المستخدم ، وما إلى ذلك. من خلال رد الاتصال هذا ، نحتاج إلى إرسال هذه البيانات إلى ElasticSearch أو الواجهة الخلفية ، أو حفظها في ملف لمزيد من التحليل والمراقبة.
1.2 لوجروك. تسجيل
من أجل إجراء تسجيل عالي الجودة لإجراءات المستخدم ، سيتعين علينا تغطية التعليمات البرمجية الخاصة بنا بمكالمات السجل.
الوحدة النمطية LogRock تأتي مع مسجل مقترن LoggerContainer
لنفترض أن لدينا عنصر
import React, { useState } from "react"; export default function Toggle(props) { const [toggleState, setToggleState] = useState("off"); function toggle() { setToggleState(toggleState === "off" ? "on" : "off"); } return <div className={`switch ${toggleState}`} onClick={toggle} />; }
من أجل تغطيته بشكل صحيح بسجل ، نحتاج إلى تعديل طريقة التبديل
function toggle() { let state = toggleState === "off" ? "on" : "off"; logger.info(`React.Toggle|Toggle component changed state ${state}`); setToggleState(state); }
لقد أضفنا المسجل الذي تنقسم فيه المعلومات إلى جزأين. React.Toggle يوضح لنا أن هذا الإجراء قد حدث على مستوى React ، المكون Toggle ، ثم لدينا تفسير شفهي للعمل والحالة الحالية التي جاءت إلى هذا المكون. مثل هذا الفصل إلى المستويات ليس ضروريًا ، ولكن مع هذا النهج ، سيكون أكثر وضوحًا حيث تم تنفيذ الكود بالضبط.
يمكننا أيضًا استخدام طريقة "componentDidCatch" ، والتي تم تقديمها في React الإصدار 16 ، في حالة حدوث خطأ.
2. خادم التفاعل
النظر في المثال التالي.
افترض أن لدينا طريقة تجمع بيانات المستخدم من الواجهة الخلفية. الطريقة غير متزامنة ، يتم إخفاء جزء من المنطق في الواجهة الخلفية. كيفية تسجيل هذا الرمز بشكل صحيح؟
أولاً ، نظرًا لأن لدينا تطبيق عميل ، فستذهب جميع الطلبات الموجهة إلى الخادم خلال جلسة مستخدم واحدة ، دون إعادة تحميل الصفحة. من أجل ربط الإجراءات على العميل بإجراءات على الخادم ، يجب علينا إنشاء SessionID عالمي وإضافته إلى الرأس لكل طلب إلى الخادم. على الخادم ، يمكننا استخدام أي مسجل من شأنه أن يغطي منطقنا مثل مثال من الواجهة الأمامية ، وفي حالة وجود خطأ إرسال هذه البيانات مع الجلسة المرفقة معرف إلى مطاط إلى لوحة الخلفية.
1. نولد SessionID على العميل
window.SESSION_ID = `sessionid-${Math.random().toString(36).substr(3, 9)}`;
2. يجب علينا تعيين SessionID لجميع الطلبات إلى الخادم. إذا كنا نستخدم المكتبات للاستعلامات ، فهذا أمر بسيط للغاية من خلال الإعلان عن SessionID لجميع الاستعلامات.
let fetch = axios.create({...}); fetch.defaults.headers.common.sessionId = window.SESSION_ID;
3. في LoggerContainer ، يوجد حقل خاص لـ SessionID
<LoggerContainer active={true|false} sessionID={window.SESSION_ID} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
4. سيبدو الطلب نفسه (على العميل) كما يلي:
logger.info(`store.getData|User is ready for loading... User ID is ${id}`); getData('/api/v1/user', { id }) .then(userData => { logger.info(`store.getData|User have already loaded. User count is ${JSON.stringify(userData)}`); }) .catch(err => { logger.error(`store.getData|User loaded fail ${err.message}`); });
كيف سيعمل كل شيء: نقوم بتسجيل الدخول قبل الطلب على العميل. وفقًا لقواعدنا ، نرى أن تحميل البيانات من الخادم سيبدأ الآن. لقد أرفقت SessionID لهذا الطلب. إذا تمت تغطية الخلفية الخاصة بنا بسجلات مع إضافة SessionID وفشل الطلب ، فيمكننا أن نرى ما حدث على الواجهة الخلفية.
وبالتالي ، نحن نراقب دورة تطبيقنا بالكامل ، ليس فقط على العميل ، ولكن أيضًا على الواجهة الخلفية.
3. اختبار
العمل مع اختبار يستحق وصفا منفصلا لهذه العملية.
نظرًا لأن لدينا شركة ناشئة ، فليس لدينا متطلبات رسمية وأحيانًا لا يكون كل شيء منطقيًا في العمل.
إذا لم يفهم المختبر السلوك ، فهذه هي الحالة التي يجب أخذها بعين الاعتبار على الأقل. أيضا ، في كثير من الأحيان ، اختبار ببساطة لا يمكن تكرار موقف واحد مرتين. منذ الخطوات التي تؤدي إلى سلوك غير صحيح يمكن أن تكون عديدة وغير تافهة. علاوة على ذلك ، ليست كل الأخطاء تؤدي إلى عواقب وخيمة ، مثل استثناء. يمكن لبعضهم فقط تغيير سلوك التطبيق ، ولكن لا يمكن تفسيره من قبل النظام على أنه خطأ. لهذه الأغراض ، عند التدريج ، يمكنك إضافة زر في رأس التطبيق لفرض إرسال السجلات. يرى المختبر أن شيئًا ما لا يعمل بشكل صحيح ، وينقر على الزر ويرسل Stack مع الإجراءات إلى ElasticSearch.
ومع ذلك ، إذا حدث خطأ فادح ، فيجب علينا حظر الواجهة حتى لا ينقر المختبر أكثر من ذلك ولا يصل إلى طريق مسدود.
لهذه الأغراض ، نعرض شاشة الموت الزرقاء.
نرى في الجزء العلوي النص مع كومة من هذا الخطأ الحرج ، وأدناه - الإجراءات التي سبقته. نحصل أيضًا على معرف الخطأ ، وهو ما يكفي للاختبار لتحديده وإرفاقه في التذكرة. في وقت لاحق ، يمكن العثور على هذا الخطأ بسهولة في Kibana بواسطة هذا المعرف.
لهذه الأغراض ، LoggerContainer له خصائصه الخاصة.
<LoggerContainer active={true|false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
bsodActive - تمكين / تعطيل BSOD (تعطيل BSOD ينطبق على رمز الإنتاج)
الموت الزرقاء هو عنصر. بشكل افتراضي ، يبدو أن لقطة الشاشة أعلاه.
لعرض زر في واجهة مستخدم LoggerContainer ، يمكننا استخدامه في السياق
context.logger.onError(context.logger.getStackData());
4. LogRock. تفاعل المستخدم
يمكنك إخراج السجلات إلى وحدة التحكم أو إظهارها للمستخدم ، لذلك تحتاج إلى استخدام طريقة stdout:
<LoggerContainer active={true|false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack); }} stdout={(level, message, important) => { console[level](message); if (important) { alert(message); } }} > <App /> </LoggerContainer>
stdout هي طريقة مسؤولة عن عرض الرسائل.
لكي تصبح الرسالة مهمة ، يكفي تمرير المعلمة الثانية إلى المسجل. وبالتالي ، يمكن عرض هذه الرسالة للمستخدم في نافذة منبثقة ، على سبيل المثال ، إذا فشل تحميل البيانات ، يمكننا عرض رسالة خطأ.
logger.log('Something was wrong', true);
قطع الأشجار المتقدمة
إذا كنت تستخدم Redux ، أو حلولاً مماثلة مع متجر واحد ، فيمكنك وضع مسجل في الوسيطة لمعالجة الإجراءات الخاصة بك ، وبالتالي ، سوف تمر جميع الإجراءات الهامة من خلال نظامنا.
للتسجيل الفعال ، يمكنك لف البيانات الخاصة بك في كائن وكيل ، ووضع قطع الأشجار على جميع الإجراءات مع الكائن.
لتغطية طرق الطرف الثالث من خلال التسجيل (أساليب المكتبة ، طرق الرمز القديم) ، يمكنك استخدام أدوات الديكور - "@".
نصائح
سجل التطبيقات ، بما في ذلك على الإنتاج ، لأنه أفضل من المستخدمين الحقيقيين ، لن يجد أي اختبار أي اختناقات.
لا تنس الإشارة إلى مجموعة السجلات في اتفاقية الترخيص.
لا تسجل كلمات المرور ، التفاصيل المصرفية وغيرها من المعلومات الشخصية!التكرار من السجلات هو أيضا سيئة ، وجعل التوقيعات واضحة قدر الإمكان.
البدائل
مع اقتراب الحلول البديلة ، أسلط الضوء على:
- Rollbar هو للتخصيص للغاية. يتيح لك تسجيل 500 ألف خطأ مقابل 150 دولارًا شهريًا. أوصي باستخدامه إذا كنت تقوم بتطوير تطبيق من البداية.
- ترقب أسهل للتكامل ، ولكن أقل للتخصيص. يتيح لك تسجيل مليون حدث مقابل 200 دولار شهريًا.
تتيح لك كلتا الخدمتين فعل الشيء نفسه تقريبًا والاندماج في الواجهة الخلفية.
ما التالي
تسجيل الدخول ليس فقط البحث عن الأخطاء ، إنه يراقب أيضًا إجراءات المستخدم ، وجمع البيانات. يمكن أن يكون التسجيل مكملاً جيدًا لاختبار Google Analytics واختبار تجربة المستخدم.
النتائج
عندما تطلق التطبيق ، فالحياة بدأت للتو. كن مسؤولاً عن بنات أفكارك ، واحصل على ملاحظات ، راقب سجلاتك وقم بتحسينه. اكتب برامج عالية الجودة وازدهار :)
PS إذا كنت ترغب في المساعدة في تطوير وحدات لالزاوي ، Vue ، الخ سأكون سعيدًا بسحب الطلبات -
هنا .