diff --git a/.travis.yml b/.travis.yml index 3a6c9f84..380eca81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,9 @@ language: go go: - tip + - 1.6.0 - 1.5.3 - 1.4.3 - - 1.3.3 services: - redis-server - mysql @@ -21,9 +21,9 @@ install: - go get github.com/bradfitz/gomemcache/memcache - go get github.com/garyburd/redigo/redis - go get github.com/beego/x2j + - go get github.com/couchbase/go-couchbase - go get github.com/beego/goyaml2 - go get github.com/belogik/goes - - go get github.com/couchbase/go-couchbase - go get github.com/siddontang/ledisdb/config - go get github.com/siddontang/ledisdb/ledis - go get golang.org/x/tools/cmd/vet diff --git a/config.go b/config.go index ffe92f06..42b35fd2 100644 --- a/config.go +++ b/config.go @@ -15,11 +15,11 @@ package beego import ( + "fmt" "html/template" "os" "path/filepath" "strings" - "fmt" "github.com/astaxie/beego/config" "github.com/astaxie/beego/session" @@ -111,7 +111,6 @@ var ( // GlobalSessions is the instance for the session manager GlobalSessions *session.Manager - workPath string // appConfigPath is the path to the config files appConfigPath string // appConfigProvider is the provider for the config, default is ini @@ -120,12 +119,8 @@ var ( func init() { AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0])) - workPath, _ = os.Getwd() - workPath, _ = filepath.Abs(workPath) - if workPath != AppPath { - os.Chdir(AppPath) - } + os.Chdir(AppPath) BConfig = &Config{ AppName: "beego", @@ -175,7 +170,7 @@ func init() { SessionName: "beegosessionID", SessionGCMaxLifetime: 3600, SessionProviderConfig: "", - SessionCookieLifeTime: 0, //set cookie default is the brower life + SessionCookieLifeTime: 0, //set cookie default is the browser life SessionAutoSetCookie: true, SessionDomain: "", }, @@ -189,7 +184,7 @@ func init() { appConfigPath = filepath.Join(AppPath, "conf", "app.conf") if !utils.FileExists(appConfigPath) { - AppConfig = &beegoAppConfig{config.NewFakeConfig()} + AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} return } @@ -202,11 +197,11 @@ func parseConfig(appConfigPath string) (err error) { if err != nil { return err } - // set the runmode first + // set the run mode first if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { BConfig.RunMode = envRunMode - } else if runmode := AppConfig.String("RunMode"); runmode != "" { - BConfig.RunMode = runmode + } else if runMode := AppConfig.String("RunMode"); runMode != "" { + BConfig.RunMode = runMode } BConfig.AppName = AppConfig.DefaultString("AppName", BConfig.AppName) @@ -299,7 +294,7 @@ func parseConfig(appConfigPath string) (err error) { } //init log - BeeLogger.Close() + BeeLogger.Reset() for adaptor, config := range BConfig.Log.Outputs { err = BeeLogger.SetLogger(adaptor, config) if err != nil { @@ -393,46 +388,46 @@ func (b *beegoAppConfig) Float(key string) (float64, error) { return b.innerConfig.Float(key) } -func (b *beegoAppConfig) DefaultString(key string, defaultval string) string { +func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string { if v := b.String(key); v != "" { return v } - return defaultval + return defaultVal } -func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string { +func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string { if v := b.Strings(key); len(v) != 0 { return v } - return defaultval + return defaultVal } -func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { +func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int { if v, err := b.Int(key); err == nil { return v } - return defaultval + return defaultVal } -func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { +func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 { if v, err := b.Int64(key); err == nil { return v } - return defaultval + return defaultVal } -func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { +func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool { if v, err := b.Bool(key); err == nil { return v } - return defaultval + return defaultVal } -func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { +func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 { if v, err := b.Float(key); err == nil { return v } - return defaultval + return defaultVal } func (b *beegoAppConfig) DIY(key string) (interface{}, error) { diff --git a/logs/console.go b/logs/console.go index d7ed8d8e..3781e6cd 100644 --- a/logs/console.go +++ b/logs/console.go @@ -47,17 +47,17 @@ var colors = []brush{ // consoleWriter implements LoggerInterface and writes messages to terminal. type consoleWriter struct { - lg *log.Logger - Level int `json:"level"` - Color bool `json:"color"` + lg *log.Logger + Level int `json:"level"` + Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color } // NewConsole create ConsoleWriter returning as LoggerInterface. func NewConsole() Logger { cw := &consoleWriter{ - lg: log.New(os.Stdout, "", 0), - Level: LevelDebug, - Color: true, + lg: log.New(os.Stdout, "", 0), + Level: LevelDebug, + Colorful: true, } return cw } @@ -68,7 +68,11 @@ func (c *consoleWriter) Init(jsonConfig string) error { if len(jsonConfig) == 0 { return nil } - return json.Unmarshal([]byte(jsonConfig), c) + err := json.Unmarshal([]byte(jsonConfig), c) + if runtime.GOOS == "windows" { + c.Colorful = false + } + return err } // WriteMsg write message in console. @@ -77,12 +81,11 @@ func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error { return nil } msg = formatLogTime(when) + msg - if runtime.GOOS == "windows" || !c.Color { + if c.Colorful { + c.lg.Println(colors[level](msg)) + } else { c.lg.Println(msg) - return nil } - c.lg.Println(colors[level](msg)) - return nil } diff --git a/logs/log.go b/logs/log.go index 2a12ed79..428e8e50 100644 --- a/logs/log.go +++ b/logs/log.go @@ -98,6 +98,8 @@ type BeeLogger struct { loggerFuncCallDepth int asynchronous bool msgChan chan *logMsg + signalChan chan string + wg sync.WaitGroup outputs []*nameLogger } @@ -109,7 +111,7 @@ type nameLogger struct { type logMsg struct { level int msg string - when time.Time + when time.Time } var logMsgPool *sync.Pool @@ -122,6 +124,7 @@ func NewLogger(channelLen int64) *BeeLogger { bl.level = LevelDebug bl.loggerFuncCallDepth = 2 bl.msgChan = make(chan *logMsg, channelLen) + bl.signalChan = make(chan string, 1) return bl } @@ -133,6 +136,7 @@ func (bl *BeeLogger) Async() *BeeLogger { return &logMsg{} }, } + bl.wg.Add(1) go bl.startLogger() return bl } @@ -232,11 +236,26 @@ func (bl *BeeLogger) EnableFuncCallDepth(b bool) { // start logger chan reading. // when chan is not empty, write logs. func (bl *BeeLogger) startLogger() { + gameOver := false for { select { case bm := <-bl.msgChan: bl.writeToLoggers(bm.when, bm.msg, bm.level) logMsgPool.Put(bm) + case sg := <-bl.signalChan: + // Now should only send "flush" or "close" to bl.signalChan + bl.flush() + if sg == "close" { + for _, l := range bl.outputs { + l.Destroy() + } + bl.outputs = nil + gameOver = true + } + bl.wg.Done() + } + if gameOver { + break } } } @@ -345,13 +364,41 @@ func (bl *BeeLogger) Trace(format string, v ...interface{}) { // Flush flush all chan data. func (bl *BeeLogger) Flush() { - for _, l := range bl.outputs { - l.Flush() + if bl.asynchronous { + bl.signalChan <- "flush" + bl.wg.Wait() + bl.wg.Add(1) + return } + bl.flush() } // Close close logger, flush all chan data and destroy all adapters in BeeLogger. func (bl *BeeLogger) Close() { + if bl.asynchronous { + bl.signalChan <- "close" + bl.wg.Wait() + } else { + bl.flush() + for _, l := range bl.outputs { + l.Destroy() + } + bl.outputs = nil + } + close(bl.msgChan) + close(bl.signalChan) +} + +// Reset close all outputs, and set bl.outputs to nil +func (bl *BeeLogger) Reset() { + bl.Flush() + for _, l := range bl.outputs { + l.Destroy() + } + bl.outputs = nil +} + +func (bl *BeeLogger) flush() { for { if len(bl.msgChan) > 0 { bm := <-bl.msgChan @@ -363,9 +410,7 @@ func (bl *BeeLogger) Close() { } for _, l := range bl.outputs { l.Flush() - l.Destroy() } - bl.outputs = nil } func formatLogTime(when time.Time) string { diff --git a/template.go b/template.go index 7a38630e..9954cf32 100644 --- a/template.go +++ b/template.go @@ -23,6 +23,7 @@ import ( "path/filepath" "regexp" "strings" + "sync" "github.com/astaxie/beego/utils" ) @@ -30,7 +31,8 @@ import ( var ( beegoTplFuncMap = make(template.FuncMap) // BeeTemplates caching map and supported template file extensions. - BeeTemplates = make(map[string]*template.Template) + BeeTemplates = make(map[string]*template.Template) + templatesLock sync.Mutex // BeeTemplateExt stores the template extension which will build BeeTemplateExt = []string{"tpl", "html"} ) @@ -66,17 +68,21 @@ func init() { } // AddFuncMap let user to register a func in the template. -func AddFuncMap(key string, funname interface{}) error { - beegoTplFuncMap[key] = funname +func AddFuncMap(key string, fn interface{}) error { + beegoTplFuncMap[key] = fn return nil } -type templatefile struct { +type templateFile struct { root string files map[string][]string } -func (tf *templatefile) visit(paths string, f os.FileInfo, err error) error { +// visit will make the paths into two part,the first is subDir (without tf.root),the second is full path(without tf.root). +// if tf.root="views" and +// paths is "views/errors/404.html",the subDir will be "errors",the file will be "errors/404.html" +// paths is "views/admin/errors/404.html",the subDir will be "admin/errors",the file will be "admin/errors/404.html" +func (tf *templateFile) visit(paths string, f os.FileInfo, err error) error { if f == nil { return err } @@ -88,18 +94,10 @@ func (tf *templatefile) visit(paths string, f os.FileInfo, err error) error { } replace := strings.NewReplacer("\\", "/") - a := []byte(paths) - a = a[len([]byte(tf.root)):] - file := strings.TrimLeft(replace.Replace(string(a)), "/") - subdir := filepath.Dir(file) - if _, ok := tf.files[subdir]; ok { - tf.files[subdir] = append(tf.files[subdir], file) - } else { - m := make([]string, 1) - m[0] = file - tf.files[subdir] = m - } + file := strings.TrimLeft(replace.Replace(paths[len(tf.root):]), "/") + subDir := filepath.Dir(file) + tf.files[subDir] = append(tf.files[subDir], file) return nil } @@ -132,7 +130,7 @@ func BuildTemplate(dir string, files ...string) error { } return errors.New("dir open err") } - self := &templatefile{ + self := &templateFile{ root: dir, files: make(map[string][]string), } @@ -146,12 +144,14 @@ func BuildTemplate(dir string, files ...string) error { for _, v := range self.files { for _, file := range v { if len(files) == 0 || utils.InSlice(file, files) { + templatesLock.Lock() t, err := getTemplate(self.root, file, v...) if err != nil { Trace("parse template err:", file, err) } else { BeeTemplates[file] = t } + templatesLock.Unlock() } } } @@ -159,16 +159,16 @@ func BuildTemplate(dir string, files ...string) error { } func getTplDeep(root, file, parent string, t *template.Template) (*template.Template, [][]string, error) { - var fileabspath string + var fileAbsPath string if filepath.HasPrefix(file, "../") { - fileabspath = filepath.Join(root, filepath.Dir(parent), file) + fileAbsPath = filepath.Join(root, filepath.Dir(parent), file) } else { - fileabspath = filepath.Join(root, file) + fileAbsPath = filepath.Join(root, file) } - if e := utils.FileExists(fileabspath); !e { + if e := utils.FileExists(fileAbsPath); !e { panic("can't find template file:" + file) } - data, err := ioutil.ReadFile(fileabspath) + data, err := ioutil.ReadFile(fileAbsPath) if err != nil { return nil, [][]string{}, err } @@ -177,11 +177,11 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp return nil, [][]string{}, err } reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*template[ ]+\"([^\"]+)\"") - allsub := reg.FindAllStringSubmatch(string(data), -1) - for _, m := range allsub { + allSub := reg.FindAllStringSubmatch(string(data), -1) + for _, m := range allSub { if len(m) == 2 { - tlook := t.Lookup(m[1]) - if tlook != nil { + tl := t.Lookup(m[1]) + if tl != nil { continue } if !HasTemplateExt(m[1]) { @@ -193,17 +193,17 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp } } } - return t, allsub, nil + return t, allSub, nil } func getTemplate(root, file string, others ...string) (t *template.Template, err error) { t = template.New(file).Delims(BConfig.WebConfig.TemplateLeft, BConfig.WebConfig.TemplateRight).Funcs(beegoTplFuncMap) - var submods [][]string - t, submods, err = getTplDeep(root, file, "", t) + var subMods [][]string + t, subMods, err = getTplDeep(root, file, "", t) if err != nil { return nil, err } - t, err = _getTemplate(t, root, submods, others...) + t, err = _getTemplate(t, root, subMods, others...) if err != nil { return nil, err @@ -211,44 +211,44 @@ func getTemplate(root, file string, others ...string) (t *template.Template, err return } -func _getTemplate(t0 *template.Template, root string, submods [][]string, others ...string) (t *template.Template, err error) { +func _getTemplate(t0 *template.Template, root string, subMods [][]string, others ...string) (t *template.Template, err error) { t = t0 - for _, m := range submods { + for _, m := range subMods { if len(m) == 2 { - templ := t.Lookup(m[1]) - if templ != nil { + tpl := t.Lookup(m[1]) + if tpl != nil { continue } //first check filename - for _, otherfile := range others { - if otherfile == m[1] { - var submods1 [][]string - t, submods1, err = getTplDeep(root, otherfile, "", t) + for _, otherFile := range others { + if otherFile == m[1] { + var subMods1 [][]string + t, subMods1, err = getTplDeep(root, otherFile, "", t) if err != nil { Trace("template parse file err:", err) - } else if submods1 != nil && len(submods1) > 0 { - t, err = _getTemplate(t, root, submods1, others...) + } else if subMods1 != nil && len(subMods1) > 0 { + t, err = _getTemplate(t, root, subMods1, others...) } break } } //second check define - for _, otherfile := range others { - fileabspath := filepath.Join(root, otherfile) - data, err := ioutil.ReadFile(fileabspath) + for _, otherFile := range others { + fileAbsPath := filepath.Join(root, otherFile) + data, err := ioutil.ReadFile(fileAbsPath) if err != nil { continue } reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*define[ ]+\"([^\"]+)\"") - allsub := reg.FindAllStringSubmatch(string(data), -1) - for _, sub := range allsub { + allSub := reg.FindAllStringSubmatch(string(data), -1) + for _, sub := range allSub { if len(sub) == 2 && sub[1] == m[1] { - var submods1 [][]string - t, submods1, err = getTplDeep(root, otherfile, "", t) + var subMods1 [][]string + t, subMods1, err = getTplDeep(root, otherFile, "", t) if err != nil { Trace("template parse file err:", err) - } else if submods1 != nil && len(submods1) > 0 { - t, err = _getTemplate(t, root, submods1, others...) + } else if subMods1 != nil && len(subMods1) > 0 { + t, err = _getTemplate(t, root, subMods1, others...) } break } diff --git a/tree.go b/tree.go index 761ae0ce..594e9999 100644 --- a/tree.go +++ b/tree.go @@ -265,15 +265,14 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, } t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) } else { - var ok bool var subTree *Tree - for _, subTree = range t.fixrouters { - if t.prefix == seg { - ok = true + for _, sub := range t.fixrouters { + if sub.prefix == seg { + subTree = sub break } } - if !ok { + if subTree == nil { subTree = NewTree() subTree.prefix = seg t.fixrouters = append(t.fixrouters, subTree) diff --git a/tree_test.go b/tree_test.go index 9f21c18c..531df046 100644 --- a/tree_test.go +++ b/tree_test.go @@ -221,6 +221,18 @@ func TestAddTree4(t *testing.T) { } } +// Test for issue #1595 +func TestAddTree5(t *testing.T) { + tr := NewTree() + tr.AddRouter("/v1/shop/:id", "shopdetail") + tr.AddRouter("/v1/shop/", "shophome") + ctx := context.NewContext() + obj := tr.Match("/v1/shop/", ctx) + if obj == nil || obj.(string) != "shophome" { + t.Fatal("url /v1/shop/ need match router /v1/shop/ ") + } +} + func TestSplitPath(t *testing.T) { a := splitPath("") if len(a) != 0 {