add package gerror; improve internal/debug,glog package

This commit is contained in:
John
2019-06-29 18:17:33 +08:00
parent d5f7ca634d
commit 58a405bfa8
14 changed files with 356 additions and 107 deletions

1
.gitignore vendored
View File

@ -14,5 +14,4 @@ bin/
cbuild
**/.DS_Store
.vscode/
go.sum

View File

@ -14,13 +14,14 @@ import (
"database/sql"
"errors"
"fmt"
"time"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/util/grand"
"time"
)
// 数据库操作接口
@ -86,9 +87,9 @@ type DB interface {
GetQueriedSqls() []*Sql
GetLastSql() *Sql
PrintQueriedSqls()
SetMaxIdleConns(n int)
SetMaxOpenConns(n int)
SetConnMaxLifetime(n int)
SetMaxIdleConnCount(n int)
SetMaxOpenConnCount(n int)
SetMaxConnLifetime(n int)
// 内部方法接口
getCache() *gcache.Cache

135
g/errors/gerror/gerror.go Normal file
View File

@ -0,0 +1,135 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package errors provides simple functions to manipulate errors.
package gerror
import (
"bytes"
"fmt"
"runtime"
"strings"
"github.com/gogf/gf/g/util/gconv"
"github.com/pkg/errors"
)
type stacker interface {
StackTrace() errors.StackTrace
}
type causer interface {
Cause() error
}
const (
gFILTER_KEY = "/g/errors/gerror/gerror.go"
)
var (
// goRootForFilter is used for stack filtering purpose.
goRootForFilter = runtime.GOROOT()
)
func init() {
if goRootForFilter != "" {
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
}
}
// New returns an error that formats as the given value.
func New(value interface{}) error {
if value == nil {
return nil
}
return NewText(gconv.String(value))
}
// NewText returns an error that formats as the given text.
func NewText(text string) error {
if text == "" {
return nil
}
return errors.New(text)
}
// Wrap wraps error with text.
func Wrap(err error, text string) error {
if err == nil {
return nil
}
return errors.Wrap(err, text)
}
// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error {
return errors.Wrapf(err, format, args...)
}
// Cause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func Cause(err error) error {
return errors.Cause(err)
}
// Stack returns the stack callers as string.
func Stack(err error) string {
if err == nil {
return ""
}
if _, ok := err.(causer); !ok {
return ""
}
index := 1
buffer := bytes.NewBuffer(nil)
for err != nil {
cause, ok := err.(causer)
if !ok {
if err, ok := err.(stacker); ok {
buffer.WriteString(fmt.Sprintf("%d.\t%s\n", index, err))
index++
formatSubStack(err, buffer)
}
break
}
if err, ok := err.(stacker); ok {
buffer.WriteString(fmt.Sprintf("%d.\t%s\n", index, err))
index++
formatSubStack(err, buffer)
}
err = cause.Cause()
}
return buffer.String()
}
// formatSubStack formats the stack for error.
func formatSubStack(err stacker, buffer *bytes.Buffer) {
index := 1
for _, f := range err.StackTrace() {
if fn := runtime.FuncForPC(uintptr(f) - 1); fn != nil {
file, line := fn.FileLine(uintptr(f) - 1)
if strings.Contains(file, gFILTER_KEY) {
continue
}
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
buffer.WriteString(fmt.Sprintf("\t%d).\t%s\n\t\t%s:%d\n", index, fn.Name(), file, line))
index++
}
}
}

View File

@ -0,0 +1,39 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gerror_test
import (
"testing"
"github.com/gogf/gf/g/internal/errors"
"github.com/gogf/gf/g/test/gtest"
)
func interfaceNil() interface{} {
return nil
}
func nilError() error {
return nil
}
func Test_Nil(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(errors.New(interfaceNil()), nil)
gtest.Assert(errors.Wrap(nilError(), "test"), nil)
})
}
func Test_Wrap(t *testing.T) {
gtest.Case(t, func() {
err := errors.New("1")
err = errors.Wrap(err, "func2 error")
err = errors.Wrap(err, "func3 error")
gtest.AssertNE(err, nil)
gtest.Assert(err.Error(), "func3 error: func2 error: 1")
})
}

View File

