تخصيص تقارير أعطال Firebase في Crashlytics

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

  • يمكنك الحصول تلقائيًا على سجلّات شريط التنقّل إذا كان تطبيقك يستخدم حزمة تطوير البرامج (SDK) لمنصة Firebase لبرنامج "إحصاءات Google". تمنحك هذه السجلّات إمكانية الاطّلاع على إجراءات المستخدم التي تؤدي إلى الحدث الذي جمعه Crashlytics في تطبيقك.

  • أوقِف الإبلاغ التلقائي عن الأعطال وفعِّل تقارير الموافقة للمستخدمين. يجمع Crashlytics تلقائيًا تقارير الأعطال لجميع مستخدمي التطبيق تلقائيًا.

إضافة مفاتيح مخصّصة

تساعدك المفاتيح المخصّصة في معرفة الحالة المحدّدة لتطبيقك التي تؤدي إلى حدوث عُطل. يمكنك ربط أزواج المفتاح/القيمة العشوائية بتقارير الأعطال، ثم استخدام المفاتيح المخصّصة للبحث في تقارير الأعطال وفلترتها في "وحدة تحكُّم Firebase".

  • في لوحة بيانات Crashlytics، يمكنك البحث عن المشاكل التي تتطابق مع مفتاح مخصّص.
  • عند مراجعة مشكلة معيّنة في وحدة التحكّم، يمكنك عرض المفاتيح المخصّصة المرتبطة لكل حدث (علامة التبويب الفرعية المفاتيح) وكذلك فلترة الأحداث حسب مفاتيح مخصَّصة (قائمة الفلترة في أعلى الصفحة).

استخدِم الطريقة setCustomValue لضبط أزواج المفتاح/القيمة. على سبيل المثال:

Swift

// Set int_key to 100.
Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set str_key to "hello".
Crashlytics.crashlytics().setCustomValue("hello", forKey: "str_key")

Objective-C

عند ضبط الأعداد الصحيحة أو القيم المنطقية أو الأعداد العائمة، ضَع القيمة في مربّع القيمة @(value).

// Set int_key to 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set str_key to "hello".
[[FIRCrashlytics crashlytics] setCustomValue:@"hello" forKey:@"str_key"];

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

Swift

Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set int_key to 50 from 100.
Crashlytics.crashlytics().setCustomValue(50, forKey: "int_key")

Objective-C

[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set int_key to 50 from 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(50) forKey:@"int_key"];

أضِف أزواج المفتاح/القيمة بشكل مجمّع باستخدام طريقة setCustomKeysAndValues مع NSDictionary باعتبارها المعلمة الوحيدة:

Swift

let keysAndValues = [
                 "string key" : "string value",
                 "string key 2" : "string value 2",
                 "boolean key" : true,
                 "boolean key 2" : false,
                 "float key" : 1.01,
                 "float key 2" : 2.02
                ] as [String : Any]

Crashlytics.crashlytics().setCustomKeysAndValues(keysAndValues)

Objective-C

NSDictionary *keysAndValues =
    @{@"string key" : @"string value",
      @"string key 2" : @"string value 2",
      @"boolean key" : @(YES),
      @"boolean key 2" : @(NO),
      @"float key" : @(1.01),
      @"float key 2" : @(2.02)};

[[FIRCrashlytics crashlytics] setCustomKeysAndValues: keysAndValues];

إضافة رسائل سجلّ مخصّصة

لمنح نفسك المزيد من السياق للأحداث التي أدت إلى حدوث تعطُّل، يمكنك إضافة سجلات Crashlytics المخصّصة إلى تطبيقك. ويربط Crashlytics السجلات ببيانات العُطل ويعرضها في صفحة Crashlytics ضمن وحدة تحكُّم Firebase ضمن علامة التبويب السجلّات.

Swift

يمكنك استخدام log() أو log(format:, arguments:) للمساعدة على تحديد المشاكل. إذا كنت تريد الحصول على مخرجات مفيدة من السجلّ للرسائل، يجب أن يتوافق الكائن الذي تمرِّره إلى log() مع السمة CustomStringConvertible. تعرض log() خاصية الوصف التي تحددها للكائن. على سبيل المثال:

