`Wrap(err)` in production

`Wrap(err)` in production

9eed44f137609e6ce3b6f1e14f80b9e1?s=128

Masayuki Izumi

October 19, 2018
Tweet

Transcript

  1. Go(Un)Conference 4kg - @izumin5210 `Wra p ( e r r

    )` i n pro du ction
  2. izumin5210 ‣ Wantedly People ‣ Web Engineer (serverside & frontend)

    ‣ Gopher & Rubyist
  3. `Wrap(err)`ͯ͠·͔͢ʁ ࠓ೔AWrap(err)Aͨ͠ਓʙʁ

  4. ෮शͳΜͰAWrap(err)A͢Δͷʁ AWrap(err)A͠ͳ͍ͱ͍͚ͳ͍ཧ༝෼͔Δਓʙʁ

  5. func TestErrors_noWrapStdError(t *testing.T) { err := err1() if err !=

    nil { printError(t, err) } } func err1() error { return stderr() } func stderr() error { return stderrors.New("error!") }
  6. func TestErrors_noWrapStdError(t *testing.T) { err := err1() if err !=

    nil { printError(t, err) } } func err1() error { return stderr() } func stderr() error { return stderrors.New("error!") } === RUN TestErrors_noWrapStdError --- PASS: TestErrors_noWrapStdError (0.00s) main.go:16: *** %#v ****************************************** main.go:17: &errors.errorString{s:"error!"} main.go:18: *** %+v ****************************************** main.go:19: error! main.go:20: *** stacktrace *********************************** main.go:20: /go/src/sandbox/main.go:16: main.TestErrors_noWrap main.go:20: /usr/local/go/src/testing/testing.go:777: testing. main.go:20: /usr/local/go/src/runtime/asm_amd64p32.s:968: runt PASS
  7. func TestErrors_noWrapStdError(t *testing.T) { err := err1() if err !=

    nil { printError(t, err) } } func err1() error { return stderr() } func stderr() error { return stderrors.New("error!") } === RUN TestErrors_noWrapStdError --- PASS: TestErrors_noWrapStdError (0.00s) main.go:16: *** %#v ****************************************** main.go:17: &errors.errorString{s:"error!"} main.go:18: *** %+v ****************************************** main.go:19: error! main.go:20: *** stacktrace *********************************** main.go:20: /go/src/sandbox/main.go:16: main.TestErrors_noWrap main.go:20: /usr/local/go/src/testing/testing.go:777: testing. main.go:20: /usr/local/go/src/runtime/asm_amd64p32.s:968: runt PASS
  8. func TestErrors_noWrapStdError(t *testing.T) { err := err1() if err !=

    nil { printError(t, err) } } func err1() error { return stderr() } func stderr() error { return stderrors.New("error!") } === RUN TestErrors_noWrapStdError --- PASS: TestErrors_noWrapStdError (0.00s) main.go:16: *** %#v ****************************************** main.go:17: &errors.errorString{s:"error!"} main.go:18: *** %+v ****************************************** main.go:19: error! main.go:20: *** stacktrace *********************************** main.go:20: /go/src/sandbox/main.go:16: main.TestErrors_noWrap main.go:20: /usr/local/go/src/testing/testing.go:777: testing. main.go:20: /usr/local/go/src/runtime/asm_amd64p32.s:968: runt PASS ΤϥʔΛฦͨؔ͠਺ͷ಺ଆͷελοΫτϨʔε͕ͱΕͳ͍
  9. func TestErrors_Wrap(t *testing.T) { err := err2() if err !=

    nil { printError(t, err) } } func err2() error { err := stderr() return pkgerrors.Wrap(err, "wrapped") } func pkgerr() error { return pkgerrors.New("error!") }
  10. func TestErrors_Wrap(t *testing.T) { err := err2() if err !=

    nil { printError(t, err) } } func err2() error { err := stderr() return pkgerrors.Wrap(err, "wrapped") } func pkgerr() error { return pkgerrors.New("error!") } === RUN TestErrors_Wrap --- PASS: TestErrors_Wrap (0.00s) main.go:47: *** %#v **************************************** main.go:48: wrapped: error! main.go:49: *** %+v **************************************** main.go:50: error! wrapped main.err2 /go/src/sandbox/main.go:30 main.TestErrors_Wrap /go/src/sandbox/main.go:21 testing.tRunner /usr/local/go/src/testing/testing.go:777 runtime.goexit /usr/local/go/src/runtime/asm_amd64p32.s:968 main.go:51: *** stacktrace ********************************* main.go:51: /go/src/sandbox/main.go:24: main.TestErrors_Wrap main.go:51: /usr/local/go/src/testing/testing.go:777: testin main.go:51: /usr/local/go/src/runtime/asm_amd64p32.s:968: ru PASS
  11. func TestErrors_Wrap(t *testing.T) { err := err2() if err !=

    nil { printError(t, err) } } func err2() error { err := stderr() return pkgerrors.Wrap(err, "wrapped") } func pkgerr() error { return pkgerrors.New("error!") } === RUN TestErrors_Wrap --- PASS: TestErrors_Wrap (0.00s) main.go:47: *** %#v **************************************** main.go:48: wrapped: error! main.go:49: *** %+v **************************************** main.go:50: error! wrapped main.err2 /go/src/sandbox/main.go:30 main.TestErrors_Wrap /go/src/sandbox/main.go:21 testing.tRunner /usr/local/go/src/testing/testing.go:777 runtime.goexit /usr/local/go/src/runtime/asm_amd64p32.s:968 main.go:51: *** stacktrace ********************************* main.go:51: /go/src/sandbox/main.go:24: main.TestErrors_Wrap main.go:51: /usr/local/go/src/testing/testing.go:777: testin main.go:51: /usr/local/go/src/runtime/asm_amd64p32.s:968: ru PASS
  12. func TestErrors_Wrap(t *testing.T) { err := err2() if err !=

    nil { printError(t, err) } } func err2() error { err := stderr() return pkgerrors.Wrap(err, "wrapped") } func pkgerr() error { return pkgerrors.New("error!") } === RUN TestErrors_Wrap --- PASS: TestErrors_Wrap (0.00s) main.go:47: *** %#v **************************************** main.go:48: wrapped: error! main.go:49: *** %+v **************************************** main.go:50: error! wrapped main.err2 /go/src/sandbox/main.go:30 main.TestErrors_Wrap /go/src/sandbox/main.go:21 testing.tRunner /usr/local/go/src/testing/testing.go:777 runtime.goexit /usr/local/go/src/runtime/asm_amd64p32.s:968 main.go:51: *** stacktrace ********************************* main.go:51: /go/src/sandbox/main.go:24: main.TestErrors_Wrap main.go:51: /usr/local/go/src/testing/testing.go:777: testin main.go:51: /usr/local/go/src/runtime/asm_amd64p32.s:968: ru PASS ࠷ॳʹ8SBQͨ͠ͱ͜Ζ͔ΒͷτϨʔε͕औΓग़ͤΔ
  13. func main() { err := auth() if err != nil

    { fmt.Fprintln(os.Stderr, errors.Wrap(err, "failed to authorization")) } } func auth() error { _, err := getUser() if err != nil { return errors.Wrap(err, "failed to get user") } return nil } func getUser() (*User, error) { return nil, errors.New("not found error") }
  14. func main() { err := auth() if err != nil

    { fmt.Fprintln(os.Stderr, errors.Wrap(err, "failed to authorization")) } } func auth() error { _, err := getUser() if err != nil { return errors.Wrap(err, "failed to get user") } return nil } func getUser() (*User, error) { return nil, errors.New("not found error") } failed to authorization: failed to get user: not found error
  15. func main() { err := auth() if err != nil

    { fmt.Fprintln(os.Stderr, errors.Wrap(err, "failed to authorization")) } } func auth() error { _, err := getUser() if err != nil { return errors.Wrap(err, "failed to get user") } return nil } func getUser() (*User, error) { return nil, errors.New("not found error") } failed to authorization: failed to get user: not found error Τϥʔϝοηʔδ΋ͲΜͲΜBOOPUBUFͰ͖Δ ˠͲ͏͍͏ίϯςΩετͰΤϥʔ͕ى͖ͯΔ͔͕Θ͔Γ΍͍͢ʂ
  16. ‣ A8SBQ FSS A͢Δ͜ͱͰʜ  ελοΫτϨʔεΛΤϥʔͷൃੜݯ·ͰḪΔ͜ͱ͕Ͱ͖Δ  ΤϥʔࣗମʹͲΜͲΜ৘ใΛ෇ՃͰ͖ΔͷͰɼݪҼΛ௥͏ͷʹ׆༻Ͱ͖Δ ‣ ৄ͘͠͸(P$POGFSFODF4QSJOHͷ,FZOPUFΛݟ·͠ΐ͏

     IUUQTEBWFDIFOFZOFUQBTUFHPDPOTQSJOHQEG
  17. ‣ A8SBQ FSS A͢Δ͜ͱͰʜ  ελοΫτϨʔεΛΤϥʔͷൃੜݯ·ͰḪΔ͜ͱ͕Ͱ͖Δ  ΤϥʔࣗମʹͲΜͲΜ৘ใΛ෇ՃͰ͖ΔͷͰɼݪҼΛ௥͏ͷʹ׆༻Ͱ͖Δ ‣ ৄ͘͠͸(P$POGFSFODF4QSJOHͷ,FZOPUFΛݟ·͠ΐ͏

     IUUQTEBWFDIFOFZOFUQBTUFHPDPOTQSJOHQEG ΄Μͱʹ͜Ε͚ͩͰ͍͍ͷ͔ʁ
  18. ‣ ͜ͷͱ͖ͷεςʔλείʔυ͸ʁ  %#͕ࢮΜͰͨΒ*OUFSOBM  ଘࡏ͠ͳ͍VTFSͳΒ/PU'PVOE  FUD func (s

    *server) GetUser(ctx context.Context, req *Get user, err := s.userRepo.GetUser(ctx, req.GetUserID() if err != nil { return nil, errors.Wrap(err, "failed to get user") } // snip }
  19. ‣ Ͳ͏΍ͬͯΤϥʔΛ൑ผ͢Δ͔  ఆ਺Խʢ4FOUJOFMFSSPST   5ZQFʹ͢Δ &SSPSUZQFTUZQFBTTFSUJPO  ‣

    ਖ਼͍͔͠΋͠Εͳ͍͕ɼຖճఆٛ͢Δͷ͸
 ਖ਼௚ΊΜͲ͍͘͞ʜ switch errors.Cause(err).(type) { case *user.NotFoundError: return nil, status.Error(codes.NotFound, "user not f } if errors.Cause(err) == user.ErrNotFound { return nil, status.Error(codes.NotFound, "user not f }
  20. ‣ ͜ͷΤϥʔ͸&SSPSSFQPSUFSʹ౤͛Δ΂͖ʁ  ࡶͳΞΫηεͰ/PU'PVOEͳΒແࢹ͍ͨ͠  ֤ʑͷϞδϡʔϧ͕উखʹSFQPSUͪ͠Ό͏ͱ
 Τϥʔ͕ॏෳ͢Δ͔΋ ‣ લड़ͷFSSPSUZQFTʹʮSFQPSU͢΂͖ʯϑϥάΛ
 ΋ͨͤΔͷ͔ʜʁ

    func (s *server) GetUser(ctx context.Context, req *Get user, err := s.userRepo.GetUser(ctx, req.GetUserID() if err != nil { return nil, errors.Wrap(err, "failed to get user") } // snip }
  21. Go(Un)Conference 4kg - @izumin5210 `Wra p ( e r r

    )` i n p rod uct ion
  22. ‣ *OPVSQSPEVDUJPO  AHJUIVCDPNTSWDGBJMAΛར༻͍ͯ͠Δ  QLHFSSPSTʹ͍͔ͭ͘ͷNFUBEBUBΛ
 ௥Ճͨ͠΋ͷ  AGBJM8SBQAʹBOOPUBUPSΛ౉͢͜ͱͰ
 ΤϥʔʹϚʔΩϯάͰ͖Δ

    func (r *repo) GetUser(ctx context.Context, id uint64) (*User, user, err := r.db.GetContext(ctx, &user, "...", id) if err == sql.ErrNoRows { return nil, fail.Wrap( err, fail.WithCode(appx.NotFound), fail.WithIgnorable(), ) } if err != nil { return nil, fail.Wrap(err, fail.WithCode(appx.Internal)) } // snip. }
  23. ‣ AGBJM8JUI$PEF DPEF A  εςʔλείʔυΛ౉͢  AG&SS$PEFAͷܗͰऔΓग़ͤΔ func (r

    *repo) GetUser(ctx context.Context, id uint64) (*User, user, err := r.db.GetContext(ctx, &user, "...", id) if err == sql.ErrNoRows { return nil, fail.Wrap( err, fail.WithCode(appx.NotFound), fail.WithIgnorable(), ) } if err != nil { return nil, fail.Wrap(err, fail.WithCode(appx.Internal)) } // snip. } 1FSTJTUFODFMBZFSʹIUUQH31$ͷ஌ࣝΛೖΕͨ͘ͳ͔ͬͨͨΊʢ࣮ଶ͸H31$ͷADPEFT$PEFAͱಉҰʣ
  24. ‣ AGBJM8JUI$PEF DPEF A  εςʔλείʔυΛ౉͢  AG&SS$PEFAͷܗͰऔΓग़ͤΔ ‣ *OPVSQSPEVDUJPO

     ABQQY A͸ࣾ಺Ͱಠࣗఆٛ ͨ͠$PEFܕ  JOUFSDFQUPSͰ͍͍ײ͡ʹH31$ͷDPEFʹ
 Ϛοϐϯά͞ΕΔ func (r *repo) GetUser(ctx context.Context, id uint64) (*User, user, err := r.db.GetContext(ctx, &user, "...", id) if err == sql.ErrNoRows { return nil, fail.Wrap( err, fail.WithCode(appx.NotFound), fail.WithIgnorable(), ) } if err != nil { return nil, fail.Wrap(err, fail.WithCode(appx.Internal)) } // snip. } 1FSTJTUFODFMBZFSʹIUUQH31$ͷ஌ࣝΛೖΕͨ͘ͳ͔ͬͨͨΊʢ࣮ଶ͸H31$ͷADPEFT$PEFAͱಉҰʣ
  25. func (r *repo) GetUser(ctx context.Context, id uint64) (*User, user, err

    := r.db.GetContext(ctx, &user, "...", id) if err == sql.ErrNoRows { return nil, fail.Wrap( err, fail.WithCode(appx.NotFound), fail.WithIgnorable(), ) } if err != nil { return nil, fail.Wrap(err, fail.WithCode(appx.Internal)) } // snip. } ‣ AGBJM8JUI*HOPSBCMF A  ʮ͜Ε͸େͨ͜͠ͱͳ͍ΑʔʯϚʔΫ  ʮ͚ͭ๨ΕͯΤϥʔʹؾ͚ͮͳ͍ʯ
 ͸ةݥͳͷͰɼGBMTFQPTJUJWFʹ౗͍ͯ͠Δ
  26. func (r *repo) GetUser(ctx context.Context, id uint64) (*User, user, err

    := r.db.GetContext(ctx, &user, "...", id) if err == sql.ErrNoRows { return nil, fail.Wrap( err, fail.WithCode(appx.NotFound), fail.WithIgnorable(), ) } if err != nil { return nil, fail.Wrap(err, fail.WithCode(appx.Internal)) } // snip. } ‣ AGBJM8JUI*HOPSBCMF A  ʮ͜Ε͸େͨ͜͠ͱͳ͍ΑʔʯϚʔΫ  ʮ͚ͭ๨ΕͯΤϥʔʹؾ͚ͮͳ͍ʯ
 ͸ةݥͳͷͰɼGBMTFQPTJUJWFʹ౗͍ͯ͠Δ ‣ *OPVSQSPEVDUJPO  ͜Ε͕AUSVFAͳΤϥʔʹ͍ͭͯ͸ɼ
 JOUFSDFQUPSͰউखʹSFQPSU͞ΕΔ  8SBQ๨Εͯ΋SFQPSU͞ΕΔ
  27. ‣ AGBJM8JUI*HOPSBCMF A  ʮ͜Ε͸େͨ͜͠ͱͳ͍ΑʔʯϚʔΫ  ʮ͚ͭ๨ΕͯΤϥʔʹؾ͚ͮͳ͍ʯ
 ͸ةݥͳͷͰɼGBMTFQPTJUJWFʹ౗͍ͯ͠Δ ‣ *OPVSQSPEVDUJPO

     ͜Ε͕AUSVFAͳΤϥʔʹ͍ͭͯ͸ɼ
 JOUFSDFQUPSͰউखʹSFQPSU͞ΕΔ  8SBQ๨Εͯ΋SFQPSU͞ΕΔ func (r *repo) GetUser(ctx context.Context, id uint64) (*User, user, err := r.db.GetContext(ctx, &user, "...", id) if err == sql.ErrNoRows { return nil, fail.Wrap( err, fail.WithCode(appx.NotFound), fail.WithIgnorable(), ) } if err != nil { return nil, fail.Wrap(err, fail.WithCode(appx.Internal)) } // snip. } ͜Ε͸JOUFSDFQUPSͰSFQPSU͞ΕΔ
  28. ‣ 0UIFSBOOPUBUPSTA8JUI5BHTA A8JUI1BSBNTA FUD ‣ AHJUIVCDPNTSWDHSQDFSSPSTA  H31$ͷJOUFSDFQUPSͰGBJM&SSPSΛ͍͍ײ͡ʹϋϯυϧ͢Δ࣮૷ ‣ ࣾ಺ڞ௨ϥΠϒϥϦATFSWJDFYA

     ͜͜ͰAHSQDFSSPSTAΛ͍͍ײ͡ʹηοτͯ͠ɼ͍͍ײ͡ʹFSSPSSFQPSU͢Δ࢓૊ΈΛ࡞͍ͬͯΔ  AHSBQJA ͷ0QUJPOΛఏڙ͍ͯͯ͠ɼߦ௥ه ؀ڥม਺ΛೖΕΔ͚ͩͰ͢΂͕ͯ༗ޮԽ͞ΕΔ HJUIVCDPNJ[VNJOHSBQJH31$ HSQDHBUFXBZΛར༻ͨ͠8FCGSBNFXPSLDPEFHFOFSBUPS
  29. ‣ 8SBQ๨Εͯ΋SFQPSU͞ΕΔͱ͸͍͑ʜ  SFQPSU͞ΕΔϝλσʔλ͕ݮΔͷ͸ࠔΔ  ͔ͩΒͱ͍ͬͯຖճϨϏϡʔͰࢦఠ͢Δͷʜʁ

  30. ‣ 8SBQ๨Εͯ΋SFQPSU͞ΕΔͱ͸͍͑ʜ  SFQPSU͞ΕΔϝλσʔλ͕ݮΔͷ͸ࠔΔ  ͔ͩΒͱ͍ͬͯຖճϨϏϡʔͰࢦఠ͢Δͷʜʁ ‣ *OPVSQSPEVDUJPO  AHJUIVCDPNTSWDXSBQFSSAͱ͍͏ɼ


    8SBQ๨Εݕग़πʔϧΛ࡞ͬͨ  ASFWJFXEPHAͱҰॹʹಈ͔͍ͯ͠Δ
  31. ‣ A8SBQ FSS A͸ΤϥʔͷίϯςΩετΛ఻ൖͤ͞Δͷʹॏཁ  4UBDLUSBDFͳͲ΋ؚΉ *OPVSQSPEVDUJPO ‣ AHJUIVCDPNTSWDGBJMA 

    ͪΐͬͱϦονͳA8SBQ FSS AͰɼ͍ΖΜͳ෇Ճ৘ใΛΤϥʔʹ৐͍ͤͯΔ  ڞ௨ϥΠϒϥϦͷJOUFSDFQUPSͰɼXSBQQFEFSSPSͷ෇Ճ৘ใ͔ΒFSSPSSFQPSUΛ͍ͯ͠Δ ‣ AHJUIVCDPNTSWDXSBQFSSA  A8SBQ FSS AΛ๨ΕΛݕ஌͢ΔπʔϧΛ࢖ͬͯɼ1VMM3FRVFTUͰࣗಈϨϏϡʔ͍ͯ͠Δ