Slide 1

Slide 1 text

Go(Un)Conference 4kg - @izumin5210 `Wra p ( e r r )` i n pro du ction

Slide 2

Slide 2 text

izumin5210 ‣ Wantedly People ‣ Web Engineer (serverside & frontend) ‣ Gopher & Rubyist

Slide 3

Slide 3 text

`Wrap(err)`ͯ͠·͔͢ʁ ࠓ೔AWrap(err)Aͨ͠ਓʙʁ

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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!") }

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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 ΤϥʔΛฦͨؔ͠਺ͷ಺ଆͷελοΫτϨʔε͕ͱΕͳ͍

Slide 9

Slide 9 text

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!") }

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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ͨ͠ͱ͜Ζ͔ΒͷτϨʔε͕औΓग़ͤΔ

Slide 13

Slide 13 text

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") }

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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Ͱ͖Δ ˠͲ͏͍͏ίϯςΩετͰΤϥʔ͕ى͖ͯΔ͔͕Θ͔Γ΍͍͢ʂ

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

‣ ͜ͷͱ͖ͷεςʔλείʔυ͸ʁ %#͕ࢮΜͰͨΒ*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 }

Slide 19

Slide 19 text

‣ Ͳ͏΍ͬͯΤϥʔΛ൑ผ͢Δ͔ ఆ਺Խʢ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 }

Slide 20

Slide 20 text

‣ ͜ͷΤϥʔ͸&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 }

Slide 21

Slide 21 text

Go(Un)Conference 4kg - @izumin5210 `Wra p ( e r r )` i n p rod uct ion

Slide 22

Slide 22 text

‣ *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. }

Slide 23

Slide 23 text

‣ 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ͱಉҰʣ

Slide 24

Slide 24 text

‣ AGBJM8JUI$PEF DPEF A εςʔλείʔυΛ౉͢ AG&SS$PEFAͷܗͰऔΓग़ͤΔ ‣ *OPVSQSPEVDUJPO ABQQYA͸ࣾ಺Ͱಠࣗఆٛͨ͠$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ͱಉҰʣ

Slide 25

Slide 25 text

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ʹ౗͍ͯ͠Δ

Slide 26

Slide 26 text

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͞ΕΔ

Slide 27

Slide 27 text

‣ 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͞ΕΔ

Slide 28

Slide 28 text

‣ 0UIFSBOOPUBUPSTA8JUI5BHTA A8JUI1BSBNTA FUD ‣ AHJUIVCDPNTSWDHSQDFSSPSTA H31$ͷJOUFSDFQUPSͰGBJM&SSPSΛ͍͍ײ͡ʹϋϯυϧ͢Δ࣮૷ ‣ ࣾ಺ڞ௨ϥΠϒϥϦATFSWJDFYA ͜͜ͰAHSQDFSSPSTAΛ͍͍ײ͡ʹηοτͯ͠ɼ͍͍ײ͡ʹFSSPSSFQPSU͢Δ࢓૊ΈΛ࡞͍ͬͯΔ AHSBQJAͷ0QUJPOΛఏڙ͍ͯͯ͠ɼߦ௥ه؀ڥม਺ΛೖΕΔ͚ͩͰ͢΂͕ͯ༗ޮԽ͞ΕΔ HJUIVCDPNJ[VNJOHSBQJH31$HSQDHBUFXBZΛར༻ͨ͠8FCGSBNFXPSLDPEFHFOFSBUPS

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

‣ 8SBQ๨Εͯ΋SFQPSU͞ΕΔͱ͸͍͑ʜ SFQPSU͞ΕΔϝλσʔλ͕ݮΔͷ͸ࠔΔ ͔ͩΒͱ͍ͬͯຖճϨϏϡʔͰࢦఠ͢Δͷʜʁ ‣ *OPVSQSPEVDUJPO AHJUIVCDPNTSWDXSBQFSSAͱ͍͏ɼ
 8SBQ๨Εݕग़πʔϧΛ࡞ͬͨ ASFWJFXEPHAͱҰॹʹಈ͔͍ͯ͠Δ

Slide 31

Slide 31 text

‣ A8SBQ FSS A͸ΤϥʔͷίϯςΩετΛ఻ൖͤ͞Δͷʹॏཁ 4UBDLUSBDFͳͲ΋ؚΉ *OPVSQSPEVDUJPO ‣ AHJUIVCDPNTSWDGBJMA ͪΐͬͱϦονͳA8SBQ FSS AͰɼ͍ΖΜͳ෇Ճ৘ใΛΤϥʔʹ৐͍ͤͯΔ ڞ௨ϥΠϒϥϦͷJOUFSDFQUPSͰɼXSBQQFEFSSPSͷ෇Ճ৘ใ͔ΒFSSPSSFQPSUΛ͍ͯ͠Δ ‣ AHJUIVCDPNTSWDXSBQFSSA A8SBQ FSS AΛ๨ΕΛݕ஌͢ΔπʔϧΛ࢖ͬͯɼ1VMM3FRVFTUͰࣗಈϨϏϡʔ͍ͯ͠Δ