改进glog的backtrace skip功能,g模块增加错误日志信息提示

This commit is contained in:
john
2018-10-10 18:39:57 +08:00
parent 029bbd2ded
commit 0733164af1
9 changed files with 127 additions and 33 deletions

View File

@ -75,7 +75,7 @@ func (db *Db) printSql(v *Sql) {
)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
glog.Backtrace(true).Error(s)
glog.Backtrace(true, 2).Error(s)
} else {
glog.Debug(s)
}

View File

@ -19,6 +19,8 @@ import (
"gitee.com/johng/gf/g/os/gview"
"gitee.com/johng/gf/g/os/gcfg"
"gitee.com/johng/gf/g/util/gregex"
"gitee.com/johng/gf/g/os/glog"
"fmt"
)
const (
gIS_DATABASE_CONFIG_CACHED = "gf.core.component.database.cached"
@ -55,10 +57,11 @@ func Config() *gcfg.Config {
func Database(name...string) *gdb.Db {
config := gins.Config()
if config == nil {
glog.Error("Config component init failed")
return nil
}
// 数据库配置是否已经设置
if gcache.Get(gIS_DATABASE_CONFIG_CACHED) == nil {
gcache.GetOrSetFuncLock(gIS_DATABASE_CONFIG_CACHED, func() interface{} {
if m := config.GetMap("database"); m != nil {
c := gdb.Config{}
for group, v := range m {
@ -109,16 +112,20 @@ func Database(name...string) *gdb.Db {
c[group] = cg
}
gdb.SetConfig(c)
gcache.Set(gIS_DATABASE_CONFIG_CACHED, struct{}{}, 0)
// 使用gfsnotify进行文件监控当配置文件有任何变化时清空数据库配置缓存
gfsnotify.Add(Config().GetFilePath(), func(event *gfsnotify.Event) {
gcache.Remove(gIS_DATABASE_CONFIG_CACHED)
})
return struct{}{}
} else {
glog.Error(fmt.Sprintf(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath()))
}
}
return nil
}, 0)
if db, err := gdb.New(name...); err == nil {
return db
} else {
glog.Error(err)
return nil
}
}

View File

@ -175,6 +175,7 @@ func GetServer(name...interface{}) (*Server) {
closeQueue : gqueue.New(),
logger : glog.New(),
}
// 日志的标准输出默认关闭,但是错误信息会特殊处理
s.logger.SetStdPrint(false)
for _, v := range strings.Split(gHTTP_METHODS, ",") {
s.methodsMap[v] = struct{}{}

View File

@ -29,24 +29,33 @@ func (s *Server) handleAccessLog(r *Request) {
)
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000)
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
s.logger.Cat("access").Println(content)
s.logger.Cat("access").Backtrace(false, 2).Println(content)
}
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
func (s *Server) handleErrorLog(error interface{}, r *Request) {
r.Response.WriteStatus(http.StatusInternalServerError)
// 错误输出默认是开启的
if !s.IsErrorLogEnabled() {
return
}
// 自定义错误处理
if v := s.GetLogHandler(); v != nil {
v(r, error)
return
}
// 错误日志信息
content := fmt.Sprintf(`%v, "%s %s %s %s"`, error, r.Method, r.Host, r.URL.String(), r.Proto)
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000)
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
s.logger.Cat("error").Error(content)
if s.logger.GetPath() == "" {
// 错误信息特殊处理,在未开启日志文件保存时强制强制输出到终端
s.logger.Cat("error").Backtrace(true, 2).StdPrint(true).Error(content)
} else {
s.logger.Cat("error").Backtrace(true, 2).Error(content)
}
}

View File

@ -64,17 +64,17 @@ func SetStdPrint(open bool) {
// 获取日志目录绝对路径
func GetPath() string {
return logger.path.Val()
return logger.GetPath()
}
// 打印文件调用回溯信息
func PrintBacktrace() {
logger.PrintBacktrace()
func PrintBacktrace(skip...int) {
logger.PrintBacktrace(skip...)
}
// 获取文件调用回溯信息
func GetBacktrace() string {
return logger.GetBacktrace()
func GetBacktrace(skip...int) string {
return logger.GetBacktrace(skip...)
}
// 设置下一次输出的分类,支持多级分类设置
@ -93,8 +93,8 @@ func Level(level int) *Logger {
}
// 设置文件调用回溯信息
func Backtrace(enabled bool) *Logger {
return logger.Backtrace(enabled)
func Backtrace(enabled bool, skip...int) *Logger {
return logger.Backtrace(enabled, skip...)
}
// 是否允许在设置输出文件时同时也输出到终端

View File

@ -30,6 +30,7 @@ type Logger struct {
path *gtype.String // 日志写入的目录路径
file *gtype.String // 日志文件名称格式
level *gtype.Int // 日志输出等级
btSkip *gtype.Int // 错误产生时的backtrace回调信息skip条数
btEnabled *gtype.Bool // 是否当打印错误时同时开启backtrace打印
alsoStdPrint *gtype.Bool // 控制台打印开关,当输出到文件/自定义输出时也同时打印到终端
}
@ -56,6 +57,7 @@ func New() *Logger {
path : gtype.NewString(),
file : gtype.NewString(gDEFAULT_FILE_FORMAT),
level : gtype.NewInt(defaultLevel.Val()),
btSkip : gtype.NewInt(),
btEnabled : gtype.NewBool(true),
alsoStdPrint : gtype.NewBool(true),
}
@ -69,6 +71,7 @@ func (l *Logger) Clone() *Logger {
path : l.path.Clone(),
file : l.file.Clone(),
level : l.level.Clone(),
btSkip : l.btSkip.Clone(),
btEnabled : l.btEnabled.Clone(),
alsoStdPrint : l.alsoStdPrint.Clone(),
}
@ -97,6 +100,11 @@ func (l *Logger) SetBacktrace(enabled bool) {
l.btEnabled.Set(enabled)
}
// 设置BacktraceSkip
func (l *Logger) SetBacktraceSkip(skip int) {
l.btSkip.Set(skip)
}
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出
func (l *Logger) SetIO(w io.Writer) {
l.mu.Lock()
@ -142,6 +150,11 @@ func (l *Logger) SetPath(path string) error {
return nil
}
// 获取设置的日志目录路径
func (l *Logger) GetPath() string {
return l.path.Val()
}
// 日志文件名称
func (l *Logger) SetFile(file string) {
l.file.Set(file)
@ -204,21 +217,34 @@ func (l *Logger) errPrint(s string) {
l.print(os.Stderr, s)
}
// 直接打印回溯信息
func (l *Logger) PrintBacktrace() {
l.Println(l.GetBacktrace())
// 直接打印回溯信息参数skip表示调用端往上多少级开始回溯
func (l *Logger) PrintBacktrace(skip...int) {
l.Println(l.GetBacktrace(skip...))
}
// 获取文件调用回溯字符串
func (l *Logger) GetBacktrace() string {
// 获取文件调用回溯字符串参数skip表示调用端往上多少级开始回溯
func (l *Logger) GetBacktrace(skip...int) string {
customSkip := 0
if len(skip) > 0 {
customSkip = skip[0]
}
backtrace := ""
index := 1
for i := 1; i < 10000; i++ {
from := 0
// 首先定位业务文件开始位置
for i := 0; i < 10; i++ {
if _, cfile, _, ok := runtime.Caller(i); ok {
if !gregex.IsMatchString("/g/os/glog/glog.+$", cfile) {
from = i
break
}
}
}
// 从业务文件开始位置根据自定义的skip开始backtrace
for i := from + customSkip + l.btSkip.Val(); i < 10000; i++ {
if _, cfile, cline, ok := runtime.Caller(i); ok {
// 不打印出go源码路径及glog包文件路径
if !gregex.IsMatchString("/g/os/glog/glog.+$", cfile) &&
!gregex.IsMatchString("^" + gfile.GoRootOfBuild(), cfile) &&
!gregex.IsMatchString(`<autogenerated>`, cfile) {
// 不打印出go源码路径及glog包文件路径日志打印必须从业务源码文件开始且从glog包文件开始检索
if !gregex.IsMatchString("^" + gfile.GoRootOfBuild(), cfile) && !gregex.IsMatchString(`<autogenerated>`, cfile) {
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, cfile, cline, ln)
index++
}

View File

@ -54,7 +54,7 @@ func (l *Logger) Level(level int) *Logger {
}
// 设置文件调用回溯信息
func (l *Logger) Backtrace(enabled bool) *Logger {
func (l *Logger) Backtrace(enabled bool, skip...int) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
logger = l.Clone()
@ -62,6 +62,9 @@ func (l *Logger) Backtrace(enabled bool) *Logger {
logger = l
}
logger.SetBacktrace(enabled)
if len(skip) > 0 {
logger.SetBacktraceSkip(skip[0])
}
return logger
}

View File

@ -6,7 +6,7 @@ viewpath = "/home/www/templates/"
host = "127.0.0.1"
port = "3306"
user = "root"
pass = "123456"
pass = ""
name = "test"
type = "mysql"
role = "master"
@ -16,7 +16,7 @@ viewpath = "/home/www/templates/"
host = "127.0.0.1"
port = "3306"
user = "root"
pass = "123456"
pass = ""
name = "test"
type = "mysql"
role = "master"

View File

@ -2,15 +2,63 @@ package main
import (
"gitee.com/johng/gf/g"
"fmt"
"gitee.com/johng/gf/g/net/ghttp"
)
type S struct {
}
func main() {
v := g.NewVar(nil)
fmt.Println(v.Val())
s := g.Server()
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
if r.Router.Uri == "/logout" {
r.Response.Write("HOOK_SERV")
r.Exit()
}
},
})
s.BindStatusHandler(404, func(r *ghttp.Request) {
r.Response.Writeln("This is customized 404 page")
})
s.BindStatusHandler(500, func(r *ghttp.Request) {
r.Response.Writeln("This is customized 500 page")
})
s.BindStatusHandler(403, func(r *ghttp.Request) {
r.Response.Writeln("This is customized 403 page")
})
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello World")
})
p := &P{}
s.BindHandler("/login", p.Login)
s.BindHandler("/logout", p.Logout)
s.BindHandler("/api/getuser", p.GetUser)
s.BindHandler("/api/:name", p.AnyName)
s.SetPort(6655)
s.Run()
}
type P struct {
}
func (p *P) Login(c *ghttp.Request) {
c.Cookie.SetCookie("username", "sdf", "", "/", 300)
c.Response.Write("this is login")
}
func (p *P) Logout(c *ghttp.Request) {
c.Response.Write("this is logout")
}
func (p *P) GetUser(c *ghttp.Request) {
c.Cookie.Remove("username", "", "/")
c.Response.Write("this is GetUser")
}
func (p *P) AnyName(c *ghttp.Request) {
c.Response.Write("this is AnyName")
}