paint-brush
د دې نوي چوکاټ سره د ودې څخه د غلطیو مخه ونیسئلخوا@olvrng
270 لوستل

د دې نوي چوکاټ سره د ودې څخه د غلطیو مخه ونیسئ

لخوا Oliver Nguyen30m2024/12/11
Read on Terminal Reader

ډېر اوږد؛ لوستل

دا د هغه کیسه ده چې موږ څنګه د یوې ساده غلطۍ اداره کولو طریقې سره پیل کړې، په بشپړه توګه خپه شو لکه څنګه چې ستونزې زیاتې شوې، او بالاخره زموږ د غلطۍ چوکاټ جوړ کړ.
featured image - د دې نوي چوکاټ سره د ودې څخه د غلطیو مخه ونیسئ
Oliver Nguyen HackerNoon profile picture
0-item
1-item

په Go کې د غلطیو اداره کول ساده او انعطاف منونکي دي - مګر هیڅ جوړښت نلري!


دا باید ساده وي، سمه ده؟ یوازې یوه error بیرته راشئ ، د پیغام سره پوښل شوی ، او پرمخ لاړشئ. ښه، دا سادگي په چټکۍ سره په ګډوډۍ بدلیږي ځکه چې زموږ کوډبیس د ډیرو کڅوړو، ډیرو پراختیا کونکو، او نور "چټک اصلاحات" سره وده کوي چې د تل لپاره پاتې کیږي. د وخت په تیریدو سره، لاګونه د "دا کار کولو کې پاتې راتلل" او "غیر متوقع هغه" څخه ډک شوي، او هیڅوک نه پوهیږي چې ایا دا د کارونکي غلطی، د سرور غلطی، بګی کوډ، یا دا د ستورو غلطه بڼه ده!


غلطۍ د متناسب پیغامونو سره رامینځته کیږي. هر بسته د خپل سټایلونو سیټ لري، ثابت، یا د دودیز غلطی ډولونه. د خطا کوډونه په خپله خوښه اضافه شوي. کومه اسانه لار نشته چې ووایاست چې کومې خطاګانې له کوم فعالیت څخه بیرته راستنیدل کیدی شي پرته لدې چې د هغې پلي کولو کې کیندنې!


نو ، ما د نوي خطا چوکاټ رامینځته کولو ننګونه واخیسته. موږ پریکړه وکړه چې د نوم ځای کوډونو په کارولو سره د جوړښت شوي، مرکزي سیسټم سره لاړ شو ترڅو غلطۍ معنی لرونکي، د موندلو وړ، او - تر ټولو مهم - موږ ته د ذهن سکون راکړئ!


دا د هغه کیسه ده چې موږ څنګه د یوې ساده غلطۍ اداره کولو طریقې سره پیل وکړ، په بشپړه توګه خپه شو لکه څنګه چې ستونزې زیاتې شوې، او بالاخره زموږ د غلطۍ چوکاټ جوړ کړ. د ډیزاین پریکړې، دا څنګه پلي کیږي، درسونه زده کړل، او ولې یې د غلطیو اداره کولو لپاره زموږ چلند بدل کړ. زه امید لرم چې دا به ستاسو لپاره یو څه نظرونه هم راوړي!


د تګ تېروتنې یوازې ارزښتونه دي

Go د غلطیو اداره کولو لپاره مستقیمه لاره لري: غلطی یوازې ارزښتونه دي. یوه تېروتنه یوازې یو ارزښت دی چې د error انٹرفیس د واحد میتود Error() string سره پلي کوي. د دې پرځای چې استثنا وغورځوي او د اوسني اجرا کولو جریان ګډوډ کړي ، د Go افعال د نورو پایلو تر څنګ د error ارزښت بیرته راولي. بیا زنګ وهونکی کولی شي پریکړه وکړي چې دا څنګه اداره کړي: د پریکړې کولو لپاره د هغې ارزښت چیک کړئ ، د نوي پیغامونو او شرایطو سره وتړئ ، یا په ساده ډول غلطي بیرته راوباسئ ، د والدینو زنګ وهونکو لپاره د مدیریت منطق پریږدي.


موږ کولی شو په دې کې د Error() string میتود اضافه کولو سره هر ډول error وکړو. دا انعطاف هر بسته ته اجازه ورکوي چې خپله د غلطۍ اداره کولو ستراتیژي تعریف کړي ، او هر هغه څه غوره کړي چې د دوی لپاره غوره کار کوي. دا د Go د کمپوزیت فلسفې سره هم ښه مدغم کوي ، د اړتیا په صورت کې د غلطیو لپاه کول ، غزول یا دودیز کول اسانه کوي.

هر بسته باید د غلطیو سره معامله وکړي

عام عمل دا دی چې د غلطۍ ارزښت بیرته راوباسئ چې د error انٹرفیس پلي کوي او زنګ وهونکي ته اجازه ورکوي پریکړه وکړي چې نور څه وکړي. دلته یو عادي مثال دی:

 func loadCredentials() (Credentials, error) { data, err := os.ReadFile("cred.json") if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("file not found: %w", err) } if err != nil { return nil, fmt.Errorf("failed to read file: %w", err) } cred, err := verifyCredentials(cred); if err != nil { return nil, fmt.Errorf("invalid credentials: %w", err) } return cred, nil }

Go د غلطیو سره کار کولو لپاره یو څو اسانتیاوې چمتو کوي:

  • د تېروتنې جوړول: errors.New() او fmt.Errorf() د ساده تېروتنو د رامنځته کولو لپاره.
  • د ریپ کولو تېروتنې: fmt.Errorf() او %w فعل په کارولو سره د اضافي شرایطو سره تېروتنې وتړئ.
  • د تېروتنو یوځای کول: errors.Join() ډیری تېروتنې په یو واحد کې یوځای کوي.
  • د تېروتنو چک کول او سمبالول: errors.Is() د یو ځانګړي ارزښت سره د یوې تېروتنې سره سمون لري، errors.As() د یو ځانګړي ډول سره د یوې تېروتنې سره سمون لري، او errors.Unwrap() اصلي تېروتنه بیا ترلاسه کوي.


په عمل کې، موږ معمولا دا نمونې ګورو:

  • د معیاري کڅوړو کارول: errors.New() یا fmt.Errorf() سره ساده غلطی بیرته راګرځول.
  • د ثابتو یا متغیرونو صادرول: د مثال په توګه، go-redis او gorm.io د بیا کارونې وړ غلطی متغیرونه تعریفوي.
  • د دودیز تېروتنې ډولونه: کتابتونونه لکه lib/pq grpc/status.Error د تېروتنې ځانګړي ډولونه رامینځته کوي، ډیری وختونه د اضافي شرایطو لپاره د تړلو کوډونو سره.
  • د پلي کولو سره د خطا انٹرفیس: aws-sdk-go د مختلف پلي کولو سره د غلطۍ ډولونو تعریف کولو لپاره د انٹرفیس پراساس چلند کاروي.
  • یا څو انٹرفیسونه: لکه د Docker's errdefs ، کوم چې د غلطیو طبقه بندي او اداره کولو لپاره ډیری انٹرفیس تعریفوي.

موږ د یوې ګډې تګلارې سره پیل وکړ

