تتبّع أعطال تطبيقات Flutter: دليل عملي للإعداد
معظم المستخدمين لا يبلّغون عن الأعطال، بل يغادرون. أعدّ تتبّع الأعطال في تطبيق Flutter خلال دقائق: ما الذي يُلتقط، وكيف تضيف السياق، وكيف تتخلص من الضجيج.
5 دقائق قراءة

معظم المستخدمين لا يبلّغون عن الأعطال، بل يغادرون. أعدّ تتبّع الأعطال في تطبيق Flutter خلال دقائق: ما الذي يُلتقط، وكيف تضيف السياق، وكيف تتخلص من الضجيج.
5 دقائق قراءة


لدى مستخدمك هاتف وحاسوب وتبويب في المتصفح. توقّف عن تخزين رموز الأجهزة: اربط الأجهزة بمعرّف المستخدم في نظامك وأرسل مرة واحدة إلى iOS وأندرويد والويب وmacOS.
5 دقائق قراءة

إصدار قديم يتعطل مع الـ API الجديدة، أو الخادم يحتاج ساعة صيانة. اضبط قواعد التحديث ونوافذ الصيانة من لوحة التحكم وستلتزم بها كل التطبيقات خلال دقائق.
معظم من يواجه عطلاً في تطبيقك لن يخبرك به. يغلق التطبيق ويحاول مرة أخرى، وإذا تكرر العطل يحذفه أو يترك تقييماً بنجمة واحدة يقول "لا يعمل". وحين يصلك الخبر يكون الخلل قد بقي أياماً بين يدي المستخدمين.
تتبّع الأعطال يقلب هذه المعادلة: كل خطأ لم يُعالَج يصلك مع مسار الاستدعاء (stack trace) والجهاز ورقم الإصدار، ومجمّعاً بحيث يظهر الخلل الذي تكرر عشرة آلاف مرة كمشكلة واحدة. يشرح هذا الدليل إعداده في تطبيق Flutter بحزمة lerix_flutter، وما الذي يُلتقط تلقائياً وما الذي لا يُلتقط، وكيف تحافظ على تقارير مفيدة.
أضِف lerix_flutter إلى ملف pubspec.yaml ثم نزّلها:
dependencies:
lerix_flutter: ^0.7.0
flutter pub get
تتبّع الأعطال يحتاج هذه الحزمة وحدها. الإشعارات (lerix_messaging) والتحديث داخل التطبيق من Google Play (lerix_app_update) حزمتان منفصلتان، فلا تحمل التطبيقات التي لا تستخدمهما شيفرتهما. وإذا أضفتهما لاحقاً فاجعل الحزم الثلاث على الإصدار نفسه.
تعمل الحزمة على أندرويد وiOS وmacOS وWindows والويب.
main()أنشئ مشروعاً في لوحة تحكم ليركس وانسخ مفتاح الـ API والاسم المختصر (slug) من إعدادات المشروع. ثم مرّر استدعاء runApp إلى Lerix.init:
import 'package:flutter/material.dart';
import 'package:lerix_flutter/lerix_flutter.dart';
void main() {
Lerix.init(
url: 'https://api.lerix.dev/v1/your-project-slug',
apiKey: 'your-api-key',
projectId: 'your-project-slug',
builder: () => runApp(const MyApp()),
);
}
تفصيلتان توفّران عليك الوقت:
projectId هو الاسم المختصر للمشروع، أي الاسم القصير الظاهر في رابط لوحة التحكم، وليس المعرّف الطويل.runZonedGuarded. تتولى Lerix.init إعداد منطقة الأخطاء (zone) وWidgetsFlutterBinding بنفسها ثم تستدعي builder. وإذا فشل الإعداد (لعدم وجود اتصال عند أول تشغيل مثلاً) يعمل تطبيقك كالمعتاد.بعد تشغيل init تُرسَل ثلاثة أنواع من الأخطاء غير المعالَجة كأعطال، على كل المنصات بما فيها Flutter Web:
FlutterError.onError: أخطاء البناء والتخطيط والرسم. وتبقى مطبوعة في وحدة التحكم في نسخ التطوير، فلا يتغير شيء في طريقة عملك.PlatformDispatcher.onError، مثل استثناء داخل دالة async لم ينتظرها أحد.يحمل كل تقرير طراز الجهاز ونظام التشغيل وإصداره ومعمارية المعالج والمنطقة الزمنية والدولة ورقم إصدار تطبيقك ورقم البناء، وتُقرأ من التطبيق نفسه. ويحمل أيضاً معرّف الجلسة الحالية، وبه تعرض لوحة التحكم نسبة الجلسات والمستخدمين الخالين من الأعطال لكل إصدار.
الاستثناء الذي تلتقطه بنفسك كثيراً ما يكون الأفيد، لأنك تعرف ماذا كان المستخدم يفعل. أرسِله بـ Lerix.throwError وأرفق ما يساعدك على إعادة إنتاجه:
try {
await payments.charge(cart);
} catch (e, stack) {
await Lerix.throwError(
e,
stack,
bugSeverity: BugSeverity.high,
metaData: {
'cartItems': cart.items.length,
'paymentMethod': 'card',
},
);
showPaymentFailed();
}
لا ترمي throwError أي استثناء: إذا تعذّر الإرسال يُسجَّل ذلك ويستمر تطبيقك. ودرجة الخطورة إحدى: critical أو high أو medium أو low، ويمكنك تحديد نوع الخطأ bugType مثل BugType.networkError أو BugType.validationError لتصفّي به لاحقاً.
"واجه هذا الخطأ 312 مستخدماً" أولويته غير "واجهه مختبِر واحد". أخبر ليركس بالمستخدم الذي سجّل دخوله، وامسحه عند الخروج:
// بعد تسجيل الدخول
await Lerix.setUser(user.id);
// عند تسجيل الخروج
await Lerix.clearUser();
استخدم معرّف المستخدم الثابت في نظامك، لا البريد ولا رقم الهاتف. عندها يرتبط كل تقرير من ذلك الجهاز بالشخص، والمعرّف نفسه يتيح لك لاحقاً إرسال إشعار إليه على كل أجهزته.
بعض الأخطاء لا تستحق تنبيهاً: انقطاع اتصال في القطار، أو حزمة خارجية تسجّل خطأً بدل أن تفشل. خياران في Lerix.init يعالجان ذلك على الجهاز نفسه:
Lerix.init(
url: 'https://api.lerix.dev/v1/your-project-slug',
apiKey: 'your-api-key',
projectId: 'your-project-slug',
ignoreErrors: [
'SocketException', // "يحتوي" دون تمييز حالة الأحرف
RegExp(r'^HttpException: 40[13]'),
],
beforeSend: (event) {
// تجاهل تقارير الاختبارات الخاصة بك
if (event.metadata?['test'] == true) return null;
// احذف أي رمز دخول وصل إلى نص الرسالة
return event.copyWith(
message: event.message.replaceAll(RegExp(r'token=\S+'), 'token=***'),
);
},
builder: () => runApp(const MyApp()),
);
يعمل ignoreErrors أولاً ثم beforeSend. والنص الذي تطابق عليه هو عنوان المشكلة، ويتضمن اسم صنف الخطأ. إرجاع null من beforeSend يُسقط التقرير بصمت. وإذا رمت دالة beforeSend نفسها استثناءً يُرسَل التقرير الأصلي، فلا يُخفي خلل في المرشّح عطلاً حقيقياً.
ويضيف الخادم بعض القواعد من عنده. مثلاً رسالة Flutter المعروفة A RenderFlex overflowed by… تبقى مسجّلة، لكن كخلل واجهة منخفض الخطورة بدل عطل حرج.
نسخ الإصدار من Flutter Web مصغّرة (minified)، فيبدو مسار الاستدعاء هكذا: main.dart.js:1:48213. ابنِ التطبيق بالخيار --source-maps وارفع الخرائط بعد كل بناء، ويشرح دليل خرائط المصدر ذلك خطوة بخطوة. أما نسخ أندرويد وiOS فلا تحتاج أي إعداد إضافي.
لا تصلك التقارير كسيل من النسخ المكررة. يجمعها ليركس في مشكلات حسب السطر الأول من الرسالة (بعد إخفاء الأرقام والمعرّفات والبريد والروابط) وأول إطار في مسار الاستدعاء من شيفرتك أنت، دون النظر إلى أرقام الأسطر. فرسالتا Order 1234 not found وOrder 5678 not found من الدالة نفسها مشكلة واحدة.
تعرض كل مشكلة عدد مرات حدوثها، والمستخدمين المتأثرين، وأول ظهور وآخر ظهور، ومسار الاستدعاء كاملاً مع تفاصيل الجهاز والتطبيق. ومن هناك يمكنك أن:
lerix_flutter: ^0.7.0 في pubspec.yaml.Lerix.init في main() بمفتاح الـ API والاسم المختصر للمشروع، وrunApp داخل builder.Lerix.throwError في كتل catch المهمة.Lerix.setUser بعد الدخول وLerix.clearUser عند الخروج.ignoreErrors وbeforeSend للضجيج الذي تعرفه مسبقاً.ثم أوقِع التطبيق عمداً مرة واحدة بزر يرمي خطأً، وتأكد أنه ظهر في لوحة التحكم. يستغرق ذلك خمس دقائق، وهو الطريقة الوحيدة لتطمئن أن العطل الحقيقي القادم سيصلك.
6 دقائق قراءة

السطر index-8f3a1c.js:1:48213 لا يخبرك بشيء. ارفع خرائط المصدر من البناء ليشير كل خطأ إلى ملفك وسطرك ودالتك، وتعرّف على طريقة تجميع الأخطاء في مشكلات.
6 دقائق قراءة