diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index 8f5a1fcfe..41a40b140 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -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) } diff --git a/g/g_object.go b/g/g_object.go index 42cb4a351..05ccaa11c 100644 --- a/g/g_object.go +++ b/g/g_object.go @@ -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 } } diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 733d546f4..04b1a68a8 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -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{}{} diff --git a/g/net/ghttp/ghttp_server_log.go b/g/net/ghttp/ghttp_server_log.go index 38fe8e2a7..e31440cdf 100644 --- a/g/net/ghttp/ghttp_server_log.go +++ b/g/net/ghttp/ghttp_server_log.go @@ -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) + } } diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index 4937172fb..993bd31b5 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -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...) } // 是否允许在设置输出文件时同时也输出到终端 diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 9eb83dc95..67aa8ed05 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -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(``, cfile) { + // 不打印出go源码路径及glog包文件路径,日志打印必须从业务源码文件开始,且从glog包文件开始检索 + if !gregex.IsMatchString("^" + gfile.GoRootOfBuild(), cfile) && !gregex.IsMatchString(``, cfile) { backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, cfile, cline, ln) index++ } diff --git a/g/os/glog/glog_logger_linkop.go b/g/os/glog/glog_logger_linkop.go index d63549b47..837d1f1ba 100644 --- a/g/os/glog/glog_logger_linkop.go +++ b/g/os/glog/glog_logger_linkop.go @@ -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 } diff --git a/geg/frame/config.toml b/geg/frame/config.toml index d58ffea7c..20fe5ffc7 100644 --- a/geg/frame/config.toml +++ b/geg/frame/config.toml @@ -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" diff --git a/geg/other/test.go b/geg/other/test.go index 7b8d1d9b6..a452b09d8 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -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") +} \ No newline at end of file