په لومړیو ورځو کې، د ډیری Go پراختیا کونکو په څیر، موږ د Go عمومي کړنې تعقیب کړې او د تېروتنې اداره کول یې لږ تر لږه فعال ساتل. دا د څو کلونو لپاره کافي ښه کار وکړ.

  • د pkg/errors په کارولو سره د سټیکټریس شامل کړئ، په هغه وخت کې یو مشهور بسته.

  • د بسته بندي ځانګړي غلطیو لپاره ثابت یا تغیرات صادر کړئ.

  • د ځانګړو تېروتنو د کتلو لپاره errors.Is() وکاروئ.

  • د نوي پیغامونو او شرایطو سره تېروتنې وتړئ.

  • د API غلطیو لپاره، موږ د پروټوبف اینوم سره د خطا ډولونه او کوډونه تعریفوو.


pkg/errors سره د سټیکټریس په شمول

موږ pkg/errors ، په هغه وخت کې د تېروتنې د سمبالولو یوه مشهوره بسته وکاروه، ترڅو زموږ په تېروتنو کې سټریکټریس شامل کړو. دا په ځانګړي ډول د ډیبګ کولو لپاره ګټور و ، ځکه چې دا موږ ته اجازه راکوله چې د غوښتنلیک په بیلابیلو برخو کې د غلطیو اصلیت تعقیب کړو.