Crashlytics.crashlytics().log("Higgs-Boson detected! Bailing out…, \(attributesDict)")

قيم تنسيقات .log(format:, arguments:) التي تم إرجاعها من طلب getVaList(). على سبيل المثال:

Crashlytics.crashlytics().log(format: "%@, %@", arguments: getVaList(["Higgs-Boson detected! Bailing out…", attributesDict]))

لمزيد من التفاصيل حول كيفية استخدام log() أو log(format:, arguments:)، يمكنك الاطّلاع على المستندات المرجعية الخاصة بأداة Crashlytics.

Objective-C

يمكنك استخدام log أو logWithFormat للمساعدة على تحديد المشاكل. يُرجى العِلم أنّك إذا كنت تريد الحصول على نتائج سجلّ مفيدة تتضمّن الرسائل، يجب أن يتجاوز الكائن الذي تمرِّره إلى أي من الطريقتين خاصية النسخة الافتراضية description. على سبيل المثال:

[[FIRCrashlytics crashlytics] log:@"Simple string message"];

[[FIRCrashlytics crashlytics] logWithFormat:@"Higgs-Boson detected! Bailing out... %@", attributesDict];

[[FIRCrashlytics crashlytics] logWithFormat:@"Logging a variable argument list %@" arguments:va_list_arg];

لمزيد من التفاصيل حول كيفية استخدام log وlogWithFormat، يمكنك الاطّلاع على المستندات المرجعية الخاصة بأداة Crashlytics.

ضبط معرّفات المستخدمين

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

لإضافة أرقام تعريف المستخدمين إلى تقاريرك، عليك تخصيص معرّف فريد لكل مستخدم على شكل رقم تعريف أو رمز مميّز أو قيمة مجزّأة:

Swift

Crashlytics.crashlytics().setUserID("123456789")

Objective-C

[[FIRCrashlytics crashlytics] setUserID:@"123456789"];

إذا احتجت في أي وقت إلى محو معرّف المستخدم بعد تعيينه، يمكنك إعادة ضبط القيمة على سلسلة فارغة. لا يؤدي محو معرّف المستخدم إلى إزالة سجلات Crashlytics الحالية. إذا كنت بحاجة إلى حذف السجلات المرتبطة برقم تعريف المستخدم، يمكنك التواصل مع فريق دعم Firebase.

الإبلاغ عن الاستثناءات غير الفادحة

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

يمكنك تسجيل الاستثناءات غير الفادحة من خلال تسجيل عناصر NSError باستخدام طريقة recordError. يلتقط recordError حزمة المكالمات الخاصة بسلسلة المحادثات من خلال طلب الرقم [NSThread callStackReturnAddresses].

Swift

Crashlytics.crashlytics().record(error: error)

Objective-C

[[FIRCrashlytics crashlytics] recordError:error];

عند استخدام طريقة recordError، من المهم فهم بنية NSError وكيفية استخدام Crashlytics للبيانات لتجميع الأعطال. فالاستخدام غير الصحيح لطريقة recordError يمكن أن يؤدي إلى سلوك غير متوقّع وقد يتسبب في تقييد Crashlytics للتطبيق من الإبلاغ عن الأخطاء المسجَّلة.

يتضمّن كائن NSError ثلاث وسيطات:

  • domain: String
  • code: Int
  • userInfo: [AnyHashable : Any]? = nil

على عكس الأعطال الفادحة التي يتم تجميعها من خلال تحليل تتبُّع تسلسل استدعاء الدوال البرمجية، يتم تجميع الأخطاء المسجَّلة حسب domain وcode. يعد ذلك فرقًا مهمًا بين الأعطال الفادحة والأخطاء المسجلة. على سبيل المثال:

Swift

let userInfo = [
  NSLocalizedDescriptionKey: NSLocalizedString("The request failed.", comment: ""),
  NSLocalizedFailureReasonErrorKey: NSLocalizedString("The response returned a 404.", comment: ""),
  NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString("Does this page exist?", comment: ""),
  "ProductID": "123456",
  "View": "MainView"
]

let error = NSError.init(domain: NSCocoaErrorDomain,
                         code: -1001,
                         userInfo: userInfo)

Objective-C

