diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index d59fa4a7d..b842d5df1 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -242,7 +242,7 @@ func (s *Server) Start() error { s.config.Handler = http.HandlerFunc(s.defaultHttpHandle) } // 不允许访问的路由注册(使用HOOK实现) - // @TODO 去掉HOOK的实现方式 + // TODO 去掉HOOK的实现方式 if s.config.DenyRoutes != nil { for _, v := range s.config.DenyRoutes { s.BindHookHandler(v, HOOK_BEFORE_SERVE, func(r *Request) { diff --git a/g/os/gcfg/gcfg_z_unit_test.go b/g/os/gcfg/gcfg_z_unit_test.go new file mode 100644 index 000000000..7e54bd513 --- /dev/null +++ b/g/os/gcfg/gcfg_z_unit_test.go @@ -0,0 +1,77 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +package gcfg_test + +import ( + "github.com/gogf/gf/g/os/gcfg" + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func Test_Basic(t *testing.T) { + config := ` +v1 = 1 +v2 = "true" +v3 = "off" +v4 = "1.23" +array = [1,2,3] +[redis] + disk = "127.0.0.1:6379,0" + cache = "127.0.0.1:6379,1" +` + gtest.Case(t, func() { + path := "config.toml" + err := gfile.PutContents(path, config) + gtest.Assert(err, nil) + defer gfile.Remove(path) + + c := gcfg.New(".") + gtest.Assert(c.Get("v1"), 1) + gtest.AssertEQ(c.GetInt("v1"), 1) + gtest.AssertEQ(c.GetInt8("v1"), int8(1)) + gtest.AssertEQ(c.GetInt16("v1"), int16(1)) + gtest.AssertEQ(c.GetInt32("v1"), int32(1)) + gtest.AssertEQ(c.GetInt64("v1"), int64(1)) + gtest.AssertEQ(c.GetUint("v1"), uint(1)) + gtest.AssertEQ(c.GetUint8("v1"), uint8(1)) + gtest.AssertEQ(c.GetUint16("v1"), uint16(1)) + gtest.AssertEQ(c.GetUint32("v1"), uint32(1)) + gtest.AssertEQ(c.GetUint64("v1"), uint64(1)) + + gtest.AssertEQ(c.GetVar("v1").String(), "1") + gtest.AssertEQ(c.GetVar("v1").Bool(), true) + gtest.AssertEQ(c.GetVar("v2").String(), "true") + gtest.AssertEQ(c.GetVar("v2").Bool(), true) + + gtest.AssertEQ(c.GetString("v1"), "1") + gtest.AssertEQ(c.GetFloat32("v4"), float32(1.23)) + gtest.AssertEQ(c.GetFloat64("v4"), float64(1.23)) + gtest.AssertEQ(c.GetString("v2"), "true") + gtest.AssertEQ(c.GetBool("v2"), true) + gtest.AssertEQ(c.GetBool("v3"), false) + + gtest.AssertEQ(c.Contains("v1"), true) + gtest.AssertEQ(c.Contains("v2"), true) + gtest.AssertEQ(c.Contains("v3"), true) + gtest.AssertEQ(c.Contains("v4"), true) + gtest.AssertEQ(c.Contains("v5"), false) + + gtest.AssertEQ(c.GetInts("array"), []int{1,2,3}) + gtest.AssertEQ(c.GetStrings("array"), []string{"1","2","3"}) + gtest.AssertEQ(c.GetArray("array"), []interface{}{"1","2","3"}) + gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1","2","3"}) + gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{ + "disk" : "127.0.0.1:6379,0", + "cache" : "127.0.0.1:6379,1", + }) + gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + "config.toml") + + }) +} diff --git a/g/os/gcron/gcron.go b/g/os/gcron/gcron.go index e983be5c0..03c07f410 100644 --- a/g/os/gcron/gcron.go +++ b/g/os/gcron/gcron.go @@ -29,7 +29,27 @@ var ( defaultCron = New() ) -// 添加执行方法,可以给定名字,以便于后续执行删除 +// 设置日志输出路径 +func SetLogPath(path string) { + defaultCron.SetLogPath(path) +} + +// 获取设置的日志输出路径 +func GetLogPath() string { + return defaultCron.GetLogPath() +} + +// 设置日志输出等级。 +func SetLogLevel(level int) { + defaultCron.SetLogLevel(level) +} + +// 获取日志输出等级。 +func GetLogLevel() int { + return defaultCron.GetLogLevel() +} + +// 添加定时任务,可以给定名字,以便于后续执行删除 func Add(pattern string, job func(), name ... string) (*Entry, error) { return defaultCron.Add(pattern, job, name...) } diff --git a/g/os/gcron/gcron_cron.go b/g/os/gcron/gcron_cron.go index 0de5c8b21..f5e8f125f 100644 --- a/g/os/gcron/gcron_cron.go +++ b/g/os/gcron/gcron_cron.go @@ -12,26 +12,51 @@ import ( "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gtype" + "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gtimer" "time" ) // 定时任务管理对象 type Cron struct { - idgen *gtype.Int // 用于唯一名称生成 - status *gtype.Int // 定时任务状态(0: 未执行; 1: 运行中; 2: 已停止; -1:删除关闭) - entries *gmap.StringInterfaceMap // 所有的定时任务项 + idGen *gtype.Int64 // 用于唯一名称生成 + status *gtype.Int // 定时任务状态(0: 未执行; 1: 运行中; 2: 已停止; -1:删除关闭) + entries *gmap.StringInterfaceMap // 所有的定时任务项 + logPath *gtype.String // 日志文件输出目录 + logLevel *gtype.Int // 日志输出等级 } // 创建自定义的定时任务管理对象 func New() *Cron { return &Cron { - idgen : gtype.NewInt(1000000), - status : gtype.NewInt(STATUS_RUNNING), - entries : gmap.NewStringInterfaceMap(), + idGen : gtype.NewInt64(), + status : gtype.NewInt(STATUS_RUNNING), + entries : gmap.NewStringInterfaceMap(), + logPath : gtype.NewString(), + logLevel : gtype.NewInt(glog.LEVEL_PROD), } } +// 设置日志输出路径 +func (c *Cron) SetLogPath(path string) { + c.logPath.Set(path) +} + +// 获取设置的日志输出路径 +func (c *Cron) GetLogPath() string { + return c.logPath.Val() +} + +// 设置日志输出等级。 +func (c *Cron) SetLogLevel(level int) { + c.logLevel.Set(level) +} + +// 获取日志输出等级。 +func (c *Cron) GetLogLevel() int { + return c.logLevel.Val() +} + // 添加定时任务 func (c *Cron) Add(pattern string, job func(), name ... string) (*Entry, error) { if len(name) > 0 { diff --git a/g/os/gcron/gcron_entry.go b/g/os/gcron/gcron_entry.go index 267cecbef..eb2eb9989 100644 --- a/g/os/gcron/gcron_entry.go +++ b/g/os/gcron/gcron_entry.go @@ -7,8 +7,11 @@ package gcron import ( + "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gtimer" - "strconv" + "github.com/gogf/gf/g/util/gconv" + "reflect" + "runtime" "time" ) @@ -17,6 +20,7 @@ type Entry struct { cron *Cron // 所属定时任务 entry *gtimer.Entry // 定时器任务对象 schedule *cronSchedule // 定时任务配置对象 + jobName string // 任务注册方法名称 Name string // 定时任务名称 Job func() // 注册定时任务方法 Time time.Time // 注册时间 @@ -31,13 +35,14 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, times int, n entry := &Entry { cron : c, schedule : schedule, + jobName : runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(), Job : job, Time : time.Now(), } if len(name) > 0 { entry.Name = name[0] } else { - entry.Name = strconv.Itoa(c.idgen.Add(1)) + entry.Name = "gcron-" + gconv.String(c.idGen.Add(1)) } entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, times, gtimer.STATUS_STOPPED) entry.entry.Start() @@ -89,20 +94,29 @@ func (entry *Entry) Close() { // 定时任务检查执行 func (entry *Entry) check() { if entry.schedule.meet(time.Now()) { + path := entry.cron.GetLogPath() + level := entry.cron.GetLogLevel() switch entry.cron.status.Val() { case STATUS_STOPPED: return case STATUS_CLOSED: entry.cron.Remove(entry.Name) + glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s remove", entry.Name, entry.schedule.pattern, entry.jobName) gtimer.Exit() case STATUS_READY: fallthrough case STATUS_RUNNING: + glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName) defer func() { if entry.entry.Status() == STATUS_CLOSED { entry.cron.Remove(entry.Name) } + if err := recover(); err != nil { + glog.Path(path).Level(level).Errorfln("[gcron] %s(%s) %s end with error: %v", entry.Name, entry.schedule.pattern, entry.jobName, err) + } else { + glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName) + } }() entry.Job() } diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index e05c741eb..65f92ba80 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -18,6 +18,8 @@ import ( const ( LEVEL_ALL = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT + LEVEL_DEV = LEVEL_ALL + LEVEL_PROD = LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT LEVEL_DEBU = 1 << iota LEVEL_INFO LEVEL_NOTI @@ -141,6 +143,14 @@ func To(writer io.Writer) *Logger { return logger.To(writer) } +// Path is a chaining function, +// which sets the directory path to for current logging content output. +// +// 链式操作,设置下一次输出的日志路径。 +func Path(path string) *Logger { + return logger.Path(path) +} + // Cat is a chaining function, // which sets the category to for current logging content output. // diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index cd142397f..a17b82db7 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -26,6 +26,23 @@ func (l *Logger) To(writer io.Writer) *Logger { return logger } +// Path is a chaining function, +// which sets the directory path to for current logging content output. +// +// 链式操作,设置下一次输出的日志路径。 +func (l *Logger) Path(path string) *Logger { + logger := (*Logger)(nil) + if l.pr == nil { + logger = l.Clone() + } else { + logger = l + } + if path != "" { + logger.SetPath(path) + } + return logger +} + // Cat is a chaining function, // which sets the category to for current logging content output. // diff --git a/g/util/grand/grand_intn.go b/g/util/grand/grand_intn.go index c45f72e90..de8d6244c 100644 --- a/g/util/grand/grand_intn.go +++ b/g/util/grand/grand_intn.go @@ -47,7 +47,7 @@ func init() { // 自定义的 rand.Intn ,绝对随机, 返回: [0, max) func Intn (max int) int { n := int(<- bufferChan)%max - if n < 0 { + if (max > 0 && n < 0) || (max < 0 && n > 0) { return -n } return n diff --git a/g/util/grand/grand_z_unit_test.go b/g/util/grand/grand_z_unit_test.go index 0a1b6b25d..5ab9b92ad 100644 --- a/g/util/grand/grand_z_unit_test.go +++ b/g/util/grand/grand_z_unit_test.go @@ -17,9 +17,15 @@ import ( func Test_Intn(t *testing.T) { gtest.Case(t, func() { - for i := 0; i < 100; i++ { + for i := 0; i < 1000000; i++ { n := grand.Intn(100) gtest.AssertLT(n, 100) + gtest.AssertGTE(n, 0) + } + for i := 0; i < 1000000; i++ { + n := grand.Intn(-100) + gtest.AssertLTE(n, 0) + gtest.AssertGT(n, -100) } }) } @@ -77,6 +83,9 @@ func Test_Rand(t *testing.T) { for i := 0; i < 100; i++ { gtest.AssertIN(grand.Rand(1, 2), []int{1, 2}) } + for i := 0; i < 100; i++ { + gtest.AssertIN(grand.Rand(-1, 2), []int{-1, 0, 1, 2}) + } }) }