د Stacktrace سره د غلطیو رامینځته کولو، لپیٹ کولو او تبلیغ کولو لپاره، موږ Newf() ، NewValuef() ، او Wrapf() په څیر افعال پلي کړل. دلته زموږ د لومړني پلي کولو یوه بیلګه ده:

 type xError struct { msg message, stack: callers(), } func Newf(msg string, args ...any) error { return &xError{ msg: fmt.Sprintf(msg, args...), stack: callers(), // 👈 stacktrace } } func NewValuef(msg string, args ...any) error { return fmt.Errorf(msg, args...) // 👈 no stacktrace } func Wrapf(err error, msg string, args ...any) error { if err == nil { return nil } stack := getStack(err) if stack == nil { stack = callers() } return &xError{ msg: fmt.Sprintf(msg, args...), stack: stack, } }


د تېروتنې تغیرات صادرول

زموږ په کوډبیس کې هر کڅوړه خپل غلطی متغیرونه تعریف کړي، ډیری وختونه د متضاد سټایلونو سره.

 package database var ErrNotFound = errors.NewValue("record not found") var ErrMultipleFound = errors.NewValue("multiple records found") var ErrTimeout = errors.NewValue("request timeout")
 package profile var ErrUserNotFound = errors.NewValue("user not found") var ErrBusinessNotFound = errors.NewValue("business not found") var ErrContextCancel = errors.NewValue("context canceled")


errors.Is() سره د تېروتنې چک کول او د اضافي شرایطو سره سمول

 res, err := repo.QueryUser(ctx, req) switch { case err == nil: // continue case errors.Is(database.NotFound): return nil, errors.Wrapf(ErrUserNotFound, "user not found (id=%v)", req.UserID) default: return nil, errors.Wrapf(ctx, "failed to query user (id=%v)", req.UserID) }

دا د ډیرو توضیحاتو سره د غلطو تبلیغاتو سره مرسته وکړه مګر ډیری وختونه په لاګونو کې د فعلیت ، نقل کولو ، او لږ وضاحت په پایله کې:

 internal server error: failed to query user: user not found (id=52a0a433-3922-48bd-a7ac-35dd8972dfe5): record not found: not found


د پروټوبف سره د بهرنۍ غلطیو تعریف کول

د بهرني مخ شوي APIs لپاره ، موږ د پروټوبف پراساس خطا ماډل غوره کړ چېد میټا ګراف API لخوا الهام شوی:

 message Error { string message = 1; ErrorType type = 2; ErrorCode code = 3; string user_title = 4; string user_message = 5; string trace_id = 6; map<string, string> details = 7; } enum ErrorType { ERROR_TYPE_UNSPECIFIED = 1; ERROR_TYPE_AUTHENTICATION = 2; ERROR_TYPE_INVALID_REQUEST = 3; ERROR_TYPE_RATE_LIMIT = 4; ERROR_TYPE_BUSINESS_LIMIT = 5; ERROR_TYPE_WEBHOOK_DELIVERY = 6; } enum ErrorCode { ERROR_CODE_UNSPECIFIED = 1 [(error_type = UNSPECIFIED)]; ERROR_CODE_UNAUTHENTICATED = 2 [(error_type = AUTHENTICATION)]; ERROR_CODE_CAMPAIGN_NOT_FOUND = 3 [(error_type = NOT_FOUND)]; ERROR_CODE_META_CHOSE_NOT_TO_DELIVER = 4 /* ... */; ERROR_CODE_MESSAGE_WABA_TEMPLATE_CAN_ONLY_EDIT_ONCE_IN_24_HOURS = 5; }

دې تګلارې د جوړښت له تېروتنو سره مرسته وکړه، خو د وخت په تېرېدو سره، د تېروتنې ډولونه او کوډونه له واضح پلان پرته اضافه شول، چې د تضاد او نقل لامل کېږي.


او د وخت په تیریدو سره ستونزې زیاتې شوې

تېروتنې هرچیرې اعلان شوې

  • هر بسته د مرکزي سیسټم پرته د خپل غلطی ثابتونکي تعریفوي.
  • ثابت او پیغامونه د کوډبیس په اوږدو کې ویشل شوي، دا روښانه نه ده چې کوم غلطیان ممکن بیرته راستانه شي - او، ایا دا gorm.ErrRecordNotFound یا user.ErrNotFound یا دواړه؟


د تصادفي تېروتنې ریپ کول د متضاد او خپلسري لاګونو لامل شوي

  • ډیری فنکشنونه د خپل غلطی ډولونو اعلانولو پرته د خپل سري، متناسب پیغامونو سره خطاګانې پوښي.
  • لاګونه لفظي، بې ځایه، او د لټون یا څارنه ستونزمن وو.
  • د تېروتنې پیغامونه عمومي وو او اکثرا یې نه تشریح کول چې څه غلط شوي یا دا څنګه پیښ شوي. همدارنګه نازک او د نه پام وړ بدلونونو سره مخ دي.
 unexpected gorm error: failed to find business channel: error received when invoking API: unexpected: context canceled


هیڅ معیاري کول د غلطې غلطۍ اداره کولو لامل شوي

  • هر کڅوړه په مختلف ډول غلطۍ اداره کوي، دا ستونزمن کوي چې پوه شي چې ایا یو فنکشن بیرته راستانه شوی، لپاس شوی، یا بدل شوی غلطی.
  • شرایط اکثرا له لاسه ورکړل شوي لکه څنګه چې د غلطو تبلیغاتو.
  • پورتنۍ پرتونه پرته له واضح اصلي لاملونو څخه مبهم 500 داخلي سرور غلطۍ ترلاسه کړې.


هیڅ کټګورۍ څارنه ناممکنه کړې

  • تېروتنې د شدت یا چلند له مخې طبقه بندي شوې نه وې: یو context.Canceled لغوه شوې تېروتنه ممکن یو عادي چلند وي کله چې کاروونکي د براوزر ټب بند کړي، مګر دا مهمه ده که غوښتنه لغوه شي ځکه چې دا پوښتنه په تصادفي ډول ورو ده.
  • مهمې مسلې د شور وړ لوګو لاندې ښخ شوي ، چې پیژندل یې سخت کړي.
  • د کټګورۍ پرته، دا ناشونې وه چې د تېروتنې فریکوینسي، شدت، یا اغیز په اغیزمنه توګه وڅیړئ.

دا د خطا اداره کولو مرکزي کولو وخت دی

بېرته د انځورګرۍ بورډ ته

د مخ پر ودې ننګونو د حل لپاره، موږ پریکړه وکړه چې د مرکزي او جوړښت شوي غلطی کوډونو اصلي مفکورې په شاوخوا کې د غلطې غوره ستراتیژي جوړه کړو.

  • تېروتنې په هر ځای کې اعلان شوي
  • متضاد او خپلمنځي لاګونه ← د روښانه او ثابت فارمینګ سره جوړ شوي خطا کوډونه.
  • د غلطې غلطۍ اداره کول ← د غلطۍ رامینځته کول معیاري کول او د نوي Error ډول چیک کول د هراړخیز مرستندویانو سره.
  • هیڅ طبقه بندي نشته ← د خطونو او میټریکونو له لارې د مؤثره څارنې لپاره د ټګونو سره د خطا کوډونه طبقه بندي کړئ.

ډیزاین پریکړې

ټول د خطا کوډونه په مرکزي ځای کې د نوم ځای جوړښت سره تعریف شوي.

د روښانه، معنی لرونکي، او د غزولو وړ غلطی کوډونو جوړولو لپاره د نوم ځایونه وکاروئ. بېلګه:

  • PRFL.USR.NOT_FOUND د "کاروونکي نه موندل" لپاره.
  • FLD.NOT_FOUND لپاره "د فلو سند ونه موندل شو."
  • دواړه کولی شي یو بنسټیز اساس کوډ شریک کړي DEPS.PG.NOT_FOUND ، پدې معنی چې "ریکارډ په PostgreSQL کې ندی موندل شوی."


د خدمت یا کتابتون هر پرت باید یوازې د خپل نوم ځای کوډونه بیرته راستانه کړي .

  • د خدماتو هر پرت، ذخیره، یا کتابتون د خپل غلطی کوډونو سیټ اعلانوي.
  • کله چې یو پرت د انحصار څخه تېروتنه ترلاسه کوي، دا باید د بیرته راستنیدو دمخه د خپل نوم ځای کوډ سره وتړي.
  • د مثال په توګه: کله چې د انحصار څخه د غلطۍ gorm.ErrRecordNotFound ترلاسه کول، د "ډیټابیس" بسته باید دا د DEPS.PG.NOT_FOUND په توګه وتړل شي. وروسته، د "پروفایل/کارن" خدمت باید دا بیا د PRFL.USR.NOT_FOUND په توګه وپلټئ.


ټولې تېروتنې باید د Error انٹرفیس پلي کړي.

  • دا د دریمې ډلې کتابتونونو غلطیو ( error ) او زموږ د داخلي Error تر مینځ روښانه سرحد رامینځته کوي.
  • دا د مهاجرت د پرمختګ لپاره هم مرسته کوي، د مهاجرت کڅوړو او نه مهاجرت شوي کڅوړو ترمنځ جلا کول.


یوه تېروتنه کولی شي یو یا څو تېروتنې وتړي. په ګډه، دوی یوه ونه جوړوي.

 [FLD.INVALID_ARGUMENT] invalid argument → [TPL.INVALID_PARAMS] invalid input params 1. [TPL.PARAM.EMPTY] name can not be empty 2. [TPL.PARAM.MALFORM] invalid format for param[2]


تل context.Context اړتیا ده . د خطا سره شرایط ضمیمه کیدی شي.

  • ډیری وختونه موږ د سټایلون غلطیو سره لاګونه لیدلي چې هیڅ شرایط نلري ، هیڅ trace_id ، او نه پوهیږو چې دا له کوم ځای څخه راځي.
  • کولی شي اضافي کلیدي / ارزښت غلطیو ته ضمیمه کړي، کوم چې په لاګ یا څارنې کې کارول کیدی شي.


کله چې تېروتنې د خدماتو د حد په اوږدو کې لیږل کیږي، یوازې د لوړې کچې غلطی کوډ ښکاره کیږي.

  • زنګ وهونکي اړتیا نلري چې د دې خدمت داخلي پلي کولو توضیحات وګوري.


د بهرنیو غلطیو لپاره، د اوسني پروټوبف ایرر کوډ او ایرر ټایپ کارولو ته دوام ورکړئ.

  • دا شاته مطابقت تضمینوي، نو زموږ پیرودونکي اړتیا نلري خپل کوډ بیا ولیکي.


د پروټوبف کوډونو، HTTP حالت کوډونو، او ټګونو ته د نوم ځای غلطی کوډونه اتوماتیک کړئ.

  • انجینران نقشه کول په مرکزي ځای کې تعریفوي، او چوکاټ به د هرې غلطۍ کوډ د اړونده پروټوبف ErrorCode ، ErrorType ، gRPC حالت، HTTP حالت، او د ننوتلو/میټریکونو لپاره ټاګونو ته نقشه کړي.
  • دا ثبات تضمینوي او نقل کموي.

د نوم ځای خطا چوکاټ

اصلي کڅوړې او ډولونه

دلته یو څو اصلي کڅوړې شتون لري چې زموږ د نوي غلطۍ اداره کولو چوکاټ بنسټ جوړوي.

connectly.ai/go/pkgs/

  • errors : اصلي بسته چې د Error ډول او کوډونه تعریفوي.
  • errors/api : د مخکني پای یا بهرني API ته د خطا لیږلو لپاره.
  • errors/E : مرستندویه بسته چې د ډاټ وارداتو سره کارول کیږي.
  • testing : د نوم ځای غلطیو سره کار کولو لپاره د اسانتیاو ازمول.


Error او Code

د Error انٹرفیس د معیاري error انٹرفیس توسیع دی، د Code بیرته راستنیدو لپاره اضافي میتودونو سره. یو Code د uint16 په توګه پلي کیږي.

 package errors // import "connectly.ai/go/pkgs/errors" type Error interface { error Code() Code } type Code struct { code uint16 } type CodeI interface { CodeDesc() CodeDesc } type GroupI interface { /* ... */ } type CodeDesc struct { /* ... */ }


د بسته errors/E ټول غلطی کوډونه او عام ډولونه صادروي

 package E // import "connectly.ai/go/pkgs/errors/E" import "connectly.ai/go/pkgs/errors" type Error = errors.Error var ( DEPS = errors.DEPS PRFL = errors.PRFL ) func MapError(ctx context.Context, err error) errors.Mapper { /* ... */ } func IsErrorCode(err error, codes ...errors.CodeI) { /* ... */ } func IsErrorGroup(err error, groups ...errors.GroupI) { /* ... */ }

د کارولو بېلګه

د تېروتنې کوډونو بېلګه:

 // dependencies → postgres DEPS.PG.NOT_FOUND DEPS.PG.UNEXPECTED // sdk → hash SDK.HASH.UNEXPECTED // profile → user PRFL.USR.NOT_FOUND PFRL.USR.UNKNOWN // profile → user → repository PRFL.USR.REPO.NOT_FOUND PRFL.USR.REPO.UNKNOWN // profile → auth PRFL.AUTH.UNAUTHENTICATED PRFL.AUTH.UNKNOWN PRFL.AUTH.UNEXPECTED


د بسته بندۍ database :

 package database // import "connectly.ai/go/pkgs/database" import "gorm.io/gorm" import . "connectly.ai/go/pkgs/errors/E" type DB struct { gorm: gorm.DB } func (d *DB) Exec(ctx context.Context, sql string, params ...any) *DB { tx := d.gorm.WithContext(ctx).Exec(sql, params...) return wrapTx(tx) } func (x *DB) Error(msgArgs ...any) Error { return wrapError(tx.Error()) // 👈 convert gorm error to 'Error' } func (x *DB) SingleRowError(msgArgs ...any) Error { if err := x.Error(); err != nil { return err } switch { case x.RowsAffected == 1: return nil case x.RowsAffected == 0: return DEPS.PG.NOT_FOUND.CallerSkip(1). New(x.Context(), formatMsgArgs(msgArgs)) default: return DEPS.PG.UNEXPECTED.CallerSkip(1). New(x.Context(), formatMsgArgs(msgArgs)) } }


بسته pb/services/profile :

 package profile // import "connectly.ai/pb/services/profile" // these types are generated from services/profile.proto type QueryUserRequest struct { BusinessId string UserId string } type LoginRequest struct { Username string Password string }


د بسته بندۍ service/profile :

 package profile import uuid "github.com/google/uuid" import . "connectly.ai/go/pkgs/errors/E" import l "connectly.ai/go/pkgs/logging/l" import profilepb "connectly.ai/pb/services/profile" // repository requests type QueryUserByUsernameRequest struct { Username string } // repository layer → query user func (r *UserRepository) QueryUserByUsernameAuth( ctx context.Context, req *QueryUserByUsernameRequest, ) (*User, Error) { if req.Username == "" { return PRFL.USR.REPO.INVALID_ARGUMENT.New(ctx, "empty request") } var user User sqlQuery := `SELECT * FROM "user" WHERE username = ? LIMIT 1` tx := r.db.Exec(ctx, sqlQuery, req.Username).Scan(&user) err := tx.SingleRowError() switch { case err == nil: return &user, nil case IsErrorCode(DEPS.PG.NOT_FOUND): return PRFL.USR.REPO.USER_NOT_FOUND. With(l.String("username", req.Username)) Wrap(ctx, "user not found") default: return PRFL.USR.REPO.UNKNOWN. Wrap(ctx, "failed to query user") } } // user service layer → query user func (u *UserService) QueryUser( ctx context.Context, req *profilepb.QueryUserRequest, ) (*profilepb.QueryUserResponse, Error) { // ... rr := QueryUserByUsernameRequest{ Username: req.Username } err := u.repo.QueryUserByUsername(ctx, rr) if err != nil { return nil, MapError(ctx, err). Map(PRFL.USR.REPO.NOT_FOUND, PRFL.USR.NOT_FOUND, "the user %q cannot be found", req.UserName, api.UserTitle("User Not Found"), api.UserMsg("The requested user id %q can not be found", req.UserId)). KeepGroup(PRFL.USR). Default(PRFL.USR.UNKNOWN, "failed to query user") } // ... return resp, nil } // auth service layer → login user func (a *AuthService) Login( ctx context.Context, req *profilepb.LoginRequest, ) (*profilepb.LoginResponse, *profilepb.LoginResponse, Error) { vl := PRFL.AUTH.INVALID_ARGUMENT.WithMsg("invalid request") vl.Vl(req.Username != "", "no username", api.Detail("username is required")) vl.Vl(req.Password != "", "no password", api.Detail("password is required")) if err := vl.ToError(ctx); err != nil { return err } hashpwd, err := hash.Hash(req.Password) if err != nil { return PRFL.AUTH.UNEXPECTED.Wrap(ctx, err, "failed to calc hash") } usrReq := profilepb.QueryUserByUsernameRequest{/*...*/} usrRes, err := a.userServiceClient.QueryUserByUsername(ctx, usrReq) if err != nil { return nil, MapError(ctx, err). Map(PRFL.USR.NOT_FOUND, PRFL.AUTH.UNAUTHENTICATED, "unauthenticated"). Default(PRFL.AUTH.UNKNOWN, "failed to query by username") } // ... }

ښه، په پورتني کوډ کې ډیری نوي افعال او مفکورې شتون لري. راځئ چې ګام په ګام دوی ته لاړ شو.

د تېروتنو جوړول او لغوه کول

لومړی، د بسته بندۍ errors/E د ډاټ واردولو په کارولو سره وارد کړئ

دا به تاسو ته اجازه درکړي چې په مستقیم ډول عام ډولونه وکاروئ لکه errors.Error پرځای Error . تېروتنه او کوډونو ته لاسرسی د PRFL.USR.NOT_FOUND پرځای errors.PRFL.USR.NOT_FOUND .

 import . "connectly.ai/go/pkgs/errors/E"


CODE.New() په کارولو سره نوې غلطۍ رامینځته کړئ

فرض کړئ چې تاسو یوه ناسمه غوښتنه ترلاسه کړئ، تاسو کولی شئ د دې له لارې نوې تېروتنه جوړه کړئ:

 err := PRFL.USR.INVALID_ARGUMENT.New(ctx, "invalid request")
  • PRFL.USR.INVALID_ARGUMENT یو Code دی.
  • یو Code د نوي غلطۍ رامینځته کولو لپاره میتودونه لکه New() یا Wrap() افشا کوي.
  • New() فنکشن د لومړي دلیل په توګه context.Context ترلاسه کوي ، وروسته پیغام او اختیاري دلیلونه.


دا fmt.Print(err) سره چاپ کړئ:

 [PRFL.USR.INVALID_ARGUMENT] invalid request


یا fmt.Printf("%+v") سره د نورو جزیاتو لیدلو لپاره:

 [PRFL.USR.INVALID_ARGUMENT] invalid request connectly.ai/go/services/profile.(*UserService).QueryUser /usr/i/src/go/services/profile/user.go:1234 connectly.ai/go/services/profile.(*UserRepository).QueryUser /usr/i/src/go/services/profile/repo/user.go:2341


CODE.Wrap() په کارولو سره په یوه نوې تېروتنه کې یوه تېروتنه وتړئ

 dbErr := DEPS.PG.NOT_FOUND.Wrap(ctx, gorm.ErrRecordNotFound, "not found") usrErr := PRFL.USR.NOT_FOUND.Wrap(ctx, dbErr, "user not found")


دا محصول به fmt.Print(usrErr) سره تولید کړي:

 [PRFL.USR.NOT_FOUND] user not found → [DEPS.PG.NOT_FOUND] not found → record not found


یا fmt.Printf("%+v", usrErr) سره

 [PRFL.USR.NOT_FOUND] user not found → [DEPS.PG.NOT_FOUND] not found → record not found connectly.ai/go/services/profile.(*UserService).QueryUser /usr/i/src/go/services/profile/user.go:1234


د سټیکټریس به د ترټولو داخلي Error څخه راشي. که تاسو یو مرستندویه فنکشن لیکئ، تاسو کولی شئ د چوکاټونو پریښودو لپاره CallerSkip(skip) وکاروئ:

 func mapUserError(ctx context.Context, err error) Error { switch { case IsErrorCode(err, DEPS.PG.NOT_FOUND): return PRFL.USR.NOT_FOUND.CallerSkip(1).Wrap(ctx, err, "...") default: return PRFL.USR.UNKNOWN.CallerSkip(1).Wrap(ctx, err, "...") } }

غلطیو ته د شرایطو اضافه کول

With() کارولو سره خطا ته شرایط اضافه کړئ

  • تاسو کولی شئ .With(l.String(...)) په واسطه غلطیو ته اضافي کلیدي/ ارزښت جوړه اضافه کړئ.
  • logging/l د ننوتلو لپاره د شوګر افعال صادرولو لپاره یو مرستندویه بسته ده.
  • l.String("flag", flag) یو Tag{String: flag} او l.UUID("user_id, userID) بیرته ستنیدنه Tag{Stringer: userID} .
 import l "connectly.ai/go/pkgs/logging/l" usrErr := PRFL.USR.NOT_FOUND. With(l.UUID("user_id", req.UserID), l.String("flag", flag)). Wrap(ctx, dbErr, "user not found")


ټاګونه fmt.Printf("%+v", usrErr) سره تولید کیدی شي :

 [PRFL.USR.NOT_FOUND] user not found {"user_id": "81febc07-5c06-4e01-8f9d-995bdc2e0a9a", "flag": "ABRW"} → [DEPS.PG.NOT_FOUND] not found {"a number": 42} → record not found


په مستقیم ډول New() , Wrap() , or MapError() دننه غلطیو ته شرایط اضافه کړئ :

l.String() فنکشن او د هغې د کورنۍ د ګټې اخیستنې په واسطه، New() او ورته افعال کولی شي په هوښیارۍ سره د فارمینګ دلیلونو په مینځ کې ټاګونه کشف کړي. اړتیا نشته چې مختلف دندې معرفي کړئ.

 err := INF.HEALTH.NOT_READY.New(ctx, "service %q is not ready (retried %v times)", req.ServiceName, l.String("flag", flag) countRetries, l.Number("count", countRetries), )


تولید به وکړي:

 [INF.HEALTH.NOT_READY] service "magic" is not ready (retried 2 times) {"flag": "ABRW", "count": 2}

مختلف ډولونه: Error0 ، VlError ، ApiError

اوس مهال، 3 ډولونه شتون لري چې د Error انٹرفیس پلي کوي. که اړتیا وي تاسو کولی شئ نور ډولونه اضافه کړئ. هر یو کولی شي مختلف جوړښت ولري، د ځانګړو اړتیاو لپاره د دودیز میتودونو سره.


Error د Go معیاري error انٹرفیس توسیع دی

 type Error interface { error Code() Message() Fields() []tags.Field StackTrace() stacktrace.StackTrace _base() *base // a private method }


دا یو شخصي میتود لري ترڅو ډاډ ترلاسه کړي چې موږ په ناڅاپي ډول د errors کڅوړې څخه بهر Error نوي ډولونه پلي نه کوو. موږ ممکن (یا ممکن) په راتلونکي کې دا محدودیت لرې کړو کله چې موږ د کارونې ډیر نمونې تجربه کړو.


ولې موږ یوازې د معیاري error انٹرفیس نه کاروو او د ډول ادعا وکاروو؟

ځکه چې موږ غواړو د دریمې ډلې تېروتنې او زموږ داخلي تېروتنې ترمنځ جلا کړو. زموږ په داخلي کوډونو کې ټولې پرتونه او کڅوړې باید تل Error راستانه کړي. پدې توګه موږ کولی شو په خوندي ډول پوه شو کله چې موږ باید د دریمې ډلې غلطۍ بدل کړو ، او کله چې موږ یوازې زموږ د داخلي خطا کوډونو سره معامله کولو ته اړتیا لرو.


دا د مهاجرت شوي کڅوړو او نه تر اوسه مهاجر شوي کڅوړو ترمنځ سرحد هم رامینځته کوي. بیرته واقعیت ته ، موږ نشو کولی یوازې یو نوی ډول اعلان کړو ، د جادو څنډه وغځوو ، د سپیل پرامپټ غږ وکړو ، او بیا د کوډ ټولې ملیونونه لاینونه په جادویی ډول بدل شوي او پرته له کوم بګ پرته کار کوي! نه، دا راتلونکې لا دلته نه ده. دا ممکن یوه ورځ راشي ، مګر د اوس لپاره ، موږ لاهم باید خپل کڅوړې یو له بل سره مهاجر کړو.

Error0 د Error اصلي ډول دی


ډیری غلطی کوډونه به د Error0 ارزښت تولید کړي. دا یو base او اختیاري فرعي تېروتنه لري. تاسو کولی شئ د Error انٹرفیس پرځای د کانکریټ *Error0 جوړښت بیرته راستنولو لپاره NewX() وکاروئ ، مګر تاسو اړتیا لرئ محتاط اوسئ .

 type Error0 struct { base err error } var errA: Error = DEPS.PG.NOT_FOUND.New (ctx, "not found") var errB: *Error0 = DEPS.PG.NOT_FOUND.NewX(ctx, "not found")


base هغه ګډ جوړښت دی چې د ټولو Error پلي کولو لخوا شریک شوی، د ګډ فعالیت چمتو کولو لپاره: Code() , Message() , StackTrace() , Fields() , او نور.


 type base struct { code Code msg string kv []tags.Field stack stacktrace.StackTrace }


VlError د تایید غلطیو لپاره دی

دا کولی شي ډیری فرعي غلطی ولري، او د اعتبار وړ مرسته کونکو سره کار کولو لپاره ښه میتودونه چمتو کړي.

 type VlError struct { base errs []error }


تاسو کولی شئ د نورو Error په څیر یو VlError جوړ کړئ:

 err := PRFL.USR.INVALID_ARGUMENT.New(ctx, "invalid request")


یا VlBuilder جوړ کړئ، په هغې کې غلطۍ اضافه کړئ، بیا یې په VlError کې بدل کړئ:

 userID, err0 := parseUUID(req.UserId) err1 := validatePassword(req.Password) vl := PRFL.USR.INVALID_ARGUMENT.WithMsg("invalid request") vl.Add(err0, err1) vlErr := vl.ToError(ctx)


او د معمول په څیر کلیدي / ارزښت جوړه شامل کړئ:

 vl := PRFL.USR.INVALID_ARGUMENT. With(l.Bool("testingenv", true)). WithMsg("invalid request") userID, err0 := parseUUID(req.UserId) err1 := validatePassword(req.Password) vl.Add(err0, err1) vlErr := vl.ToError(ctx, l.String("user_id", req.UserId))


fmt.Printf("%+v", vlErr) په کارولو سره به تولید شي:

 [PRFL.USR.INVALID_ARGUMENT] invalid request {"testingenv": true, "user_id": "A1234567890"}


ApiError د API غلطیو مهاجرت لپاره اډاپټر دی

مخکې، موږ یو جلا api.Error جوړښت کارولی ترڅو د API غلطیو بیرته راستنیدو لپاره مخکینۍ پای او بهرني مراجعینو ته. پدې کې ErrorType ErrorCode په توګه شامل دي لکه څنګه چې مخکې یادونه وشوه .

 package api import errorpb "connectly.ai/pb/models/error" // Deprecated type Error struct { pbType errorpb.ErrorType pbCode errorpb.ErrorCode cause error msg string usrMsg string usrTitle string // ... }


دا ډول اوس رد شوی دی. پرځای یې، موږ به ټول نقشه ( ErrorType ، ErrorCode ، gRPC کوډ، HTTP کوډ) په مرکزي ځای کې اعلان کړو، او په اړونده حدودو کې یې بدل کړو. زه به په راتلونکې برخه کې د کوډ اعلان په اړه بحث وکړم.


د نوي نوم ځای خطا چوکاټ ته د مهاجرت کولو لپاره، موږ یو لنډمهاله نوم ځای ZZZ.API_TODO اضافه کړ. هر ErrorCode د ZZZ.API_TODO کوډ کیږي.

 ZZZ.API_TODO.UNEXPECTED ZZZ.API_TODO.INVALID_REQUEST ZZZ.API_TODO.USERNAME_ ZZZ.API_TODO.META_CHOSE_NOT_TO_DELIVER ZZZ.API_TODO.MESSAGE_WABA_TEMPLATE_CAN_ONLY_EDIT_ONCE_IN_24_HOURS


او ApiError د اډاپټر په توګه رامینځته شوی. ټول هغه فنکشنونه چې مخکې *api.Error بیرته راګرځي د بیرته راستنیدو په Error بدل شوي (د *ApiError لخوا پلي شوي) پرځای.

 package api import . "connectly.ai/go/pkgs/errors/E" // previous func FailPreconditionf(err error, msg string, args ...any) *Error { return &Error{ pbType: ERROR_TYPE_FAILED_PRECONDITION, pbCode: ERROR_CODE_MESSAGE_WABA_TEMPLATE_CAN_ONLY_EDIT_ONCE_IN_24_HOURS, cause: err, msg: fmt.Sprintf(msg, args...) } } // current: this is deprecated, and serves and an adapter func FailPreconditionf(err error, msg string, args ...any) *Error { ctx := context.TODO() return ZZZ.API_TODO.MESSAGE_WABA_TEMPLATE_CAN_ONLY_EDIT_ONCE_IN_24_HOURS. CallerSkip(1). // correct the stacktrace by 1 frame Wrap(ctx, err, msg, args...) }


کله چې ټول مهاجرت ترسره شي، پخوانی کارول:

 wabaErr := verifyWabaTemplateStatus(tpl) apiErr := api.FailPreconditionf(wabaErr, "template cannot be edited"). WithErrorCode(ERROR_CODE_MESSAGE_WABA_TEMPLATE_CAN_ONLY_EDIT_ONCE_IN_24_HOURS). WithUserMsg("According to WhatsApp, the message template can be only edited once in 24 hours. Consider creating a new message template instead."). ErrorOrNil()


باید شي:

 CPG.TPL.EDIT_ONCE_IN_24_HOURS.Wrap( wabaErr, "template cannot be edited", api.UserMsg("According to WhatsApp, the message template can be only edited once in 24 hours. Consider creating a new message template instead."))


په یاد ولرئ چې د ErrorCode په ښکاره ډول د داخلي نوم ځای کوډ څخه اخیستل شوی. اړتیا نشته چې هر ځل یې په واضح ډول وټاکئ. مګر څنګه د کوډونو ترمنځ اړیکه اعلان کړئ؟ دا به په راتلونکې برخه کې تشریح شي.

د نوي غلطی کوډونو اعلان کول

پدې مرحله کې ، تاسو دمخه پوهیږئ چې څنګه د موجوده کوډونو څخه نوې غلطۍ رامینځته کړئ. دا وخت دی چې د کوډونو په اړه تشریح کړئ او څنګه یو نوی اضافه کړئ.


یو Code د uint16 ارزښت په توګه پلي کیږي ، کوم چې د ورته تار پریزنټشن لري.

 type Code struct { code: uint16 } fmt.Printf("%q", DEPS.PG.NOT_FOUND) // "DEPS.PG.NOT_FOUND"


د دې تارونو ذخیره کولو لپاره ، دلته د ټولو موجود CodeDesc لړۍ شتون لري:

 const MaxCode = 321 // 👈 this value is generated var allCodes [MaxCode]CodeDesc type CodeDesc { c int // 42 code string // DEPS.PG.NOT_FOUND api APICodeDesc } type APICodeDesc { ErrorType errorpb.ErrorType ErrorCode errorpb.ErrorCode HttpCode int DefMessage string UserMessage string UserTitle string }


دلته د کوډونو اعلان کولو څرنګوالی دی:

 var DEPS deps // dependencies var PRFL prfl // profile var FLD fld // flow document type deps struct { PG pg // postgres RD rd // redis } // tag:postgres type pg struct { NOT_FOUND Code0 // record not found CONFLICT Code0 // record already exist MALFORM_SQL Code0 } // tag:profile type PRFL struct { REPO prfl_repo USR usr AUTH auth } // tag:profile type prfl_repo struct { NOT_FOUND Code0 // internal error code INVALID_ARGUMENT VlCode // internal error code } // tag:usr type usr struct { NOT_FOUND Code0 `api-code:"USER_NOT_FOUND"` INVALID_ARGUMENT VlCode `api-code:"INVALID_ARGUMENT"` DISABlED_ACCOUNT Code0 `api-code:"DISABLED_ACCOUNT"` } // tag:auth type auth struct { UNAUTHENTICATED Code0 `api-code:"UNAUTHENTICATED"` PERMISSION_DENIED Code0 `api-code:"PERMISSION_DENIED"` }


د نوي کوډونو اعلانولو وروسته، تاسو اړتیا لرئ چې د نسل سکریپټ چل کړئ :

 run gen-errors


تولید شوی کوډ به داسې ښکاري:

 // Code generated by error-codes. DO NOT EDIT. func init() { // ... PRFL.AUTH.UNAUTHENTICATED = Code0{Code{code: 143}} PRFL.AUTH.PERMISSION_DENIED = Code0{Code{code: 144}} // ... allCodes[143] = CodeDesc{ c: 143, code: "PRFL.AUTH.UNAUTHENTICATED", tags: []string{"auth", "profile"}, api: APICodeDesc{ ErrorType: ERROR_TYPE_UNAUTHENTICATED, ErrorCode: ERROR_CODE_UNAUTHENTICATED, HTTPCode: 401, DefMessage: "Unauthenticated error", UserMessage: "You are not authenticated.", UserTitle: "Unauthenticated error", })) }


د هرې Error ډول د ورته Code ډول لري

کله مو فکر کړی چې څنګه PRFL.USR.NOT_FOUND.New() یو *Error0 جوړوي او PRFL.USR.INVALID_ARGUMENTS.New() یو *VlError جوړوي؟ دا ځکه چې دوی مختلف کوډ ډولونه کاروي.


او د هر Code ډول مختلف Error ډول بیرته راګرځوي، هر یو کولی شي خپل اضافي میتودونه ولري:

 type Code0 struct { Code } type VlCode struct { Code } func (c Code0) New(/*...*/) Error { return &Error0{/*...*/} } func (c VlCode) New(/*...*/) Error { return &VlError{/*...*/} } // extra methods on VlCode to create VlBuilder func (c VlCode) WithMsg(msg string, args ...any) *VlBuilder {/*...*/} type VlBuilder struct { code VlCode msg string args []any } func (b *VlBuilder) ToError(/*...*/) Error { return &VlError{Code: code, /*...*/ } }


د بهرني API لپاره موجود کوډونو په نښه کولو لپاره api-code وکاروئ

  • د نوم ځای خطا کوډ باید په داخلي توګه وکارول شي.

  • په بهرني HTTP API کې د بیرته راستنیدو لپاره کوډ چمتو کولو لپاره ، تاسو اړتیا لرئ دا api-code سره په نښه کړئ. ارزښت د اړونده errorpb.ErrorCode دی.

  • که د تېروتنې کوډ api-code سره په نښه شوی نه وي، دا داخلي کوډ دی او د عمومي Internal Server Error په توګه به ښودل شي.

  • په یاد ولرئ چې PRFL.USR.NOT_FOUND بهرنی کوډ دی، پداسې حال کې چې PRFL.USR.REPO.NOT_FOUND داخلي کوډ دی.


د اینوم اختیار په کارولو سره په پروټوبف کې ErrorCode ، ErrorType ، او gRPC/HTTP کوډونو ترمنځ نقشه اعلان کړئ :

 // error/type.proto ERROR_TYPE_PERMISSION_DENIED = 707 [(error_type_detail_option) = { type: "PermissionDeniedError", grpc_code: PERMISSION_DENIED, http_code: 403, // Forbidden message: "permission denied", user_title: "Permission denied", user_message: "The caller does not have permission to execute the specified operation.", }]; // error/code.proto ERROR_CODE_DISABlED_ACCOUNT = 70020 [(error_code_detail_option) = { error_type: ERROR_TYPE_DISABlED_ACCOUNT, grpc_code: PERMISSION_DENIED, http_code: 403, // Forbidden message: "account is disabled", user_title: "Account is disabled", user_message: "Your account is disabled. Please contact support for more information.", }];

UNEXPECTED او UNKNOWN کوډونه

هره طبقه معمولا دوه عمومي کوډونه لري UNEXPECTED او UNKNOWN . دوی یو څه مختلف اهدافو ته خدمت کوي:

  • UNEXPECTED کوډ د غلطیو لپاره کارول کیږي چې هیڅکله باید پیښ نشي.
  • UNKNOWN کوډ د هغو غلطیو لپاره کارول کیږي چې په ښکاره توګه نه اداره کیږي.

نوي کوډ ته د نقشه کولو تېروتنې

کله چې د فنکشن څخه بیرته راستون شوې تېروتنه ترلاسه کول، تاسو اړتیا لرئ چې دا اداره کړئ: د دریمې ډلې تېروتنې د داخلي نوم ځای غلطیو ته بدل کړئ او د نقشې غلطی کوډونه د داخلي پرتونو څخه بهر پرتونو ته.


د دریمې ډلې غلطۍ د داخلي نوم ځای غلطیو ته واړوئ

تاسو څنګه خطاګانې اداره کوئ پدې پورې اړه لري: د دریمې ډلې کڅوړه څه بیرته راګرځي او ستاسو غوښتنلیک څه ته اړتیا لري. د مثال په توګه، کله چې د ډیټابیس یا بهرني API غلطیو اداره کول:

 switch { case errors.Is(err, sql.ErrNoRows): // map a database "no rows" error to an internal "not found" error return nil, PRFL.USR.NOT_FOUND.Wrap(ctx, err, "user not found") case errors.Is(err, context.DeadlineExceeded): // map a context deadline exceeded error to a timeout error return nil, PRFL.USR.TIMEOUT.Wrap(ctx, err, "query timeout") default: // wrap any other error as unknown return nil, PRFL.USR.UNKNOWN.Wrap(ctx, err, "unexpected error") }


د داخلي نوم ځای غلطیو لپاره د مرستندویانو کارول

  • IsErrorCode(err, CODES...) : چک کوي چې آیا تېروتنه کوم ټاکل شوي کوډونه لري.
  • IsErrorGroup(err, GROUP) : ریښتیا راشئ که چیرې خطا د ان پټ ګروپ پورې اړه ولري.


د کارونې عادي بڼه:

 user, err := queryUser(ctx, userReq) switch { case err == nil: // continue case IsErrorCode(PRL.USR.REPO.NOT_FOUND): // check for specific error code and convert to external code // and return as HTTP 400 Not Found return nil, PRFL.USR.NOT_FOUND.Wrap(ctx, err, "user not found") case IsGroup(PRL.USR): // errors belong to the PRFL.USR group are returned as is return nil, err default: return nil, PRL.USR.UNKNOWN.Wrap(ctx, err, "failed to query user") }


MapError() د نقشه کولو کوډ لیکلو لپاره آسانه:

څنګه چې د نقشه کولو خطا کوډونه یو عام نمونه ده ، نو د کوډ لیکلو ګړندي کولو لپاره د MapError() مرسته کونکی شتون لري. پورتني کوډ په لاندې ډول لیکل کیدی شي:

 user, err := queryUser(ctx, userReq) if err != nil { return nil, MapError(ctx, err). Map(PRL.USR.REPO.NOT_FOUND, PRFL.USR.NOT_FOUND, "user not found"). KeepGroup(PRF.USR). Default(PRL.USR.UNKNOWN, "failed to query user") }


تاسو کولی شئ دلیلونه فارمیټ کړئ او د معمول په توګه کلیدي / ارزښت جوړه اضافه کړئ:

 return nil, MapError(ctx, err). Map(PRL.USR.REPO.NOT_FOUND, PRFL.USR.NOT_FOUND, "user %v not found", username, l.String("flag", flag)). KeepGroup(PRF.USR). Default(PRL.USR.UNKNOWN, "failed to query user", l.Any("retries", retryCount))

د نوم ځای سره ازموینه Error s

ازموینه د هر جدي کوډ اساس لپاره خورا مهمه ده. چوکاټ ځانګړي مرسته کونکي چمتو کوي لکه ΩxError() ترڅو په ازموینو کې د خطا شرایطو لیکل او تاکید اسانه او ډیر څرګند کړي.

 // 👉 return true if the error contains the message ΩxError(err).Contains("not found") // 👉 return true if the error does not contain the message ΩxError(err).NOT().Contains("not found")


ډیری نور میتودونه شتون لري، او تاسو کولی شئ دوی هم زنځیر کړئ:

 ΩxError(err). MatchCode(DEPS.PG.NOT_FOUND). // match any code in top or wrapped errors TopErrorMatchCode(PRFL.TPL.NOT_FOUND) // only match code from the top error MatchAPICode(API_CODE.WABA_TEMPLATE_NOTE_FOUND). // match errorpb.ErrorCode MatchExact("exact message to match")


ولې د Ω(err).To(testing.MatchCode()) پر ځای میتودونه وکاروئ ؟

ځکه چې میتودونه د موندلو وړ دي. کله چې تاسو د لسګونو دندو لکه testing.MatchValues() سره مخ شئ، دا ستونزمنه ده چې پوه شئ چې کوم به Error s سره کار وکړي او کوم به نه. د میتودونو سره، تاسو کولی شئ په ساده ډول یو ټکی ټایپ کړئ . ، او ستاسو IDE به ټول موجود میتودونه لیست کړي چې په ځانګړي ډول Error ادعا کولو لپاره ډیزاین شوي.


مهاجرت

چوکاټ یوازې د کیسې نیمایي دی. کوډ لیکل؟ دا اسانه برخه ده. اصلي ننګونه هغه وخت پیل کیږي کله چې تاسو باید دا په پراخه ، ژوندي کوډبیس کې راوړئ چیرې چې لسګونه انجینران هره ورځ بدلونونه هڅوي ، پیرودونکي تمه لري چې هرڅه سم کار وکړي ، او سیسټم نشي کولی د چلولو مخه ونیسي.


مهاجرت د مسؤلیت سره راځي. دا په احتیاط سره د ویښتو کوچني کوډونو ویشلو ، په یو وخت کې کوچني بدلونونه رامینځته کول ، په پروسه کې د ټن ازموینې ماتولو په اړه دي. بیا په لاسي ډول معاینه کول او یو یو یې تنظیم کول ، په اصلي څانګه کې ضمیمه کول ، تولید ته ځای په ځای کول ، د لاګونو او خبرتیاو لیدل. په وار وار یې تکرارول...


دلته د مهاجرت لپاره ځینې لارښوونې دي چې موږ د لارې په اوږدو کې زده کړل:


د لټون او بدلولو سره پیل کړئ: د نوي چوکاټ سره د زړو نمونو بدلولو سره پیل کړئ. د تالیف کومې ستونزې حل کړئ چې د دې پروسې څخه راپورته کیږي.

د مثال په توګه، په دې بسته کې ټولې error Error سره بدل کړئ.

 type ProfileController interface { LoginUser(req *LoginRequest) (*LoginResponse, error) QueryUser(req *QueryUserRequest) (*QueryUserResponse, error) }

نوی کوډ به داسې ښکاري:

 import . "connectly.ai/go/pkgs/errors" type ProfileController interface { LoginUser(req *LoginRequest) (*LoginResponse, Error) QueryUser(req *QueryUserRequest) (*QueryUserResponse, Error) }


په یو وخت کې یوه کڅوړه مهاجرت کړئ: د ټیټې کچې کڅوړو سره پیل وکړئ او خپل کار پورته کړئ. په دې توګه، تاسو کولی شئ ډاډ ترلاسه کړئ چې د ټیټې کچې کڅوړې په بشپړه توګه لیږدول شوي مخکې له دې چې لوړې کچې ته لاړ شي.


د ورک شوي واحد ازموینې اضافه کړئ: که چیرې د کوډبیس برخې ازموینې نلري ، نو اضافه کړئ. که تاسو په خپلو بدلونونو ډاډه نه یاست، نور ازموینې اضافه کړئ. دوی ګټور دي ترڅو ډاډ ترلاسه کړي چې ستاسو بدلونونه موجوده فعالیت نه ماتوي.


که ستاسو بسته د لوړې کچې کڅوړو په زنګ وهلو پورې اړه ولري: اړونده افعال په پام کې ونیسئ په DEPRECATED کې بدل کړئ بیا د نوي Error ډول سره نوي افعال اضافه کړئ.


فرض کړئ چې تاسو د ډیټابیس کڅوړه مهاجرت کوئ، کوم چې د Transaction() میتود لري:

 package database func (db *DB) Transaction(ctx context.Context, fn func(tx *gorm.DB) error) error { return db.gorm.Transaction(func(tx *gorm.DB) error { return fn(tx) }) }


او دا د کارونکي خدماتو بسته کې کارول کیږي:

 err = s.DB(ctx).Transaction(func(tx *database.DB) error { user, usrErr := s.repo.CreateUser(ctx, tx, user) if usrErr != nil { return usrErr } }


له هغه ځایه چې تاسو لومړی د database کڅوړه مهاجرت کوئ ، نو user او لسګونه نور کڅوړې ورته پریږدئ. د s.repo.CreateUser() زنګ لاهم د پخوانۍ error ډول بیرته راګرځوي پداسې حال کې چې د Transaction() میتود اړتیا لري چې د نوي Error ډول بیرته راستانه کړي. تاسو کولی شئ د Transaction() میتود په DEPRECATED بدل کړئ او یو نوی TransactionV2() میتود اضافه کړئ:

 package database // DEPRECATED: use TransactionV2 instead func (db *DB) Transaction_DEPRECATED(ctx context.Context, fn func(tx *gorm.DB) error) error { return db.gorm.Transaction(func(tx *gorm.DB) error { return fn(tx) }) } func (db *DB) TransactionV2(ctx context.Context, fn func(tx *gorm.DB) error) Error { err := db.gorm.Transaction(func(tx *gorm.DB) error { return fn(tx) }) return adaptToErrorV2(err) }


د تېروتنې نوي کوډونه اضافه کړئ لکه څنګه چې تاسو لاړ شئ : کله چې تاسو د یوې تېروتنې سره مخ شئ چې په موجوده کې مناسب نه وي، نو نوی کوډ اضافه کړئ. دا به تاسو سره د وخت په تیریدو سره د خطا کوډونو جامع سیټ رامینځته کولو کې مرسته وکړي. د نورو کڅوړو کوډونه تل د حوالې په توګه شتون لري.


پایله

په Go کې د خطا اداره کول په لومړي سر کې ساده احساس کولی شي — یوازې یوه error بیرته راګرځئ او پرمخ لاړشئ. مګر لکه څنګه چې زموږ کوډبیس وده وکړه ، دا سادگي د مبهم لاګونو ، متناسب اداره کولو ، او نه ختمیدونکي ډیبګ سیشنونو په پیچلي ګډوډي بدله شوه.


په شا تګ او بیا فکر کولو سره چې موږ څنګه تېروتنې اداره کوو، موږ یو سیسټم جوړ کړی چې زموږ لپاره کار کوي، نه زموږ په وړاندې. مرکزي او جوړښت شوي نوم ځای کوډونه موږ ته روښانه کوي، پداسې حال کې چې د نقشه کولو، ریپ کولو، او ازموینې تېروتنې وسایل زموږ ژوند اسانه کوي. د دې پر ځای چې د لوګو د بحر له لارې لامبو وهو، موږ اوس معنی لرونکي، د موندلو وړ تېروتنې لرو چې موږ ته ووایي چې څه غلط دي او چیرته یې وګورو.


دا چوکاټ یوازې زموږ د کوډ پاکولو په اړه ندی؛ دا د وخت خوندي کولو، د مایوسۍ کمولو، او د نامعلومو لپاره چمتو کولو کې مرسته کوي. دا یوازې د سفر پیل دی - موږ لاهم نور نمونې کشف کوو - مګر پایله یې یو سیسټم دی چې کولی شي په یو ډول د غلطۍ اداره کولو لپاره د ذهن سوله راولي. په امید سره، دا کولی شي ستاسو د پروژو لپاره ځینې نظرونه هم روښانه کړي! 😊



لیکوال

زه Oliver Nguyen یم یو سافټویر جوړونکی چې ډیری یې په Go او JavaScript کې کار کوي. زه هره ورځ د خپل ځان غوره نسخه زده کولو او لیدو څخه خوند اخلم. ځینې وختونه د خلاصې سرچینې نوې پروژې بند کړئ. زما د سفر په جریان کې پوهه او فکرونه شریک کړئ.

پوسټ په blog.connectly.ai او olivernguyen.io کې هم خپور شوی 👋