NSDictionary *userInfo = @{
  NSLocalizedDescriptionKey: NSLocalizedString(@"The request failed.", nil),
  NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The response returned a 404.", nil),
  NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Does this page exist?", nil),
  @"ProductID": @"123456",
  @"View": @"MainView",
};

NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
                                     code:-1001
                                 userInfo:userInfo];

عند تسجيل الخطأ أعلاه، يؤدي ذلك إلى ظهور مشكلة جديدة تم تجميعها حسب NSSomeErrorDomain و-1001. يتم تجميع الأخطاء المُسجَّلة الإضافية التي تستخدم نفس قيم النطاق والرموز ضمن المشكلة نفسها. ويتم تحويل البيانات المضمّنة في الكائن userInfo إلى أزواج المفتاح/القيمة وعرضها في قسم المفاتيح/السجلات ضمن مشكلة فردية.

السجلات والمفاتيح المخصصة

يمكنك تضمين السجلّات والمفاتيح المخصّصة لإضافة سياق إلى NSError، تمامًا مثل تقارير الأعطال. ومع ذلك، هناك فرق بين السجلات المرفقة بالأعطال مقابل الأخطاء المسجلة. عند حدوث عطل وإعادة تشغيل التطبيق، تكون السجلات التي يستردها Crashlytics من القرص هي السجلات التي تمت كتابتها حتى وقت حدوث العطل. عند تسجيل NSError، لا يتم إنهاء التطبيق على الفور. ولأنّ Crashlytics لا ترسل تقرير الخطأ المُسجَّل سوى عند إطلاق التطبيق التالي ويجب أن تحد من مقدار المساحة المخصّصة للسجلات على القرص، من الممكن تسجيل بيانات كافية بعد تسجيل NSError بحيث يتم تدوير جميع السجلات ذات الصلة بحلول الوقت الذي يُرسل فيه Crashlytics التقرير من الجهاز. يُرجى تذكُّر هذا التوازن عند تسجيل NSErrors واستخدام السجلّات والمفاتيح المخصّصة في تطبيقك.

اعتبارات الأداء

يُرجى العِلم أنّ تسجيل NSError يمكن أن يكون مكلفًا نوعًا ما. في أثناء إجراء المكالمة، يلتقط Crashlytics حزمة الاتصالات في سلسلة التعليمات الحالية باستخدام عملية تُسمّى تفكيك تسلسل استدعاء الدوال البرمجية. قد تستهلك هذه العملية قدرًا كبيرًا من طاقة وحدة المعالجة المركزية (CPU) ووحدات الإدخال والإخراج، خاصةً على البُنى التي تعتمد على فكّ DWARF (arm64 وx86). بعد اكتمال عملية الاسترخاء، تتم كتابة المعلومات على القرص بشكل متزامن. وهذا يمنع فقدان البيانات إذا تعطل السطر التالي.

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

ماذا عن NSExceptions؟

لا يوفّر Crashlytics مرفقًا لتسجيل مثيلات NSException وتسجيلها مباشرةً. بشكل عام، إنّ واجهات برمجة التطبيقات Cocoa وCocoa Touch ليست آمنة بشكل استثنائي. وهذا يعني أنّ استخدام @catch يمكن أن يؤدي إلى آثار جانبية خطيرة جدًا غير مقصودة في العملية، حتى عند استخدامها بحذر شديد. لا يجوز أبدًا استخدام عبارات @catch في الرمز الخاص بك. يُرجى مراجعة مستندات Apple حول هذا الموضوع.

تخصيص عمليات تتبُّع تسلسل استدعاء الدوال البرمجية

إذا كان تطبيقك يعمل في بيئة غير أصلية (مثل C++ أو Unity)، يمكنك استخدام Exception Model API للإبلاغ عن البيانات الوصفية للأعطال بتنسيق الاستثناء الأصلي لتطبيقك. تم وضع علامة على الاستثناءات التي تم الإبلاغ عنها على أنّها غير خطيرة.

Swift

var  ex = ExceptionModel(name:"FooException", reason:"There was a foo.")
ex.stackTrace = [
  StackFrame(symbol:"makeError", file:"handler.js", line:495),
  StackFrame(symbol:"then", file:"routes.js", line:102),
  StackFrame(symbol:"main", file:"app.js", line:12),
]

