تغليف الأخطاء باستخدام `%w`
جزء من قسم Logic & Flow في رحلة GO على Coddy — الدرس 39 من 68.
عندما تحدث أخطاء في تطبيقك، فإنها غالباً ما تحتاج إلى سياق إضافي أثناء انتقالها عبر طبقات مختلفة من الكود الخاص بك. توفر Go آلية قوية لذلك من خلال تغليف الأخطاء (error wrapping) باستخدام فعل التنسيق %w مع fmt.Errorf.
يسمح لك تغليف الأخطاء (Error wrapping) بإضافة معلومات سياقية إلى خطأ موجود مع الحفاظ على الخطأ الأصلي تحته. يؤدي هذا إلى إنشاء سلسلة أخطاء تحافظ على السجل الكامل لما حدث بشكل خاطئ:
originalErr := errors.New("file not found")
wrappedErr := fmt.Errorf("failed to load config: %w", originalErr)إنّ واصف التنسيق %w خاص - فهو يخبر fmt.Errorf بتغليف الخطأ بدلاً من مجرد تحويله إلى سلسلة نصية. وهذا يحفظ الخطأ الأصلي بحيث يمكن فحصه لاحقاً باستخدام دوال مثل errors.Is و errors.As.
هذا النمط ذو قيمة خاصة في التطبيقات الكبيرة حيث قد يمر الخطأ عبر وظائف متعددة، تضيف كل منها سياقها الخاص. على سبيل المثال، قد يتم تغليف خطأ في الاتصال بقاعدة البيانات بعبارة "failed to connect to database"، ثم يتم تغليفه مرة أخرى بعبارة "unable to start server"، مما يخلق مساراً واضحاً لما حدث وأين.
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
التحدي
سهلقم ببناء نظام معالجة ملفات يقوم بتغليف الأخطاء بمعلومات سياقية أثناء انتقالها عبر طبقات مختلفة من تطبيقك. يوضح هذا التحدي كيفية استخدام فعل التنسيق %w مع fmt.Errorf لإنشاء سلاسل أخطاء تحافظ على الخطأ الأصلي مع إضافة سياق ذي معنى.
ستتلقى مدخلين:
- سلسلة نصية تحتوي على تفاصيل عملية الملف بتنسيق
"filename,operation,status"(على سبيل المثال،"config.json,read,permission_denied") - سلسلة نصية تحتوي على سياق النظام بتنسيق
"service_name,component"(على سبيل المثال،"UserService,ConfigLoader")
مهمتك هي:
- تحليل المدخل الأول عن طريق التقسيم عند الفواصل للحصول على اسم الملف (filename)، والعملية (operation)، والحالة (status)
- تحليل المدخل الثاني عن طريق التقسيم عند الفواصل للحصول على اسم الخدمة (service name) والمكون (component)
- إنشاء خطأ أصلي بناءً على الحالة باستخدام
errors.New():- إذا كانت الحالة
"permission_denied":"permission denied" - إذا كانت الحالة
"not_found":"file not found" - إذا كانت الحالة
"corrupted":"file corrupted" - لأي حالة أخرى:
"unknown error"
- إذا كانت الحالة
- إنشاء خطأ مغلف على مستوى الملف باستخدام
fmt.Errorfمع%w:"failed to [operation] file [filename]: %w" - إنشاء خطأ مغلف على مستوى المكون:
"[component] error: %w" - إنشاء خطأ مغلف على مستوى الخدمة:
"[service_name] operation failed: %w" - عرض سلسلة الأخطاء عن طريق طباعة كل مستوى:
"Original error: [original_error_message]""File level: [file_level_error_message]""Component level: [component_level_error_message]""Service level: [service_level_error_message]"
- عرض ملخص لسلسلة الأخطاء:
"Error Chain Summary:""File: [filename]""Operation: [operation]""Component: [component]""Service: [service_name]""Root cause: [original_error_message]"
استخدم حزمة strings لتقسيم سلاسل المدخلات عند الفواصل، وحزمة errors لإنشاء الخطأ الأصلي، وحزمة fmt لإنشاء الأخطاء المغلفة. يوضح هذا التحدي كيف ينشئ تغليف الأخطاء باستخدام %w مساراً واضحاً للسياق أثناء انتقال الأخطاء عبر طبقات مختلفة من تطبيقك، مما يجعل تصحيح الأخطاء أكثر فعالية بكثير.
ورقة مرجعية
يتيح لك تغليف الأخطاء (Error wrapping) إضافة معلومات سياقية إلى خطأ موجود مع الحفاظ على الخطأ الأصلي. استخدم فعل التنسيق %w مع fmt.Errorf:
originalErr := errors.New("file not found")
wrappedErr := fmt.Errorf("failed to load config: %w", originalErr)يحافظ الفعل %w على الخطأ الأصلي بحيث يمكن فحصه لاحقاً باستخدام دوال مثل errors.Is و errors.As. يؤدي هذا إلى إنشاء سلسلة أخطاء تحافظ على السجل الكامل لما حدث بشكل خاطئ، وهو أمر قيم عندما تمر الأخطاء عبر دوال متعددة في التطبيقات الكبيرة.
جرّب بنفسك
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
// قراءة المدخلات
var fileDetails string
var systemContext string
fmt.Scanln(&fileDetails)
fmt.Scanln(&systemContext)
// تحليل المدخلات
fileInfo := strings.Split(fileDetails, ",")
filename := fileInfo[0]
operation := fileInfo[1]
status := fileInfo[2]
contextInfo := strings.Split(systemContext, ",")
serviceName := contextInfo[0]
component := contextInfo[1]
// TODO: اكتب الكود الخاص بك أدناه
// إنشاء الخطأ الأصلي بناءً على الحالة
// إنشاء خطأ مغلف على مستوى الملف
// إنشاء خطأ مغلف على مستوى المكون
// إنشاء خطأ مغلف على مستوى الخدمة
// طباعة كل مستوى من مستويات الخطأ والملخص
}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Logic & Flow
1تدفق التحكم المتقدم
جملة Switch مع `fallthrough`الخروج من الحلقات المتداخلةالاستمرار في حلقة محددةجملة `goto`ملخص - التحكم المتقدم في الحلقات7معالجة الأخطاء بعمق
أنواع الأخطاء المخصصةتغليف الأخطاء باستخدام `%w`فك التغليف باستخدام `errors.Is`فك التغليف باستخدام `errors.As`فهم `panic`استخدام `recover`ملخص - القسمة الآمنة2الـ Structs والميثودز (Methods)
تعريف الـ Methods على الـ Structsالـ Value Receiversالـ Pointer Receiversاختيار الـ Receiversالـ Methods مقابل الـ Functionsملخص - سلوك الـ Struct5تعمق في الـ Maps
Maps من نوع Structsالـ Pointers كقيم في الـ Mapsالتحقق من الـ Nil Mapsمقارنة الـ Mapsمراجعة - عداد تكرار الكلمات3الواجهات (الأساسيات)
ما هي الواجهة؟تعريف الواجهةتطبيق الواجهةاستخدام أنواع الواجهاتالواجهة الفارغةتأكيدات النوع (Type Assertions)تبديل النوع (Type Switch)ملخص - الأشكال والسلوكيات