mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
改进glog的backtrace skip功能,g模块增加错误日志信息提示
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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{}{}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
// 处理服务错误信息,主要是panic,http请求的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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
// 是否允许在设置输出文件时同时也输出到终端
|
||||
|
||||
@ -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++
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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")
|
||||
}
|
||||
Reference in New Issue
Block a user