Skip to content

fwhezfwhez/errorx

Repository files navigation

errorx

Godoc Build Status Gitter

a very convenient error handler.

Table of Contents generated with DocToc

1. What is different from the officials

  • Supporting generate error stacktrace, locating more efficiently than runtime stacktrace.
  • Supporting report error to HTTP URL.
  • Supporting generate json-marshalled error detail for outer pipeline.
  • Supporting differrent mode uses different error handlers.

2. Start

go get github.com/fwhezfwhez/errorx

3. Module

3.1 Error stacktrace

package main

import (
	"fmt"
	"github.com/fwhezfwhez/errorx"
)

func main() {
	e := fmt.Errorf("nil return")
	fmt.Println(errorx.Wrap(e).Error())
}

Output

2019-08-30 17:51:42 | G:/go_workspace/GOPATH/src/test_X/tmp/main.go: 10 | nil return

3.2 Service error

In most cases, client requires server to provide specific errmsg and errcode. Service error is exact what you expects.

func Control(c *gin.Context) {
	e := Service()

	if se, ok := errorx.IsServiceErr(e, balanceLackErr); ok {
		c.JSON(200, gin.H{
			"errcode": se.Errcode,
			"errmsg": se.Errmsg,
		})
		return
	}
	if e != nil {
		fmt.Println(Wrap(e).Error())
        c.JSON(200, gin.H{
            "errmsg": "unexpected error",
            "errcode": -1,
            "debug_message": errorx.Wrap(e).Error()
        })
		return
	}
	c.JSON(200, gin.H{"errcode":0})
}


func ManyService() error {
	if e:= ServiceToCash();e!=nil {
		return errorx.Wrap(e)
	}
	return nil
}
func ServiceToCash() error {
	if e:=UtilToCash();e!=nil {
		return errorx.Wrap(e)
	}
	return nil
}

var balanceLackErr = NewServiceError("balance not enough", 10001)

func UtilToCash() error {
	return balanceLackErr
}

HTTP Response:

{"errcode": 10001, "errmsg":"balance not enough"}

3.3 Error Report

Using defaultHandler print in console

error will be log as json on console.

package main

import (
	"github.com/fwhezfwhez/errorx"
	"fmt"
)

var rp *errorx.Reporter

func init() {
	rp = errorx.NewReporter("dev")
	rp.AddModeHandler("dev", rp.DefaultHandler)
}
func main() {
	e := fmt.Errorf("nil return")
	if e != nil {
		rp.SaveError(errorx.Wrap(e), map[string]interface{}{
			"username": "errorx",
			"age":      1,
		})
		return
	}
}

output:

{
    "context": {
      "api": "/xxx/yyy/"
    },
    "error_uuid": "11d35e60-5abc-462d-9df1-bb5b01d79807",
    "message": "2019-08-31 08:58:29 | G:/go_workspace/GOPATH/src/errorX/error-report.go: 123 | err 'nil return' \n goroutine 51 [running]:\nruntime/debug.Stack(0xc0002022e0, 0xb3604d, 0xa)\n\tE:/go1.12/src/runtime/debug/stack.go:24 +0xa4\nerrorX.Reporter.SaveError(0xc0001fc1e0, 0xb30f5d, 0x3, 0xc0001fc180, 0x0, 0x0, 0x0, 0xc0001fc1b0, 0x0, 0x0, ...)\n\tG:/go_workspace/GOPATH/src/errorX/error-report.go:123 +0x30b\ncreated by errorX.TestReporter\n\tG:/go_workspace/GOPATH/src/errorX/error-report_test.go:45 +0x702\n\n2019-08-31 08:58:29 | G:/go_workspace/GOPATH/src/errorX/error-report.go: 144 | err 'nil return' \n goroutine 51 [running]:\nruntime/debug.Stack(0xc0002022e0, 0xb3604d, 0xa)\n\tE:/go1.12/src/runtime/debug/stack.go:24 +0xa4\nerrorX.Reporter.SaveError(0xc0001fc1e0, 0xb30f5d, 0x3, 0xc0001fc180, 0x0, 0x0, 0x0, 0xc0001fc1b0, 0x0, 0x0, ...)\n\tG:/go_workspace/GOPATH/src/errorX/error-report.go:123 +0x30b\ncreated by errorX.TestReporter\n\tG:/go_workspace/GOPATH/src/errorX/error-report_test.go:45 +0x702\n\n2019-08-31 08:58:29 | G:/go_workspace/GOPATH/src/errorX/error-report.go: 49 | err 'nil return' \n goroutine 51 [running]:\nruntime/debug.Stack(0xc0002022e0, 0xb3604d, 0xa)\n\tE:/go1.12/src/runtime/debug/stack.go:24 +0xa4\nerrorX.Reporter.SaveError(0xc0001fc1e0, 0xb30f5d, 0x3, 0xc0001fc180, 0x0, 0x0, 0x0, 0xc0001fc1b0, 0x0, 0x0, ...)\n\tG:/go_workspace/GOPATH/src/errorX/error-report.go:123 +0x30b\ncreated by errorX.TestReporter\n\tG:/go_workspace/GOPATH/src/errorX/error-report_test.go:45 +0x702\n\n"
}

