From ecab397073bf1585048849fa74f03084169235f2 Mon Sep 17 00:00:00 2001 From: youngsterxyf Date: Thu, 28 Jan 2016 21:53:44 +0800 Subject: [PATCH 1/5] try to fix the little bug when calling Close or Flush in async mode --- logs/log.go | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/logs/log.go b/logs/log.go index 2a12ed79..0323e0d3 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 } @@ -122,6 +124,8 @@ func NewLogger(channelLen int64) *BeeLogger { bl.level = LevelDebug bl.loggerFuncCallDepth = 2 bl.msgChan = make(chan *logMsg, channelLen) + bl.signalChan = make(chan string, 1) + bl.wg.Add(1) return bl } @@ -237,6 +241,27 @@ func (bl *BeeLogger) startLogger() { 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 + for { + if len(bl.msgChan) > 0 { + bm := <-bl.msgChan + bl.writeToLoggers(bm.when, bm.msg, bm.level) + logMsgPool.Put(bm) + continue + } + break + } + for _, l := range bl.outputs { + l.Flush() + } + if sg == "close" { + for _, l := range bl.outputs { + l.Destroy() + } + bl.outputs = nil + } + bl.wg.Done() } } } @@ -345,27 +370,16 @@ func (bl *BeeLogger) Trace(format string, v ...interface{}) { // Flush flush all chan data. func (bl *BeeLogger) Flush() { - for _, l := range bl.outputs { - l.Flush() - } + bl.signalChan <- "flush" + bl.wg.Wait() + bl.wg.Add(1) } // Close close logger, flush all chan data and destroy all adapters in BeeLogger. func (bl *BeeLogger) Close() { - for { - if len(bl.msgChan) > 0 { - bm := <-bl.msgChan - bl.writeToLoggers(bm.when, bm.msg, bm.level) - logMsgPool.Put(bm) - continue - } - break - } - for _, l := range bl.outputs { - l.Flush() - l.Destroy() - } - bl.outputs = nil + bl.signalChan <- "close" + bl.wg.Wait() + bl.wg.Add(1) } func formatLogTime(when time.Time) string { From 2efe7c4c89dc9ca5120f2762db46ce731f265945 Mon Sep 17 00:00:00 2001 From: youngsterxyf Date: Tue, 2 Feb 2016 17:12:47 +0800 Subject: [PATCH 2/5] merge multi commit --- config.go | 4 +-- logs/log.go | 75 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/config.go b/config.go index ffe92f06..f9810778 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" @@ -299,7 +299,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 { diff --git a/logs/log.go b/logs/log.go index 0323e0d3..428e8e50 100644 --- a/logs/log.go +++ b/logs/log.go @@ -98,8 +98,8 @@ type BeeLogger struct { loggerFuncCallDepth int asynchronous bool msgChan chan *logMsg - signalChan chan string - wg sync.WaitGroup + signalChan chan string + wg sync.WaitGroup outputs []*nameLogger } @@ -111,7 +111,7 @@ type nameLogger struct { type logMsg struct { level int msg string - when time.Time + when time.Time } var logMsgPool *sync.Pool @@ -125,7 +125,6 @@ func NewLogger(channelLen int64) *BeeLogger { bl.loggerFuncCallDepth = 2 bl.msgChan = make(chan *logMsg, channelLen) bl.signalChan = make(chan string, 1) - bl.wg.Add(1) return bl } @@ -137,6 +136,7 @@ func (bl *BeeLogger) Async() *BeeLogger { return &logMsg{} }, } + bl.wg.Add(1) go bl.startLogger() return bl } @@ -236,6 +236,7 @@ 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: @@ -243,26 +244,19 @@ func (bl *BeeLogger) startLogger() { logMsgPool.Put(bm) case sg := <-bl.signalChan: // Now should only send "flush" or "close" to bl.signalChan - for { - if len(bl.msgChan) > 0 { - bm := <-bl.msgChan - bl.writeToLoggers(bm.when, bm.msg, bm.level) - logMsgPool.Put(bm) - continue - } - break - } - for _, l := range bl.outputs { - l.Flush() - } + bl.flush() if sg == "close" { for _, l := range bl.outputs { l.Destroy() } bl.outputs = nil + gameOver = true } bl.wg.Done() } + if gameOver { + break + } } } @@ -370,16 +364,53 @@ func (bl *BeeLogger) Trace(format string, v ...interface{}) { // Flush flush all chan data. func (bl *BeeLogger) Flush() { - bl.signalChan <- "flush" - bl.wg.Wait() - bl.wg.Add(1) + 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() { - bl.signalChan <- "close" - bl.wg.Wait() - bl.wg.Add(1) + 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 + bl.writeToLoggers(bm.when, bm.msg, bm.level) + logMsgPool.Put(bm) + continue + } + break + } + for _, l := range bl.outputs { + l.Flush() + } } func formatLogTime(when time.Time) string { From 2301633d421d0e628463da24263a8dbce5c0480c Mon Sep 17 00:00:00 2001 From: Lei Cao Date: Tue, 2 Feb 2016 23:34:32 +0800 Subject: [PATCH 3/5] Added slack --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fec6113f..6c589584 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Please see [Documentation](http://beego.me/docs) for more. ## Community * [http://beego.me/community](http://beego.me/community) +* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232) ## LICENSE From 9411063574444ae932d1e0731cad965f2fc9b06a Mon Sep 17 00:00:00 2001 From: ysqi Date: Fri, 12 Feb 2016 14:45:45 +0800 Subject: [PATCH 4/5] fix #1595 --- tree.go | 9 ++++----- tree_test.go | 12 ++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) 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 { From 3da28535fe5aae6f0515cfb8c13b13041ac2d1d6 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Thu, 25 Feb 2016 17:37:28 +0800 Subject: [PATCH 5/5] lock the templates map when goroutie update the map --- template.go | 98 ++++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) 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 }