@ -1,6 +1,8 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package debug contains facilities for programs to debug themselves while
// they are running.
@ -10,41 +12,106 @@ import (
"bytes"
"fmt"
"runtime"
"strconv"
"strings"
)
const (
gMAX_DEPTH = 1000
gFILTER_KEY = "/g/internal/debug/stack.go"
)
var (
// goRootForFilter is used for stack filtering purpose.
goRootForFilter = runtime.GOROOT()
)
func init() {
if goRootForFilter != "" {
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
}
}
// PrintStack prints to standard error the stack trace returned by runtime.Stack.
func PrintStack(skip ...int) {
fmt.Print(string(Stack(skip...)))
fmt.Print(Stack(skip...))
}
// Stack returns a formatted stack trace of the goroutine that calls it.
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
func Stack(skip ...int) []byte {
buffer := make([]byte, 512)
func Stack(skip ...int) string {
return StackWithFilter("", skip...)
}
// StackWithFilter returns a formatted stack trace of the goroutine that calls it.
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
//
// The parameter <filter> is used to filter the path of the caller.
func StackWithFilter(filter string, skip ...int) string {
number := 0
if len(skip) > 0 {
number = skip[0]
}
for {
n := runtime.Stack(buffer, false)
if n < len(buffer) {
lines := bytes.Split(buffer[:n], []byte{'\n'})
index := 1
stacks := bytes.NewBuffer(nil)
for i, line := range lines {
if i < 5+number*2 || len(line) == 0 {
continue
}
if i%2 != 0 {
stacks.WriteString(strconv.Itoa(index) + ".\t")
index++
}
stacks.Write(line)
stacks.WriteByte('\n')
name := ""
index := 1
buffer := bytes.NewBuffer(nil)
for i := callerFromIndex() + number; i < gMAX_DEPTH; i++ {
if pc, file, line, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
return stacks.Bytes()
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
if fn := runtime.FuncForPC(pc); fn == nil {
name = "unknown"
} else {
name = fn.Name()
}
buffer.WriteString(fmt.Sprintf("%d.\t%s\n\t%s:%d\n", index, name, file, line))
index++
} else {
break
}
buffer = make([]byte, 2*len(buffer))
}
return buffer.String()
}
// CallerPath returns the absolute file path along with its line number of the caller.
func Caller(skip ...int) string {
return CallerWithFilter("", skip...)
}
// CallerPathWithFilter returns the absolute file path along with its line number of the caller.
//
// The parameter <filter> is used to filter the path of the caller.
func CallerWithFilter(filter string, skip ...int) string {
number := 0
if len(skip) > 0 {
number = skip[0]
}
for i := callerFromIndex() + number; i < gMAX_DEPTH; i++ {
if _, file, line, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
return fmt.Sprintf(`%s:%d`, file, line)
} else {
break
}
}
return ""
}
// callerFromIndex returns the caller position exclusive of the debug package.
func callerFromIndex() int {
for i := 0; i < gMAX_DEPTH; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if strings.Contains(file, gFILTER_KEY) {
continue
}
// exclude the depth from the function of current package.
return i - 1
}
}
return 0
}

View File