Using url report

error will POST into specific url.

server

package main

import (
	"fmt"
	"github.com/fwhezfwhez/errorx"
	"github.com/gin-gonic/gin"
	"io/ioutil"
)

func main() {

	r := gin.Default()

	r.POST("/", func(c *gin.Context) {
		buf, e := ioutil.ReadAll(c.Request.Body)
		if e != nil {
			fmt.Println(errorx.Wrap(e).Error())
			return
		}
		fmt.Println("Recv:", string(buf))
	})
	r.Run(":9191")
}
package main

import (
	"github.com/fwhezfwhez/errorx"
	"fmt"
)

var rp *errorx.Reporter

func init() {
	rp = errorx.NewReporter("pro")
	rp.AddURL("pro", "http://localhost:9191")
	rp.AddURL("dev", "http://localhost:9192")
	rp.AddModeHandler("pro", rp.ReportURLHandler)
	rp.AddModeHandler("dev", rp.Mode("dev").DefaultHandler)
}
func main() {
	e := fmt.Errorf("nil return")
	if e != nil {
	    // rp's mode is pro, it will send error to localhost:9191
		_ = rp.SaveError(errorx.Wrap(e), map[string]interface{}{
			"username": "errorx",
			"age":      1,
		})
		// clone a rp and reset its mode to dev, it will print error in console by DefaultHandler
        _ = rp.Mode("dev").SaveError(errorx.Wrap(e), map[string]interface{}{
            "username": "errorx",
            "age":      1,
        })
		return
	}
}

Output in server panel:

Recv:
{
    "context": {
      "api": "/xxx/yyy/"
    },
    "error_uuid": "11d35e60-5abc-462d-9df1-bb5b01d79807",
    "message": "2019-08-31 08:58:29 | G:/go_workspace/GOPATH/src/errorX/error-report.go: 123 | err 'nil return' \n goroutine 51 [running]:\nruntime/debug.Stack(0xc0002022e0, 0xb3604d, 0xa)\n\tE:/go1.12/src/runtime/debug/stack.go:24 +0xa4\nerrorX.Reporter.SaveError(0xc0001fc1e0, 0xb30f5d, 0x3, 0xc0001fc180, 0x0, 0x0, 0x0, 0xc0001fc1b0, 0x0, 0x0, ...)\n\tG:/go_workspace/GOPATH/src/errorX/error-report.go:123 +0x30b\ncreated by errorX.TestReporter\n\tG:/go_workspace/GOPATH/src/errorX/error-report_test.go:45 +0x702\n\n2019-08-31 08:58:29 | G:/go_workspace/GOPATH/src/errorX/error-report.go: 144 | err 'nil return' \n goroutine 51 [running]:\nruntime/debug.Stack(0xc0002022e0, 0xb3604d, 0xa)\n\tE:/go1.12/src/runtime/debug/stack.go:24 +0xa4\nerrorX.Reporter.SaveError(0xc0001fc1e0, 0xb30f5d, 0x3, 0xc0001fc180, 0x0, 0x0, 0x0, 0xc0001fc1b0, 0x0, 0x0, ...)\n\tG:/go_workspace/GOPATH/src/errorX/error-report.go:123 +0x30b\ncreated by errorX.TestReporter\n\tG:/go_workspace/GOPATH/src/errorX/error-report_test.go:45 +0x702\n\n2019-08-31 08:58:29 | G:/go_workspace/GOPATH/src/errorX/error-report.go: 49 | err 'nil return' \n goroutine 51 [running]:\nruntime/debug.Stack(0xc0002022e0, 0xb3604d, 0xa)\n\tE:/go1.12/src/runtime/debug/stack.go:24 +0xa4\nerrorX.Reporter.SaveError(0xc0001fc1e0, 0xb30f5d, 0x3, 0xc0001fc180, 0x0, 0x0, 0x0, 0xc0001fc1b0, 0x0, 0x0, ...)\n\tG:/go_workspace/GOPATH/src/errorX/error-report.go:123 +0x30b\ncreated by errorX.TestReporter\n\tG:/go_workspace/GOPATH/src/errorX/error-report_test.go:45 +0x702\n\n"
}

3.4 JSON

JSON and JSONIndent will generate a json buf from error and context.

package main

import (
	"github.com/fwhezfwhez/errorx"
	"fmt"
)

func main() {
	eUuid, buf, e := errorx.JSON(errorx.NewFromString("nil return"), map[string]interface{}{
		"api": "/xx/xxx/xx",
	})
	fmt.Println(eUuid)
	fmt.Println(string(buf))
	fmt.Println(e)
}

Output:

befe4742-6905-4817-96aa-19fdb63bd83f
{"context":{"api":"/xx/xxx/xx"},"error_uuid":"befe4742-6905-4817-96aa-19fdb63bd83f","message":"2019-08-31 09:18:41 | G:/go_workspace/GOPATH/src/errorX/error-report_test.go: 56 | nil return\n2019-08-31 09:18:41 | G:/go_workspace/GOPATH/src/errorX/error-report.go: 171 | nil return\n"}