crashlytics.record(exceptionModel:ex)

Objective-C

FIRExceptionModel *model =
    [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."];
model.stackTrace = @[
  [FIRStackFrame stackFrameWithSymbol:@"makeError" file:@"handler.js" line:495],
  [FIRStackFrame stackFrameWithSymbol:@"then" file:@"routes.js" line:102],
  [FIRStackFrame stackFrameWithSymbol:@"main" file:@"app.js" line:12],
];

[[FIRCrashlytics crashlytics] recordExceptionModel:model];

يمكن أيضًا تهيئة إطارات تسلسل استدعاء الدوال البرمجية المخصصة باستخدام عناوين فقط:

Swift

var  ex = ExceptionModel.init(name:"FooException", reason:"There was a foo.")
ex.stackTrace = [
  StackFrame(address:0xfa12123),
  StackFrame(address:12412412),
  StackFrame(address:194129124),
]

crashlytics.record(exceptionModel:ex)

Objective-C

FIRExceptionModel *model =
    [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."];
model.stackTrace = @[
  [FIRStackFrame stackFrameWithAddress:0xfa12123],
  [FIRStackFrame stackFrameWithAddress:12412412],
  [FIRStackFrame stackFrameWithAddress:194129124],
];


[[FIRCrashlytics crashlytics] recordExceptionModel:model];

الحصول على سجلّات شريط التنقّل

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

يتم تشغيل سجلات شريط التنقّل من خلال "إحصاءات Google"، لذا للحصول على سجلّات شريط التنقّل، عليك تفعيل "إحصاءات Google" لمشروعك في Firebase وإضافة حزمة تطوير البرامج (SDK) لمنصة Firebase لخدمة "إحصاءات Google" إلى تطبيقك. وبعد استيفاء هذه المتطلبات، يتم تضمين سجلّات شريط التنقّل تلقائيًا مع بيانات الحدث ضمن علامة التبويب السجلات عند عرض تفاصيل مشكلة.

تُسجِّل حزمة تطوير البرامج (SDK) لخدمة "إحصاءات Google" حدث screen_view تلقائيًا الذي يتيح لسجلّات شريط التنقّل عرض قائمة بالشاشات التي تم عرضها قبل حدث التعطُّل أو غير الفادح أو حدث خطأ ANR. يحتوي سجلّ شريط التنقّل screen_view على معلَمة firebase_screen_class.

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

تجدر الإشارة إلى أنّه يمكنك التحكّم في جمع بيانات "إحصاءات Google" واستخدامها، التي تتضمّن البيانات التي تعمل على تعبئة سجلات شريط التنقّل.

تفعيل إعداد تقارير الموافقة

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

  1. يمكنك إيقاف ميزة "الجمع التلقائي للبيانات" من خلال إضافة مفتاح جديد إلى ملف Info.plist:

    • المفتاح: FirebaseCrashlyticsCollectionEnabled
    • القيمة: false
  2. يمكنك تفعيل عملية جمع البيانات لمستخدمين محدَّدين من خلال استدعاء ميزة "تجاوز جمع البيانات في Crashlytics" في وقت التشغيل. تظل قيمة الإلغاء موجودة في جميع إصدارات التطبيق، حتى يتمكن تطبيق Crashlytics من جمع التقارير تلقائيًا.

    لإيقاف ميزة إعداد تقارير الأعطال التلقائية، اختَر القيمة false كقيمة الإلغاء. عند ضبط القيمة على false، لا تنطبق القيمة الجديدة حتى التشغيل التالي للتطبيق.

    Swift

    Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)

    Objective-C

    [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:YES];

إدارة بيانات "إحصاءات الأعطال"

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

تستخدم أداة "إحصاءات الأعطال" بيانات الأعطال المجمَّعة لتحديد مؤشرات الثبات الشائعة. إذا كنت تفضّل عدم مشاركة بيانات تطبيقك، يمكنك إيقاف ميزة "إحصاءات الأعطال" من قائمة إحصاءات الأعطال في أعلى قائمة مشاكل Crashlytics في وحدة تحكُّم Firebase.