@ -44,6 +44,7 @@ const (
gDEFAULT_FILE_POOL_FLAGS = os.O_CREATE | os.O_WRONLY | os.O_APPEND
gDEFAULT_FPOOL_PERM = os.FileMode(0666)
gDEFAULT_FPOOL_EXPIRE = 60000
gPATH_FILTER_KEY = "/g/os/glog/glog"
)
const (
@ -256,10 +257,10 @@ func (l *Logger) print(std io.Writer, lead string, value ...interface{}) {
// Caller path.
callerPath := ""
if l.flags&F_FILE_LONG > 0 {
callerPath = l.getLongFile() + ": "
callerPath = debug.CallerWithFilter(gPATH_FILTER_KEY) + ": "
}
if l.flags&F_FILE_SHORT > 0 {
callerPath = gfile.Basename(l.getLongFile()) + ": "
callerPath = gfile.Basename(debug.CallerWithFilter(gPATH_FILTER_KEY)) + ": "
}
if len(callerPath) > 0 {
buffer.WriteString(callerPath)
@ -345,31 +346,5 @@ func (l *Logger) GetStack(skip ...int) string {
if len(skip) > 0 {
number = skip[0] + 1
}
return string(debug.Stack(number))
}
// getLongFile returns the absolute file path along with its line number of the caller.
func (l *Logger) getLongFile() string {
from := 0
// Find the caller position exclusive of the glog file.
for i := 0; i < 1000; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if !gregex.IsMatchString("/os/glog/glog.+$", file) {
from = i
break
}
}
}
// Find the true caller file path using custom skip.
goRoot := runtime.GOROOT()
for i := from + l.stSkip; i < 1000; i++ {
if _, file, line, ok := runtime.Caller(i); ok && len(file) > 2 {
if (goRoot == "" || !gregex.IsMatchString("^"+goRoot, file)) && !gregex.IsMatchString(`<autogenerated>`, file) {
return fmt.Sprintf(`%s:%d`, file, line)
}
} else {
break
}
}
return ""
return debug.StackWithFilter(gPATH_FILTER_KEY, number)
}

View File

@ -115,6 +115,16 @@ func (t *Time) Add(d time.Duration) *Time {
return t
}
// 当前时间加上指定时间段(使用字符串格式)
func (t *Time) AddStr(duration string) error {
if d, err := time.ParseDuration(duration); err != nil {
return err
} else {
t.Time = t.Time.Add(d)
}
return nil
}
// 时区转换为指定的时区(通过time.Location)
func (t *Time) ToLocation(location *time.Location) *Time {
t.Time = t.Time.In(location)

View File

@ -10,12 +10,6 @@ import (
"github.com/gogf/gf/g/internal/debug"
)
// PrintStack is alias for PrintStack.
// Deprecated.
func PrintStack() {
PrintStack()
}
// PrintStack prints to standard error the stack trace returned by runtime.Stack.
func PrintStack(skip ...int) {
number := 1
@ -27,7 +21,7 @@ func PrintStack(skip ...int) {
// Stack returns a formatted stack trace of the goroutine that calls it.
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
func Stack(skip ...int) []byte {
func Stack(skip ...int) string {
number := 1
if len(skip) > 0 {
number = skip[0] + 1

View File

@ -1,11 +1,12 @@
package main
import (
"github.com/gogf/gf/g/os/glog"
)
import "github.com/gogf/gf/g/os/glog"
func main() {
//glog.SetPath("/tmp/")
func Test() {
glog.Error("This is error!")
glog.Errorf("This is error, %d!", 2)
}
func main() {
Test()
}

View File

@ -6,7 +6,7 @@ import (
func PrintLog(content string) {
glog.Skip(1).Line().Println("line number with skip:", content)
glog.Line().Println("line number without skip:", content)
glog.Line(true).Println("line number without skip:", content)
}
func main() {

View File

@ -1,13 +1,65 @@
package main
import (
"github.com/gogf/gf/g/util/gutil"
"fmt"
"github.com/pkg/errors"
"github.com/gogf/gf/g/errors/gerror"
)
func Test(s interface{}) {
gutil.PrintStack()
func Test1() error {
return gerror.New("test")
}
func Test2() error {
return gerror.Wrap(Test1(), "error test1")
}
type stackTracer interface {
StackTrace() errors.StackTrace
}
func main() {
Test(nil)
err := Test2()
//fmt.Printf("%+v", err)
//fmt.Println("==============")
fmt.Println(gerror.Stack(err))
return
type causer interface {
Cause() error
}
for err != nil {
cause, ok := err.(causer)
if !ok {
fmt.Println("ERROR:", err)
if err, ok := err.(stackTracer); ok {
for _, f := range err.StackTrace() {
fmt.Printf("%+s:%d\n", f, f)
}
}
break
}
fmt.Println("ERROR:", err)
if err, ok := err.(stackTracer); ok {
for _, f := range err.StackTrace() {
fmt.Printf("%+s:%d\n", f, f)
}
}
fmt.Println()
err = cause.Cause()
}
//err, ok := Test2().(stackTracer)
//if !ok {
// panic("oops, err does not implement stackTracer")
//}
//
//st := err.StackTrace()
//fmt.Printf("%+v\n", st)
//fmt.Println()
//fmt.Printf("%+v\n", Test2())
}

View File

@ -1,39 +1,11 @@
package main
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
)
import "github.com/gogf/gf/g/os/glog"
type Schedule struct{}
func Test() {
type Task struct{}
func (c *Schedule) ListDir(r *ghttp.Request) {
r.Response.Writeln("ListDir")
}
func (c *Task) Add(r *ghttp.Request) {
r.Response.Writeln("Add")
}
func (c *Task) Task(r *ghttp.Request) {
r.Response.Writeln("Task")
}
// 实现权限校验
// 通过事件回调,类似于中间件机制,但是可控制的粒度更细,可以精准注册到路由规则
func AuthHookHandler(r *ghttp.Request) {
// 如果权限校验失败,调用 r.ExitAll() 退出执行流程
}
func main() {
s := g.Server()
s.Group("/schedule").Bind([]ghttp.GroupItem{
{"ALL", "*", AuthHookHandler, ghttp.HOOK_BEFORE_SERVE},
{"POST", "/schedule", new(Schedule)},
{"POST", "/task", new(Task)},
})
s.SetPort(8199)
s.Run()
glog.Line().Println("123")
}

2
go.mod
View File

@ -1 +1,3 @@
module github.com/gogf/gf
require github.com/pkg/errors v0.8.1

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=