Errors x Stack x Info
===
<!-- .slide: data-background="pink" -->
<!-- .slide: data-transition="zoom" -->
> :hash: errors
> [name=郭學聰 Hsueh-Tsung Kuo] [time=Wed, 23 Sep 2020] [color=red]
note: 2020-05-23
---
<!-- .slide: data-transition="convex" -->
## Who am I?
![fieliapm](https://www.gravatar.com/avatar/2aef78f04240a6ac9ccd473ba1cbd1e3?size=2048 =512x512)
----
<!-- .slide: data-transition="convex" -->
* programmer from Rayark, a game company in Taiwan
* backend (and temporary frontend) engineer, focus on common service
* usually develop something related to my work in Python, Ruby, ECMAScript, Golang, C#
* ECMAScript hater since **Netscape** is dead
* built CDN-aware game asset update system
* built almost entire VOEZ game server by myself only
* supported Sdorica backend development
---
<!-- .slide: data-transition="convex" -->
## Outline
----
<!-- .slide: data-transition="convex" -->
4. TL;DR
5. Theory
6. Troll
7. Conclusion
8. Q&A
---
<!-- .slide: data-transition="convex" -->
## TL;DR
----
<!-- .slide: data-transition="convex" -->
* :no_entry_sign: Go 1.13 error system
* :no_entry_sign: golang/xerrors
* https://github.com/golang/xerrors
* https://godoc.org/golang.org/x/xerrors
----
<!-- .slide: data-transition="convex" -->
* :thumbsup: pkg/errors
* https://github.com/pkg/errors
* https://godoc.org/github.com/pkg/errors
----
<!-- .slide: data-transition="convex" -->
* for http server
* use the gin, luke
* <small>https://github.com/gin-gonic/gin#model-binding-and-validation</small>
----
<!-- .slide: data-transition="convex" -->
or follow the below
----
<!-- .slide: data-transition="convex" -->
```go=
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"reflect"
"strings"
"github.com/julienschmidt/httprouter"
)
// utility function
func _wrapError(msg string, err error) error {
return fmt.Errorf("%s: %w", msg, err)
}
func GetErrorMessageTitle(err error) string {
return strings.SplitN(err.Error(), ":", 2)[0]
}
// ApiError
type ApiError struct {
statusCode int
msg string
err error
}
func NewApiError(statusCode int, msg string, err error) error {
if err != nil {
msg = fmt.Sprintf("%s: %s", msg, err.Error())
}
return &ApiError{statusCode: statusCode, msg: msg, err: err}
}
func (e *ApiError) Error() string {
return e.msg
}
func (e *ApiError) Unwrap() error {
return e.err
}
func (e *ApiError) StatusCode() int {
return e.statusCode
}
func ExtractErrorMessageChain(err error) string {
e := err
var b strings.Builder
for {
b.WriteString(e.Error() + "\n")
e = errors.Unwrap(e)
if e == nil {
break
}
}
return b.String()
}
func IsErrorEqual(e1 error, e2 error) bool {
if e1 == nil || e2 == nil {
return e1 == e2
} else {
return reflect.TypeOf(e1) == reflect.TypeOf(e2) && GetErrorMessageTitle(e1) == GetErrorMessageTitle(e2)
}
}
func WrapError(template error, err error) error {
apiErrorTemplate, ok := template.(*ApiError)
if ok {
err = NewApiError(apiErrorTemplate.StatusCode(), apiErrorTemplate.Error(), err)
} else {
err = _wrapError(template.Error(), err)
}
return err
}
// handler
type HandleE func(http.ResponseWriter, *http.Request, httprouter.Params) error
func ErrAwareHandle(h HandleE) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
err := h(w, r, ps)
if err != nil {
switch e := err.(type) {
case *ApiError:
/*var respBody string
cause := errors.Unwrap(e)
if cause != nil {
respBody = e.Error() + ": " + cause.Error()
} else {
respBody = e.Error()
}
http.Error(w, respBody, e.StatusCode())*/
http.Error(w, e.Error(), e.StatusCode())
default:
http.Error(w, e.Error(), 500)
}
messageChain := ExtractErrorMessageChain(err)
fmt.Fprintf(os.Stderr, "error:\n%s\n", messageChain)
}
}
}
// usage
type Input struct {
Msg string `json:msg`
}
func example(w http.ResponseWriter, r *http.Request, ps httprouter.Params) error {
name := ps.ByName("name")
if name != "say" {
return NewApiError(404, "not found", errors.New("must be /error_test/say"))
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return fmt.Errorf("body read failed: %v", err)
}
if len(body) < 2 {
return errors.New("body is not enough")
}
var input Input
if err = json.Unmarshal(body, &input); err != nil {
return NewApiError(400, "bad request", err)
}
if input.Msg == "" {
return NewApiError(400, "msg lost", nil)
}
if input.Msg == "FQ" {
return NewApiError(403, "you dirty", fmt.Errorf("変態 %w", errors.New("助兵衛")))
}
w.WriteHeader(200)
w.Write([]byte("OK\n"))
return nil
}
func main() {
router := httprouter.New()
router.POST("/error_test/:name", ErrAwareHandle(example))
err := http.ListenAndServe(":8000", router)
if err != nil {
panic(err)
}
}
// client example
// curl -v -X POST --data '{"msg": "FQ2"}' http://localhost:8000/error_test/say
```
---
<!-- .slide: data-transition="convex" -->
## Theory
* what
* error title
* which
* error type
* when
* line number
* where
* calling stack
----
<!-- .slide: data-transition="convex" -->
### :thumbsdown: Go
* [x] error title
* [ ] error type
* [ ] line number
* [ ] calling stack
----
<!-- .slide: data-transition="convex" -->
### Wrap
```go=
err = fmt.Errorf("... %w ...", ..., err, ...)
err.Unwrap()
```
----
<!-- .slide: data-transition="convex" -->
### Inspect Error Chain
```go=
errors.Is(err, ErrorInstance) // must be same address
var perr *ErrorType
errors.As(err, &perr)
```
----
<!-- .slide: data-transition="convex" -->
```go=
package main
import (
"errors"
"fmt"
)
type MyError struct {
msg string
err error
}
func NewMyError(msg string, err error) *MyError {
if err != nil {
msg = fmt.Sprintf("%s: %s", msg, err.Error())
}
return &MyError{
msg: msg,
err: err,
}
}
func (e *MyError) Error() string {
return e.msg
}
func (e *MyError) Unwrap() error {
return e.err
}
func main() {
var err error
err = errors.New("err1")
err = fmt.Errorf("err2: %w", err)
myErr := NewMyError("my err3", err)
myErr2 := NewMyError("my err3", err)
err = myErr
err = fmt.Errorf("err4: %w", err)
err = fmt.Errorf("err5: %w", err)
if err != nil {
fmt.Println("error is:", err)
if errors.Is(err, myErr) {
fmt.Println("It is myErr!")
}
if !errors.Is(err, myErr2) {
fmt.Println("It is NOT myErr2!")
}
fmt.Println("")
var myError *MyError
if errors.As(err, &myError) {
fmt.Println("failed:", err.Error())
fmt.Println("MyError:", myError.Error())
}
}
}
```
---
<!-- .slide: data-transition="convex" -->
## Troll
----
<!-- .slide: data-transition="convex" -->
# Go error system SUCKS
again, Go error system SUCKS
----
<!-- .slide: data-transition="convex" -->
### Rust
![rust](https://www.rust-lang.org/static/images/rust-logo-blk.svg)
----
<!-- .slide: data-transition="convex" -->
```rust=
panic!()
Result<T, E>
Option<T>
myobj.myfn()? // ? operator
```
<small>https://doc.rust-lang.org/book/ch09-00-error-handling.html</small>
----
<!-- .slide: data-transition="convex" -->
```rust=
e.source()
e.backtrace()
```
<small>https://doc.rust-lang.org/std/error/trait.Error.html</small>
---
<!-- .slide: data-transition="convex" -->
## Conclusion
----
<!-- .slide: data-transition="convex" -->
* :thumbsup: pkg/errors
* https://github.com/pkg/errors
* https://godoc.org/github.com/pkg/errors
---
<!-- .slide: data-transition="zoom" -->
## Q&A
---
<style>
.reveal {
background: #FFDFEF;
color: black;
}
.reveal h2,
.reveal h3,
.reveal h4 {
color: black;
}
.reveal code {
font-size: 18px !important;
line-height: 1.2;
}
.rightpart{
float:right;
width:50%;
}
.leftpart{
margin-right: 50% !important;
height:50%;
}
.reveal section img { background:none; border:none; box-shadow:none; }
p.blo {
font-size: 50px !important;
background:#B6BDBB;
border:1px solid silver;
display:inline-block;
padding:0.5em 0.75em;
border-radius: 10px;
box-shadow: 5px 5px 5px #666;
}
p.blo1 {
background: #c7c2bb;
}
p.blo2 {
background: #b8c0c8;
}
p.blo3 {
background: #c7cedd;
}
p.bloT {
font-size: 60px !important;
background:#B6BDD3;
border:1px solid silver;
display:inline-block;
padding:0.5em 0.75em;
border-radius: 8px;
box-shadow: 1px 2px 5px #333;
}
p.bloA {
background: #B6BDE3;
}
p.bloB {
background: #E3BDB3;
}
.slide-number{
margin-bottom:10px !important;
width:100%;
text-align:center;
font-size:25px !important;
background-color:transparent !important;
}
iframe.myclass{
width:100px;
height:100px;
bottom:0;
left:0;
position:fixed;
border:none;
z-index:99999;
}
h1.raw {
color: #fff;
background-image: linear-gradient(90deg,#f35626,#feab3a);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: hue 5s infinite linear;
}
@keyframes hue {
from {
filter: hue-rotate(0deg);
}
to {
filter: hue-rotate(360deg);
}
}
.progress{
height:14px !important;
}
.progress span{
height:14px !important;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAMCAIAAAAs6UAAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUNCQzIyREQ0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUNCQzIyREU0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQ0JDMjJEQjRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQ0JDMjJEQzRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PovDFgYAAAAmSURBVHjaYvjPwMAAxjMZmBhA9H8INv4P4TPM/A+m04zBNECAAQBCWQv9SUQpVgAAAABJRU5ErkJggg==") repeat-x !important;
}
.progress span:after,
.progress span.nyancat{
content: "";
background: url('data:image/gif;base64,R0lGODlhIgAVAKIHAL3/9/+Zmf8zmf/MmZmZmf+Z/wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpDMkJBNjY5RTU1NEJFMzExOUM4QUM2MDAwNDQzRERBQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREIzOEIzMzRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREIzOEIzMjRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM1QkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMyQkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkECQcABwAsAAAAACIAFQAAA6J4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93bqCA7f7TFaYoIFAMMwczB5EkTzJllEUttmIGoG5bfPBjDawD7CsJC67uWcv2CRov929C/q2ZpcBbYBmLGk6W1BRY4MUDnMvJEsBAXdlknk2fCeRk2iJliAijpBlEmigjR0plKSgpKWvEUheF4tUZqZID1RHjEe8PsDBBwkAIfkECQcABwAsAAAAACIAFQAAA6B4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93TqS40XiKSYgTLBgIBAMqE/zmQSaZEzns+jQ9pC/5dQJ0VIv5KMVWxqb36opxHrNvu9ptPfGbmsBbgSAeRdydCdjXWRPchQPh1hNAQF4TpM9NnwukpRyi5chGjqJEoSOIh0plaYsZBKvsCuNjY5ptElgDyFIuj6+vwcJACH5BAkHAAcALAAAAAAiABUAAAOfeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GKifWaddvNQAtszXYCxgR/Zy5jYTFeXmSDiIZGdQEBd06QSBQ5e4cEkE9nnZQaG2J4F4MSLx8rkqUSZBeurhlTUqsLsi60DpZxSWBJugcJACH5BAkHAAcALAAAAAAiABUAAAOgeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GuifWaddvNwMkZtmY7AWMEgGcKY2ExXl5khFMVc0Z1AQF3TpJShDl8iASST2efloV5JTyJFpgOch8dgW9KZxexshGNLqgLtbW0SXFwvaJfCQAh+QQJBwAHACwAAAAAIgAVAAADoXi63P7wmUmrnVGOzbvfRsYYXGGe6MmF4kEOaSGYMwq2LizHfDGwIlLPNKGZfi6gZmggEAy2iVPZEKZqzakq+1xUFFYe90lxTsHmim6HGpvf3eR7skYJ3PC5tyystc0AboFnVXQ9XFJTZIQOYUYFTQEBeWaSVF4bbCeRk1meBJYSL3WbaReMIxQfHXh6jaYXsbEQni6oaF21ERR7l0ksvA0JACH5BAkHAAcALAAAAAAiABUAAAOeeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMfFlA4hTITEMxkIBMOuADwmhzqeM6mashTCXKw2TVKQyKuTRSx2wegnNkyJ1ozpOFiMLqcEU8BZHx6NYW8nVlZefQ1tZgQBAXJIi1eHUTRwi0lhl48QL0sogxaGDhMlUo2gh14fHhcVmnOrrxNqrU9joX21Q0IUElm7DQkAIfkECQcABwAsAAAAACIAFQAAA6J4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93bqCA7f7TFaYoIFAMMwczB5EkTzJllEUttmIGoG5bfPBjDawD7CsJC67uWcv2CRov929C/q2ZpcBbYBmLGk6W1BRY4MUDnMvJEsBAXdlknk2fCeRk2iJliAijpBlEmigjR0plKSgpKWvEUheF4tUZqZID1RHjEe8PsDBBwkAIfkECQcABwAsAAAAACIAFQAAA6B4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93TqS40XiKSYgTLBgIBAMqE/zmQSaZEzns+jQ9pC/5dQJ0VIv5KMVWxqb36opxHrNvu9ptPfGbmsBbgSAeRdydCdjXWRPchQPh1hNAQF4TpM9NnwukpRyi5chGjqJEoSOIh0plaYsZBKvsCuNjY5ptElgDyFIuj6+vwcJACH5BAkHAAcALAAAAAAiABUAAAOfeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GKifWaddvNQAtszXYCxgR/Zy5jYTFeXmSDiIZGdQEBd06QSBQ5e4cEkE9nnZQaG2J4F4MSLx8rkqUSZBeurhlTUqsLsi60DpZxSWBJugcJACH5BAkHAAcALAAAAAAiABUAAAOgeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GuifWaddvNwMkZtmY7AWMEgGcKY2ExXl5khFMVc0Z1AQF3TpJShDl8iASST2efloV5JTyJFpgOch8dgW9KZxexshGNLqgLtbW0SXFwvaJfCQAh+QQJBwAHACwAAAAAIgAVAAADoXi63P7wmUmrnVGOzbvfRsYYXGGe6MmF4kEOaSGYMwq2LizHfDGwIlLPNKGZfi6gZmggEAy2iVPZEKZqzakq+1xUFFYe90lxTsHmim6HGpvf3eR7skYJ3PC5tyystc0AboFnVXQ9XFJTZIQOYUYFTQEBeWaSVF4bbCeRk1meBJYSL3WbaReMIxQfHXh6jaYXsbEQni6oaF21ERR7l0ksvA0JACH5BAkHAAcALAAAAAAiABUAAAOeeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMfFlA4hTITEMxkIBMOuADwmhzqeM6mashTCXKw2TVKQyKuTRSx2wegnNkyJ1ozpOFiMLqcEU8BZHx6NYW8nVlZefQ1tZgQBAXJIi1eHUTRwi0lhl48QL0sogxaGDhMlUo2gh14fHhcVmnOrrxNqrU9joX21Q0IUElm7DQkAOw==') !important;
width: 34px !important;
height: 21px !important;
border: none !important;
float:right;
margin-top:-7px;
margin-right:-10px;
}
</style>
{"metaMigratedAt":"2023-06-15T08:52:26.347Z","metaMigratedFrom":"Content","title":"Errors x Stack x Info","breaks":true,"contributors":"[{\"id\":\"ea27dcd7-a3f2-47c2-b25e-6760e7936c38\",\"add\":33506,\"del\":18351}]"}