Errorx - مكتبة للتعامل مع الأخطاء في Go

ما هو Errorx وكيف أنها مفيدة


Errorx هي مكتبة للتعامل مع الأخطاء في Go. يوفر أدوات لحل المشكلات المرتبطة بآلية الخطأ في المشاريع الكبيرة ، وصيغة واحدة للعمل معها.


الصورة


تمت كتابة معظم مكونات خادم Joom على Go منذ تأسيس الشركة. لقد أتاح هذا الاختيار ثماره في المراحل الأولى من التطوير وعمر الخدمة ، وفي ضوء الإعلانات حول آفاق Go 2 ، نحن على يقين من أننا لن نأسف لذلك في المستقبل. تتمثل إحدى المزايا الرئيسية لـ Go في البساطة ، ويوضح نهج الأخطاء هذا المبدأ مثل أي شيء آخر. لا يصل كل مشروع إلى نطاق كافٍ بحيث لا تكون إمكانات المكتبة القياسية كافية ، مما يطالبك بالبحث عن حلولك الخاصة في هذا المجال. صادف أننا خضنا بعض التطور في أساليب التعامل مع الأخطاء ، وتعكس مكتبة errorx نتيجة هذا التطور. نحن مقتنعون بأنه يمكن أن يكون مفيدًا لكثير من الناس ، بما في ذلك أولئك الذين لا يشعرون بعد بعدم ارتياح كبير في التعامل مع الأخطاء في مشاريعهم.


أخطاء في الذهاب


قبل الانتقال إلى القصة حول errorx ، يجب إجراء بعض التوضيح. في النهاية ، ما هو الخطأ في البق؟


type error interface { Error() string } 

بسيط جدا ، أليس كذلك؟ من الناحية العملية ، غالبًا ما لا يحمل التنفيذ أي شيء سوى وصف سلسلة للخطأ. ترتبط هذه البساطة مع النهج الذي بموجبه لا يعني الخطأ بالضرورة شيئًا "استثنائيًا". الأخطاء الأكثر استخدامًا. New () من المكتبة القياسية صحيحة لهذه الفكرة:


 func New(text string) error { return &errorString{text} } 

إذا تذكرنا أن الأخطاء في لغة ما ليس لها وضع خاص وأنها كائنات عادية ، فإن السؤال الذي يطرح نفسه: ما هي خصوصية العمل معهم؟


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


ما الخطب؟


تستفيد العديد من المشاريع من الأخطاء في Go ، كما هي ، ولا تواجه أدنى صعوبة في ذلك. ومع ذلك ، مع تزايد تعقيد النظام ، بدأ عدد من المشاكل في الظهور تجذب الانتباه حتى في غياب التوقعات العالية. التوضيح الجيد هو خط مشابه في سجل الخدمة:


Error: duplicate key


هنا ، تصبح المشكلة الأولى واضحة على الفور: إذا لم تهتم بهذا عن قصد ، ففي نظام كبير إلى حد ما ، يكاد يكون من المستحيل فهم الخطأ الذي حدث ، فقط من خلال الرسالة الأولية. يفتقر هذا المنشور إلى التفاصيل وسياق أوسع للمشكلة. هذا خطأ المبرمج ، ولكن يحدث في كثير من الأحيان لإهماله. تستحق التعليمات البرمجية المخصصة للفروع "الإيجابية" في الرسم البياني للتحكم دائمًا اهتمامًا أكبر في الممارسة العملية وتغطيها الاختبارات بشكل أفضل من الرمز "السلبي" المرتبط بانقطاع التنفيذ أو المشاكل الخارجية. كم مرة تتكرر if err != nil {return err} في برامج Go مما يجعل هذا الإشراف أكثر احتمالًا.


كحفر صغير ، ضع في اعتبارك هذا المثال:


 func (m *Manager) ApplyToUsers(action func(User) (*Data, error), ids []UserID) error { users, err := m.LoadUsers(ids) if err != nil { return err } var actionData []*Data for _, user := range users { data, err := action(user) if err != nil { return err } ok, err := m.validateData(data) if err != nil { return nil } if !ok { log.Error("Validation failed for %v", data) continue } actionData = append(actionData, data) } return m.Apply(actionData) } 

ما مدى سرعة ظهور الخطأ في هذا الرمز؟ ولكن تم القيام بذلك مرة واحدة على الأقل ، ربما من قبل أي مبرمج Go. تلميح: خطأ في التعبير if err != nil { return nil } .


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


