Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

فك التغليف باستخدام `errors.Is`

جزء من قسم Logic & Flow في رحلة GO على Coddy — الدرس 40 من 68.

عند العمل مع الأخطاء المغلفة (wrapped errors)، فأنت بحاجة إلى طريقة للتحقق مما إذا كان هناك خطأ معين موجوداً في أي مكان ضمن سلسلة الأخطاء. توفر الدالة errors.Is هذه الإمكانية تماماً، مما يسمح لك بتحديد أخطاء الحراسة (sentinel errors) حتى عندما يتم تغليفها عدة مرات.

تأخذ الدالة errors.Is معاملين: الخطأ الذي تريد فحصه والخطأ المستهدف الذي تبحث عنه. وتعيد القيمة true إذا تم العثور على الخطأ المستهدف في أي مكان ضمن سلسلة الأخطاء:

var ErrNotFound = errors.New("not found")

wrappedErr := fmt.Errorf("failed to load user: %w", ErrNotFound)

if errors.Is(wrappedErr, ErrNotFound) {
    fmt.Println("This is a not found error")
}

هذه هي الطريقة الحديثة والموصى بها للتحقق من أخطاء معينة في Go. على عكس مقارنة النصوص أو فحوصات المساواة المباشرة، تعمل errors.Is بشكل صحيح مع الأخطاء المغلفة (wrapped errors) وتتبع سلسلة الأخطاء للعثور على التطابقات. وهذا يجعل معالجة الأخطاء لديك قوية وموثوقة، حتى مع مرور الأخطاء عبر طبقات متعددة من تطبيقك.

quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

challenge icon

التحدي

سهل

قم ببناء نظام مراقبة لاتصال قاعدة البيانات يستخدم errors.Is لتحديد أخطاء اتصال محددة في سلاسل الأخطاء المغلفة (wrapped error chains). يوضح هذا التحدي كيفية استخدام errors.Is لاكتشاف أخطاء الحراسة (sentinel errors) حتى عندما يتم تغليفها بسياق إضافي من خلال طبقات تطبيقات متعددة.

ستتلقى مدخلين:

  • سلسلة نصية تحتوي على تفاصيل عملية قاعدة البيانات بالتنسيق "database_name,operation,error_type" (على سبيل المثال، "users_db,connect,connection_timeout")
  • سلسلة نصية تحتوي على سياق التطبيق بالتنسيق "service_name,retry_count" (على سبيل المثال، "AuthService,3")

مهمتك هي:

  1. تعريف ثلاثة أخطاء حراسة (sentinel errors) باستخدام errors.New():
    • ErrConnectionTimeout مع الرسالة "connection timeout"
    • ErrConnectionRefused مع الرسالة "connection refused"
    • ErrDatabaseNotFound مع الرسالة "database not found"
  2. تحليل المدخل الأول عن طريق التقسيم عند الفواصل للحصول على اسم قاعدة البيانات، والعملية، ونوع الخطأ
  3. تحليل المدخل الثاني عن طريق التقسيم عند الفواصل للحصول على اسم الخدمة وعدد محاولات إعادة المحاولة
  4. إنشاء الخطأ الأصلي بناءً على نوع الخطأ:
    • إذا كان نوع الخطأ هو "connection_timeout": استخدم ErrConnectionTimeout
    • إذا كان نوع الخطأ هو "connection_refused": استخدم ErrConnectionRefused
    • إذا كان نوع الخطأ هو "database_not_found": استخدم ErrDatabaseNotFound
    • لأي نوع خطأ آخر: استخدم errors.New("unknown database error")
  5. إنشاء خطأ مغلف على مستوى قاعدة البيانات باستخدام fmt.Errorf مع %w: "failed to [operation] database [database_name]: %w"
  6. إنشاء خطأ مغلف على مستوى الخدمة: "[service_name] database operation failed after [retry_count] retries: %w"
  7. استخدام errors.Is للتحقق مما إذا كان الخطأ المغلف النهائي يطابق كل خطأ حراسة وعرض النتائج:
    • "Checking for connection timeout: [true/false]"
    • "Checking for connection refused: [true/false]"
    • "Checking for database not found: [true/false]"
  8. عرض سلسلة الأخطاء (error chain):
    • "Error Chain:"
    • "Original: [original_error_message]"
    • "Database level: [database_level_error_message]"
    • "Service level: [service_level_error_message]"
  9. عرض تحليل الخطأ:
    • "Error Analysis:"
    • "Database: [database_name]"
    • "Operation: [operation]"
    • "Service: [service_name]"
    • "Retries: [retry_count]"
    • "Error type detected: [detected_error_type]" حيث يكون detected_error_type هو واحد من "Connection Timeout" أو "Connection Refused" أو "Database Not Found" أو "Unknown Error"

استخدم حزمة strings لتقسيم سلاسل المدخلات عند الفواصل، وحزمة errors لأخطاء الحراسة و errors.Is، وحزمة fmt لتغليف الأخطاء. يوضح هذا التحدي كيف توفر errors.Is تحديداً قوياً للأخطاء يعمل بشكل صحيح مع الأخطاء المغلفة، مما يجعلها ضرورية لمعالجة الأخطاء الموثوقة في التطبيقات المعقدة.

ورقة مرجعية

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

var ErrNotFound = errors.New("not found")

wrappedErr := fmt.Errorf("failed to load user: %w", ErrNotFound)

if errors.Is(wrappedErr, ErrNotFound) {
    fmt.Println("This is a not found error")
}

تأخذ errors.Is معاملين: الخطأ المراد فحصه والخطأ المستهدف للعثور عليه. وتُرجع true إذا تم العثور على الخطأ المستهدف في أي مكان في سلسلة الأخطاء.

هذه هي الطريقة الحديثة والموصى بها للتحقق من أخطاء معينة في Go، حيث أنها تعمل بشكل صحيح مع الأخطاء المغلفة (wrapped errors) على عكس مقارنة النصوص أو فحوصات المساواة المباشرة.

جرّب بنفسك

package main

import (
	"errors"
	"fmt"
	"strings"
)

func main() {
	// قراءة المدخلات
	var dbOperation string
	var appContext string
	fmt.Scanln(&dbOperation)
	fmt.Scanln(&appContext)
	
	// تحليل المدخلات
	dbParts := strings.Split(dbOperation, ",")
	databaseName := dbParts[0]
	operation := dbParts[1]
	errorType := dbParts[2]
	
	contextParts := strings.Split(appContext, ",")
	serviceName := contextParts[0]
	retryCount := contextParts[1]
	
	// TODO: اكتب الكود الخاص بك أدناه
	// 1. قم بتعريف أخطاء الحراسة (sentinel errors) الثلاثة باستخدام errors.New()
	// 2. قم بإنشاء الخطأ الأصلي بناءً على نوع الخطأ
	// 3. قم بإنشاء خطأ مغلف على مستوى قاعدة البيانات باستخدام fmt.Errorf مع %w
	// 4. قم بإنشاء خطأ مغلف على مستوى الخدمة
	// 5. استخدم errors.Is للتحقق من كل خطأ حراسة
	// 6. عرض النتائج وسلسلة الأخطاء
	
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Logic & Flow