diff --git a/dotweb.go b/dotweb.go index 049d7bd..928af12 100644 --- a/dotweb.go +++ b/dotweb.go @@ -2,59 +2,56 @@ package dotweb import ( "fmt" - "github.com/devfeel/dotweb/framework/crypto/uuid" "net/http" _ "net/http/pprof" + "runtime" "runtime/debug" + "runtime/pprof" "strconv" "strings" "context" "errors" - "reflect" - "sync" - "time" - "github.com/devfeel/dotweb/cache" "github.com/devfeel/dotweb/config" "github.com/devfeel/dotweb/core" + "github.com/devfeel/dotweb/framework/json" "github.com/devfeel/dotweb/logger" + "github.com/devfeel/dotweb/notify" + "github.com/devfeel/dotweb/servers" "github.com/devfeel/dotweb/session" + "os" + "reflect" + "sync" + "time" ) type ( DotWeb struct { HttpServer *HttpServer cache cache.Cache + OfflineServer servers.Server Config *config.Config Mock Mock Middlewares []Middleware ExceptionHandler ExceptionHandle - NotFoundHandler StandardHandle // NotFoundHandler supports user defined 404 handler - MethodNotAllowedHandler StandardHandle // MethodNotAllowedHandler fixed for #64 supports user defined MethodNotAllowed handler + NotFoundHandler StandardHandle // NotFoundHandler 支持自定义404处理代码能力 + MethodNotAllowedHandler StandardHandle // MethodNotAllowedHandler fixed for #64 增加MethodNotAllowed自定义处理 Items core.ConcurrenceMap middlewareMap map[string]MiddlewareFunc middlewareMutex *sync.RWMutex StartMode string - IDGenerater IdGenerate - globalUniqueID string - appLog logger.AppLog - serverStateInfo *core.ServerStateInfo } - // ExceptionHandle supports exception handling + // ExceptionHandle 支持自定义异常处理代码能力 ExceptionHandle func(Context, error) - // StandardHandle for standard request handling + // StandardHandle 标准处理函数,需传入Context参数 StandardHandle func(Context) - // HttpHandle is a function that can be registered to a route to handle HTTP + // Handle is a function that can be registered to a route to handle HTTP // requests. Like http.HandlerFunc, but has a special parameter Context contain all request and response data. HttpHandle func(Context) error - - // IdGenerater the handler for create Unique Id - // default is use dotweb. - IdGenerate func() string ) const ( @@ -68,34 +65,34 @@ const ( // RunMode_Production app runmode in production mode RunMode_Production = "production" - // StartMode_New app startmode in New mode + //StartMode_New app startmode in New mode StartMode_New = "New" - // StartMode_Classic app startmode in Classic mode + //StartMode_Classic app startmode in Classic mode StartMode_Classic = "Classic" ) -// New create and return DotApp instance -// default run mode is RunMode_Production +//New create and return DotApp instance +//default run mode is RunMode_Production func New() *DotWeb { app := &DotWeb{ HttpServer: NewHttpServer(), + OfflineServer: servers.NewOfflineServer(), Middlewares: make([]Middleware, 0), Items: core.NewConcurrenceMap(), Config: config.NewConfig(), middlewareMap: make(map[string]MiddlewareFunc), middlewareMutex: new(sync.RWMutex), StartMode: StartMode_New, - serverStateInfo: core.NewServerStateInfo(), } - // set default run mode = RunMode_Production - app.Config.App.RunMode = RunMode_Production + //set default run mode = RunMode_Development + app.Config.App.RunMode = RunMode_Development app.HttpServer.setDotApp(app) - // add default httphandler with middlewares - // fixed for issue #100 + //add default httphandler with middlewares + //fixed for issue #100 app.Use(&xMiddleware{}) - // init logger - app.appLog = logger.NewAppLog() + //init logger + logger.InitLog() return app } @@ -114,10 +111,10 @@ func Classic(logPath string) *DotWeb { } app.SetEnabledLog(true) - // print logo - app.printDotLogo() + //print logo + printDotLogo() - app.Logger().Debug("DotWeb Start New AppServer", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb Start New AppServer", LogTarget_HttpServer) return app } @@ -129,24 +126,14 @@ func ClassicWithConf(config *config.Config) *DotWeb { return app } -// Logger return app's logger -func (app *DotWeb) Logger() logger.AppLog { - return app.appLog -} - -// StateInfo return app's ServerStateInfo -func (app *DotWeb) StateInfo() *core.ServerStateInfo { - return app.serverStateInfo -} - -// RegisterMiddlewareFunc register middleware with given name & middleware +// RegisterMiddlewareFunc register middleware with gived name & middleware func (app *DotWeb) RegisterMiddlewareFunc(name string, middleFunc MiddlewareFunc) { app.middlewareMutex.Lock() app.middlewareMap[name] = middleFunc app.middlewareMutex.Unlock() } -// GetMiddlewareFunc get middleware with given name +// GetMiddlewareFunc get middleware with gived name func (app *DotWeb) GetMiddlewareFunc(name string) (MiddlewareFunc, bool) { app.middlewareMutex.RLock() v, exists := app.middlewareMap[name] @@ -154,12 +141,6 @@ func (app *DotWeb) GetMiddlewareFunc(name string) (MiddlewareFunc, bool) { return v, exists } -// GlobalUniqueID return app's GlobalUniqueID -// it will be Initializationed when StartServer -func (app *DotWeb) GlobalUniqueID() string { - return app.globalUniqueID -} - // Cache return cache interface func (app *DotWeb) Cache() cache.Cache { return app.cache @@ -178,6 +159,19 @@ func (app *DotWeb) RunMode() string { return app.Config.App.RunMode } +//获取文件检测目录 +func (app *DotWeb) root() string { + if app.Config.App.Root == "" { + if cwd, err := os.Getwd(); err != nil { + panic(fmt.Sprintf("获取工作目录失败:%s", err)) + } else { + app.Config.App.Root = cwd + } + } + return app.Config.App.Root + +} + // IsDevelopmentMode check current run mode is development mode func (app *DotWeb) IsDevelopmentMode() bool { return app.RunMode() == RunMode_Development @@ -188,21 +182,15 @@ func (app *DotWeb) IsDevelopmentMode() bool { // 2.SetEnabledConsole(true) func (app *DotWeb) SetDevelopmentMode() { app.Config.App.RunMode = RunMode_Development - - // enabled auto OPTIONS - app.HttpServer.SetEnabledAutoOPTIONS(true) - // enabled auto HEAD - app.HttpServer.SetEnabledAutoHEAD(true) - app.SetEnabledLog(true) app.Use(new(RequestLogMiddleware)) - app.Logger().SetEnabledConsole(true) + logger.SetEnabledConsole(true) } // SetProductionMode set run mode on production mode func (app *DotWeb) SetProductionMode() { app.Config.App.RunMode = RunMode_Production - app.appLog.SetEnabledConsole(true) + logger.SetEnabledConsole(true) } // ExcludeUse registers a middleware exclude routers @@ -248,7 +236,7 @@ func (app *DotWeb) UseTimeoutHook(handler StandardHandle, timeout time.Duration) // SetMock set mock logic func (app *DotWeb) SetMock(mock Mock) { app.Mock = mock - app.Logger().Debug("DotWeb Mock SetMock", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb Mock SetMock", LogTarget_HttpServer) } // SetExceptionHandle set custom error handler @@ -271,31 +259,32 @@ func (app *DotWeb) SetMethodNotAllowedHandle(handler StandardHandle) { func (app *DotWeb) SetPProfConfig(enabledPProf bool, httpport int) { app.Config.App.EnabledPProf = enabledPProf app.Config.App.PProfPort = httpport - app.Logger().Debug("DotWeb SetPProfConfig ["+strconv.FormatBool(enabledPProf)+", "+strconv.Itoa(httpport)+"]", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb SetPProfConfig ["+strconv.FormatBool(enabledPProf)+", "+strconv.Itoa(httpport)+"]", LogTarget_HttpServer) } // SetLogger set user logger, the logger must implement logger.AppLog interface func (app *DotWeb) SetLogger(log logger.AppLog) { - app.appLog = log + logger.SetLogger(log) } // SetLogPath set log root path func (app *DotWeb) SetLogPath(path string) { - app.Logger().SetLogPath(path) - // fixed #74 dotweb.SetEnabledLog 无效 + logger.SetLogPath(path) + //fixed #74 dotweb.SetEnabledLog 无效 app.Config.App.LogPath = path } // SetEnabledLog set enabled log flag func (app *DotWeb) SetEnabledLog(enabledLog bool) { - app.Logger().SetEnabledLog(enabledLog) - // fixed #74 dotweb.SetEnabledLog 无效 + logger.SetEnabledLog(enabledLog) + //fixed #74 dotweb.SetEnabledLog 无效 app.Config.App.EnabledLog = enabledLog } // SetConfig set config for app -func (app *DotWeb) SetConfig(config *config.Config) { +func (app *DotWeb) SetConfig(config *config.Config) error { app.Config = config + return nil } // StartServer start server with http port @@ -312,7 +301,7 @@ func (app *DotWeb) Start() error { if app.Config == nil { return errors.New("no config exists") } - // start server + //start server port := app.Config.Server.Port if port <= 0 { port = DefaultHTTPPort @@ -337,61 +326,69 @@ func (app *DotWeb) ListenAndServe(addr string) error { app.initRegisterConfigMiddleware() app.initRegisterConfigRoute() app.initRegisterConfigGroup() + app.initServerEnvironment() - app.initBindMiddleware() - // create unique id for dotweb app - app.globalUniqueID = app.IDGenerater() + app.initBindMiddleware() if app.StartMode == StartMode_Classic { app.IncludeDotwebGroup() } - - // special, if run mode is not develop, auto stop mock + //special, if run mode is not develop, auto stop mock if app.RunMode() != RunMode_Development { if app.Mock != nil { - app.Logger().Debug("DotWeb Mock RunMode is not DevelopMode, Auto stop mock", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb Mock RunMode is not DevelopMode, Auto stop mock", LogTarget_HttpServer) } app.Mock = nil } - // output run mode - app.Logger().Debug("DotWeb RunMode is "+app.RunMode(), LogTarget_HttpServer) + //output run mode + logger.Logger().Debug("DotWeb RunMode is "+app.RunMode(), LogTarget_HttpServer) + if app.RunMode() == RunMode_Development { + notify.Start(app.root(), 500) + } if app.HttpServer.ServerConfig().EnabledTLS { err := app.HttpServer.ListenAndServeTLS(addr, app.HttpServer.ServerConfig().TLSCertFile, app.HttpServer.ServerConfig().TLSKeyFile) return err } - err := app.HttpServer.ListenAndServe(addr) - return err + + return app.HttpServer.ListenAndServe(addr) } // init App Config func (app *DotWeb) initAppConfig() { config := app.Config - // log config + + //log config if config.App.LogPath != "" { - app.SetLogPath(config.App.LogPath) + logger.SetLogPath(config.App.LogPath) } - app.SetEnabledLog(config.App.EnabledLog) + logger.SetEnabledLog(config.App.EnabledLog) - // run mode config + //run mode config if app.Config.App.RunMode != RunMode_Development && app.Config.App.RunMode != RunMode_Production { app.Config.App.RunMode = RunMode_Development } app.HttpServer.initConfig() - // detailed request metrics + //设置维护 + if config.Offline.Offline { + app.HttpServer.SetOffline(config.Offline.Offline, config.Offline.OfflineText, config.Offline.OfflineUrl) + app.OfflineServer.SetOffline(config.Offline.Offline, config.Offline.OfflineText, config.Offline.OfflineUrl) + } + + //设置启用详细请求数据统计 if config.Server.EnabledDetailRequestData { - app.StateInfo().EnabledDetailRequestData = config.Server.EnabledDetailRequestData + core.GlobalState.EnabledDetailRequestData = config.Server.EnabledDetailRequestData } } // init register config's Middleware func (app *DotWeb) initRegisterConfigMiddleware() { config := app.Config - // register app's middleware + //register app's middleware for _, m := range config.Middlewares { if !m.IsUse { continue @@ -405,12 +402,12 @@ func (app *DotWeb) initRegisterConfigMiddleware() { // init register config's route func (app *DotWeb) initRegisterConfigRoute() { config := app.Config - // load router and register + //load router and register for _, r := range config.Routers { - // fmt.Println("config.Routers ", i, " ", config.Routers[i]) + //fmt.Println("config.Routers ", i, " ", config.Routers[i]) if h, isok := app.HttpServer.Router().GetHandler(r.HandlerName); isok && r.IsUse { node := app.HttpServer.Router().RegisterRoute(strings.ToUpper(r.Method), r.Path, h) - // use middleware + //use middleware for _, m := range r.Middlewares { if !m.IsUse { continue @@ -426,13 +423,13 @@ func (app *DotWeb) initRegisterConfigRoute() { // init register config's route func (app *DotWeb) initRegisterConfigGroup() { config := app.Config - // support group + //support group for _, v := range config.Groups { if !v.IsUse { continue } g := app.HttpServer.Group(v.Path) - // use middleware + //use middleware for _, m := range v.Middlewares { if !m.IsUse { continue @@ -441,11 +438,11 @@ func (app *DotWeb) initRegisterConfigGroup() { g.Use(mf()) } } - // init group's router + //init group's router for _, r := range v.Routers { if h, isok := app.HttpServer.Router().GetHandler(r.HandlerName); isok && r.IsUse { node := g.RegisterRoute(strings.ToUpper(r.Method), r.Path, h) - // use middleware + //use middleware for _, m := range r.Middlewares { if !m.IsUse { continue @@ -462,7 +459,7 @@ func (app *DotWeb) initRegisterConfigGroup() { // init bind app's middleware to router node func (app *DotWeb) initBindMiddleware() { router := app.HttpServer.Router().(*router) - // bind app middlewares + //bind app middlewares for fullExpress, _ := range router.allRouterExpress { expresses := strings.Split(fullExpress, routerExpressSplit) if len(expresses) < 2 { @@ -476,10 +473,10 @@ func (app *DotWeb) initBindMiddleware() { node.appMiddlewares = app.Middlewares for _, m := range node.appMiddlewares { if m.HasExclude() && m.ExistsExcludeRouter(node.fullPath) { - app.Logger().Debug("DotWeb initBindMiddleware [app] "+fullExpress+" "+reflect.TypeOf(m).String()+" exclude", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb initBindMiddleware [app] "+fullExpress+" "+reflect.TypeOf(m).String()+" exclude", LogTarget_HttpServer) node.hasExcludeMiddleware = true } else { - app.Logger().Debug("DotWeb initBindMiddleware [app] "+fullExpress+" "+reflect.TypeOf(m).String()+" match", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb initBindMiddleware [app] "+fullExpress+" "+reflect.TypeOf(m).String()+" match", LogTarget_HttpServer) } } if len(node.middlewares) > 0 { @@ -489,7 +486,7 @@ func (app *DotWeb) initBindMiddleware() { } } - // bind group middlewares + //bind group middlewares for _, g := range app.HttpServer.groups { xg := g.(*xGroup) if len(xg.middlewares) <= 0 { @@ -507,19 +504,25 @@ func (app *DotWeb) initBindMiddleware() { node.groupMiddlewares = xg.middlewares for _, m := range node.groupMiddlewares { if m.HasExclude() && m.ExistsExcludeRouter(node.fullPath) { - app.Logger().Debug("DotWeb initBindMiddleware [group] "+fullExpress+" "+reflect.TypeOf(m).String()+" exclude", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb initBindMiddleware [group] "+fullExpress+" "+reflect.TypeOf(m).String()+" exclude", LogTarget_HttpServer) node.hasExcludeMiddleware = true } else { - app.Logger().Debug("DotWeb initBindMiddleware [group] "+fullExpress+" "+reflect.TypeOf(m).String()+" match", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb initBindMiddleware [group] "+fullExpress+" "+reflect.TypeOf(m).String()+" match", LogTarget_HttpServer) } } } } } -// IncludeDotwebGroup init inner routers which start with /dotweb/ +// IncludeDotwebGroup init inner routers func (app *DotWeb) IncludeDotwebGroup() { - initDotwebGroup(app.HttpServer) + //默认支持pprof信息查看 + gInner := app.HttpServer.Group("/dotweb") + gInner.GET("/debug/pprof/:key", initPProf) + gInner.GET("/debug/freemem", freeMemory) + gInner.GET("/state", showServerState) + gInner.GET("/state/interval", showIntervalData) + gInner.GET("/query/:key", showQuery) } // init Server Environment @@ -529,35 +532,30 @@ func (app *DotWeb) initServerEnvironment() { } if app.NotFoundHandler == nil { - app.SetNotFoundHandle(DefaultNotFoundHandler) + app.SetNotFoundHandle(app.DefaultNotFoundHandler) } if app.MethodNotAllowedHandler == nil { - app.SetMethodNotAllowedHandle(DefaultMethodNotAllowedHandler) - } - - // set default unique id generater - if app.IDGenerater == nil { - app.IDGenerater = DefaultUniqueIDGenerater + app.SetMethodNotAllowedHandle(app.DefaultMethodNotAllowedHandler) } - // init session manager + //init session manager if app.HttpServer.SessionConfig().EnabledSession { if app.HttpServer.SessionConfig().SessionMode == "" { - // panic("no set SessionConfig, but set enabledsession true") - app.Logger().Warn("not set SessionMode, but set enabledsession true, now will use default runtime session", LogTarget_HttpServer) + //panic("no set SessionConfig, but set enabledsession true") + logger.Logger().Warn("not set SessionMode, but set enabledsession true, now will use default runtime session", LogTarget_HttpServer) app.HttpServer.SetSessionConfig(session.NewDefaultRuntimeConfig()) } app.HttpServer.InitSessionManager() } - // if cache not set, create default runtime cache + //if cache not set, create default runtime cache if app.Cache() == nil { app.cache = cache.NewRuntimeCache() } - // if renderer not set, create inner renderer - // if is develop mode, it will use nocache mode + //if renderer not set, create inner renderer + //if is develop mode, it will use nocache mode if app.HttpServer.Renderer() == nil { if app.RunMode() == RunMode_Development { app.HttpServer.SetRenderer(NewInnerRendererNoCache()) @@ -566,14 +564,14 @@ func (app *DotWeb) initServerEnvironment() { } } - // start pprof server + //start pprof server if app.Config.App.EnabledPProf { - app.Logger().Debug("DotWeb:StartPProfServer["+strconv.Itoa(app.Config.App.PProfPort)+"] Begin", LogTarget_HttpServer) + logger.Logger().Debug("DotWeb:StartPProfServer["+strconv.Itoa(app.Config.App.PProfPort)+"] Begin", LogTarget_HttpServer) go func() { err := http.ListenAndServe(":"+strconv.Itoa(app.Config.App.PProfPort), nil) if err != nil { - app.Logger().Error("DotWeb:StartPProfServer["+strconv.Itoa(app.Config.App.PProfPort)+"] error: "+err.Error(), LogTarget_HttpServer) - // panic the error + logger.Logger().Error("DotWeb:StartPProfServer["+strconv.Itoa(app.Config.App.PProfPort)+"] error: "+err.Error(), LogTarget_HttpServer) + //panic the error panic(err) } }() @@ -582,8 +580,9 @@ func (app *DotWeb) initServerEnvironment() { // DefaultHTTPErrorHandler default exception handler func (app *DotWeb) DefaultHTTPErrorHandler(ctx Context, err error) { + //输出内容 ctx.Response().Header().Set(HeaderContentType, CharsetUTF8) - // if in development mode, output the error info + //if in development mode, output the error info if app.IsDevelopmentMode() { stack := string(debug.Stack()) ctx.WriteStringC(http.StatusInternalServerError, fmt.Sprintln(err)+stack) @@ -592,12 +591,22 @@ func (app *DotWeb) DefaultHTTPErrorHandler(ctx Context, err error) { } } -func (app *DotWeb) printDotLogo() { - app.Logger().Print(` ____ __ __`, LogTarget_HttpServer) - app.Logger().Print(` / __ \ ____ / /_ _ __ ___ / /_`, LogTarget_HttpServer) - app.Logger().Print(` / / / / / __ \ / __/| | /| / / / _ \ / __ \`, LogTarget_HttpServer) - app.Logger().Print(` / /_/ / / /_/ // /_ | |/ |/ / / __/ / /_/ /`, LogTarget_HttpServer) - app.Logger().Print(`/_____/ \____/ \__/ |__/|__/ \___/ /_.___/`, LogTarget_HttpServer) +// DefaultNotFoundHandler default exception handler +func (app *DotWeb) DefaultNotFoundHandler(ctx Context) { + ctx.Response().Header().Set(HeaderContentType, CharsetUTF8) + ctx.WriteStringC(http.StatusNotFound, http.StatusText(http.StatusNotFound)) +} + +// DefaultMethodNotAllowedHandler default exception handler +func (app *DotWeb) DefaultMethodNotAllowedHandler(ctx Context) { + ctx.Response().Header().Set(HeaderContentType, CharsetUTF8) + ctx.WriteStringC(http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed)) +} + +func DefaultTimeoutHookHandler(ctx Context) { + realDration := ctx.Items().GetTimeDuration(ItemKeyHandleDuration) + logs := fmt.Sprintf("req %v, cost %v", ctx.Request().Url(), realDration.Seconds()) + logger.Logger().Warn(logs, LogTarget_RequestTimeout) } // Close immediately stops the server. @@ -606,7 +615,7 @@ func (app *DotWeb) Close() error { return app.HttpServer.stdServer.Close() } -// Shutdown stops server gracefully. +// Shutdown stops server the gracefully. // It internally calls `http.Server#Shutdown()`. func (app *DotWeb) Shutdown(ctx context.Context) error { return app.HttpServer.stdServer.Shutdown(ctx) @@ -617,31 +626,60 @@ func HTTPNotFound(ctx Context) { http.NotFound(ctx.Response().Writer(), ctx.Request().Request) } -// DefaultNotFoundHandler default exception handler -func DefaultNotFoundHandler(ctx Context) { - ctx.Response().Header().Set(HeaderContentType, CharsetUTF8) - ctx.WriteStringC(http.StatusNotFound, http.StatusText(http.StatusNotFound)) +//query pprof debug info +//key:heap goroutine threadcreate block +func initPProf(ctx Context) error { + querykey := ctx.GetRouterName("key") + runtime.GC() + pprof.Lookup(querykey).WriteTo(ctx.Response().Writer(), 1) + return nil } -// DefaultMethodNotAllowedHandler default exception handler -func DefaultMethodNotAllowedHandler(ctx Context) { - ctx.Response().Header().Set(HeaderContentType, CharsetUTF8) - ctx.WriteStringC(http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed)) +func freeMemory(ctx Context) error { + debug.FreeOSMemory() + return nil } -// DefaultAutoOPTIONSHandler default handler for options request -// if set HttpServer.EnabledAutoOPTIONS, auto bind this handler -func DefaultAutoOPTIONSHandler(ctx Context) error { - return ctx.WriteStringC(http.StatusNoContent, "") -} - -// DefaultUniqueIDGenerater default generater used to create Unique Id -func DefaultUniqueIDGenerater() string { - return uuid.NewV1().String32() +func showIntervalData(ctx Context) error { + type data struct { + Time string + RequestCount uint64 + ErrorCount uint64 + } + queryKey := ctx.QueryString("querykey") + + d := new(data) + d.Time = queryKey + d.RequestCount = core.GlobalState.QueryIntervalRequestData(queryKey) + d.ErrorCount = core.GlobalState.QueryIntervalErrorData(queryKey) + ctx.WriteJson(d) + return nil +} + +//显示服务器状态信息 +func showServerState(ctx Context) error { + ctx.WriteHtml(core.GlobalState.ShowHtmlData(Version)) + return nil +} + +//显示服务器状态信息 +func showQuery(ctx Context) error { + querykey := ctx.GetRouterName("key") + switch querykey { + case "state": + ctx.WriteString(jsonutil.GetJsonString(core.GlobalState)) + case "": + ctx.WriteString("please input key") + default: + ctx.WriteString("not support key => " + querykey) + } + return nil } -func DefaultTimeoutHookHandler(ctx Context) { - realDration := ctx.Items().GetTimeDuration(ItemKeyHandleDuration) - logs := fmt.Sprintf("req %v, cost %v", ctx.Request().Url(), realDration.Seconds()) - ctx.HttpServer().DotApp.Logger().Warn(logs, LogTarget_RequestTimeout) +func printDotLogo() { + logger.Logger().Print(` ____ __ __`, LogTarget_HttpServer) + logger.Logger().Print(` / __ \ ____ / /_ _ __ ___ / /_`, LogTarget_HttpServer) + logger.Logger().Print(` / / / / / __ \ / __/| | /| / / / _ \ / __ \`, LogTarget_HttpServer) + logger.Logger().Print(` / /_/ / / /_/ // /_ | |/ |/ / / __/ / /_/ /`, LogTarget_HttpServer) + logger.Logger().Print(`/_____/ \____/ \__/ |__/|__/ \___/ /_.___/`, LogTarget_HttpServer) } diff --git a/notify/notify.go b/notify/notify.go new file mode 100644 index 0000000..58b0e3b --- /dev/null +++ b/notify/notify.go @@ -0,0 +1,102 @@ +package notify + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" +) + +const ( + win = "windows" + linux = "linux" + minLoopTime = 500 + exitErr = "exit status 3" +) + +type Notify struct { + Root string + LoopTime int + ModTimes map[string]time.Time +} + +func Start(root string, loopTime int) error { + if loopTime < minLoopTime { + loopTime = minLoopTime + } + notify := &Notify{Root: root, LoopTime: loopTime} + notify.ModTimes = make(map[string]time.Time) + return notify.start() +} +func (n *Notify) reloadLoop() { + for { + filepath.Walk(n.Root, n.visit) + time.Sleep(time.Duration(n.LoopTime) * time.Millisecond) + } +} +func (n *Notify) visit(path string, fileinfo os.FileInfo, err error) error { + if err != nil { + return fmt.Errorf("访问文件失败%s", err) + } + if !fileinfo.IsDir() && strings.HasSuffix(path, "go") { + modTime := fileinfo.ModTime() + if oldModTime, ok := n.ModTimes[path]; !ok { + n.ModTimes[path] = modTime + } else { + if oldModTime.Before(modTime) { + fmt.Printf("%s文件发生变化,重新加载\n", filepath.Base(path)) + os.Exit(3) + } + } + } + return nil +} + +func (n *Notify) start() error { + if reloadEnv := os.Getenv("DOTWEB_RELOAD"); reloadEnv != "true" { + stdErr := make(chan string) + for { + read, write, _ := os.Pipe() + go func() { + var buf bytes.Buffer + io.Copy(&buf, read) + stdErr <- buf.String() + }() + arg := []string{"run"} + _, file := filepath.Split(os.Args[0]) + if runtime.GOOS == win { + file = filepath.Base(file) + ext := filepath.Ext(file) + file = strings.TrimSuffix(file, ext) + } + file = file + ".go" + arg = append(arg, file) + arg = append(arg, os.Args[1:]...) + command := exec.Command("go", arg...) + command.Env = append(command.Env, "DOTWEB_RELOAD=true") + command.Env = append(command.Env, os.Environ()...) + command.Stdout = os.Stdout + command.Stderr = write + if err := command.Run(); err != nil { + write.Close() + if !strings.Contains(<-stdErr, exitErr) { + fmt.Println(stdErr) + return err + } + } else { + return nil + } + + } + } else { + go func() { + n.reloadLoop() + }() + } + return nil +}