افترض أن مبرمج خبرة مريرة يريد إضافة سياق مقدمًا إلى الخطأ الذي يتم إرجاعه. الطريقة الساذجة للقيام بذلك شيء من هذا القبيل:


 func InsertUser(u *User) error { err := usersTable.Insert(u) if err != nil { return errors.New(fmt.Sprintf("failed to insert user %s: %v", u.Name, err) } return nil } 

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


لمعرفة سبب خطورة ذلك ، ضع في اعتبارك وجود رمز مشابه في برنامج تشغيل قاعدة البيانات:


 var ErrDuplicateKey = errors.New("duplicate key") func (t *Table) Insert(entity interface{}) error { // returns ErrDuplicateKey if a unique constraint is violated by insert } func IsDuplicateKeyError(err error) bool { return err == ErrDuplicateKey } 

الآن تم تدمير الاختيار IsDuplicateKeyError() ، على الرغم من أنه في الوقت الذي أضفنا فيه نصنا إلى الخطأ ، لم يكن لدينا أي نية لتغيير دلالاته. سيؤدي هذا بدوره إلى كسر الرمز الذي يعتمد على هذا التحقق:


 func RegisterUser(u *User) error { err := InsertUser(u) if db.IsDuplicateKeyError(err) { // find existing user, handle conflict } else { return err } } 

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


  1. الآن في مكان معالجة الخطأ ، تحتاج إلى معرفة أن السبب الحقيقي يكمن في Cause()
  2. لا توجد طريقة لتعليم المكتبات الخارجية هذه المعرفة ، وستظل الوظائف المساعدة المكتوبة فيها غير مجدية
  3. يمكن أن يتوقع تطبيقنا أن يرجع Cause() السبب الفوري للخطأ (أو لا شيء إذا لم يكن كذلك) ، بينما يتوقع التنفيذ في مكتبة أخرى أن ترجع الطريقة السبب غير الجذر الجذر ؛ يهدد الافتقار إلى الأدوات القياسية أو العقد المقبول بشكل عام مفاجآت غير سارة للغاية

ومع ذلك ، يتم استخدام هذا الحل الجزئي في العديد من مكتبات الأخطاء ، بما في ذلك ، إلى حد ما ، مكتباتنا. هناك خطط في Go 2 لترويج هذا النهج - إذا حدث ذلك ، فسيكون من الأسهل التعامل مع المشكلات الموضحة أعلاه.


Errorx


أدناه سنتحدث عن ما تقدمه errorx ، ولكن أولاً حاول صياغة الاعتبارات التي تكمن وراء المكتبة.


  • التشخيص أكثر أهمية من توفير الموارد. أداء إنشاء وعرض الأخطاء مهم. ومع ذلك ، فإنها تمثل مسارًا سلبيًا وليس مسارًا إيجابيًا ، وفي معظم الحالات تكون بمثابة إشارة إلى مشكلة ما ، وبالتالي فإن وجود معلومات تشخيصية في الخطأ أكثر أهمية.
  • تتبع المكدس بشكل افتراضي. لكي يختفي الخطأ مع امتلاء التشخيص ، لا ينبغي بذل أي جهد. على العكس من ذلك ، من الأفضل استبعاد بعض المعلومات (للإيجاز أو لأسباب الأداء) التي قد تتطلب إجراءات إضافية.
  • دلالات الأخطاء. يجب أن تكون هناك طريقة بسيطة وموثوقة للتحقق من معنى الخطأ: نوعه وتنوعه وخصائصه.
  • سهولة الإضافة. يجب أن تكون إضافة المعلومات التشخيصية إلى خطأ عابر أمرًا بسيطًا ، ويجب ألا يفسد التحقق من دلالاته.
  • البساطة. غالبًا ما تتم كتابة التعليمات البرمجية المخصصة للأخطاء وبشكل روتيني ، لذلك يجب أن تكون بنية التلاعبات الأساسية بسيطة وموجزة. هذا يقلل من عدد الأخطاء ويسهل قراءتها.
  • القليل أكثر. إن فهم الرمز وتوحيده أكثر أهمية من الميزات الاختيارية وخيارات التوسيع (التي ربما لن يستخدمها أحد).
  • دلالات الخطأ جزء من API. الأخطاء التي تتطلب معالجة منفصلة في رمز الاتصال هي في الواقع جزء من حزمة API العامة. لا تحتاج إلى محاولة إخفاءه أو جعله أقل وضوحًا ، ولكن يمكنك جعل المعالجة أكثر ملاءمة ، والاعتمادات الخارجية أقل هشاشة.
  • معظم البق معتم. لمزيد من أنواع الأخطاء لمستخدم خارجي لا يمكن تمييزها عن بعضها البعض ، كان ذلك أفضل. تحميل أنواع أخطاء API التي تتطلب معالجة خاصة ، وكذلك تحميل الأخطاء نفسها بالبيانات اللازمة لمعالجتها هو عيب في التصميم يجب تجنبه.

كان أصعب سؤال بالنسبة لنا هو القابلية للتوسعة: هل ينبغي أن توفر errorx بدائل لإنشاء أنواع مخصصة من الأخطاء التي تختلف بشكل تعسفي في السلوك ، أم أن هناك تطبيقًا يسمح لك بإخراج كل ما تحتاجه من الصندوق؟ لقد اخترنا الخيار الثاني. أولاً ، يحل errorx مشكلة عملية للغاية - وتوضح تجربتنا في استخدامها أنه لهذا الغرض ، من الأفضل أن يكون هناك حل ، بدلاً من قطع الغيار اللازمة لإنشائه. ثانيًا ، يعتبر الاعتبار المتعلق بالبساطة مهمًا جدًا: نظرًا لأنه يتم إيلاء اهتمام أقل للأخطاء ، يجب تصميم الرمز بطريقة تجعل من الصعب العمل معها. وقد أظهرت الممارسة أنه لهذا من المهم أن تبدو جميع هذه الرموز وتعمل بنفس الطريقة.


TL ؛ DR من ميزات المكتبة الرئيسية:


  • مواقع إنشاء تتبع المكدس في جميع الأخطاء بشكل افتراضي
  • نوع التحقق من الأخطاء ، وأصناف عديدة
  • القدرة على إضافة معلومات إلى خطأ موجود دون كسر أي شيء
  • اكتب التحكم في الرؤية إذا كنت تريد إخفاء السبب الأصلي عن المتصل
  • معالجة الخطأ في آلية تعميم الكود (النوع الهرمي ، السمات)
  • خطأ التخصيص حسب الخصائص الديناميكية
  • أنواع الأخطاء القياسية
  • أدوات بناء الجملة لتحسين إمكانية قراءة التعليمات البرمجية لمعالجة الأخطاء

مقدمة


إذا أعدنا صياغة المثال الذي حللناه أعلاه باستخدام errorx ، نحصل على ما يلي:


 var ( DBErrors = errorx.NewNamespace("db") ErrDuplicateKey = DBErrors.NewType("duplicate_key") ) func (t *Table) Insert(entity interface{}) error { // ... return ErrDuplicateKey.New("violated constraint %s", details) } func IsDuplicateKeyError(err error) bool { return errorx.IsOfType(err, ErrDuplicateKey) } 

 func InsertUser(u *User) error { err := usersTable.Insert(u) if err != nil { return errorx.Decorate(err, "failed to insert user %s", u.Name) } return nil } 

لن يتغير رمز المتصل باستخدام IsDuplicateKeyError() .


ما الذي تغير في هذا المثال؟


  • أصبح ErrDuplicateKey نوعًا ، وليس ErrDuplicateKey لخطأ ؛ التحقق من أنها مقاومة لأخطاء النسخ ؛ لا يوجد اعتماد هش على المساواة الدقيقة
  • يوجد مساحة اسم لأخطاء قاعدة البيانات؛ من المحتمل أن يحتوي على أخطاء أخرى ، ومثل هذا التجميع مفيد للقراءة وفي بعض الحالات يمكن استخدامه في التعليمات البرمجية
  • تقوم ميزة Insert بإرجاع خطأ جديد لكل مكالمة:
    • يحتوي الخطأ على مزيد من التفاصيل ؛ هذا ، بالطبع ، ممكن بدون errorx ، ولكن من المستحيل إذا تم إرجاع نفس مثيل الخطأ في كل مرة ، وهو ما كان مطلوبًا سابقًا لـ IsDuplicateKeyError()
    • يمكن أن تحمل هذه الأخطاء تتبع مكدس مختلف ، وهو أمر مفيد لأنه ليس لجميع المكالمات إلى وظيفة إدراج هذا الموقف مقبول
  • InsertUser() بتكملة نص الخطأ ، ولكنه يطبق الخطأ الأصلي ، والذي يتم الاحتفاظ به بالكامل للعمليات اللاحقة
  • IsDuplicateKeyError() الآن: لا يمكن IsDuplicateKeyError() عن طريق نسخ خطأ أو أي عدد من طبقات Decorate ()

ليس من الضروري دائمًا اتباع مثل هذا المخطط:


  • نوع الخطأ ليس دائمًا فريدًا من نوعه: يمكن استخدام نفس الأنواع في العديد من الأماكن
  • إذا رغبت في ذلك ، يمكن تعطيل مجموعة تتبع المكدس ، ولا يمكنك إنشاء خطأ جديد في كل مرة ، ولكن إرجاع نفس الخطأ كما في المثال الأصلي ؛ هذه هي ما يسمى أخطاء الحارس ، ولا نوصي باستخدامها ، ولكن يمكن أن يكون مفيدًا إذا تم استخدام الخطأ فقط كعلامة في الرمز ، وكنت تريد الحفظ عند إنشاء الكائنات
  • هناك طريقة لجعل errorx.IsOfType(err, ErrDuplicateKey) يتوقف عن العمل إذا كنت تريد إخفاء دلالات السبب الجذري من أعين المتطفلين
  • هناك طرق أخرى لفحص النوع إلى جانب المقارنة بالنوع الدقيق

يحتوي Godoc على معلومات تفصيلية حول كل هذا. فيما يلي سوف نتعمق قليلاً في الميزات الرئيسية ، وهي كافية للعمل اليومي.


أنواع


ينتمي أي خطأ errorx إلى نوع ما. اكتب يهم لأن قد يتم تمرير خصائص الخطأ الموروثة من خلاله ؛ من خلاله أو من خلال صفاته ، سيتم إجراء اختبار الدلالات إذا لزم الأمر. بالإضافة إلى ذلك ، فإن الاسم التعبيري للنوع يكمل رسالة الخطأ وقد يستبدلها في بعض الحالات.


 AuthErrors = errorx.NewNamespace("auth") ErrInvalidToken = AuthErrors.NewType("invalid_token") 

 return ErrInvalidToken.NewWithNoMessage() 

ستحتوي رسالة الخطأ على auth.invalid_token . قد يبدو إعلان الخطأ مختلفًا:


 ErrInvalidToken = AuthErrors.NewType("invalid_token").ApplyModifiers(errorx.TypeModifierOmitStackTrace) 

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


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


إنشاء خطأ


 return MyType.New("fail") 

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


 return errorx.IllegalArgument.New("negative value %d", value) 

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


 return errorx.Decorate(err, "failed to upload '%s' to '%s'", filename, location) 

سيظهر النص المضاف إلى الخطأ في السجل ، ولكن لن يضر التحقق من نوع الخطأ الأصلي.


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


 return service.ErrBadRequest.Wrap(err, "failed to load user data") 

هناك فرق مهم يجعل Wrap البديل المفضل لـ New هو أن الخطأ الأصلي ينعكس بالكامل في السجلات. وعلى وجه الخصوص ، سيجلب معه مجموعة مكالمات أولية مفيدة.


خدعة أخرى مفيدة تسمح لك بحفظ جميع المعلومات الممكنة حول مكدس المكالمات تبدو كما يلي:


 return errorx.EnhanceStackTrace(err, "operation fail") 

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


يحتوي Godoc على مزيد من المعلومات ويصف أيضًا ميزات إضافية مثل DecorateMany.


معالجة الخطأ


الأفضل إذا كانت معالجة الخطأ تنحصر فيما يلي:


 log.Error("Error: %+v", err) 

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


 if errorx.IsOfType(err, MyType) { /* handle */ } 

سينجح هذا الفحص في كل من الخطأ من النوع MyType وأنواعه الفرعية ، وهو مقاوم errorx.Decorate() . ومع ذلك ، هناك اعتماد مباشر على نوع الخطأ ، وهو أمر طبيعي تمامًا داخل العبوة ، ولكن يمكن أن يكون غير سار إذا تم استخدامه خارجها. في بعض الحالات ، يكون نوع هذا الخطأ جزءًا من واجهة برمجة تطبيقات خارجية مستقرة ، وأحيانًا نرغب في استبدال هذا الاختيار بفحص خاصية وليس نوع الخطأ الدقيق.


في أخطاء Go الكلاسيكية ، يتم ذلك من خلال واجهة ، اكتب يلقي عليها بمثابة مؤشر لنوع الخطأ. أنواع Errorx لا تدعم هذا الملحق ، ولكن يمكنك استخدام آلية Trait بدلاً من ذلك. على سبيل المثال:


 func IsTemporary(err error) bool { return HasTrait(err, Temporary()) } 

تتحقق هذه الوظيفة المضمنة في errorx مما إذا كان الخطأ يحتوي على خاصية قياسية Temporary ، أي سواء كانت مؤقتة. تقع مسؤولية تحديد أنواع الأخطاء باستخدام السمات على مصدر الخطأ ، ومن خلالها يمكن أن ترسل إشارة مفيدة دون جعل أنواع داخلية محددة جزءًا من واجهة برمجة التطبيقات الخارجية.


 return errorx.IgnoreWithTrait(err, errorx.NotFound()) 

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


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


  • يجب أن يقوم الرمز الذي يتلقى خطأ دائمًا بتسجيله بالكامل ؛ إذا كان جزء من المعلومات غير ضروري ، فدع الرمز الذي ينتج الخطأ يعتني بذلك
  • يجب ألا تستخدم نص الخطأ أو نتيجة دالة Error() لمعالجته في التعليمات البرمجية ؛ فقط اختبارات النوع / السمة هي المناسبة لهذا ، أو تأكيد النوع في حالة وجود أخطاء غير errorx
  • يجب ألا ينكسر رمز المستخدم نظرًا لحقيقة أنه لا تتم معالجة نوع من الخطأ بطريقة خاصة ، حتى إذا كانت هذه المعالجة ممكنة وتعطيها ميزات إضافية
  • الأخطاء التي يتم فحصها بواسطة الخصائص هي أفضل مما يسمى أخطاء الحراسة ، لأن مثل هذه الشيكات أقل هشاشة

errorx خارج


هنا وصفنا ما هو متاح لمستخدم المكتبة من خارج منطقة الجزاء ، ولكن في Joom ، فإن اختراق الشفرة المتعلقة بالأخطاء كبير جدًا. تقبل وحدة التسجيل بشكل صريح الأخطاء في توقيعها وتشارك في طباعتها من أجل استبعاد إمكانية التنسيق غير الصحيح ، وكذلك استخراج المعلومات السياقية المتاحة اختياريًا من سلسلة الخطأ. الوحدة النمطية المسؤولة عن العمل الآمن مع الذعر مع goroutins تكشف عن خطأ إذا وصلت مع ذعر ، وتعرف أيضًا كيفية تقديم الذعر باستخدام بنية الخطأ دون فقدان تتبع المكدس الأصلي. بعض من هذا ، ربما سننشر أيضا.


قضايا التوافق


على الرغم من حقيقة أننا سعداء للغاية بكيفية سماحنا لـ errorx بالعمل مع الأخطاء ، إلا أن الوضع مع رمز المكتبة المخصص لهذا الموضوع أبعد ما يكون عن المثالية. نحن في Joom نحل مشاكل عملية محددة تمامًا مع errorx ، ولكن من وجهة نظر نظام Go البيئي ، سيكون من الأفضل وجود هذه المجموعة الكاملة من الأدوات في المكتبة القياسية. يجب النظر إلى الخطأ ، الذي ينتمي مصدره بالفعل أو من المحتمل إلى نموذج آخر ، على أنه غريب ، أي يحتمل ألا تحمل معلومات بالشكل المقبول في المشروع.


ومع ذلك ، تم القيام ببعض الأشياء حتى لا تتعارض مع الحلول الأخرى القائمة.


يتم استخدام التنسيق '%+v' لطباعة خطأ مع تتبع المكدس ، إذا كان موجودًا. هذا هو المعيار الواقعي في النظام البيئي Go ، بل يتم تضمينه في مسودة تصميم Go 2.


Cause() error errorx , , , Causer, errorx Wrap().



, Go 2, . .


, errorx Go 1. , Go 2, . , , errorx.


Check-handle , errorx , a Unwrap() error Wrap() errorx (.. , , Wrap ), . , , .


design draft Go 2, errorx.Is() errorx.As() , errors .


الخلاصة


, , , - , . , API : , , . 1.0 , Joom. , - .


: https://github.com/joomcode/errorx


, !


الصورة

Source: https://habr.com/ru/post/ar429690/


All Articles