mirror of
https://gitee.com/johng/gf
synced 2026-07-06 13:42:46 +08:00
add package gerror; improve internal/debug,glog package
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,5 +14,4 @@ bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
|
||||
|
||||
@ -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
135
g/errors/gerror/gerror.go
Normal 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++
|
||||
}
|
||||
}
|
||||
}
|
||||
39
g/errors/gerror/gerror_test.go
Normal file
39
g/errors/gerror/gerror_test.go
Normal 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")
|
||||
})
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user