From 635d228c86ac52a86fa215d6dc6fd1a171b14f4a Mon Sep 17 00:00:00 2001 From: John Date: Thu, 16 May 2019 13:56:49 +0800 Subject: [PATCH 01/50] adding inherit struct support for gdb --- g/database/gdb/gdb_func.go | 34 ++++++++++++++++++++++++++++++++++ g/util/gconv/gconv_map.go | 10 ++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go index 63a11f846..e7e8b8181 100644 --- a/g/database/gdb/gdb_func.go +++ b/g/database/gdb/gdb_func.go @@ -20,6 +20,11 @@ import ( "time" ) +// Type assert api for String(). +type apiString interface { + String() string +} + // 格式化SQL查询条件 func formatCondition(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) { // 条件字符串处理 @@ -187,3 +192,32 @@ func getInsertOperationByOption(option int) string { } return operator } + +// 将对象转换为map,如果对象带有继承对象,那么执行递归转换。 +// 该方法用于将变量传递给数据库执行之前。 +func structToMap(obj interface{}) map[string]interface{} { + data := gconv.Map(obj) + for k, v := range data { + rv := reflect.ValueOf(v) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Struct: + // 底层数据库引擎支持 time.Time 类型 + if _, ok := v.(time.Time); ok { + continue + } + // 如果执行String方法,那么执行字符串转换 + if s, ok := v.(apiString); ok { + data[k] = s.String() + } + for k, v := range structToMap(v) { + data[k] = v + } + } + } + return data +} \ No newline at end of file diff --git a/g/util/gconv/gconv_map.go b/g/util/gconv/gconv_map.go index 94f6f72e9..6da49ee48 100644 --- a/g/util/gconv/gconv_map.go +++ b/g/util/gconv/gconv_map.go @@ -13,6 +13,9 @@ import ( "strings" ) +const ( + gGCONV_TAG = "gconv" +) // Map converts any variable to map[string]interface{}. // If the parameter is not a map type, then the conversion will fail and returns nil. // If is a struct object, the second parameter specifies the most priority @@ -102,8 +105,7 @@ func Map(value interface{}, tags...string) map[string]interface{} { case reflect.Struct: rt := rv.Type() name := "" - gconvTag := "gconv" - tagArray := []string{gconvTag, "json"} + tagArray := []string{gGCONV_TAG, "json"} switch len(tags) { case 0: // No need handle. @@ -112,8 +114,8 @@ func Map(value interface{}, tags...string) map[string]interface{} { default: tagArray = tags } - if gstr.SearchArray(tagArray, gconvTag) < 0 { - tagArray = append(tagArray, gconvTag) + if gstr.SearchArray(tagArray, gGCONV_TAG) < 0 { + tagArray = append(tagArray, gGCONV_TAG) } for i := 0; i < rv.NumField(); i++ { // Only convert the public attributes. From e31861af2e339db501256705a9c2dd48f9d4f6b8 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 18 May 2019 13:52:18 +0800 Subject: [PATCH 02/50] README updates --- README.MD | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 5933032b5..733a794f0 100644 --- a/README.MD +++ b/README.MD @@ -1,5 +1,6 @@ # GoFrame - +
+ [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) [![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) @@ -8,12 +9,15 @@ [![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) [![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) +
+ `GF(GoFrame)` is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: memcache, configure, validator, logging, array/queue/set/map containers, timer/timing tasks, file/memory lock, object pool, database ORM, etc. Supporting web server integrated with router, cookie, session, logger, template, https, hooks, rewrites and many more features. + # Installation ``` go get -u github.com/gogf/gf From e9f7b8bc0c8fee289572f6e9d74b2aceaf8bbb95 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 18 May 2019 14:55:15 +0800 Subject: [PATCH 03/50] README updates --- README.MD | 16 +++++++++++++--- README_ZH.MD | 17 ++++++++++------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/README.MD b/README.MD index 733a794f0..06202e302 100644 --- a/README.MD +++ b/README.MD @@ -1,6 +1,6 @@ # GoFrame
- + [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) [![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) @@ -29,7 +29,7 @@ require github.com/gogf/gf latest # Limitation ``` -golang version >= 1.9.2 +golang version >= 1.10 ``` # Documentation @@ -68,7 +68,6 @@ func main() { `GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever. - # Contributors - [aloncn](https://github.com/aloncn) @@ -91,6 +90,8 @@ func main() { # Donators +We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](https://goframe.org/images/donate.png)? + - [flyke-xu](https://gitee.com/flyke-xu) - [hailaz](https://gitee.com/hailaz) - [ireadx](https://github.com/ireadx) @@ -106,3 +107,12 @@ func main() { + +# Sponsor + +We appreciate any kind of sponsorship for `GF` development. If you've got some interested, please contact john@goframe.org. + + + + + diff --git a/README_ZH.MD b/README_ZH.MD index fc43009cd..30b6ed12e 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -1,5 +1,6 @@ # GoFrame - +
+ [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) [![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) @@ -8,6 +9,8 @@ [![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) [![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) +
+ `GF(Go Frame)`是一款模块化、松耦合、生产级Go应用开发框架。提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、 并发安全容器等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。 @@ -33,7 +36,7 @@ require github.com/gogf/gf latest # 限制 ```shell -golang版本 >= 1.9.2 +golang版本 >= 1.10 ``` # 架构 @@ -76,12 +79,12 @@ func main() { # 捐赠 -捐赠支持`GF`框架的研发, -请在捐赠时备注您的`github`/`gitee`账号名称。 +如果您喜欢`GF`,要不[给开发者来杯咖啡吧](https://goframe.org/images/donate.png)! +请在捐赠时备注您的`github`/`gitee`账号名称。 - - - +# 赞助 + +赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 john@goframe.org 。 # 贡献者 From 68fa235412212fa7b3dfb01a21bbf9327fec14c5 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 21 May 2019 21:52:17 +0800 Subject: [PATCH 04/50] add gjson.GetJsonMap; refract glog to improve performance; --- g/encoding/gjson/gjson_api.go | 27 +- g/encoding/gjson/gjson_z_unit_basic_test.go | 36 +++ g/net/ghttp/ghttp_server_config.go | 4 +- g/net/ghttp/ghttp_server_config_logger.go | 4 +- g/net/ghttp/ghttp_server_log.go | 4 +- g/os/glog/glog.go | 19 +- g/os/glog/glog_logger.go | 264 ++++++++++++-------- g/os/glog/glog_logger_chaining.go | 45 ++-- geg/os/glog/glog_stdout.go | 21 ++ geg/other/test.go | 6 +- 10 files changed, 291 insertions(+), 139 deletions(-) create mode 100644 geg/os/glog/glog_stdout.go diff --git a/g/encoding/gjson/gjson_api.go b/g/encoding/gjson/gjson_api.go index 028478b8e..25db8a035 100644 --- a/g/encoding/gjson/gjson_api.go +++ b/g/encoding/gjson/gjson_api.go @@ -64,29 +64,44 @@ func (j *Json) GetMap(pattern string, def...interface{}) map[string]interface{} } // GetJson gets the value by specified , -// and converts it to a Json object. +// and converts it to a un-concurrent-safe Json object. func (j *Json) GetJson(pattern string, def...interface{}) *Json { result := j.Get(pattern, def...) if result != nil { - return New(result) + return New(result, true) } return nil } // GetJsons gets the value by specified , -// and converts it to a slice of Json object. +// and converts it to a slice of un-concurrent-safe Json object. func (j *Json) GetJsons(pattern string, def...interface{}) []*Json { array := j.GetArray(pattern, def...) if len(array) > 0 { - jsons := make([]*Json, len(array)) + jsonSlice := make([]*Json, len(array)) for i := 0; i < len(array); i++ { - jsons[i] = New(array[i], !j.mu.IsSafe()) + jsonSlice[i] = New(array[i], true) } - return jsons + return jsonSlice } return nil } +// GetJsonMap gets the value by specified , +// and converts it to a map of un-concurrent-safe Json object. +func (j *Json) GetJsonMap(pattern string, def...interface{}) map[string]*Json { + m := j.GetMap(pattern, def...) + if len(m) > 0 { + jsonMap := make(map[string]*Json, len(m)) + for k, v := range m { + jsonMap[k] = New(v, true) + } + return jsonMap + } + return nil +} + + // GetArray gets the value by specified , // and converts it to a slice of []interface{}. func (j *Json) GetArray(pattern string, def...interface{}) []interface{} { diff --git a/g/encoding/gjson/gjson_z_unit_basic_test.go b/g/encoding/gjson/gjson_z_unit_basic_test.go index b1d3430f1..0a43eee58 100644 --- a/g/encoding/gjson/gjson_z_unit_basic_test.go +++ b/g/encoding/gjson/gjson_z_unit_basic_test.go @@ -264,3 +264,39 @@ func TestJson_ToJson(t *testing.T) { }) } +func TestJson_Default(t *testing.T) { + gtest.Case(t, func() { + j := gjson.New(nil) + gtest.AssertEQ(j.Get("no", 100), 100) + gtest.AssertEQ(j.GetString("no", 100), "100") + gtest.AssertEQ(j.GetBool("no", "on"), true) + gtest.AssertEQ(j.GetInt("no", 100), 100) + gtest.AssertEQ(j.GetInt8("no", 100), int8(100)) + gtest.AssertEQ(j.GetInt16("no", 100), int16(100)) + gtest.AssertEQ(j.GetInt32("no", 100), int32(100)) + gtest.AssertEQ(j.GetInt64("no", 100), int64(100)) + gtest.AssertEQ(j.GetUint("no", 100), uint(100)) + gtest.AssertEQ(j.GetUint8("no", 100), uint8(100)) + gtest.AssertEQ(j.GetUint16("no", 100), uint16(100)) + gtest.AssertEQ(j.GetUint32("no", 100), uint32(100)) + gtest.AssertEQ(j.GetUint64("no", 100), uint64(100)) + gtest.AssertEQ(j.GetFloat32("no", 123.456), float32(123.456)) + gtest.AssertEQ(j.GetFloat64("no", 123.456), float64(123.456)) + gtest.AssertEQ(j.GetArray("no", g.Slice{1,2,3}), g.Slice{1,2,3}) + gtest.AssertEQ(j.GetInts("no", g.Slice{1,2,3}), g.SliceInt{1,2,3}) + gtest.AssertEQ(j.GetFloats("no", g.Slice{1,2,3}), []float64{1,2,3}) + gtest.AssertEQ(j.GetMap("no", g.Map{"k":"v"}), g.Map{"k":"v"}) + gtest.AssertEQ(j.GetVar("no", 123.456).Float64(), float64(123.456)) + gtest.AssertEQ(j.GetJson("no", g.Map{"k":"v"}).Get("k"), "v") + gtest.AssertEQ(j.GetJsons("no", g.Slice{ + g.Map{"k1":"v1"}, + g.Map{"k2":"v2"}, + g.Map{"k3":"v3"}, + })[0].Get("k1"), "v1") + gtest.AssertEQ(j.GetJsonMap("no", g.Map{ + "m1" : g.Map{"k1":"v1"}, + "m2" : g.Map{"k2":"v2"}, + })["m2"].Get("k2"), "v2") + }) +} + diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go index 5619d2e7c..6f7fbad39 100644 --- a/g/net/ghttp/ghttp_server_config.go +++ b/g/net/ghttp/ghttp_server_config.go @@ -76,7 +76,7 @@ type ServerConfig struct { // 日志配置 LogPath string // 存放日志的目录路径(默认为空,表示不写文件) LogHandler LogHandler // 自定义日志处理回调方法(默认为空) - LogStdPrint bool // 是否打印日志到终端(默认开启) + LogStdout bool // 是否打印日志到终端(默认开启) ErrorLogEnabled bool // 是否开启error log(默认开启) AccessLogEnabled bool // 是否开启access log(默认关闭) @@ -111,7 +111,7 @@ var defaultServerConfig = ServerConfig { SessionMaxAge : gDEFAULT_SESSION_MAX_AGE, SessionIdName : gDEFAULT_SESSION_ID_NAME, - LogStdPrint : true, + LogStdout : true, ErrorLogEnabled : true, AccessLogEnabled : false, GzipContentTypes : defaultGzipContentTypes, diff --git a/g/net/ghttp/ghttp_server_config_logger.go b/g/net/ghttp/ghttp_server_config_logger.go index 5482de3fe..fce013d2d 100644 --- a/g/net/ghttp/ghttp_server_config_logger.go +++ b/g/net/ghttp/ghttp_server_config_logger.go @@ -28,12 +28,12 @@ func (s *Server)SetLogPath(path string) { // 设置日志内容是否输出到终端,默认情况下只有错误日志才会自动输出到终端。 // 如果需要输出请求日志到终端,默认情况下使用SetAccessLogEnabled方法开启请求日志特性即可。 -func (s *Server)SetLogStdPrint(enabled bool) { +func (s *Server)SetLogStdout(enabled bool) { if s.Status() == SERVER_STATUS_RUNNING { glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR) return } - s.config.LogStdPrint = enabled + s.config.LogStdout = enabled } // 设置是否开启access log日志功能 diff --git a/g/net/ghttp/ghttp_server_log.go b/g/net/ghttp/ghttp_server_log.go index 3801abf4f..2f4b3158d 100644 --- a/g/net/ghttp/ghttp_server_log.go +++ b/g/net/ghttp/ghttp_server_log.go @@ -32,7 +32,7 @@ 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").Backtrace(false, 2).StdPrint(s.config.LogStdPrint).Println(content) + s.logger.Cat("access").Backtrace(false, 2).Stdout(s.config.LogStdout).Println(content) } // 处理服务错误信息,主要是panic,http请求的status由access log进行管理 @@ -60,5 +60,5 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) { content += fmt.Sprintf(` %.3f`, float64(gtime.Microsecond() - r.EnterTime)/1000) } content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent()) - s.logger.Cat("error").Backtrace(true, 2).StdPrint(s.config.LogStdPrint).Error(content) + s.logger.Cat("error").Backtrace(true, 2).Stdout(s.config.LogStdout).Error(content) } diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index bdd08f641..adf1af58a 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -83,8 +83,19 @@ func SetDebug(debug bool) { } // SetStdPrint sets whether ouptput the logging contents to stdout, which is false in default. -func SetStdPrint(open bool) { - logger.SetStdPrint(open) +func SetStdoutPrint(enabled bool) { + logger.SetStdoutPrint(enabled) +} + +// SetHeaderPrint sets whether output header of the logging contents, which is true in default. +func SetHeaderPrint(enabled bool) { + logger.SetHeaderPrint(enabled) +} + +// SetPrefix sets prefix string for every logging content. +// Prefix is part of header, which means if header output is shut, no prefix will be output. +func SetPrefix(prefix string) { + logger.SetPrefix(prefix) } // GetPath returns the logging directory path for file logging. @@ -148,8 +159,8 @@ func Backtrace(enabled bool, skip...int) *Logger { // StdPrint is a chaining function, // which enables/disables stdout for the current logging content output. -func StdPrint(enabled bool) *Logger { - return logger.StdPrint(enabled) +func Stdout(enabled bool) *Logger { + return logger.Stdout(enabled) } // Header is a chaining function, diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index c75333071..976be26eb 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -9,9 +9,9 @@ package glog import ( + "bytes" "errors" "fmt" - "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/os/gfpool" "github.com/gogf/gf/g/os/gtime" @@ -20,21 +20,21 @@ import ( "os" "runtime" "strings" - "sync" "time" ) type Logger struct { - mu sync.RWMutex - pr *Logger // Parent logger. - writer io.Writer // Customized io.Writer. - path *gtype.String // Logging directory path. - file *gtype.String // Format for logging file. - level *gtype.Int // Output level. - btSkip *gtype.Int // Skip count for backtrace. - btStatus *gtype.Int // Backtrace status(1: enabled - default; 0: disabled) - printHeader *gtype.Bool // Print header or not(true in default). - alsoStdPrint *gtype.Bool // Output to stdout or not(true in default). + parent *Logger // Parent logger. + writer io.Writer // Customized io.Writer. + flags int // Extra flags for logging output features. + path string // Logging directory path. + file string // Format for logging file. + level int // Output level. + prefix string // Prefix string for every logging content. + btSkip int // Skip count for backtrace. + btStatus int // Backtrace status(1: enabled - default; 0: disabled) + headerPrint bool // Print header or not(true in default). + stdoutPrint bool // Output to stdout or not(true in default). } const ( @@ -44,11 +44,20 @@ const ( gDEFAULT_FPOOL_EXPIRE = 60000 ) +const ( + F_NO_HEADER = 1 << iota // No header print. + F_NO_STDOUT // No stdout print. + F_FILE_LONG // Print full file name and line number: /a/b/c/d.go:23. + F_FILE_SHORT // Print final file name element and line number: d.go:23. overrides F_FILE_LONG. + F_TIME_DATE // Print the date in the local time zone: 2009-01-23. + F_TIME_TIME // Print the time in the local time zone: 01:23:23. + F_TIME_MILLI // Print the time with milliseconds in the local time zone: 01:23:23.675. + F_TIME_STD = F_TIME_DATE | F_TIME_MILLI +) + var ( // Default line break. - ln = "\n" - // Mutex to ensure log output sequence. - stdMu = sync.RWMutex{} + ln = "\n" ) func init() { @@ -61,14 +70,13 @@ func init() { // New creates and returns a custom logger. func New() *Logger { logger := &Logger { - path : gtype.NewString(), - file : gtype.NewString(gDEFAULT_FILE_FORMAT), - level : gtype.NewInt(defaultLevel.Val()), - btSkip : gtype.NewInt(), - btStatus : gtype.NewInt(1), - printHeader : gtype.NewBool(true), - alsoStdPrint : gtype.NewBool(true), + file : gDEFAULT_FILE_FORMAT, + level : defaultLevel.Val(), + btStatus : 1, + headerPrint : true, + stdoutPrint : true, } + // Default writer logger.writer = &Writer { logger : logger, } @@ -78,15 +86,18 @@ func New() *Logger { // Clone returns a new logger, which is the clone the current logger. func (l *Logger) Clone() *Logger { logger := &Logger { - pr : l, - path : l.path.Clone(), - file : l.file.Clone(), - level : l.level.Clone(), - btSkip : l.btSkip.Clone(), - btStatus : l.btStatus.Clone(), - printHeader : l.printHeader.Clone(), - alsoStdPrint : l.alsoStdPrint.Clone(), + parent : l, + path : l.path, + file : l.file, + level : l.level, + flags : l.flags, + prefix : l.prefix, + btSkip : l.btSkip, + btStatus : l.btStatus, + headerPrint : l.headerPrint, + stdoutPrint : l.stdoutPrint, } + // Default writer logger.writer = &Writer { logger : logger, } @@ -95,36 +106,46 @@ func (l *Logger) Clone() *Logger { // SetLevel sets the logging level. func (l *Logger) SetLevel(level int) { - l.level.Set(level) + l.level = level } // GetLevel returns the logging level value. func (l *Logger) GetLevel() int { - return l.level.Val() + return l.level } // SetDebug enables/disables the debug level for logger. // The debug level is enabled in default. func (l *Logger) SetDebug(debug bool) { if debug { - l.level.Set(l.level.Val() | LEVEL_DEBU) + l.level = l.level | LEVEL_DEBU } else { - l.level.Set(l.level.Val() & ^LEVEL_DEBU) + l.level = l.level & ^LEVEL_DEBU } } +// SetFlags sets extra flags for logging output features +func (l *Logger) SetFlags(flags int) { + l.flags = flags +} + +// GetFlags returns the flags of logger. +func (l *Logger) GetFlags() int { + return l.flags +} + // SetBacktrace enables/disables the backtrace feature in failure logging outputs. func (l *Logger) SetBacktrace(enabled bool) { if enabled { - l.btStatus.Set(1) + l.btStatus = 1 } else { - l.btStatus.Set(0) + l.btStatus = 0 } } // SetBacktraceSkip sets the backtrace offset from the end point. func (l *Logger) SetBacktraceSkip(skip int) { - l.btSkip.Set(skip) + l.btSkip = skip } // SetWriter sets the customized logging for logging. @@ -132,26 +153,21 @@ func (l *Logger) SetBacktraceSkip(skip int) { // Developer can use customized logging to redirect logging output to another service, // eg: kafka, mysql, mongodb, etc. func (l *Logger) SetWriter(writer io.Writer) { - l.mu.Lock() l.writer = writer - l.mu.Unlock() } // GetWriter returns the customized writer object, which implements the io.Writer interface. // It returns a default writer if no customized writer set. func (l *Logger) GetWriter() io.Writer { - l.mu.RLock() - r := l.writer - l.mu.RUnlock() - return r + return l.writer } // getFilePointer returns the file pinter for file logging. // It returns nil if file logging is disabled, or file opening fails. func (l *Logger) getFilePointer() *gfpool.File { - if path := l.path.Val(); path != "" { + if path := l.path; path != "" { // Content containing "{}" in the file name is formatted using gtime - file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file.Val(), func(s string) string { + file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file, func(s string) string { return gtime.Now().Format(strings.Trim(s, "{}")) }) // Create path if it does not exist。 @@ -182,33 +198,44 @@ func (l *Logger) SetPath(path string) error { return err } } - l.path.Set(strings.TrimRight(path, gfile.Separator)) + l.path = strings.TrimRight(path, gfile.Separator) return nil } // GetPath returns the logging directory path for file logging. // It returns empty string if no directory path set. func (l *Logger) GetPath() string { - return l.path.Val() + return l.path } // SetFile sets the file name for file logging. // Datetime pattern can be used in , eg: access-{Ymd}.log. // The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log func (l *Logger) SetFile(pattern string) { - l.file.Set(pattern) + l.file = pattern } -// SetStdPrint sets whether output the logging contents to stdout, which is false in default. -func (l *Logger) SetStdPrint(enabled bool) { - l.alsoStdPrint.Set(enabled) +// SetStdPrint sets whether output the logging contents to stdout, which is true in default. +func (l *Logger) SetStdoutPrint(enabled bool) { + l.stdoutPrint = enabled +} + +// SetHeaderPrint sets whether output header of the logging contents, which is true in default. +func (l *Logger) SetHeaderPrint(enabled bool) { + l.headerPrint = enabled +} + +// SetPrefix sets prefix string for every logging content. +// Prefix is part of header, which means if header output is shut, no prefix will be output. +func (l *Logger) SetPrefix(prefix string) { + l.prefix = prefix } // print prints to defined writer, logging file or passed . // It internally uses memory lock for file logging to ensure logging sequence. func (l *Logger) print(std io.Writer, s string) { // Customized writer has the most high priority. - if l.printHeader.Val() { + if l.headerPrint { s = l.format(s) } writer := l.GetWriter() @@ -220,31 +247,26 @@ func (l *Logger) print(std io.Writer, s string) { } } // Also output to stdout? - if l.alsoStdPrint.Val() { - l.doStdLockPrint(std, s) + if l.stdoutPrint { + if _, err := std.Write([]byte(s)); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } } } else { - l.doStdLockPrint(writer, s) + if _, err := std.Write([]byte(s)); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } } } -// doStdLockPrint prints to concurrent-safely. -func (l *Logger) doStdLockPrint(std io.Writer, s string) { - stdMu.Lock() - if _, err := std.Write([]byte(s)); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - stdMu.Unlock() -} - -// stdPrint prints content without backtrace. -func (l *Logger) stdPrint(s string) { +// printStd prints content without backtrace. +func (l *Logger) printStd(s string) { l.print(os.Stdout, s) } -// stdPrint prints content with backtrace check. -func (l *Logger) errPrint(s string) { - if l.btStatus.Val() == 1 { +// printStd prints content with backtrace check. +func (l *Logger) printErr(s string) { + if l.btStatus == 1 { s = l.appendBacktrace(s) } // In matter of sequence, do not use stderr here, but use the same stdout. @@ -296,7 +318,7 @@ func (l *Logger) GetBacktrace(skip...int) string { } // 从业务文件开始位置根据自定义的skip开始backtrace goRoot := runtime.GOROOT() - for i := from + customSkip + l.btSkip.Val(); i < 10000; i++ { + for i := from + customSkip + l.btSkip; i < 10000; i++ { if _, file, cline, ok := runtime.Caller(i); ok && file != "" { // 不打印出go源码路径及glog包文件路径,日志打印必须从业务源码文件开始,且从glog包文件开始检索 if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { @@ -310,171 +332,211 @@ func (l *Logger) GetBacktrace(skip...int) string { return backtrace } -func (l *Logger) format(s string) string { - return time.Now().Format("2006-01-02 15:04:05.000 ") + s +func (l *Logger) format(content string) string { + buffer := bytes.NewBuffer(nil) + timeFormat := "" + if l.flags & F_TIME_DATE > 0 { + timeFormat += "2006-01-02 " + } + if l.flags & F_TIME_TIME > 0 { + timeFormat += "15:04:05 " + } + if l.flags & F_TIME_MILLI > 0 { + timeFormat += "15:04:05.000 " + } + if len(timeFormat) > 0 { + buffer.WriteString(time.Now().Format(timeFormat)) + } + callerPath := "" + if l.flags & F_FILE_LONG > 0 { + callerPath = l.getLongFile() + ": " + } + if l.flags & F_FILE_SHORT > 0 { + callerPath = gfile.Basename(l.getLongFile()) + ": " + } + if len(callerPath) > 0 { + buffer.WriteString(callerPath) + } + if len(l.prefix) > 0 { + buffer.WriteString(l.prefix + " ") + } + buffer.WriteString(content) + return buffer.String() +} + +// getLongFile returns the absolute file path of the caller. +func (l *Logger) getLongFile() string { + for i := 0; i < 100; i++ { + if _, file, line, ok := runtime.Caller(i); ok { + if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { + return fmt.Sprintf(`%s:%d`, file, line) + } + } + } + return "" } func (l *Logger) Print(v ...interface{}) { - l.stdPrint(fmt.Sprintln(v...)) + l.printStd(fmt.Sprintln(v...)) } func (l *Logger) Printf(format string, v ...interface{}) { - l.stdPrint(fmt.Sprintf(format, v...)) + l.printStd(fmt.Sprintf(format, v...)) } func (l *Logger) Println(v ...interface{}) { - l.stdPrint(fmt.Sprintln(v...)) + l.printStd(fmt.Sprintln(v...)) } func (l *Logger) Printfln(format string, v ...interface{}) { - l.stdPrint(fmt.Sprintf(format + ln, v...)) + l.printStd(fmt.Sprintf(format + ln, v...)) } // Fatal prints the logging content with [FATA] header and newline, then exit the current process. func (l *Logger) Fatal(v ...interface{}) { - l.errPrint("[FATA] " + fmt.Sprintln(v...)) + l.printErr("[FATA] " + fmt.Sprintln(v...)) os.Exit(1) } // Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. func (l *Logger) Fatalf(format string, v ...interface{}) { - l.errPrint("[FATA] " + fmt.Sprintf(format, v...)) + l.printErr("[FATA] " + fmt.Sprintf(format, v...)) os.Exit(1) } // Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. func (l *Logger) Fatalfln(format string, v ...interface{}) { - l.errPrint("[FATA] " + fmt.Sprintf(format + ln, v...)) + l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) os.Exit(1) } func (l *Logger) Panic(v ...interface{}) { s := fmt.Sprintln(v...) - l.errPrint("[PANI] " + s) + l.printErr("[PANI] " + s) panic(s) } func (l *Logger) Panicf(format string, v ...interface{}) { s := fmt.Sprintf(format, v...) - l.errPrint("[PANI] " + s) + l.printErr("[PANI] " + s) panic(s) } func (l *Logger) Panicfln(format string, v ...interface{}) { s := fmt.Sprintf(format + ln, v...) - l.errPrint("[PANI] " + s) + l.printErr("[PANI] " + s) panic(s) } func (l *Logger) Info(v ...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.stdPrint("[INFO] " + fmt.Sprintln(v...)) + l.printStd("[INFO] " + fmt.Sprintln(v...)) } } func (l *Logger) Infof(format string, v ...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.stdPrint("[INFO] " + fmt.Sprintf(format, v...)) + l.printStd("[INFO] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Infofln(format string, v ...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.stdPrint("[INFO] " + fmt.Sprintf(format, v...) + ln) + l.printStd("[INFO] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Debug(v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.stdPrint("[DEBU] " + fmt.Sprintln(v...)) + l.printStd("[DEBU] " + fmt.Sprintln(v...)) } } func (l *Logger) Debugf(format string, v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...)) + l.printStd("[DEBU] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Debugfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...) + ln) + l.printStd("[DEBU] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Notice(v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.errPrint("[NOTI] " + fmt.Sprintln(v...)) + l.printErr("[NOTI] " + fmt.Sprintln(v...)) } } func (l *Logger) Noticef(format string, v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.errPrint("[NOTI] " + fmt.Sprintf(format, v...)) + l.printErr("[NOTI] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Noticefln(format string, v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.errPrint("[NOTI] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[NOTI] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Warning(v ...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.errPrint("[WARN] " + fmt.Sprintln(v...)) + l.printErr("[WARN] " + fmt.Sprintln(v...)) } } func (l *Logger) Warningf(format string, v ...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.errPrint("[WARN] " + fmt.Sprintf(format, v...)) + l.printErr("[WARN] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Warningfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.errPrint("[WARN] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[WARN] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Error(v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.errPrint("[ERRO] " + fmt.Sprintln(v...)) + l.printErr("[ERRO] " + fmt.Sprintln(v...)) } } func (l *Logger) Errorf(format string, v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.errPrint("[ERRO] " + fmt.Sprintf(format, v...)) + l.printErr("[ERRO] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Errorfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.errPrint("[ERRO] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[ERRO] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Critical(v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.errPrint("[CRIT] " + fmt.Sprintln(v...)) + l.printErr("[CRIT] " + fmt.Sprintln(v...)) } } func (l *Logger) Criticalf(format string, v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.errPrint("[CRIT] " + fmt.Sprintf(format, v...)) + l.printErr("[CRIT] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Criticalfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.errPrint("[CRIT] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[CRIT] " + fmt.Sprintf(format, v...) + ln) } } // checkLevel checks whether the given could be output. func (l *Logger) checkLevel(level int) bool { - return l.level.Val() & level > 0 + return l.level & level > 0 } \ No newline at end of file diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index b478a6c76..d02eca9e2 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -15,7 +15,7 @@ import ( // which redirects current logging content output to the specified . func (l *Logger) To(writer io.Writer) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -28,7 +28,7 @@ func (l *Logger) To(writer io.Writer) *Logger { // 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 { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -44,14 +44,13 @@ func (l *Logger) Path(path string) *Logger { // Param can be hierarchical, eg: module/user. func (l *Logger) Cat(category string) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l } - path := l.path.Val() - if path != "" { - logger.SetPath(path + gfile.Separator + category) + if logger.path != "" { + logger.SetPath(logger.path + gfile.Separator + category) } return logger } @@ -60,7 +59,7 @@ func (l *Logger) Cat(category string) *Logger { // which sets file name for the current logging content output. func (l *Logger) File(file string) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -73,7 +72,7 @@ func (l *Logger) File(file string) *Logger { // which sets logging level for the current logging content output. func (l *Logger) Level(level int) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -86,7 +85,7 @@ func (l *Logger) Level(level int) *Logger { // which sets backtrace options for the current logging content output . func (l *Logger) Backtrace(enabled bool, skip...int) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -98,28 +97,34 @@ func (l *Logger) Backtrace(enabled bool, skip...int) *Logger { return logger } -// StdPrint is a chaining function, +// Stdout is a chaining function, // which enables/disables stdout for the current logging content output. +func (l *Logger) Stdout(enabled bool) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + logger.stdoutPrint = enabled + return logger +} + +// See Stdout. +// Deprecated. func (l *Logger) StdPrint(enabled bool) *Logger { - logger := (*Logger)(nil) - if l.pr == nil { - logger = l.Clone() - } else { - logger = l - } - logger.SetStdPrint(enabled) - return logger + return l.Stdout(enabled) } // Header is a chaining function, // which enables/disables log header for the current logging content output. func (l *Logger) Header(enabled bool) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l } - logger.printHeader.Set(enabled) + logger.SetHeaderPrint(enabled) return logger } \ No newline at end of file diff --git a/geg/os/glog/glog_stdout.go b/geg/os/glog/glog_stdout.go new file mode 100644 index 000000000..3935519ff --- /dev/null +++ b/geg/os/glog/glog_stdout.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" + "sync" +) + +func main() { + wg := sync.WaitGroup{} + c := make(chan struct{}) + wg.Add(3000) + for i := 0; i < 3000; i++ { + go func() { + <-c + glog.Println("abcdefghijklmnopqrstuvwxyz1234567890") + wg.Done() + }() + } + close(c) + wg.Wait() +} diff --git a/geg/other/test.go b/geg/other/test.go index 47632c222..4f5b4d622 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -3,12 +3,14 @@ package main import ( "fmt" "github.com/gogf/gf/g" - "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/util/gconv" + "log" + "os" ) func main() { - fmt.Println(gfile.Dir("/")) + var mylog = log.New(os.Stdout, "[Api] ", log.LstdFlags|log.Lshortfile) + mylog.Println(123) return a := []int{1,2,3} fmt.Println(a[:0]) From 0839ea385f23a3b5c5414b93d1b0d39035581213 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 21 May 2019 23:57:03 +0800 Subject: [PATCH 05/50] refract glog to improve performance; --- g/os/glog/glog.go | 163 ------------------------------ g/os/glog/glog_api.go | 122 ++++++++++++++++++++++ g/os/glog/glog_chaining.go | 66 ++++++++++++ g/os/glog/glog_logger.go | 18 ++-- g/os/glog/glog_logger_chaining.go | 18 ++++ geg/os/glog/glog_flags.go | 15 +++ geg/os/glog/glog_line.go | 10 ++ geg/os/glog/glog_prefix.go | 12 +++ 8 files changed, 252 insertions(+), 172 deletions(-) create mode 100644 g/os/glog/glog_api.go create mode 100644 g/os/glog/glog_chaining.go create mode 100644 geg/os/glog/glog_flags.go create mode 100644 geg/os/glog/glog_line.go create mode 100644 geg/os/glog/glog_prefix.go diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index adf1af58a..7059fff25 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -120,166 +120,3 @@ func GetBacktrace(skip...int) string { func SetBacktrace(enabled bool) { logger.SetBacktrace(enabled) } - -// To is a chaining function, -// which redirects current logging content output to the sepecified . -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. -func Cat(category string) *Logger { - return logger.Cat(category) -} - -// File is a chaining function, -// which sets file name for the current logging content output. -func File(pattern string) *Logger { - return logger.File(pattern) -} - -// Level is a chaining function, -// which sets logging level for the current logging content output. -func Level(level int) *Logger { - return logger.Level(level) -} - -// Backtrace is a chaining function, -// which sets backtrace options for the current logging content output . -func Backtrace(enabled bool, skip...int) *Logger { - return logger.Backtrace(enabled, skip...) -} - -// StdPrint is a chaining function, -// which enables/disables stdout for the current logging content output. -func Stdout(enabled bool) *Logger { - return logger.Stdout(enabled) -} - -// Header is a chaining function, -// which enables/disables log header for the current logging content output. -func Header(enabled bool) *Logger { - return logger.Header(enabled) -} - -func Print(v ...interface{}) { - logger.Print(v ...) -} - -func Printf(format string, v ...interface{}) { - logger.Printf(format, v ...) -} - -func Println(v ...interface{}) { - logger.Println(v ...) -} - -func Printfln(format string, v ...interface{}) { - logger.Printfln(format, v ...) -} - -// Fatal prints the logging content with [FATA] header and newline, then exit the current process. -func Fatal(v ...interface{}) { - logger.Fatal(v ...) -} - -// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. -func Fatalf(format string, v ...interface{}) { - logger.Fatalf(format, v ...) -} - -// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. -func Fatalfln(format string, v ...interface{}) { - logger.Fatalfln(format, v ...) -} - -func Panic(v ...interface{}) { - logger.Panic(v ...) -} - -func Panicf(format string, v ...interface{}) { - logger.Panicf(format, v ...) -} - -func Panicfln(format string, v ...interface{}) { - logger.Panicfln(format, v ...) -} - -func Info(v ...interface{}) { - logger.Info(v...) -} - -func Debug(v ...interface{}) { - logger.Debug(v...) -} - -func Notice(v ...interface{}) { - logger.Notice(v...) -} - -func Warning(v ...interface{}) { - logger.Warning(v...) -} - -func Error(v ...interface{}) { - logger.Error(v...) -} - -func Critical(v ...interface{}) { - logger.Critical(v...) -} - -func Infof(format string, v ...interface{}) { - logger.Infof(format, v...) -} - -func Debugf(format string, v ...interface{}) { - logger.Debugf(format, v...) -} - -func Noticef(format string, v ...interface{}) { - logger.Noticef(format, v...) -} - -func Warningf(format string, v ...interface{}) { - logger.Warningf(format, v...) -} - -func Errorf(format string, v ...interface{}) { - logger.Errorf(format, v...) -} - -func Criticalf(format string, v ...interface{}) { - logger.Criticalf(format, v...) -} - -func Infofln(format string, v ...interface{}) { - logger.Infofln(format, v...) -} - -func Debugfln(format string, v ...interface{}) { - logger.Debugfln(format, v...) -} - -func Noticefln(format string, v ...interface{}) { - logger.Noticefln(format, v...) -} - -func Warningfln(format string, v ...interface{}) { - logger.Warningfln(format, v...) -} - -func Errorfln(format string, v ...interface{}) { - logger.Errorfln(format, v...) -} - -func Criticalfln(format string, v ...interface{}) { - logger.Criticalfln(format, v...) -} diff --git a/g/os/glog/glog_api.go b/g/os/glog/glog_api.go new file mode 100644 index 000000000..fa932bc07 --- /dev/null +++ b/g/os/glog/glog_api.go @@ -0,0 +1,122 @@ +// 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. + +package glog + +func Print(v ...interface{}) { + logger.Print(v ...) +} + +func Printf(format string, v ...interface{}) { + logger.Printf(format, v ...) +} + +func Println(v ...interface{}) { + logger.Println(v ...) +} + +func Printfln(format string, v ...interface{}) { + logger.Printfln(format, v ...) +} + +// Fatal prints the logging content with [FATA] header and newline, then exit the current process. +func Fatal(v ...interface{}) { + logger.Fatal(v ...) +} + +// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. +func Fatalf(format string, v ...interface{}) { + logger.Fatalf(format, v ...) +} + +// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. +func Fatalfln(format string, v ...interface{}) { + logger.Fatalfln(format, v ...) +} + +func Panic(v ...interface{}) { + logger.Panic(v ...) +} + +func Panicf(format string, v ...interface{}) { + logger.Panicf(format, v ...) +} + +func Panicfln(format string, v ...interface{}) { + logger.Panicfln(format, v ...) +} + +func Info(v ...interface{}) { + logger.Info(v...) +} + +func Debug(v ...interface{}) { + logger.Debug(v...) +} + +func Notice(v ...interface{}) { + logger.Notice(v...) +} + +func Warning(v ...interface{}) { + logger.Warning(v...) +} + +func Error(v ...interface{}) { + logger.Error(v...) +} + +func Critical(v ...interface{}) { + logger.Critical(v...) +} + +func Infof(format string, v ...interface{}) { + logger.Infof(format, v...) +} + +func Debugf(format string, v ...interface{}) { + logger.Debugf(format, v...) +} + +func Noticef(format string, v ...interface{}) { + logger.Noticef(format, v...) +} + +func Warningf(format string, v ...interface{}) { + logger.Warningf(format, v...) +} + +func Errorf(format string, v ...interface{}) { + logger.Errorf(format, v...) +} + +func Criticalf(format string, v ...interface{}) { + logger.Criticalf(format, v...) +} + +func Infofln(format string, v ...interface{}) { + logger.Infofln(format, v...) +} + +func Debugfln(format string, v ...interface{}) { + logger.Debugfln(format, v...) +} + +func Noticefln(format string, v ...interface{}) { + logger.Noticefln(format, v...) +} + +func Warningfln(format string, v ...interface{}) { + logger.Warningfln(format, v...) +} + +func Errorfln(format string, v ...interface{}) { + logger.Errorfln(format, v...) +} + +func Criticalfln(format string, v ...interface{}) { + logger.Criticalfln(format, v...) +} diff --git a/g/os/glog/glog_chaining.go b/g/os/glog/glog_chaining.go new file mode 100644 index 000000000..231af31fa --- /dev/null +++ b/g/os/glog/glog_chaining.go @@ -0,0 +1,66 @@ +// 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. + +package glog + +import ( + "io" +) + +// To is a chaining function, +// which redirects current logging content output to the sepecified . +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. +func Cat(category string) *Logger { + return logger.Cat(category) +} + +// File is a chaining function, +// which sets file name for the current logging content output. +func File(pattern string) *Logger { + return logger.File(pattern) +} + +// Level is a chaining function, +// which sets logging level for the current logging content output. +func Level(level int) *Logger { + return logger.Level(level) +} + +// Backtrace is a chaining function, +// which sets backtrace options for the current logging content output . +func Backtrace(enabled bool, skip...int) *Logger { + return logger.Backtrace(enabled, skip...) +} + +// StdPrint is a chaining function, +// which enables/disables stdout for the current logging content output. +func Stdout(enabled bool) *Logger { + return logger.Stdout(enabled) +} + +// Header is a chaining function, +// which enables/disables log header for the current logging content output. +func Header(enabled bool) *Logger { + return logger.Header(enabled) +} + +// Line is a chaining function, +// which enables/disables printing its caller file along with its line number. +// The param specified whether print the long absolute file path, eg: /a/b/c/d.go:23. +func Line(long...bool) *Logger { + return logger.Line(long...) +} diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 976be26eb..2c3d4d60b 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -45,13 +45,11 @@ const ( ) const ( - F_NO_HEADER = 1 << iota // No header print. - F_NO_STDOUT // No stdout print. - F_FILE_LONG // Print full file name and line number: /a/b/c/d.go:23. - F_FILE_SHORT // Print final file name element and line number: d.go:23. overrides F_FILE_LONG. - F_TIME_DATE // Print the date in the local time zone: 2009-01-23. - F_TIME_TIME // Print the time in the local time zone: 01:23:23. - F_TIME_MILLI // Print the time with milliseconds in the local time zone: 01:23:23.675. + F_FILE_LONG = 1 << iota // Print full file name and line number: /a/b/c/d.go:23. + F_FILE_SHORT // Print final file name element and line number: d.go:23. overrides F_FILE_LONG. + F_TIME_DATE // Print the date in the local time zone: 2009-01-23. + F_TIME_TIME // Print the time in the local time zone: 01:23:23. + F_TIME_MILLI // Print the time with milliseconds in the local time zone: 01:23:23.675. F_TIME_STD = F_TIME_DATE | F_TIME_MILLI ) @@ -71,6 +69,7 @@ func init() { func New() *Logger { logger := &Logger { file : gDEFAULT_FILE_FORMAT, + flags : F_TIME_STD, level : defaultLevel.Val(), btStatus : 1, headerPrint : true, @@ -124,7 +123,7 @@ func (l *Logger) SetDebug(debug bool) { } } -// SetFlags sets extra flags for logging output features +// SetFlags sets extra flags for logging output features. func (l *Logger) SetFlags(flags int) { l.flags = flags } @@ -332,6 +331,7 @@ func (l *Logger) GetBacktrace(skip...int) string { return backtrace } +// format formats the content according the flags. func (l *Logger) format(content string) string { buffer := bytes.NewBuffer(nil) timeFormat := "" @@ -364,7 +364,7 @@ func (l *Logger) format(content string) string { return buffer.String() } -// getLongFile returns the absolute file path of the caller. +// getLongFile returns the absolute file path along with its line number of the caller. func (l *Logger) getLongFile() string { for i := 0; i < 100; i++ { if _, file, line, ok := runtime.Caller(i); ok { diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index d02eca9e2..89f375e78 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -127,4 +127,22 @@ func (l *Logger) Header(enabled bool) *Logger { } logger.SetHeaderPrint(enabled) return logger +} + +// Line is a chaining function, +// which enables/disables printing its caller file along with its line number. +// The param specified whether print the long absolute file path, eg: /a/b/c/d.go:23. +func (l *Logger) Line(long...bool) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + if len(long) > 0 && long[0] { + logger.flags |= F_FILE_LONG + } else { + logger.flags |= F_FILE_SHORT + } + return logger } \ No newline at end of file diff --git a/geg/os/glog/glog_flags.go b/geg/os/glog/glog_flags.go new file mode 100644 index 000000000..eb94230a7 --- /dev/null +++ b/geg/os/glog/glog_flags.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func main() { + l := glog.New() + l.SetFlags(glog.F_TIME_TIME|glog.F_FILE_SHORT) + l.Println("123") + l.SetFlags(glog.F_TIME_MILLI|glog.F_FILE_LONG) + l.Println("123") + l.SetFlags(glog.F_TIME_STD|glog.F_FILE_LONG) + l.Println("123") +} diff --git a/geg/os/glog/glog_line.go b/geg/os/glog/glog_line.go new file mode 100644 index 000000000..c4771fd02 --- /dev/null +++ b/geg/os/glog/glog_line.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func main() { + glog.Line().Println("123") + glog.Line(true).Println("123") +} diff --git a/geg/os/glog/glog_prefix.go b/geg/os/glog/glog_prefix.go new file mode 100644 index 000000000..7ce5a3d61 --- /dev/null +++ b/geg/os/glog/glog_prefix.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func main() { + l := glog.New() + l.SetPrefix("[API]") + l.Println("hello world") + l.Error("error occurred") +} From 6c658813cd9a62b33039116bdbff5a4a7ae1b263 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 22 May 2019 09:19:21 +0800 Subject: [PATCH 06/50] refract glog to improve performance --- g/os/glog/glog.go | 37 ++--- g/os/glog/glog_logger.go | 180 ++----------------------- g/os/glog/glog_logger_api.go | 217 ++++++++++++++++++++++++++++++ g/os/glog/glog_logger_chaining.go | 5 +- 4 files changed, 252 insertions(+), 187 deletions(-) create mode 100644 g/os/glog/glog_logger_api.go diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index 7059fff25..a0a46cece 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -10,9 +10,8 @@ package glog import ( - "github.com/gogf/gf/g/container/gtype" - "github.com/gogf/gf/g/internal/cmdenv" - "io" + "github.com/gogf/gf/g/internal/cmdenv" + "io" ) const ( @@ -28,9 +27,6 @@ const ( ) var ( - // Default level for log - defaultLevel = gtype.NewInt(LEVEL_ALL) - // Default logger object, for package method usage logger = New() ) @@ -44,6 +40,12 @@ func SetPath(path string) { logger.SetPath(path) } +// GetPath returns the logging directory path for file logging. +// It returns empty string if no directory path set. +func GetPath() string { + return logger.GetPath() +} + // SetFile sets the file name for file logging. // Datetime pattern can be used in , eg: access-{Ymd}.log. // The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log @@ -54,7 +56,11 @@ func SetFile(pattern string) { // SetLevel sets the default logging level. func SetLevel(level int) { logger.SetLevel(level) - defaultLevel.Set(level) +} + +// GetLevel returns the default logging level value. +func GetLevel() int { + return logger.GetLevel() } // SetWriter sets the customized logging for logging. @@ -71,11 +77,6 @@ func GetWriter() io.Writer { return logger.GetWriter() } -// GetLevel returns the default logging level value. -func GetLevel() int { - return defaultLevel.Val() -} - // SetDebug enables/disables the debug level for default logger. // The debug level is enbaled in default. func SetDebug(debug bool) { @@ -98,10 +99,14 @@ func SetPrefix(prefix string) { logger.SetPrefix(prefix) } -// GetPath returns the logging directory path for file logging. -// It returns empty string if no directory path set. -func GetPath() string { - return logger.GetPath() +// SetFlags sets extra flags for logging output features. +func SetFlags(flags int) { + logger.SetFlags(flags) +} + +// GetFlags returns the flags of logger. +func GetFlags() int { + return logger.GetFlags() } // PrintBacktrace prints the caller backtrace, diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 2c3d4d60b..c7ce97c37 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -70,7 +70,7 @@ func New() *Logger { logger := &Logger { file : gDEFAULT_FILE_FORMAT, flags : F_TIME_STD, - level : defaultLevel.Val(), + level : LEVEL_ALL, btStatus : 1, headerPrint : true, stdoutPrint : true, @@ -176,8 +176,11 @@ func (l *Logger) getFilePointer() *gfpool.File { return nil } } - fpath := path + gfile.Separator + file - if fp, err := gfpool.Open(fpath, gDEFAULT_FILE_POOL_FLAGS, gDEFAULT_FPOOL_PERM, gDEFAULT_FPOOL_EXPIRE); err == nil { + if fp, err := gfpool.Open( + path + gfile.Separator + file, + gDEFAULT_FILE_POOL_FLAGS, + gDEFAULT_FPOOL_PERM, + gDEFAULT_FPOOL_EXPIRE); err == nil { return fp } else { fmt.Fprintln(os.Stderr, err) @@ -245,7 +248,7 @@ func (l *Logger) print(std io.Writer, s string) { fmt.Fprintln(os.Stderr, err.Error()) } } - // Also output to stdout? + // Allow output to stdout? if l.stdoutPrint { if _, err := std.Write([]byte(s)); err != nil { fmt.Fprintln(os.Stderr, err.Error()) @@ -334,6 +337,7 @@ func (l *Logger) GetBacktrace(skip...int) string { // format formats the content according the flags. func (l *Logger) format(content string) string { buffer := bytes.NewBuffer(nil) + // Time. timeFormat := "" if l.flags & F_TIME_DATE > 0 { timeFormat += "2006-01-02 " @@ -347,6 +351,7 @@ func (l *Logger) format(content string) string { if len(timeFormat) > 0 { buffer.WriteString(time.Now().Format(timeFormat)) } + // Caller path. callerPath := "" if l.flags & F_FILE_LONG > 0 { callerPath = l.getLongFile() + ": " @@ -357,9 +362,11 @@ func (l *Logger) format(content string) string { if len(callerPath) > 0 { buffer.WriteString(callerPath) } + // Prefix. if len(l.prefix) > 0 { buffer.WriteString(l.prefix + " ") } + // Content. buffer.WriteString(content) return buffer.String() } @@ -375,168 +382,3 @@ func (l *Logger) getLongFile() string { } return "" } - -func (l *Logger) Print(v ...interface{}) { - l.printStd(fmt.Sprintln(v...)) -} - -func (l *Logger) Printf(format string, v ...interface{}) { - l.printStd(fmt.Sprintf(format, v...)) -} - -func (l *Logger) Println(v ...interface{}) { - l.printStd(fmt.Sprintln(v...)) -} - -func (l *Logger) Printfln(format string, v ...interface{}) { - l.printStd(fmt.Sprintf(format + ln, v...)) -} - -// Fatal prints the logging content with [FATA] header and newline, then exit the current process. -func (l *Logger) Fatal(v ...interface{}) { - l.printErr("[FATA] " + fmt.Sprintln(v...)) - os.Exit(1) -} - -// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. -func (l *Logger) Fatalf(format string, v ...interface{}) { - l.printErr("[FATA] " + fmt.Sprintf(format, v...)) - os.Exit(1) -} - -// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. -func (l *Logger) Fatalfln(format string, v ...interface{}) { - l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) - os.Exit(1) -} - -func (l *Logger) Panic(v ...interface{}) { - s := fmt.Sprintln(v...) - l.printErr("[PANI] " + s) - panic(s) -} - -func (l *Logger) Panicf(format string, v ...interface{}) { - s := fmt.Sprintf(format, v...) - l.printErr("[PANI] " + s) - panic(s) -} - -func (l *Logger) Panicfln(format string, v ...interface{}) { - s := fmt.Sprintf(format + ln, v...) - l.printErr("[PANI] " + s) - panic(s) -} - -func (l *Logger) Info(v ...interface{}) { - if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Infof(format string, v ...interface{}) { - if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Infofln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Debug(v ...interface{}) { - if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Debugf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Debugfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Notice(v ...interface{}) { - if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Noticef(format string, v ...interface{}) { - if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Noticefln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Warning(v ...interface{}) { - if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Warningf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Warningfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Error(v ...interface{}) { - if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Errorf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Errorfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Critical(v ...interface{}) { - if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Criticalf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Criticalfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintf(format, v...) + ln) - } -} - -// checkLevel checks whether the given could be output. -func (l *Logger) checkLevel(level int) bool { - return l.level & level > 0 -} \ No newline at end of file diff --git a/g/os/glog/glog_logger_api.go b/g/os/glog/glog_logger_api.go new file mode 100644 index 000000000..92b880a94 --- /dev/null +++ b/g/os/glog/glog_logger_api.go @@ -0,0 +1,217 @@ +// 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. + +package glog + +import ( + "fmt" + "os" +) + +// Print prints with newline using fmt.Sprintln. +// The param can be multiple variables. +func (l *Logger) Print(v ...interface{}) { + l.printStd(fmt.Sprintln(v...)) +} + +// Printf prints with format using fmt.Sprintf. +// The param can be multiple variables. +func (l *Logger) Printf(format string, v ...interface{}) { + l.printStd(fmt.Sprintf(format, v...)) +} + +// See Print. +func (l *Logger) Println(v ...interface{}) { + l.Print(v...) +} + +// Printf prints with newline and format using fmt.Sprintf. +// The param can be multiple variables. +func (l *Logger) Printfln(format string, v ...interface{}) { + l.printStd(fmt.Sprintf(format + ln, v...)) +} + +// Fatal prints the logging content with [FATA] header and newline, then exit the current process. +func (l *Logger) Fatal(v ...interface{}) { + l.printErr("[FATA] " + fmt.Sprintln(v...)) + os.Exit(1) +} + +// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. +func (l *Logger) Fatalf(format string, v ...interface{}) { + l.printErr("[FATA] " + fmt.Sprintf(format, v...)) + os.Exit(1) +} + +// Fatalfln prints the logging content with [FATA] header, custom format and newline, then exit the current process. +func (l *Logger) Fatalfln(format string, v ...interface{}) { + l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) + os.Exit(1) +} + +// Panic prints the logging content with [PANI] header and newline, then panics. +func (l *Logger) Panic(v ...interface{}) { + s := fmt.Sprintln(v...) + l.printErr("[PANI] " + s) + panic(s) +} + +// Panicf prints the logging content with [PANI] header and custom format, then panics. +func (l *Logger) Panicf(format string, v ...interface{}) { + s := fmt.Sprintf(format, v...) + l.printErr("[PANI] " + s) + panic(s) +} + +// Panicfln prints the logging content with [PANI] header, newline and custom format, then panics. +func (l *Logger) Panicfln(format string, v ...interface{}) { + s := fmt.Sprintf(format + ln, v...) + l.printErr("[PANI] " + s) + panic(s) +} + +// Info prints the logging content with [INFO] header and newline. +func (l *Logger) Info(v ...interface{}) { + if l.checkLevel(LEVEL_INFO) { + l.printStd("[INFO] " + fmt.Sprintln(v...)) + } +} + +// Infof prints the logging content with [INFO] header and custom format. +func (l *Logger) Infof(format string, v ...interface{}) { + if l.checkLevel(LEVEL_INFO) { + l.printStd("[INFO] " + fmt.Sprintf(format, v...)) + } +} + +// Infofln prints the logging content with [INFO] header, newline and custom format. +func (l *Logger) Infofln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_INFO) { + l.printStd("[INFO] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Debug prints the logging content with [DEBU] header and newline. +func (l *Logger) Debug(v ...interface{}) { + if l.checkLevel(LEVEL_DEBU) { + l.printStd("[DEBU] " + fmt.Sprintln(v...)) + } +} + +// Debugf prints the logging content with [DEBU] header and custom format. +func (l *Logger) Debugf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_DEBU) { + l.printStd("[DEBU] " + fmt.Sprintf(format, v...)) + } +} + +// Debugfln prints the logging content with [DEBU] header, newline and custom format. +func (l *Logger) Debugfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_DEBU) { + l.printStd("[DEBU] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Notice prints the logging content with [NOTI] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Notice(v ...interface{}) { + if l.checkLevel(LEVEL_NOTI) { + l.printErr("[NOTI] " + fmt.Sprintln(v...)) + } +} + +// Noticef prints the logging content with [NOTI] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Noticef(format string, v ...interface{}) { + if l.checkLevel(LEVEL_NOTI) { + l.printErr("[NOTI] " + fmt.Sprintf(format, v...)) + } +} + +// Noticefln prints the logging content with [NOTI] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Noticefln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_NOTI) { + l.printErr("[NOTI] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Warning prints the logging content with [WARN] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Warning(v ...interface{}) { + if l.checkLevel(LEVEL_WARN) { + l.printErr("[WARN] " + fmt.Sprintln(v...)) + } +} + +// Warningf prints the logging content with [WARN] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Warningf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_WARN) { + l.printErr("[WARN] " + fmt.Sprintf(format, v...)) + } +} + +// Warningfln prints the logging content with [WARN] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Warningfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_WARN) { + l.printErr("[WARN] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Error prints the logging content with [ERRO] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Error(v ...interface{}) { + if l.checkLevel(LEVEL_ERRO) { + l.printErr("[ERRO] " + fmt.Sprintln(v...)) + } +} + +// Errorf prints the logging content with [ERRO] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Errorf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_ERRO) { + l.printErr("[ERRO] " + fmt.Sprintf(format, v...)) + } +} + +// Errorfln prints the logging content with [ERRO] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Errorfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_ERRO) { + l.printErr("[ERRO] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Critical prints the logging content with [CRIT] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Critical(v ...interface{}) { + if l.checkLevel(LEVEL_CRIT) { + l.printErr("[CRIT] " + fmt.Sprintln(v...)) + } +} + +// Criticalf prints the logging content with [CRIT] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Criticalf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_CRIT) { + l.printErr("[CRIT] " + fmt.Sprintf(format, v...)) + } +} + +// Criticalfln prints the logging content with [CRIT] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Criticalfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_CRIT) { + l.printErr("[CRIT] " + fmt.Sprintf(format, v...) + ln) + } +} + +// checkLevel checks whether the given could be output. +func (l *Logger) checkLevel(level int) bool { + return l.level & level > 0 +} \ No newline at end of file diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index 89f375e78..63e13a46b 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -130,8 +130,9 @@ func (l *Logger) Header(enabled bool) *Logger { } // Line is a chaining function, -// which enables/disables printing its caller file along with its line number. -// The param specified whether print the long absolute file path, eg: /a/b/c/d.go:23. +// which enables/disables printing its caller file path along with its line number. +// The param specified whether print the long absolute file path, eg: /a/b/c/d.go:23, +// or else short one: d.go:23. func (l *Logger) Line(long...bool) *Logger { logger := (*Logger)(nil) if l.parent == nil { From 162e8f7e51d0df9036d9a44f49129f759ebc35c9 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 22 May 2019 20:31:14 +0800 Subject: [PATCH 07/50] comment updates for glog --- g/os/glog/glog_api.go | 120 +++++++++++++++++++++++------------ g/os/glog/glog_logger_api.go | 2 +- 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/g/os/glog/glog_api.go b/g/os/glog/glog_api.go index fa932bc07..1381b7e63 100644 --- a/g/os/glog/glog_api.go +++ b/g/os/glog/glog_api.go @@ -6,18 +6,25 @@ package glog +// Print prints with newline using fmt.Sprintln. +// The param can be multiple variables. func Print(v ...interface{}) { logger.Print(v ...) } +// Printf prints with format using fmt.Sprintf. +// The param can be multiple variables. func Printf(format string, v ...interface{}) { logger.Printf(format, v ...) } +// See Print. func Println(v ...interface{}) { logger.Println(v ...) } +// Printf prints with newline and format using fmt.Sprintf. +// The param can be multiple variables. func Printfln(format string, v ...interface{}) { logger.Printfln(format, v ...) } @@ -37,86 +44,119 @@ func Fatalfln(format string, v ...interface{}) { logger.Fatalfln(format, v ...) } +// Panic prints the logging content with [PANI] header and newline, then panics. func Panic(v ...interface{}) { logger.Panic(v ...) } +// Panicf prints the logging content with [PANI] header and custom format, then panics. func Panicf(format string, v ...interface{}) { logger.Panicf(format, v ...) } +// Panicfln prints the logging content with [PANI] header, newline and custom format, then panics. func Panicfln(format string, v ...interface{}) { logger.Panicfln(format, v ...) } +// Info prints the logging content with [INFO] header and newline. func Info(v ...interface{}) { logger.Info(v...) } +// Infof prints the logging content with [INFO] header and custom format. +func Infof(format string, v ...interface{}) { + logger.Infof(format, v...) +} + +// Infofln prints the logging content with [INFO] header, newline and custom format. +func Infofln(format string, v ...interface{}) { + logger.Infofln(format, v...) +} + +// Debug prints the logging content with [DEBU] header and newline. func Debug(v ...interface{}) { logger.Debug(v...) } +// Debugf prints the logging content with [DEBU] header and custom format. +func Debugf(format string, v ...interface{}) { + logger.Debugf(format, v...) +} + +// Debugfln prints the logging content with [DEBU] header, newline and custom format. +func Debugfln(format string, v ...interface{}) { + logger.Debugfln(format, v...) +} + +// Notice prints the logging content with [NOTI] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. func Notice(v ...interface{}) { logger.Notice(v...) } +// Noticef prints the logging content with [NOTI] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Noticef(format string, v ...interface{}) { + logger.Noticef(format, v...) +} + +// Noticefln prints the logging content with [NOTI] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Noticefln(format string, v ...interface{}) { + logger.Noticefln(format, v...) +} + +// Warning prints the logging content with [WARN] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. func Warning(v ...interface{}) { logger.Warning(v...) } +// Warningf prints the logging content with [WARN] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Warningf(format string, v ...interface{}) { + logger.Warningf(format, v...) +} + +// Warningfln prints the logging content with [WARN] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Warningfln(format string, v ...interface{}) { + logger.Warningfln(format, v...) +} + +// Error prints the logging content with [ERRO] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. func Error(v ...interface{}) { logger.Error(v...) } +// Errorf prints the logging content with [ERRO] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Errorf(format string, v ...interface{}) { + logger.Errorf(format, v...) +} + +// Errorfln prints the logging content with [ERRO] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Errorfln(format string, v ...interface{}) { + logger.Errorfln(format, v...) +} + +// Critical prints the logging content with [CRIT] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. func Critical(v ...interface{}) { logger.Critical(v...) } -func Infof(format string, v ...interface{}) { - logger.Infof(format, v...) -} - -func Debugf(format string, v ...interface{}) { - logger.Debugf(format, v...) -} - -func Noticef(format string, v ...interface{}) { - logger.Noticef(format, v...) -} - -func Warningf(format string, v ...interface{}) { - logger.Warningf(format, v...) -} - -func Errorf(format string, v ...interface{}) { - logger.Errorf(format, v...) -} - +// Criticalf prints the logging content with [CRIT] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. func Criticalf(format string, v ...interface{}) { logger.Criticalf(format, v...) } -func Infofln(format string, v ...interface{}) { - logger.Infofln(format, v...) -} - -func Debugfln(format string, v ...interface{}) { - logger.Debugfln(format, v...) -} - -func Noticefln(format string, v ...interface{}) { - logger.Noticefln(format, v...) -} - -func Warningfln(format string, v ...interface{}) { - logger.Warningfln(format, v...) -} - -func Errorfln(format string, v ...interface{}) { - logger.Errorfln(format, v...) -} - +// Criticalfln prints the logging content with [CRIT] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. func Criticalfln(format string, v ...interface{}) { logger.Criticalfln(format, v...) } diff --git a/g/os/glog/glog_logger_api.go b/g/os/glog/glog_logger_api.go index 92b880a94..08327bc78 100644 --- a/g/os/glog/glog_logger_api.go +++ b/g/os/glog/glog_logger_api.go @@ -28,7 +28,7 @@ func (l *Logger) Println(v ...interface{}) { l.Print(v...) } -// Printf prints with newline and format using fmt.Sprintf. +// Printfln prints with newline and format using fmt.Sprintf. // The param can be multiple variables. func (l *Logger) Printfln(format string, v ...interface{}) { l.printStd(fmt.Sprintf(format + ln, v...)) From b2a8285ecbbbe1b7789aca2427f08e0163ef0e5a Mon Sep 17 00:00:00 2001 From: John Date: Wed, 22 May 2019 21:20:17 +0800 Subject: [PATCH 08/50] remove unnecessary pkg gscanner; update comment for gsmtp --- g/net/gscanner/scanner.go | 93 ------------------------------- g/net/gsmtp/{smtp.go => gsmtp.go} | 42 +++++++------- 2 files changed, 23 insertions(+), 112 deletions(-) delete mode 100644 g/net/gscanner/scanner.go rename g/net/gsmtp/{smtp.go => gsmtp.go} (61%) diff --git a/g/net/gscanner/scanner.go b/g/net/gscanner/scanner.go deleted file mode 100644 index 84a42fd45..000000000 --- a/g/net/gscanner/scanner.go +++ /dev/null @@ -1,93 +0,0 @@ -// 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. - -// Package gscanner provides a port scanner for local intranet. -package gscanner - -import ( - "net" - "fmt" - "sync" - "time" - "errors" - "github.com/gogf/gf/g/net/gipv4" -) - -type scanner struct { - timeout time.Duration -} - -// 初始化一个扫描器 -func New() *scanner { - return &scanner{ - 6*time.Second, - } -} - -// 设置超时时间,注意这个时间是每一次扫描的超时时间,而不是总共的超时时间 -func (s *scanner) SetTimeout(t time.Duration) *scanner { - s.timeout = t - return s -} - -// 异步TCP扫描网段及端口,如果扫描的端口是打开的,那么将链接给定给回调函数进行调用 -// 注意startIp和endIp需要是同一个网段,否则会报错,并且回调函数不会执行 -func (s *scanner) ScanIp(startIp string, endIp string, port int, callback func(net.Conn)) error { - if callback == nil { - return errors.New("callback function should not be nil") - } - var waitGroup sync.WaitGroup - startIplong := gipv4.Ip2long(startIp) - endIplong := gipv4.Ip2long(endIp) - result := endIplong - startIplong - if startIplong == 0 || endIplong == 0 { - return errors.New("invalid startip or endip: ipv4 string should be given") - } - if result < 0 || result > 255 { - return errors.New("invalid startip and endip: startip and endip should be in the same ip segment") - } - - for i := startIplong; i <= endIplong; i++ { - waitGroup.Add(1) - go func(ip string) { - //fmt.Println("scanning:", ip) - // 这里必需设置超时时间 - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), s.timeout) - if err == nil { - callback(conn) - conn.Close() - } - //fmt.Println("scanning:", ip, "done") - waitGroup.Done() - }(gipv4.Long2ip(i)) - } - waitGroup.Wait() - return nil -} - -// 扫描目标主机打开的端口列表 -func (s *scanner) ScanPort(ip string, callback func(net.Conn)) error { - if callback == nil { - return errors.New("callback function should not be nil") - } - - var waitGroup sync.WaitGroup - for i := 0; i <= 65536; i++ { - waitGroup.Add(1) - //fmt.Println("scanning:", i) - go func(port int) { - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), s.timeout) - if err == nil { - callback(conn) - conn.Close() - } - waitGroup.Done() - }(i) - } - waitGroup.Wait() - return nil -} - diff --git a/g/net/gsmtp/smtp.go b/g/net/gsmtp/gsmtp.go similarity index 61% rename from g/net/gsmtp/smtp.go rename to g/net/gsmtp/gsmtp.go index 7bf53a9c4..c0c787480 100644 --- a/g/net/gsmtp/smtp.go +++ b/g/net/gsmtp/gsmtp.go @@ -5,6 +5,10 @@ // You can obtain one at https://github.com/gogf/gf. // Package gsmtp provides a SMTP client to access remote mail server. +// +// eg: +// s := smtp.New("smtp.exmail.qq.com:25", "notify@a.com", "password") +// glog.Println(s.SendMail("notify@a.com", "ulric@b.com;rain@c.com", "subject", "body, red")) package gsmtp import ( @@ -14,30 +18,31 @@ import ( "strings" ) -// 示例: -// s := smtp.New("smtp.exmail.qq.com:25", "notify@a.com", "password") -// glog.Println(s.SendMail("notify@a.com", "ulric@b.com;rain@c.com", "这是subject", "这是body,red")) - -type Smtp struct { +type SMTP struct { Address string Username string Password string } -func New(address, username, password string) *Smtp { - return &Smtp{ +// New creates and returns a new SMTP object. +func New(address, username, password string) *SMTP { + return &SMTP{ Address: address, Username: username, Password: password, } } -func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...string) error { - if this.Address == "" { +// SendMail connects to the server at addr, switches to TLS if +// possible, authenticates with the optional mechanism a if possible, +// and then sends an email from address from, to addresses to, with +// message msg. +func (s *SMTP) SendMail(from, tos, subject, body string, contentType ...string) error { + if s.Address == "" { return fmt.Errorf("address is necessary") } - hp := strings.Split(this.Address, ":") + hp := strings.Split(s.Address, ":") if len(hp) != 2 { return fmt.Errorf("address format error") } @@ -56,14 +61,13 @@ func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...strin return fmt.Errorf("tos invalid") } - tos = strings.Join(safeArr, ";") - + tos = strings.Join(safeArr, ";") b64 := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") - header := make(map[string]string) - header["From"] = from - header["To"] = tos - header["Subject"] = fmt.Sprintf("=?UTF-8?B?%s?=", b64.EncodeToString([]byte(subject))) + header := make(map[string]string) + header["From"] = from + header["To"] = tos + header["Subject"] = fmt.Sprintf("=?UTF-8?B?%s?=", b64.EncodeToString([]byte(subject))) header["MIME-Version"] = "1.0" ct := "text/plain; charset=UTF-8" @@ -71,7 +75,7 @@ func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...strin ct = "text/html; charset=UTF-8" } - header["Content-Type"] = ct + header["Content-Type"] = ct header["Content-Transfer-Encoding"] = "base64" message := "" @@ -80,6 +84,6 @@ func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...strin } message += "\r\n" + b64.EncodeToString([]byte(body)) - auth := smtp.PlainAuth("", this.Username, this.Password, hp[0]) - return smtp.SendMail(this.Address, auth, from, strings.Split(tos, ";"), []byte(message)) + auth := smtp.PlainAuth("", s.Username, s.Password, hp[0]) + return smtp.SendMail(s.Address, auth, from, strings.Split(tos, ";"), []byte(message)) } \ No newline at end of file From 8d9fdfeafc09af14447890b91fff2783541d74f7 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 22 May 2019 21:43:56 +0800 Subject: [PATCH 09/50] change parameter to unnecssary for glog.Header/Stdout; version updates --- g/os/glog/glog_chaining.go | 10 ++++++---- g/os/glog/glog_logger_chaining.go | 22 ++++++++++++++++------ version.go | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/g/os/glog/glog_chaining.go b/g/os/glog/glog_chaining.go index 231af31fa..7b989f507 100644 --- a/g/os/glog/glog_chaining.go +++ b/g/os/glog/glog_chaining.go @@ -48,14 +48,16 @@ func Backtrace(enabled bool, skip...int) *Logger { // StdPrint is a chaining function, // which enables/disables stdout for the current logging content output. -func Stdout(enabled bool) *Logger { - return logger.Stdout(enabled) +// It's enabled in default. +func Stdout(enabled...bool) *Logger { + return logger.Stdout(enabled...) } // Header is a chaining function, // which enables/disables log header for the current logging content output. -func Header(enabled bool) *Logger { - return logger.Header(enabled) +// It's enabled in default. +func Header(enabled...bool) *Logger { + return logger.Header(enabled...) } // Line is a chaining function, diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index 63e13a46b..6a9111031 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -99,33 +99,43 @@ func (l *Logger) Backtrace(enabled bool, skip...int) *Logger { // Stdout is a chaining function, // which enables/disables stdout for the current logging content output. -func (l *Logger) Stdout(enabled bool) *Logger { +// It's enabled in default. +func (l *Logger) Stdout(enabled...bool) *Logger { logger := (*Logger)(nil) if l.parent == nil { logger = l.Clone() } else { logger = l } - logger.stdoutPrint = enabled + if len(enabled) > 0 && enabled[0] { + logger.stdoutPrint = true + } else { + logger.stdoutPrint = false + } return logger } // See Stdout. // Deprecated. -func (l *Logger) StdPrint(enabled bool) *Logger { - return l.Stdout(enabled) +func (l *Logger) StdPrint(enabled...bool) *Logger { + return l.Stdout(enabled...) } // Header is a chaining function, // which enables/disables log header for the current logging content output. -func (l *Logger) Header(enabled bool) *Logger { +// It's enabled in default. +func (l *Logger) Header(enabled...bool) *Logger { logger := (*Logger)(nil) if l.parent == nil { logger = l.Clone() } else { logger = l } - logger.SetHeaderPrint(enabled) + if len(enabled) > 0 && enabled[0] { + logger.SetHeaderPrint(true) + } else { + logger.SetHeaderPrint(false) + } return logger } diff --git a/version.go b/version.go index 3a5151943..6589dd5ed 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.6.13" +const VERSION = "v1.6.14" const AUTHORS = "john" From ea3e03aaba4895a6fb816efdd0183eca98e1dc26 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 May 2019 09:26:37 +0800 Subject: [PATCH 10/50] example updates for glog --- geg/os/glog/glog_flags.go | 6 +++--- geg/os/glog/glog_line.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/geg/os/glog/glog_flags.go b/geg/os/glog/glog_flags.go index eb94230a7..b2561ee33 100644 --- a/geg/os/glog/glog_flags.go +++ b/geg/os/glog/glog_flags.go @@ -7,9 +7,9 @@ import ( func main() { l := glog.New() l.SetFlags(glog.F_TIME_TIME|glog.F_FILE_SHORT) - l.Println("123") + l.Println("time and short line number") l.SetFlags(glog.F_TIME_MILLI|glog.F_FILE_LONG) - l.Println("123") + l.Println("time with millisecond and long line number") l.SetFlags(glog.F_TIME_STD|glog.F_FILE_LONG) - l.Println("123") + l.Println("standard time format and long line number") } diff --git a/geg/os/glog/glog_line.go b/geg/os/glog/glog_line.go index c4771fd02..41d2e0779 100644 --- a/geg/os/glog/glog_line.go +++ b/geg/os/glog/glog_line.go @@ -5,6 +5,6 @@ import ( ) func main() { - glog.Line().Println("123") - glog.Line(true).Println("123") + glog.Line().Println("this is the short file name with its line number") + glog.Line(true).Println("lone file name with line number") } From 9da883a50c23f8fe99796167c1a473f27474c88a Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 May 2019 11:49:33 +0800 Subject: [PATCH 11/50] comment updates --- g/os/glog/glog.go | 2 +- g/os/glog/glog_logger.go | 2 +- geg/os/glog/glog_file.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index a0a46cece..7dd62c664 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -83,7 +83,7 @@ func SetDebug(debug bool) { logger.SetDebug(debug) } -// SetStdPrint sets whether ouptput the logging contents to stdout, which is false in default. +// SetStdoutPrint sets whether ouptput the logging contents to stdout, which is false in default. func SetStdoutPrint(enabled bool) { logger.SetStdoutPrint(enabled) } diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index c7ce97c37..afd0b18d3 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -217,7 +217,7 @@ func (l *Logger) SetFile(pattern string) { l.file = pattern } -// SetStdPrint sets whether output the logging contents to stdout, which is true in default. +// SetStdoutPrint sets whether output the logging contents to stdout, which is true in default. func (l *Logger) SetStdoutPrint(enabled bool) { l.stdoutPrint = enabled } diff --git a/geg/os/glog/glog_file.go b/geg/os/glog/glog_file.go index 8a409863f..29797dff0 100644 --- a/geg/os/glog/glog_file.go +++ b/geg/os/glog/glog_file.go @@ -11,7 +11,7 @@ func main() { l := glog.New() path := "/tmp/glog" l.SetPath(path) - l.SetStdPrint(false) + l.SetStdoutPrint(false) // 使用默认文件名称格式 l.Println("标准文件名称格式,使用当前时间时期") // 通过SetFile设置文件名称格式 From 3e7416ca7d76e9bd1ef22c113f96d6b1da0ebc38 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 May 2019 19:14:16 +0800 Subject: [PATCH 12/50] add Skip for glog --- g/os/glog/glog_chaining.go | 7 +++++ g/os/glog/glog_logger.go | 50 ++++++++++++++++++++----------- g/os/glog/glog_logger_chaining.go | 14 +++++++++ geg/os/glog/glog_line2.go | 14 +++++++++ 4 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 geg/os/glog/glog_line2.go diff --git a/g/os/glog/glog_chaining.go b/g/os/glog/glog_chaining.go index 231af31fa..6ac8c858b 100644 --- a/g/os/glog/glog_chaining.go +++ b/g/os/glog/glog_chaining.go @@ -40,6 +40,13 @@ func Level(level int) *Logger { return logger.Level(level) } +// Skip is a chaining function, +// which sets backtrace skip for the current logging content output. +// It also affects the caller file path checks when line number printing enabled. +func Skip(skip int) *Logger { + return logger.Skip(skip) +} + // Backtrace is a chaining function, // which sets backtrace options for the current logging content output . func Backtrace(enabled bool, skip...int) *Logger { diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index c7ce97c37..7935bca1e 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -307,22 +307,21 @@ func (l *Logger) GetBacktrace(skip...int) string { customSkip = skip[0] } backtrace := "" - index := 1 from := 0 - // 首先定位业务文件开始位置 - for i := 0; i < 10; i++ { + // Find the caller position exclusive of the glog file. + for i := 0; i < 100; i++ { if _, file, _, ok := runtime.Caller(i); ok { if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { - from = i + from = i + 1 break } } } - // 从业务文件开始位置根据自定义的skip开始backtrace + // Find the true caller file path using custom skip. + index := 1 goRoot := runtime.GOROOT() for i := from + customSkip + l.btSkip; i < 10000; i++ { if _, file, cline, ok := runtime.Caller(i); ok && file != "" { - // 不打印出go源码路径及glog包文件路径,日志打印必须从业务源码文件开始,且从glog包文件开始检索 if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, ln) index++ @@ -334,6 +333,34 @@ func (l *Logger) GetBacktrace(skip...int) string { return backtrace } +// getLongFile returns the absolute file path along with its line number of the caller. +func (l *Logger) getLongFile() string { + from := 0 + // Find the caller position exclusive of the glog file. + for i := 0; i < 100; i++ { + if _, file, line, ok := runtime.Caller(i); ok { + if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { + from = i + 1 + break + return fmt.Sprintf(`%s:%d`, file, line) + } + } + } + // Find the true caller file path using custom skip. + goRoot := runtime.GOROOT() + for i := from + l.btSkip; i < 10000; i++ { + if _, file, line, ok := runtime.Caller(i); ok && file != "" { + if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { + return fmt.Sprintf(`%s:%d`, file, line) + } + } else { + break + } + } + return "" +} + + // format formats the content according the flags. func (l *Logger) format(content string) string { buffer := bytes.NewBuffer(nil) @@ -371,14 +398,3 @@ func (l *Logger) format(content string) string { return buffer.String() } -// getLongFile returns the absolute file path along with its line number of the caller. -func (l *Logger) getLongFile() string { - for i := 0; i < 100; i++ { - if _, file, line, ok := runtime.Caller(i); ok { - if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { - return fmt.Sprintf(`%s:%d`, file, line) - } - } - } - return "" -} diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index 63e13a46b..ea6fb788f 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -81,6 +81,20 @@ func (l *Logger) Level(level int) *Logger { return logger } +// Skip is a chaining function, +// which sets backtrace skip for the current logging content output. +// It also affects the caller file path checks when line number printing enabled. +func (l *Logger) Skip(skip int) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + logger.SetBacktraceSkip(skip) + return logger +} + // Backtrace is a chaining function, // which sets backtrace options for the current logging content output . func (l *Logger) Backtrace(enabled bool, skip...int) *Logger { diff --git a/geg/os/glog/glog_line2.go b/geg/os/glog/glog_line2.go new file mode 100644 index 000000000..454aedf57 --- /dev/null +++ b/geg/os/glog/glog_line2.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func print() { + glog.Line(true).Println("123") +} + +func main() { + glog.Line().Println("123") + glog.Line(true).Println("123") +} From 3575e201379d9833e9fd4e013ab43d75df8371f3 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 May 2019 20:34:06 +0800 Subject: [PATCH 13/50] update Writer feature of glog --- g/os/glog/glog_logger.go | 22 ++++++---------------- g/os/glog/glog_logger_writer.go | 10 +++------- geg/os/glog/glog_line2.go | 8 ++++---- geg/os/glog/glog_writer.go | 12 ------------ geg/os/glog/glog_writer_greylog.go | 30 ++++++++++++++++++++++++++++++ geg/os/glog/glog_writer_hook.go | 28 ++++++++++++++++++++++++++++ 6 files changed, 71 insertions(+), 39 deletions(-) delete mode 100644 geg/os/glog/glog_writer.go create mode 100644 geg/os/glog/glog_writer_greylog.go create mode 100644 geg/os/glog/glog_writer_hook.go diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index d6255f626..4332eb4af 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -75,10 +75,6 @@ func New() *Logger { headerPrint : true, stdoutPrint : true, } - // Default writer - logger.writer = &Writer { - logger : logger, - } return logger } @@ -96,10 +92,6 @@ func (l *Logger) Clone() *Logger { headerPrint : l.headerPrint, stdoutPrint : l.stdoutPrint, } - // Default writer - logger.writer = &Writer { - logger : logger, - } return logger } @@ -156,7 +148,7 @@ func (l *Logger) SetWriter(writer io.Writer) { } // GetWriter returns the customized writer object, which implements the io.Writer interface. -// It returns a default writer if no customized writer set. +// It returns nil if no writer previously set. func (l *Logger) GetWriter() io.Writer { return l.writer } @@ -234,14 +226,12 @@ func (l *Logger) SetPrefix(prefix string) { } // print prints to defined writer, logging file or passed . -// It internally uses memory lock for file logging to ensure logging sequence. func (l *Logger) print(std io.Writer, s string) { - // Customized writer has the most high priority. + // Custom writer has the most high priority. if l.headerPrint { s = l.format(s) } - writer := l.GetWriter() - if _, ok := writer.(*Writer); ok { + if l.writer == nil { if f := l.getFilePointer(); f != nil { defer f.Close() if _, err := io.WriteString(f, s); err != nil { @@ -255,7 +245,7 @@ func (l *Logger) print(std io.Writer, s string) { } } } else { - if _, err := std.Write([]byte(s)); err != nil { + if _, err := l.writer.Write([]byte(s)); err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } @@ -312,7 +302,7 @@ func (l *Logger) GetBacktrace(skip...int) string { for i := 0; i < 100; i++ { if _, file, _, ok := runtime.Caller(i); ok { if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { - from = i + 1 + from = i break } } @@ -340,7 +330,7 @@ func (l *Logger) getLongFile() string { for i := 0; i < 100; i++ { if _, file, line, ok := runtime.Caller(i); ok { if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { - from = i + 1 + from = i break return fmt.Sprintf(`%s:%d`, file, line) } diff --git a/g/os/glog/glog_logger_writer.go b/g/os/glog/glog_logger_writer.go index 525fe438f..d2eef9dee 100644 --- a/g/os/glog/glog_logger_writer.go +++ b/g/os/glog/glog_logger_writer.go @@ -6,13 +6,9 @@ package glog -type Writer struct { - logger *Logger -} - // Write implements the io.Writer interface. -// It just prints the content with header or level. -func (w *Writer) Write(p []byte) (n int, err error) { - w.logger.Header(false).Print(string(p)) +// It just prints the content using Print. +func (l *Logger) Write(p []byte) (n int, err error) { + l.Header(false).Print(string(p)) return len(p), nil } \ No newline at end of file diff --git a/geg/os/glog/glog_line2.go b/geg/os/glog/glog_line2.go index 454aedf57..2e3730114 100644 --- a/geg/os/glog/glog_line2.go +++ b/geg/os/glog/glog_line2.go @@ -4,11 +4,11 @@ import ( "github.com/gogf/gf/g/os/glog" ) -func print() { - glog.Line(true).Println("123") +func PrintLog(content string) { + glog.Skip(1).Line().Println("line number with skip:", content) + glog.Line().Println("line number without skip:", content) } func main() { - glog.Line().Println("123") - glog.Line(true).Println("123") + PrintLog("just test") } diff --git a/geg/os/glog/glog_writer.go b/geg/os/glog/glog_writer.go deleted file mode 100644 index 792678261..000000000 --- a/geg/os/glog/glog_writer.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/g/os/glog" -) - -func main() { - w := glog.GetWriter() - w.Write([]byte("hello")) - - glog.Path("/tmp/glog/test").GetWriter().Write([]byte("hello")) -} diff --git a/geg/os/glog/glog_writer_greylog.go b/geg/os/glog/glog_writer_greylog.go new file mode 100644 index 000000000..7d154fa62 --- /dev/null +++ b/geg/os/glog/glog_writer_greylog.go @@ -0,0 +1,30 @@ +package main + +//import ( +// "github.com/gogf/gf/g/os/glog" +// "github.com/robertkowalski/graylog-golang" +//) +// +//type MyGrayLogWriter struct { +// gelf *gelf.Gelf +// logger *glog.Logger +//} +// +//func (w *MyGrayLogWriter) Write(p []byte) (n int, err error) { +// w.gelf.Send(p) +// return w.logger.Write(p) +//} +// +//func main() { +// glog.SetWriter(&MyGrayLogWriter{ +// logger : glog.New(), +// gelf : gelf.New(gelf.Config{ +// GraylogPort : 80, +// GraylogHostname : "graylog-host.com", +// Connection : "wan", +// MaxChunkSizeWan : 42, +// MaxChunkSizeLan : 1337, +// }), +// }) +// glog.Println("test log") +//} diff --git a/geg/os/glog/glog_writer_hook.go b/geg/os/glog/glog_writer_hook.go new file mode 100644 index 000000000..02e999248 --- /dev/null +++ b/geg/os/glog/glog_writer_hook.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/os/glog" + "github.com/gogf/gf/g/text/gregex" +) + +type MyWriter struct { + logger *glog.Logger +} + +func (w *MyWriter) Write(p []byte) (n int, err error) { + s := string(p) + if gregex.IsMatchString(`\[(PANI|FATA)\]`, s) { + fmt.Println("SERIOUS ISSUE OCCURRED!! I'd better tell monitor in first time!") + ghttp.PostContent("http://monitor.mydomain.com", s) + } + return w.logger.Write(p) +} + +func main() { + glog.SetWriter(&MyWriter{ + logger : glog.New(), + }) + glog.Fatal("FATAL ERROR") +} From 6f0aee1cc55b0ce593d282ab7d8b530e2c392ff6 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 May 2019 20:43:09 +0800 Subject: [PATCH 14/50] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 6589dd5ed..76fc245fa 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.6.14" +const VERSION = "v1.6.15" const AUTHORS = "john" From 592bf76eb04550ebe93938d367ffce93f42a0e49 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 May 2019 21:20:46 +0800 Subject: [PATCH 15/50] add browser mode support for ghttp.Client.Post --- g/net/ghttp/ghttp_client_request.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/g/net/ghttp/ghttp_client_request.go b/g/net/ghttp/ghttp_client_request.go index e907e8297..0178492d5 100644 --- a/g/net/ghttp/ghttp_client_request.go +++ b/g/net/ghttp/ghttp_client_request.go @@ -180,6 +180,17 @@ func (c *Client) Post(url string, data...interface{}) (*ClientResponse, error) { cookies : make(map[string]string), } r.Response = resp + // 浏览器模式 + if c.browserMode { + now := time.Now() + for _, v := range r.Cookies() { + if v.Expires.UnixNano() < now.UnixNano() { + delete(c.cookies, v.Name) + } else { + c.cookies[v.Name] = v.Value + } + } + } return r, nil } @@ -258,7 +269,7 @@ func (c *Client) DoRequestContent(method string, url string, data...interface{}) return string(response.ReadAll()) } -// 请求并返回response对象,该方法支持二进制提交数据 +// 请求并返回response对象 func (c *Client) DoRequest(method, url string, data...interface{}) (*ClientResponse, error) { if strings.EqualFold("POST", method) { return c.Post(url, data...) From 084f6c31cbd30324a9ec7bf620ec6719a04545fd Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 May 2019 21:58:04 +0800 Subject: [PATCH 16/50] comment updates for gfile --- g/os/gfile/gfile.go | 177 ++++++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 106 deletions(-) diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go index 47bbdb680..0097d0468 100644 --- a/g/os/gfile/gfile.go +++ b/g/os/gfile/gfile.go @@ -27,23 +27,20 @@ import ( ) const ( - // 文件分隔符 + // Separator for file system. Separator = string(filepath.Separator) - // 默认的文件打开权限 + // Default perm for file opening. gDEFAULT_PERM = 0666 ) var ( - // 源码的main包所在目录,仅仅会设置一次 - mainPkgPath = gtype.NewString() - - // 编译时的 GOROOT 数值 - goRootOfBuild = gtype.NewString() + // The absolute file path for main package. + // It can be only checked and set once. + mainPkgPath = gtype.NewString() ) -// Create directories recursively. -// -// 给定目录的绝对路径创建目录(递归创建)。 +// Mkdir creates directories recursively with given . +// The parameter is suggested to be absolute path. func Mkdir(path string) error { err := os.MkdirAll(path, os.ModePerm) if err != nil { @@ -52,9 +49,8 @@ func Mkdir(path string) error { return nil } -// Create file with given path recursively. -// -// 给定文件的绝对路径创建文件。 +// Create creates file with given recursively. +// The parameter is suggested to be absolute path. func Create(path string) (*os.File, error) { dir := Dir(path) if !Exists(dir) { @@ -63,23 +59,17 @@ func Create(path string) (*os.File, error) { return os.Create(path) } -// Open file/directory with readonly. -// -// 只读打开文件 +// Open opens file/directory readonly. func Open(path string) (*os.File, error) { return os.Open(path) } -// Open file/directory with given and . -// -// 打开文件(带flag&perm) +// OpenFile opens file/directory with given and . func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { return os.OpenFile(path, flag, perm) } -// Open file/directory with default perm and given . -// -// 打开文件(带flag) +// OpenWithFlag opens file/directory with default perm and given . func OpenWithFlag(path string, flag int) (*os.File, error) { f, err := os.OpenFile(path, flag, gDEFAULT_PERM) if err != nil { @@ -88,9 +78,7 @@ func OpenWithFlag(path string, flag int) (*os.File, error) { return f, nil } -// Open file/directory with given and . -// -// 打开文件(带flag&perm) +// OpenWithFlagPerm opens file/directory with given and . func OpenWithFlagPerm(path string, flag int, perm int) (*os.File, error) { f, err := os.OpenFile(path, flag, os.FileMode(perm)) if err != nil { @@ -99,9 +87,7 @@ func OpenWithFlagPerm(path string, flag int, perm int) (*os.File, error) { return f, nil } -// Check whether given path exist. -// -// 判断所给路径文件/文件夹是否存在 +// Exists checks whether given exist. func Exists(path string) bool { if _, err := os.Stat(path); !os.IsNotExist(err) { return true @@ -109,9 +95,7 @@ func Exists(path string) bool { return false } -// Check whether given path a directory. -// -// 判断所给路径是否为文件夹 +// IsDir checks whether given a directory. func IsDir(path string) bool { s, err := os.Stat(path) if err != nil { @@ -120,17 +104,13 @@ func IsDir(path string) bool { return s.IsDir() } -// Get current working directory absolute path. -// -// 获取当前工作目录(注意与SelfDir的区别). +// Pwd returns absolute path of current working directory. func Pwd() string { path, _ := os.Getwd() return path } -// Check whether given path a file(not a directory). -// -// 判断所给路径是否为文件 +// IsFile checks whether given a file, which means it's not a directory. func IsFile(path string) bool { s, err := os.Stat(path) if err != nil { @@ -139,39 +119,32 @@ func IsFile(path string) bool { return !s.IsDir() } +// Alias of Stat. // See Stat. -// -// Stat 方法的别名。 func Info(path string) (os.FileInfo, error) { return Stat(path) } // Stat returns a FileInfo describing the named file. // If there is an error, it will be of type *PathError. -// -// 获取文件或目录信息. func Stat(path string) (os.FileInfo, error) { return os.Stat(path) } -// Move renames (moves) src to dst path. -// -// 文件移动/重命名 +// Move renames (moves) to path. func Move(src string, dst string) error { return os.Rename(src, dst) } -// Rename renames (moves) src to dst path. -// -// 文件移动/重命名. +// Alias of Move. +// See Move. func Rename(src string, dst string) error { return Move(src, dst) } -// Copy file from src to dst. +// Copy file from to . // -// 文件复制. -// @TODO 支持目录复制. +// @TODO directory copy support. func Copy(src string, dst string) error { srcFile, err := Open(src) if err != nil { @@ -194,9 +167,7 @@ func Copy(src string, dst string) error { return nil } -// Get sub-file names of path. -// -// 返回目录下的文件名列表 +// DirNames returns sub-file names of given directory . func DirNames(path string) ([]string, error) { f, err := os.Open(path) if err != nil { @@ -218,8 +189,6 @@ func DirNames(path string) ([]string, error) { // Glob ignores file system errors such as I/O errors reading directories. // The only possible returned error is ErrBadPattern, when pattern // is malformed. -// -// 文件名正则匹配查找,第二个可选参数指定返回的列表是否仅为文件名(非绝对路径),默认返回绝对路径 func Glob(pattern string, onlyNames...bool) ([]string, error) { if list, err := filepath.Glob(pattern); err == nil { if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 { @@ -235,16 +204,13 @@ func Glob(pattern string, onlyNames...bool) ([]string, error) { } } -// Remove file/directory with parameter. -// -// 文件/目录删除 +// Remove deletes all file/directory with parameter. +// If parameter is directory, it deletes it recursively. func Remove(path string) error { return os.RemoveAll(path) } -// Check whether given is readable. -// -// 文件是否可读(支持文件/目录) +// IsReadable checks whether given is readable. func IsReadable(path string) bool { result := true file, err := os.OpenFile(path, os.O_RDONLY, gDEFAULT_PERM) @@ -255,14 +221,13 @@ func IsReadable(path string) bool { return result } -// Check whether given is writable. +// IsWritable checks whether given is writable. // -// 文件是否可写(支持文件/目录) -// @TODO 改进性能,利用 golang.org/x/sys 来实现跨平台的权限判断。 +// @TODO improve performance; use golang.org/x/sys to cross-plat-form func IsWritable(path string) bool { result := true if IsDir(path) { - // 如果是目录,那么创建一个临时文件进行写入测试 + // If it's a directory, create a temporary file to test whether it's writable. tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano()) if f, err := Create(tmpFile); err != nil || !Exists(tmpFile){ result = false @@ -282,16 +247,12 @@ func IsWritable(path string) bool { } // See os.Chmod. -// -// 修改文件/目录权限 func Chmod(path string, mode os.FileMode) error { return os.Chmod(path, mode) } -// Get all sub-files(absolute) of given , -// can be recursively with given parameter true. -// -// 打开目录,并返回其下一级文件列表(绝对路径),按照文件名称大小写进行排序,支持目录递归遍历。 +// ScanDir returns all sub-files with absolute paths of given , +// It scans directory recursively if given parameter is true. func ScanDir(path string, pattern string, recursive ... bool) ([]string, error) { list, err := doScanDir(path, pattern, recursive...) if err != nil { @@ -303,22 +264,24 @@ func ScanDir(path string, pattern string, recursive ... bool) ([]string, error) return list, nil } -// 内部检索目录方法,支持递归,返回没有排序的文件绝对路径列表结果。 -// pattern参数支持多个文件名称模式匹配,使用','符号分隔多个模式。 +// doScanDir is an internal method which scans directory +// and returns the absolute path list of files that are not sorted. +// +// The pattern parameter supports multiple file name patterns, +// using the ',' symbol to separate multiple patterns. +// +// It scans directory recursively if given parameter is true. func doScanDir(path string, pattern string, recursive ... bool) ([]string, error) { list := ([]string)(nil) - // 打开目录 file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() - // 读取目录下的文件列表 names, err := file.Readdirnames(-1) if err != nil { return nil, err } - // 是否递归遍历 for _, name := range names { path := fmt.Sprintf("%s%s%s", path, Separator, name) if IsDir(path) && len(recursive) > 0 && recursive[0] { @@ -327,7 +290,7 @@ func doScanDir(path string, pattern string, recursive ... bool) ([]string, error list = append(list, array...) } } - // 满足pattern才加入结果列表 + // If it meets pattern, then add it to the result list. for _, p := range strings.Split(pattern, ",") { if match, err := filepath.Match(strings.TrimSpace(p), name); err == nil && match { list = append(list, path) @@ -337,10 +300,9 @@ func doScanDir(path string, pattern string, recursive ... bool) ([]string, error return list, nil } -// See filepath.Abs. -// -// 将所给定的路径转换为绝对路径 -// 并判断文件路径是否存在,如果文件不存在,那么返回空字符串 +// RealPath converts the given to its absolute path +// and checks if the file path exists. +// If the file does not exist, return an empty string. func RealPath(path string) string { p, err := filepath.Abs(path) if err != nil { @@ -352,45 +314,46 @@ func RealPath(path string) string { return p } -// Get absolute file path of current running process(binary). -// -// 获取当前执行文件的绝对路径 +// SelfPath returns absolute file path of current running process(binary). func SelfPath() string { p, _ := filepath.Abs(os.Args[0]) return p } -// Get absolute directory path of current running process(binary). -// -// 获取当前执行文件的目录绝对路径 +// SelfDir returns absolute directory path of current running process(binary). func SelfDir() string { return filepath.Dir(SelfPath()) } -// See filepath.Base. -// -// 获取指定文件路径的文件名称 +// Basename returns the last element of path. +// Trailing path separators are removed before extracting the last element. +// If the path is empty, Base returns ".". +// If the path consists entirely of separators, Base returns a single separator. func Basename(path string) string { return filepath.Base(path) } -// See filepath.Dir. -// -// 获取指定文件路径的目录地址绝对路径. +// Dir returns all but the last element of path, typically the path's directory. +// After dropping the final element, Dir calls Clean on the path and trailing +// slashes are removed. +// If the path is empty, Dir returns ".". +// If the path consists entirely of separators, Dir returns a single separator. +// The returned path does not end in a separator unless it is the root directory. func Dir(path string) string { return filepath.Dir(path) } -// See filepath.Ext. +// Ext returns the file name extension used by path. +// The extension is the suffix beginning at the final dot +// in the final element of path; it is empty if there is +// no dot. // -// 获取指定文件路径的文件扩展名(包含"."号) +// Note: the result contains symbol '.'. func Ext(path string) string { return filepath.Ext(path) } -// Get absolute home directory path of current user. -// -// 获取用户主目录 +// Home returns absolute path of current user's home directory. func Home() (string, error) { u, err := user.Current() if nil == err { @@ -435,12 +398,15 @@ func homeWindows() (string, error) { return home, nil } -// Get absolute file path of main file, which contains the entrance function main. -// Available in develop environment. +// MainPkgPath returns absolute file path of package main, +// which contains the entrance function main. // -// 获取入口函数文件所在目录(main包文件目录), -// **仅对源码开发环境有效(即仅对生成该可执行文件的系统下有效)**。 -// 注意:该方法被第一次调用时,如果是在异步的goroutine中,该方法可能无法获取到main包路径。 +// It's only available in develop environment. +// +// Note1: Only valid for source development environments, +// IE only valid for systems that generate this executable. +// Note2: When the method is called for the first time, if it is in an asynchronous goroutine, +// the method may not get the main package path. func MainPkgPath() string { path := mainPkgPath.Val() if path != "" { @@ -473,14 +439,13 @@ func MainPkgPath() string { break } } - // 找不到,下次不用再检索了 + // If it fails finding the path, then mark it as "-", + // which means it will never do this search again. mainPkgPath.Set("-") return "" } // See os.TempDir(). -// -// 系统临时目录 func TempDir() string { return os.TempDir() } From fee1c9eccf3d39446dfaba01a88fd74de8f5fb9d Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 24 May 2019 19:24:25 +0800 Subject: [PATCH 17/50] Create FUNDING.yml --- .github/FUNDING.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..f53dc3b9c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,8 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +custom: # Replace with a single custom sponsorship URL From 1b3243c09ccb251b3c2ccb2fc7893f8213c6fd88 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 24 May 2019 19:59:08 +0800 Subject: [PATCH 18/50] comment update for gfile/gaes/gcrc32/gdes/gmd5/gsha1 --- g/crypto/gaes/gaes.go | 1 - g/crypto/gcrc32/gcrc32.go | 13 ++++- g/crypto/gdes/gdes.go | 29 +++++----- g/crypto/gmd5/gmd5.go | 19 ++++--- g/crypto/gsha1/gsha1.go | 14 ++--- g/os/gfile/gfile_contents.go | 57 ++++++++++++------- g/os/gfile/gfile_search.go | 24 +++----- g/os/gfile/gfile_size.go | 6 +- g/os/gfile/gfile_time.go | 4 +- ...tents_test.go => gfile_z_contents_test.go} | 0 ..._search_test.go => gfile_z_search_test.go} | 0 ...file_size_test.go => gfile_z_size_test.go} | 0 g/os/gfile/{gfile_test.go => gfile_z_test.go} | 0 ...file_time_test.go => gfile_z_time_test.go} | 0 14 files changed, 95 insertions(+), 72 deletions(-) rename g/os/gfile/{gfile_contents_test.go => gfile_z_contents_test.go} (100%) rename g/os/gfile/{gfile_search_test.go => gfile_z_search_test.go} (100%) rename g/os/gfile/{gfile_size_test.go => gfile_z_size_test.go} (100%) rename g/os/gfile/{gfile_test.go => gfile_z_test.go} (100%) rename g/os/gfile/{gfile_time_test.go => gfile_z_time_test.go} (100%) diff --git a/g/crypto/gaes/gaes.go b/g/crypto/gaes/gaes.go index 62b77c257..746deba74 100644 --- a/g/crypto/gaes/gaes.go +++ b/g/crypto/gaes/gaes.go @@ -65,7 +65,6 @@ func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) { if e != nil { return nil, e } - return plainText, nil } diff --git a/g/crypto/gcrc32/gcrc32.go b/g/crypto/gcrc32/gcrc32.go index 8ee9f1a99..28052d0d4 100644 --- a/g/crypto/gcrc32/gcrc32.go +++ b/g/crypto/gcrc32/gcrc32.go @@ -4,17 +4,26 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gcrc32 provides useful API for CRC32 encryption/decryption algorithms. +// Package gcrc32 provides useful API for CRC32 encryption algorithms. package gcrc32 import ( - "hash/crc32" + "github.com/gogf/gf/g/util/gconv" + "hash/crc32" ) +// Encrypt encrypts any type of variable using CRC32 algorithms. +// It uses gconv package to convert to its bytes type. +func Encrypt(v interface{}) uint32 { + return crc32.ChecksumIEEE(gconv.Bytes(v)) +} + +// Deprecated. func EncryptString(v string) uint32 { return crc32.ChecksumIEEE([]byte(v)) } +// Deprecated. func EncryptBytes(v []byte) uint32 { return crc32.ChecksumIEEE(v) } diff --git a/g/crypto/gdes/gdes.go b/g/crypto/gdes/gdes.go index 0ca8dcb8a..f115692a4 100644 --- a/g/crypto/gdes/gdes.go +++ b/g/crypto/gdes/gdes.go @@ -3,7 +3,8 @@ // 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. -// @author: wenzi1 +// +// @author wenzi1 // Package gdes provides useful API for DES encryption/decryption algorithms. package gdes @@ -16,11 +17,11 @@ import ( ) const ( - NOPADDING = iota + NOPADDING = iota PKCS5PADDING ) -//ECB模式DES加密 +// ECB模式DES加密 func DesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, error) { text, err := Padding(clearText, padding) if err != nil { @@ -42,7 +43,7 @@ func DesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, error) { return cipherText, nil } -//ECB模式DES解密 +// ECB模式DES解密 func DesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) { text := make([]byte, len(cipherText)) block, err := des.NewCipher(key) @@ -63,7 +64,7 @@ func DesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) { return clearText, nil } -//ECB模式3DES加密,密钥长度可以是16或24位长 +// ECB模式3DES加密,密钥长度可以是16或24位长 func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ( []byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length error") @@ -96,7 +97,7 @@ func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ( []byte, er return cipherText, nil } -//ECB模式3DES解密,密钥长度可以是16或24位长 +// ECB模式3DES解密,密钥长度可以是16或24位长 func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length error") @@ -129,7 +130,7 @@ func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, e return clearText, nil } -//CBC模式DES加密 +// CBC模式DES加密 func DesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { @@ -152,7 +153,7 @@ func DesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ([]byte return cipherText, nil } -//CBC模式DES解密 +// CBC模式DES解密 func DesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { @@ -175,7 +176,7 @@ func DesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ([]byt return clearText, nil } -//CBC模式3DES加密 +// CBC模式3DES加密 func TripleDesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length invalid") @@ -210,7 +211,7 @@ func TripleDesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ( return cipherText, nil } -//CBC模式3DES解密 +// CBC模式3DES解密 func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ( []byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length invalid") @@ -245,21 +246,21 @@ func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) return clearText, nil } -//PKCS5补位 +// PKCS5补位 func PKCS5Padding(text []byte, blockSize int) []byte { padding := blockSize - len(text) % blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(text, padtext...) } -//去除PKCS5补位 +// 去除PKCS5补位 func PKCS5Unpadding(text []byte) []byte{ length := len(text) padtext := int(text[length - 1]) return text[:(length - padtext)] } -//补位方法 +// 补位方法 func Padding(text []byte, padding int)([]byte, error) { switch padding { case NOPADDING: @@ -275,7 +276,7 @@ func Padding(text []byte, padding int)([]byte, error) { return text, nil } -//去除补位方法 +// 去除补位方法 func UnPadding(text []byte, padding int)([]byte, error) { switch padding { case NOPADDING: diff --git a/g/crypto/gmd5/gmd5.go b/g/crypto/gmd5/gmd5.go index aa5fab0a3..63fc08911 100644 --- a/g/crypto/gmd5/gmd5.go +++ b/g/crypto/gmd5/gmd5.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gmd5 provides useful API for MD5 encryption/decryption algorithms. +// Package gmd5 provides useful API for MD5 encryption algorithms. package gmd5 import ( @@ -15,28 +15,31 @@ import ( "github.com/gogf/gf/g/util/gconv" ) -// 将任意类型的变量进行md5摘要(注意map等非排序变量造成的不同结果) +// Encrypt encrypts any type of variable using MD5 algorithms. +// It uses gconv package to convert to its bytes type. func Encrypt(v interface{}) string { h := md5.New() h.Write([]byte(gconv.Bytes(v))) return fmt.Sprintf("%x", h.Sum(nil)) } -// 将字符串进行MD5哈希摘要计算 + +// Deprecated. func EncryptString(v string) string { - h := md5.New() - h.Write([]byte(v)) - return fmt.Sprintf("%x", h.Sum(nil)) + h := md5.New() + h.Write([]byte(v)) + return fmt.Sprintf("%x", h.Sum(nil)) } -// 将文件内容进行MD5哈希摘要计算 + +// EncryptFile encrypts file content of using MD5 algorithms. func EncryptFile(path string) string { f, e := os.Open(path) if e != nil { return "" } defer f.Close() - h := md5.New() + h := md5.New() _, e = io.Copy(h, f) if e != nil { return "" diff --git a/g/crypto/gsha1/gsha1.go b/g/crypto/gsha1/gsha1.go index 8caa35aee..a12a02884 100644 --- a/g/crypto/gsha1/gsha1.go +++ b/g/crypto/gsha1/gsha1.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gsha1 provides useful API for SHA1 encryption/decryption algorithms. +// Package gsha1 provides useful API for SHA1 encryption algorithms. package gsha1 import ( @@ -15,20 +15,20 @@ import ( "github.com/gogf/gf/g/util/gconv" ) -// 将任意类型的变量进行SHA摘要(注意map等非排序变量造成的不同结果) -// 内部使用了md5计算,因此效率会稍微差一些,更多情况请使用 EncodeString +// Encrypt encrypts any type of variable using SHA1 algorithms. +// It uses gconv package to convert to its bytes type. func Encrypt(v interface{}) string { r := sha1.Sum(gconv.Bytes(v)) return hex.EncodeToString(r[:]) } -// 对字符串行SHA1摘要计算 +// Deprecated. func EncryptString(s string) string { - r := sha1.Sum([]byte(s)) - return hex.EncodeToString(r[:]) + r := sha1.Sum([]byte(s)) + return hex.EncodeToString(r[:]) } -// 对文件内容进行SHA1摘要计算 +// EncryptFile encrypts file content of using SHA1 algorithms. func EncryptFile(path string) string { f, e := os.Open(path) if e != nil { diff --git a/g/os/gfile/gfile_contents.go b/g/os/gfile/gfile_contents.go index 63f611686..e129e9850 100644 --- a/g/os/gfile/gfile_contents.go +++ b/g/os/gfile/gfile_contents.go @@ -13,18 +13,18 @@ import ( ) const ( - // 方法中涉及到读取的时候的缓冲大小 - gREAD_BUFFER = 1024 - // 方法中涉及到文件指针池的默认缓存时间(毫秒) - //gFILE_POOL_EXPIRE = 60000 + // Buffer size for reading file content. + gREAD_BUFFER = 1024 ) -// (文本)读取文件内容 +// GetContents returns the file content of as string. +// It returns en empty string if it fails reading. func GetContents(path string) string { return string(GetBinContents(path)) } -// (二进制)读取文件内容,如果文件不存在或者读取失败,返回nil。 +// GetBinContents returns the file content of as []byte. +// It returns nil if it fails reading. func GetBinContents(path string) []byte { data, err := ioutil.ReadFile(path) if err != nil { @@ -33,16 +33,16 @@ func GetBinContents(path string) []byte { return data } -// 写入文件内容 +// putContents puts binary content to file of . func putContents(path string, data []byte, flag int, perm int) error { - // 支持目录递归创建 + // It supports creating file of recursively. dir := Dir(path) if !Exists(dir) { if err := Mkdir(dir); err != nil { return err } } - // 创建/打开文件 + // Opening file with given and . f, err := OpenWithFlagPerm(path, flag, perm) if err != nil { return err @@ -56,32 +56,36 @@ func putContents(path string, data []byte, flag int, perm int) error { return nil } -// Truncate +// Truncate truncates file of to given size by . func Truncate(path string, size int) error { return os.Truncate(path, int64(size)) } -// (文本)写入文件内容 +// PutContents puts string to file of . +// It creates file of recursively if it does not exist. func PutContents(path string, content string) error { return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, gDEFAULT_PERM) } -// (文本)追加内容到文件末尾 +// PutContentsAppend appends string to file of . +// It creates file of recursively if it does not exist. func PutContentsAppend(path string, content string) error { return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_APPEND, gDEFAULT_PERM) } -// (二进制)写入文件内容 +// PutBinContents puts binary to file of . +// It creates file of recursively if it does not exist. func PutBinContents(path string, content []byte) error { return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, gDEFAULT_PERM) } -// (二进制)追加内容到文件末尾 +// PutBinContentsAppend appends binary to file of . +// It creates file of recursively if it does not exist. func PutBinContentsAppend(path string, content []byte) error { return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_APPEND, gDEFAULT_PERM) } -// 获得文件内容下一个指定字节的位置 +// GetNextCharOffset returns the file offset for given starting from . func GetNextCharOffset(reader io.ReaderAt, char byte, start int64) int64 { buffer := make([]byte, gREAD_BUFFER) offset := start @@ -100,7 +104,8 @@ func GetNextCharOffset(reader io.ReaderAt, char byte, start int64) int64 { return -1 } -// 获得文件内容下一个指定字节的位置 +// GetNextCharOffsetByPath returns the file offset for given starting from . +// It opens file of for reading with os.O_RDONLY flag and default perm. func GetNextCharOffsetByPath(path string, char byte, start int64) int64 { if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { defer f.Close() @@ -109,7 +114,10 @@ func GetNextCharOffsetByPath(path string, char byte, start int64) int64 { return -1 } -// 获得文件内容直到下一个指定字节的位置(返回值包含该位置字符内容) +// GetBinContentsTilChar returns the contents of the file as []byte +// until the next specified byte position. +// +// Note: Returned value contains the character of the last position. func GetBinContentsTilChar(reader io.ReaderAt, char byte, start int64) ([]byte, int64) { if offset := GetNextCharOffset(reader, char, start); offset != -1 { return GetBinContentsByTwoOffsets(reader, start, offset + 1), offset @@ -117,7 +125,11 @@ func GetBinContentsTilChar(reader io.ReaderAt, char byte, start int64) ([]byte, return nil, -1 } -// 获得文件内容直到下一个指定字节的位置(返回值包含该位置字符内容) +// GetBinContentsTilCharByPath returns the contents of the file given by as []byte +// until the next specified byte position. +// It opens file of for reading with os.O_RDONLY flag and default perm. +// +// Note: Returned value contains the character of the last position. func GetBinContentsTilCharByPath(path string, char byte, start int64) ([]byte, int64) { if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { defer f.Close() @@ -126,7 +138,9 @@ func GetBinContentsTilCharByPath(path string, char byte, start int64) ([]byte, i return nil, -1 } -// 获得文件内容中两个offset之间的内容 [start, end) +// GetBinContentsByTwoOffsets returns the binary content as []byte from to . +// Note: Returned value does not contain the character of the last position, which means +// it returns content range as [start, end). func GetBinContentsByTwoOffsets(reader io.ReaderAt, start int64, end int64) []byte { buffer := make([]byte, end - start) if _, err := reader.ReadAt(buffer, start); err != nil { @@ -135,7 +149,10 @@ func GetBinContentsByTwoOffsets(reader io.ReaderAt, start int64, end int64) []by return buffer } -// 获得文件内容中两个offset之间的内容 [start, end) +// GetBinContentsByTwoOffsetsByPath returns the binary content as []byte from to . +// Note: Returned value does not contain the character of the last position, which means +// it returns content range as [start, end). +// It opens file of for reading with os.O_RDONLY flag and default perm. func GetBinContentsByTwoOffsetsByPath(path string, start int64, end int64) []byte { if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { defer f.Close() diff --git a/g/os/gfile/gfile_search.go b/g/os/gfile/gfile_search.go index fbc6e10f0..acce3d0eb 100644 --- a/g/os/gfile/gfile_search.go +++ b/g/os/gfile/gfile_search.go @@ -13,31 +13,25 @@ import ( "github.com/gogf/gf/g/container/garray" ) - -// 如果给定绝对路径将会去掉其中的相对路径符号后返回; -// 如果是给定的相对路径,那么将会按照以下路径优先级搜索文件(重复路径会去重): -// prioritySearchPaths、当前工作目录、二进制文件目录、源码main包目录(开发环境下) +// Search searches file by name in following paths with priority: +// prioritySearchPaths, Pwd()、SelfDir()、MainPkgPath(). +// It returns the absolute file path of if found, or en empty string if not found. func Search(name string, prioritySearchPaths...string) (realPath string, err error) { - // 是否绝对路径 + // Check if it's a absolute path. realPath = RealPath(name) if realPath != "" { return } - // 相对路径搜索 + // Search paths array. array := garray.NewStringArray(true) - // 自定义优先路径 array.Append(prioritySearchPaths...) - // 用户工作目录 - array.Append(Pwd()) - // 二进制所在目录 - array.Append(SelfDir()) - // 源码main包目录 + array.Append(Pwd(), SelfDir()) if path := MainPkgPath(); path != "" { array.Append(path) } - // 路径去重 + // Remove repeated items. array.Unique() - // 执行相对路径搜索 + // Do the searching. array.RLockFunc(func(array []string) { path := "" for _, v := range array { @@ -48,7 +42,7 @@ func Search(name string, prioritySearchPaths...string) (realPath string, err err } } }) - // 目录不存在错误处理 + // If it fails searching, it returns formatted error. if realPath == "" { buffer := bytes.NewBuffer(nil) buffer.WriteString(fmt.Sprintf("cannot find file/folder \"%s\" in following paths:", name)) diff --git a/g/os/gfile/gfile_size.go b/g/os/gfile/gfile_size.go index 463b443ba..df37205b2 100644 --- a/g/os/gfile/gfile_size.go +++ b/g/os/gfile/gfile_size.go @@ -11,7 +11,7 @@ import ( "os" ) -// 文件大小(bytes) +// Size returns the size of file specified by in byte. func Size(path string) int64 { s, e := os.Stat(path) if e != nil { @@ -20,12 +20,12 @@ func Size(path string) int64 { return s.Size() } -// 格式化文件大小 +// ReadableSize formats size of file given by , for more human readable. func ReadableSize(path string) string { return FormatSize(float64(Size(path))) } -// 格式化文件大小 +// FormatSize formats size for more human readable. func FormatSize(raw float64) string { var t float64 = 1024 var d float64 = 1 diff --git a/g/os/gfile/gfile_time.go b/g/os/gfile/gfile_time.go index 9391c1827..a2e138fc6 100644 --- a/g/os/gfile/gfile_time.go +++ b/g/os/gfile/gfile_time.go @@ -10,7 +10,7 @@ import ( "os" ) -// 文件修改时间(时间戳,秒) +// MTime returns the modification time of file given by in second. func MTime(path string) int64 { s, e := os.Stat(path) if e != nil { @@ -19,7 +19,7 @@ func MTime(path string) int64 { return s.ModTime().Unix() } -// 文件修改时间(时间戳,毫秒) +// MTimeMillisecond returns the modification time of file given by in millisecond. func MTimeMillisecond(path string) int64 { s, e := os.Stat(path) if e != nil { diff --git a/g/os/gfile/gfile_contents_test.go b/g/os/gfile/gfile_z_contents_test.go similarity index 100% rename from g/os/gfile/gfile_contents_test.go rename to g/os/gfile/gfile_z_contents_test.go diff --git a/g/os/gfile/gfile_search_test.go b/g/os/gfile/gfile_z_search_test.go similarity index 100% rename from g/os/gfile/gfile_search_test.go rename to g/os/gfile/gfile_z_search_test.go diff --git a/g/os/gfile/gfile_size_test.go b/g/os/gfile/gfile_z_size_test.go similarity index 100% rename from g/os/gfile/gfile_size_test.go rename to g/os/gfile/gfile_z_size_test.go diff --git a/g/os/gfile/gfile_test.go b/g/os/gfile/gfile_z_test.go similarity index 100% rename from g/os/gfile/gfile_test.go rename to g/os/gfile/gfile_z_test.go diff --git a/g/os/gfile/gfile_time_test.go b/g/os/gfile/gfile_z_time_test.go similarity index 100% rename from g/os/gfile/gfile_time_test.go rename to g/os/gfile/gfile_z_time_test.go From 6b4763c7da6452e3a131c2e31e36e370dc0f2f10 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 27 May 2019 11:00:04 +0800 Subject: [PATCH 19/50] Update README.MD README updates. --- README.MD | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.MD b/README.MD index 06202e302..96b60d09b 100644 --- a/README.MD +++ b/README.MD @@ -107,11 +107,10 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee - + From 41f33af51b3383a2e79e588c213f7591e2f27fe1 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 28 May 2019 21:41:00 +0800 Subject: [PATCH 20/50] add gconv.StructDeep/MapDeep functions for gconv; add struct inherit converting support for gdb --- g/container/gvar/gvar.go | 6 +- g/database/gdb/gdb_base.go | 8 +- g/database/gdb/gdb_func.go | 49 ++++---- g/database/gdb/gdb_model.go | 4 +- g/database/gdb/gdb_type_record.go | 7 +- .../gdb/gdb_unit_struct_inherit_test.go | 99 ++++++++++++++++ g/net/ghttp/ghttp_request_post.go | 8 +- g/net/ghttp/ghttp_request_query.go | 6 +- g/net/ghttp/ghttp_request_request.go | 6 +- g/util/gconv/gconv_map.go | 40 +++++-- g/util/gconv/gconv_struct.go | 106 ++++++++++++------ g/util/gconv/gconv_z_unit_map_test.go | 47 ++++---- g/util/gconv/gconv_z_unit_struct_test.go | 42 ++++++- 13 files changed, 314 insertions(+), 114 deletions(-) create mode 100644 g/database/gdb/gdb_unit_struct_inherit_test.go diff --git a/g/container/gvar/gvar.go b/g/container/gvar/gvar.go index 16e573fe0..62e2ea20a 100644 --- a/g/container/gvar/gvar.go +++ b/g/container/gvar/gvar.go @@ -80,9 +80,9 @@ func (v *Var) GTime(format...string) *gtime.Time { // Struct maps value of to . // The param should be a pointer to a struct instance. -// The param is used to specify the key-to-attribute mapping rules. -func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error { - return gconv.Struct(v.Val(), objPointer, attrMapping...) +// The param is used to specify the key-to-attribute mapping rules. +func (v *Var) Struct(pointer interface{}, mapping...map[string]string) error { + return gconv.Struct(v.Val(), pointer, mapping...) } func (v *Var) IsNil() bool { return v.Val() == nil } diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index d16be23d1..ebc745c49 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -312,7 +312,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i return bs.db.doBatchInsert(link, table, data, option, batch...) case reflect.Map: fallthrough case reflect.Struct: - dataMap = gconv.Map(data) + dataMap = structToMap(data) default: return result, errors.New(fmt.Sprint("unsupported data type:", kind)) } @@ -390,11 +390,11 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt case reflect.Array: listMap = make(List, rv.Len()) for i := 0; i < rv.Len(); i++ { - listMap[i] = gconv.Map(rv.Index(i).Interface()) + listMap[i] = structToMap(rv.Index(i).Interface()) } case reflect.Map: fallthrough case reflect.Struct: - listMap = List{Map(gconv.Map(list))} + listMap = List{Map(structToMap(list))} default: return result, errors.New(fmt.Sprint("unsupported list type:", kind)) } @@ -504,7 +504,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio case reflect.Map: fallthrough case reflect.Struct: var fields []string - for k, v := range gconv.Map(data) { + for k, v := range structToMap(data) { fields = append(fields, fmt.Sprintf("%s%s%s=?", charL, k, charR)) params = append(params, convertParam(v)) } diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go index e7e8b8181..374def4ee 100644 --- a/g/database/gdb/gdb_func.go +++ b/g/database/gdb/gdb_func.go @@ -7,16 +7,16 @@ package gdb import ( - "bytes" - "errors" - "fmt" - "github.com/gogf/gf/g/os/glog" - "github.com/gogf/gf/g/os/gtime" - "github.com/gogf/gf/g/text/gregex" - "github.com/gogf/gf/g/text/gstr" - "github.com/gogf/gf/g/util/gconv" - "reflect" - "strings" + "bytes" + "errors" + "fmt" + "github.com/gogf/gf/g/os/glog" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/text/gregex" + "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/util/gconv" + "reflect" + "strings" "time" ) @@ -41,7 +41,7 @@ func formatCondition(where interface{}, args []interface{}) (newWhere string, ne // map/struct类型 case reflect.Map: fallthrough case reflect.Struct: - for key, value := range gconv.Map(where) { + for key, value := range structToMap(where) { if buffer.Len() > 0 { buffer.WriteString(" AND ") } @@ -170,12 +170,12 @@ func printSql(v *Sql) { // 格式化错误信息 func formatError(err error, query string, args ...interface{}) error { if err != nil { - errstr := fmt.Sprintf("DB ERROR: %s\n", err.Error()) - errstr += fmt.Sprintf("DB QUERY: %s\n", query) + errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error()) + errStr += fmt.Sprintf("DB QUERY: %s\n", query) if len(args) > 0 { - errstr += fmt.Sprintf("DB PARAM: %v\n", args) + errStr += fmt.Sprintf("DB PARAM: %v\n", args) } - err = errors.New(errstr) + err = errors.New(errStr) } return err } @@ -197,8 +197,8 @@ func getInsertOperationByOption(option int) string { // 该方法用于将变量传递给数据库执行之前。 func structToMap(obj interface{}) map[string]interface{} { data := gconv.Map(obj) - for k, v := range data { - rv := reflect.ValueOf(v) + for key, value := range data { + rv := reflect.ValueOf(value) kind := rv.Kind() if kind == reflect.Ptr { rv = rv.Elem() @@ -207,17 +207,24 @@ func structToMap(obj interface{}) map[string]interface{} { switch kind { case reflect.Struct: // 底层数据库引擎支持 time.Time 类型 - if _, ok := v.(time.Time); ok { + if _, ok := value.(time.Time); ok { continue } // 如果执行String方法,那么执行字符串转换 - if s, ok := v.(apiString); ok { - data[k] = s.String() + if s, ok := value.(apiString); ok { + data[key] = s.String() + continue } - for k, v := range structToMap(v) { + delete(data, key) + for k, v := range structToMap(value) { data[k] = v } } } return data +} + +// 使用递归的方式将map键值对映射到struct对象上,注意参数是一个指向struct的指针。 +func mapToStruct(data map[string]interface{}, pointer interface{}) error { + return gconv.StructDeep(data, pointer) } \ No newline at end of file diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index f039a64ae..e53044cde 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -268,12 +268,12 @@ func (md *Model) Data(data ...interface{}) *Model { case reflect.Array: list := make(List, rv.Len()) for i := 0; i < rv.Len(); i++ { - list[i] = gconv.Map(rv.Index(i).Interface()) + list[i] = structToMap(rv.Index(i).Interface()) } model.data = list case reflect.Map: fallthrough case reflect.Struct: - model.data = Map(gconv.Map(data[0])) + model.data = Map(structToMap(data[0])) default: model.data = data[0] } diff --git a/g/database/gdb/gdb_type_record.go b/g/database/gdb/gdb_type_record.go index 16e3f51c9..8d9597666 100644 --- a/g/database/gdb/gdb_type_record.go +++ b/g/database/gdb/gdb_type_record.go @@ -7,8 +7,7 @@ package gdb import ( - "github.com/gogf/gf/g/encoding/gparser" - "github.com/gogf/gf/g/util/gconv" + "github.com/gogf/gf/g/encoding/gparser" ) // 将记录结果转换为JSON字符串 @@ -33,6 +32,6 @@ func (r Record) ToMap() Map { } // 将Map变量映射到指定的struct对象中,注意参数应当是一个对象的指针 -func (r Record) ToStruct(objPointer interface{}) error { - return gconv.Struct(r.ToMap(), objPointer) +func (r Record) ToStruct(pointer interface{}) error { + return mapToStruct(r.ToMap(), pointer) } diff --git a/g/database/gdb/gdb_unit_struct_inherit_test.go b/g/database/gdb/gdb_unit_struct_inherit_test.go new file mode 100644 index 000000000..dfdd09eef --- /dev/null +++ b/g/database/gdb/gdb_unit_struct_inherit_test.go @@ -0,0 +1,99 @@ +// Copyright 2019 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. + +package gdb_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func TestModel_Inherit_Insert(t *testing.T) { + gtest.Case(t, func() { + type Base struct { + Id int `json:"id"` + Uid int `json:"uid"` + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + result, err := db.Table("user").Filter().Data(User{ + Passport : "john-test", + Password : "123456", + Nickname : "John", + Base : Base { + Id : 100, + Uid : 100, + CreateTime : gtime.Now().String(), + }, + }).Insert() + gtest.Assert(err, nil) + n, _ := result.RowsAffected() + gtest.Assert(n, 1) + value, err := db.Table("user").Fields("passport").Where("id=100").Value() + gtest.Assert(err, nil) + gtest.Assert(value.String(), "john-test") + // Delete this test data. + _, err = db.Table("user").Where("id", 100).Delete() + gtest.Assert(err, nil) + }) +} + +func TestModel_Inherit_MapToStruct(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + data := g.Map{ + "id" : 100, + "uid" : 101, + "passport" : "t1", + "password" : "123456", + "nickname" : "T1", + "create_time" : gtime.Now().String(), + } + result, err := db.Table("user").Filter().Data(data).Insert() + gtest.Assert(err, nil) + n, _ := result.RowsAffected() + gtest.Assert(n, 1) + + one, err := db.Table("user").Where("id=100").One() + gtest.Assert(err, nil) + + user := new(User) + + gtest.Assert(one.ToStruct(user), nil) + gtest.Assert(user.Id, data["id"]) + gtest.Assert(user.Passport, data["passport"]) + gtest.Assert(user.Password, data["password"]) + gtest.Assert(user.Nickname, data["nickname"]) + gtest.Assert(user.CreateTime, data["create_time"]) + + // Delete this test data. + _, err = db.Table("user").Where("id", 100).Delete() + gtest.Assert(err, nil) + }) + +} + + diff --git a/g/net/ghttp/ghttp_request_post.go b/g/net/ghttp/ghttp_request_post.go index f78c3bfbb..8255572c9 100644 --- a/g/net/ghttp/ghttp_request_post.go +++ b/g/net/ghttp/ghttp_request_post.go @@ -142,16 +142,16 @@ func (r *Request) GetPostMap(def...map[string]string) map[string]string { } // 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 -func (r *Request) GetPostToStruct(object interface{}, mapping...map[string]string) error { - tagmap := r.getStructParamsTagMap(object) +func (r *Request) GetPostToStruct(pointer interface{}, mapping...map[string]string) error { + tagMap := r.getStructParamsTagMap(pointer) if len(mapping) > 0 { for k, v := range mapping[0] { - tagmap[k] = v + tagMap[k] = v } } params := make(map[string]interface{}) for k, v := range r.GetPostMap() { params[k] = v } - return gconv.Struct(params, object, tagmap) + return gconv.Struct(params, pointer, tagMap) } \ No newline at end of file diff --git a/g/net/ghttp/ghttp_request_query.go b/g/net/ghttp/ghttp_request_query.go index bfc437aef..0b399ce64 100644 --- a/g/net/ghttp/ghttp_request_query.go +++ b/g/net/ghttp/ghttp_request_query.go @@ -150,8 +150,8 @@ func (r *Request) GetQueryMap(def... map[string]string) map[string]string { } // 将所有的get参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 -func (r *Request) GetQueryToStruct(object interface{}, mapping...map[string]string) error { - tagmap := r.getStructParamsTagMap(object) +func (r *Request) GetQueryToStruct(pointer interface{}, mapping...map[string]string) error { + tagmap := r.getStructParamsTagMap(pointer) if len(mapping) > 0 { for k, v := range mapping[0] { tagmap[k] = v @@ -161,5 +161,5 @@ func (r *Request) GetQueryToStruct(object interface{}, mapping...map[string]stri for k, v := range r.GetQueryMap() { params[k] = v } - return gconv.Struct(params, object, tagmap) + return gconv.Struct(params, pointer, tagmap) } \ No newline at end of file diff --git a/g/net/ghttp/ghttp_request_request.go b/g/net/ghttp/ghttp_request_request.go index f51cd4b79..a836903a3 100644 --- a/g/net/ghttp/ghttp_request_request.go +++ b/g/net/ghttp/ghttp_request_request.go @@ -133,10 +133,10 @@ func (r *Request) GetRequestMap(def...map[string]string) map[string]string { // 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 func (r *Request) GetRequestToStruct(pointer interface{}, mapping...map[string]string) error { - tagmap := r.getStructParamsTagMap(pointer) + tagMap := r.getStructParamsTagMap(pointer) if len(mapping) > 0 { for k, v := range mapping[0] { - tagmap[k] = v + tagMap[k] = v } } params := make(map[string]interface{}) @@ -148,6 +148,6 @@ func (r *Request) GetRequestToStruct(pointer interface{}, mapping...map[string]s params = j.ToMap() } } - return gconv.Struct(params, pointer, tagmap) + return gconv.Struct(params, pointer, tagMap) } diff --git a/g/util/gconv/gconv_map.go b/g/util/gconv/gconv_map.go index 6da49ee48..bc9cca1a5 100644 --- a/g/util/gconv/gconv_map.go +++ b/g/util/gconv/gconv_map.go @@ -7,18 +7,19 @@ package gconv import ( - "github.com/gogf/gf/g/internal/empty" - "github.com/gogf/gf/g/text/gstr" - "reflect" - "strings" + "github.com/gogf/gf/g/internal/empty" + "github.com/gogf/gf/g/text/gstr" + "reflect" + "strings" ) const ( gGCONV_TAG = "gconv" ) -// Map converts any variable to map[string]interface{}. -// If the parameter is not a map type, then the conversion will fail and returns nil. -// If is a struct object, the second parameter specifies the most priority + +// Map converts any variable to map[string]interface{}. +// If the parameter is not a map type, then the conversion will fail and returns nil. +// If is a struct object, the second parameter specifies the most priority // tags that will be detected, otherwise it detects the tags in order of: gconv, json. func Map(value interface{}, tags...string) map[string]interface{} { if value == nil { @@ -27,7 +28,7 @@ func Map(value interface{}, tags...string) map[string]interface{} { if r, ok := value.(map[string]interface{}); ok { return r } else { - // Only assert the common combination type of maps, and finally use reflection. + // Only assert the common combination of types, and finally it uses reflection. m := make(map[string]interface{}) switch value.(type) { case map[interface{}]interface{}: @@ -74,7 +75,6 @@ func Map(value interface{}, tags...string) map[string]interface{} { for k, v := range value.(map[string]float64) { m[k] = v } - case map[int]interface{}: for k, v := range value.(map[int]interface{}) { m[String(k)] = v @@ -161,3 +161,25 @@ func Map(value interface{}, tags...string) map[string]interface{} { return m } } + +// MapDeep do Map function recursively. +// See Map. +func MapDeep(value interface{}, tags...string) map[string]interface{} { + data := Map(value, tags...) + for key, value := range data { + rv := reflect.ValueOf(value) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Struct: + delete(data, key) + for k, v := range MapDeep(value, tags...) { + data[k] = v + } + } + } + return data +} diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index 879bf7826..8b1b6a325 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2017-2019 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, @@ -15,35 +15,36 @@ import ( "strings" ) -// Struct maps the params key-value pairs to the corresponding struct object properties. -// The third parameter mapping is unnecessary, indicating the mapping between the custom name -// and the attribute name. +// Struct maps the params key-value pairs to the corresponding struct object's properties. +// The third parameter is unnecessary, indicating the mapping rules between the custom key name +// and the attribute name(case sensitive). // // Note: -// 1. The can be any type of may/struct, usually a map; -// 2. The second parameter should be a pointer to the struct object; -// 3. Only the public attributes of struct object can be mapped; +// 1. The can be any type of map/struct, usually a map. +// 2. The second parameter should be a pointer to the struct object. +// 3. Only the public attributes of struct object can be mapped. // 4. If is a map, the key of the map can be lowercase. // It will automatically convert the first letter of the key to uppercase // in mapping procedure to do the matching. -// If it does not match, ignore the key; -func Struct(params interface{}, objPointer interface{}, attrMapping...map[string]string) error { +// It ignores the map key, if it does not match. +func Struct(params interface{}, pointer interface{}, mapping...map[string]string) error { if params == nil { return errors.New("params cannot be nil") } - if objPointer == nil { + if pointer == nil { return errors.New("object pointer cannot be nil") } paramsMap := Map(params) if paramsMap == nil { return fmt.Errorf("invalid params: %v", params) } - // struct的反射对象 + // Using reflect to do the converting, + // it also supports type of reflect.Value for (always in internal usage). elem := reflect.Value{} - if v, ok := objPointer.(reflect.Value); ok { + if v, ok := pointer.(reflect.Value); ok { elem = v } else { - rv := reflect.ValueOf(objPointer) + rv := reflect.ValueOf(pointer) if kind := rv.Kind(); kind != reflect.Ptr { return fmt.Errorf("object pointer should be type of: %v", kind) } @@ -52,12 +53,12 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string } elem = rv.Elem() } - // 已执行过转换的属性,只执行一次转换。 - // 或者是已经执行过转换检查的属性(即使不进行转换), 以便重复判断。 + // It only performs one converting to the same attribute. + // doneMap is used to check repeated converting. doneMap := make(map[string]bool) - // 首先按照传递的映射关系进行匹配 - if len(attrMapping) > 0 && len(attrMapping[0]) > 0 { - for mapK, mapV := range attrMapping[0] { + // It first checks the passed mapping rules. + if len(mapping) > 0 && len(mapping[0]) > 0 { + for mapK, mapV := range mapping[0] { if v, ok := paramsMap[mapK]; ok { doneMap[mapV] = true if err := bindVarToStructAttr(elem, mapV, v); err != nil { @@ -66,25 +67,24 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string } } } - // 其次匹配对象定义时绑定的属性名称, - // 标签映射关系map,如果有的话 - tagMap := getTagMapOfStruct(objPointer) - for tagk, tagv := range tagMap { - if _, ok := doneMap[tagv]; ok { + // It secondly checks the tags of attributes. + tagMap := getTagMapOfStruct(pointer) + for tagK, tagV := range tagMap { + if _, ok := doneMap[tagV]; ok { continue } - if v, ok := paramsMap[tagk]; ok { - doneMap[tagv] = true - if err := bindVarToStructAttr(elem, tagv, v); err != nil { + if v, ok := paramsMap[tagK]; ok { + doneMap[tagV] = true + if err := bindVarToStructAttr(elem, tagV, v); err != nil { return err } } } - // 最后按照默认规则进行匹配 + // It finally do the converting with default rules. attrMap := make(map[string]struct{}) elemType := elem.Type() for i := 0; i < elem.NumField(); i++ { - // 只转换公开属性 + // Only do converting to public attributes. if !gstr.IsLetterUpper(elemType.Field(i).Name[0]) { continue } @@ -105,7 +105,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string if _, ok := tagMap[checkName]; ok { continue } - // 循环查找属性名称进行匹配 + // Loop to find the matched attribute name. for value, _ := range attrMap { if strings.EqualFold(checkName, value) { name = value @@ -121,7 +121,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string break } } - // 如果没有匹配到属性名称,放弃 + // No matching, give up this attribute converting. if name == "" { continue } @@ -132,15 +132,49 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string return nil } +// StructDeep do Struct function recursively. +// See Struct. +func StructDeep(params interface{}, pointer interface{}, mapping...map[string]string) error { + if err := Struct(params, pointer, mapping...); err != nil { + return err + } else { + rv, ok := pointer.(reflect.Value) + if !ok { + rv = reflect.ValueOf(pointer) + } + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Struct: + rt := rv.Type() + for i := 0; i < rv.NumField(); i++ { + // Only do converting to public attributes. + if !gstr.IsLetterUpper(rt.Field(i).Name[0]) { + continue + } + trv := rv.Field(i) + switch trv.Kind() { + case reflect.Struct: + StructDeep(params, trv, mapping...) + } + } + } + } + return nil +} + // 解析指针对象的tag -func getTagMapOfStruct(objPointer interface{}) map[string]string { - tagmap := make(map[string]string) +func getTagMapOfStruct(pointer interface{}) map[string]string { + tagMap := make(map[string]string) // 反射类型判断 fields := ([]*structs.Field)(nil) - if v, ok := objPointer.(reflect.Value); ok { + if v, ok := pointer.(reflect.Value); ok { fields = structs.Fields(v.Interface()) } else { - fields = structs.Fields(objPointer) + fields = structs.Fields(pointer) } // 将struct中定义的属性转换名称构建成tagmap for _, field := range fields { @@ -150,11 +184,11 @@ func getTagMapOfStruct(objPointer interface{}) map[string]string { } if tag != "" { for _, v := range strings.Split(tag, ",") { - tagmap[strings.TrimSpace(v)] = field.Name() + tagMap[strings.TrimSpace(v)] = field.Name() } } } - return tagmap + return tagMap } // 将参数值绑定到对象指定名称的属性上 diff --git a/g/util/gconv/gconv_z_unit_map_test.go b/g/util/gconv/gconv_z_unit_map_test.go index 15d2ac16a..e9b4c3f40 100644 --- a/g/util/gconv/gconv_z_unit_map_test.go +++ b/g/util/gconv/gconv_z_unit_map_test.go @@ -123,23 +123,30 @@ func Test_Map_PrivateAttribute(t *testing.T) { gtest.Assert(gconv.Map(user), g.Map{"Id" : 1}) }) } -// -//func Test_Map_StructInherit(t *testing.T) { -// type Base struct { -// Id int -// } -// type User struct { -// Base -// Name string -// } -// gtest.Case(t, func() { -// user := &User{ -// Base : Base { -// Id : 100, -// }, -// Name : "john", -// } -// fmt.Println(gconv.Map(user)) -// //gtest.Assert(gconv.Map(user), g.Map{"Id" : 1}) -// }) -//} \ No newline at end of file + +func Test_Map_StructInherit(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + user := new(User) + user.Id = 100 + user.Nickname = "john" + user.CreateTime = "2019" + m := gconv.MapDeep(user) + gtest.Assert(m["id"], user.Id) + gtest.Assert(m["nickname"], user.Nickname) + gtest.Assert(m["create_time"], user.CreateTime) + }) +} \ No newline at end of file diff --git a/g/util/gconv/gconv_z_unit_struct_test.go b/g/util/gconv/gconv_z_unit_struct_test.go index 78af7942c..c2426083a 100644 --- a/g/util/gconv/gconv_z_unit_struct_test.go +++ b/g/util/gconv/gconv_z_unit_struct_test.go @@ -7,10 +7,10 @@ package gconv_test import ( - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/test/gtest" - "github.com/gogf/gf/g/util/gconv" - "testing" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gconv" + "testing" ) func Test_Struct_Basic1(t *testing.T) { @@ -302,7 +302,6 @@ func Test_Struct_Attr_Struct_Slice_Ptr(t *testing.T) { }) } -// 私有属性不会进行转换 func Test_Struct_PrivateAttribute(t *testing.T) { type User struct { Id int @@ -315,4 +314,37 @@ func Test_Struct_PrivateAttribute(t *testing.T) { gtest.Assert(user.Id, 1) gtest.Assert(user.name, "") }) +} + +func Test_Struct_Deep(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + data := g.Map{ + "id" : 100, + "uid" : 101, + "passport" : "t1", + "password" : "123456", + "nickname" : "T1", + "create_time" : "2019", + } + user := new(User) + gconv.StructDeep(data, user) + gtest.Assert(user.Id, 100) + gtest.Assert(user.Uid, 101) + gtest.Assert(user.Nickname, "T1") + gtest.Assert(user.CreateTime, "2019") + }) } \ No newline at end of file From 7034e2015ebd6c472364c0ae67779e5a2638f9fe Mon Sep 17 00:00:00 2001 From: John Date: Wed, 29 May 2019 11:25:11 +0800 Subject: [PATCH 21/50] add FormatTo/LayoutTo functions for gtime; use custom ExpireFunc to close poool items when pool is close for gpool --- .../{gchan_test.go => gchan_bench_test.go} | 0 g/container/gpool/gpool.go | 23 ++- .../{gpool_test.go => gpool_bench_test.go} | 7 +- g/os/gtime/gtime_format.go | 134 ++++++++++-------- g/os/gtime/gtime_time.go | 22 +-- g/os/gtime/gtime_z_unit_format_test.go | 15 ++ g/os/gtime/gtime_z_unit_time_test.go | 2 +- g/util/gconv/gconv_struct.go | 6 +- geg/container/gpool/gpool_expire.go | 16 +++ geg/other/test.go | 35 ++--- 10 files changed, 151 insertions(+), 109 deletions(-) rename g/container/gchan/{gchan_test.go => gchan_bench_test.go} (100%) rename g/container/gpool/{gpool_test.go => gpool_bench_test.go} (87%) create mode 100644 geg/container/gpool/gpool_expire.go diff --git a/g/container/gchan/gchan_test.go b/g/container/gchan/gchan_bench_test.go similarity index 100% rename from g/container/gchan/gchan_test.go rename to g/container/gchan/gchan_bench_test.go diff --git a/g/container/gpool/gpool.go b/g/container/gpool/gpool.go index 69bbfc6cc..43f0647d7 100644 --- a/g/container/gpool/gpool.go +++ b/g/container/gpool/gpool.go @@ -43,10 +43,11 @@ type ExpireFunc func(interface{}) // New returns a new object pool. // To ensure execution efficiency, the expiration time cannot be modified once it is set. -// Expire: +// +// Expiration logistics: // expire = 0 : not expired; -// expire < 0 : immediate recovery after use; -// expire > 0 : timeout recovery; +// expire < 0 : immediate expired after use; +// expire > 0 : timeout expired; // Note that the expiration time unit is ** milliseconds **. func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool { r := &Pool { @@ -103,14 +104,26 @@ func (p *Pool) Size() int { return p.list.Len() } -// Close closes the pool. +// Close closes the pool. If

has ExpireFunc, +// then it automatically closes all items using this function before it's closed. func (p *Pool) Close() { p.closed.Set(true) } -// checkExpire secondly removes expired items from pool. +// checkExpire removes expired items from pool every second. func (p *Pool) checkExpire() { if p.closed.Val() { + // If p has ExpireFunc, + // then it must close all items using this function. + if p.ExpireFunc != nil { + for { + if r := p.list.PopFront(); r != nil { + p.ExpireFunc(r.(*poolItem).value) + } else { + break + } + } + } gtimer.Exit() } for { diff --git a/g/container/gpool/gpool_test.go b/g/container/gpool/gpool_bench_test.go similarity index 87% rename from g/container/gpool/gpool_test.go rename to g/container/gpool/gpool_bench_test.go index d86172519..d2f243479 100644 --- a/g/container/gpool/gpool_test.go +++ b/g/container/gpool/gpool_bench_test.go @@ -6,14 +6,15 @@ // go test *.go -bench=".*" -package gpool +package gpool_test import ( - "testing" + "github.com/gogf/gf/g/container/gpool" + "testing" "sync" ) -var pool = New(99999999, nil) +var pool = gpool.New(99999999, nil) var syncp = sync.Pool{} func BenchmarkGPoolPut(b *testing.B) { diff --git a/g/os/gtime/gtime_format.go b/g/os/gtime/gtime_format.go index f0ef77a16..2df378f1f 100644 --- a/g/os/gtime/gtime_format.go +++ b/g/os/gtime/gtime_format.go @@ -75,56 +75,9 @@ var ( dayOfMonth = []int{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334} ) -// 将自定义的格式转换为标准库时间格式 -func formatToStdLayout(format string) string { - b := bytes.NewBuffer(nil) - for i := 0; i < len(format); { - switch format[i] { - case '\\': - if i < len(format)-1 { - b.WriteByte(format[i+1]) - i += 2 - continue - } else { - return b.String() - } - - default: - if f, ok := formats[format[i]]; ok { - // 有几个转换的符号需要特殊处理 - switch format[i] { - case 'j': b.WriteString("02") - case 'G': b.WriteString("15") - case 'u': - if i > 0 && format[i-1] == '.' { - b.WriteString("000") - } else { - b.WriteString(".000") - } - - default: - b.WriteString(f) - } - } else { - b.WriteByte(format[i]) - } - i++ - } - } - return b.String() -} - -// 将format格式转换为正则表达式规则 -func formatToRegexPattern(format string) string { - s := gregex.Quote(formatToStdLayout(format)) - s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s) - s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s) - return s -} - -// 格式化,使用自定义日期格式 +// 使用自定义日期格式格式化输出日期。 func (t *Time) Format(format string) string { - runes := []rune(format) + runes := []rune(format) buffer := bytes.NewBuffer(nil) for i := 0; i < len(runes); { switch runes[i] { @@ -165,14 +118,21 @@ func (t *Time) Format(format string) string { return buffer.String() } -// 每月天数后面的英文后缀,2 个字符st nd,rd 或者 th -func formatMonthDaySuffixMap(day string) string { - switch day { - case "01": return "st" - case "02": return "nd" - case "03": return "rd" - default: return "th" - } +// 通过自定义格式转换当前日期为新的日期。 +func (t *Time) FormatTo(format string) *Time { + t.Time = NewFromStr(t.Format(format)).Time + return t +} + +// 使用标准库格式格式化输出日期。 +func (t *Time) Layout(layout string) string { + return t.Time.Format(layout) +} + +// 通过标准库格式转换当前日期为新的日期。 +func (t *Time) LayoutTo(layout string) *Time { + t.Time = NewFromStr(t.Layout(layout)).Time + return t } // 返回是否是润年 @@ -221,7 +181,61 @@ func (t *Time) WeeksOfYear() int { return week } -// 格式化使用标准库格式 -func (t *Time) Layout(layout string) string { - return t.Time.Format(layout) +// 将自定义的格式转换为标准库时间格式 +func formatToStdLayout(format string) string { + b := bytes.NewBuffer(nil) + for i := 0; i < len(format); { + switch format[i] { + case '\\': + if i < len(format)-1 { + b.WriteByte(format[i+1]) + i += 2 + continue + } else { + return b.String() + } + + default: + if f, ok := formats[format[i]]; ok { + // 有几个转换的符号需要特殊处理 + switch format[i] { + case 'j': b.WriteString("02") + case 'G': b.WriteString("15") + case 'u': + if i > 0 && format[i-1] == '.' { + b.WriteString("000") + } else { + b.WriteString(".000") + } + + default: + b.WriteString(f) + } + } else { + b.WriteByte(format[i]) + } + i++ + } + } + return b.String() } + +// 将format格式转换为正则表达式规则 +func formatToRegexPattern(format string) string { + s := gregex.Quote(formatToStdLayout(format)) + s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s) + s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s) + return s +} + +// 每月天数后面的英文后缀,2 个字符st nd,rd 或者 th +func formatMonthDaySuffixMap(day string) string { + switch day { + case "01": return "st" + case "02": return "nd" + case "03": return "rd" + default: return "th" + } +} + + diff --git a/g/os/gtime/gtime_time.go b/g/os/gtime/gtime_time.go index 3256945e6..f017472cb 100644 --- a/g/os/gtime/gtime_time.go +++ b/g/os/gtime/gtime_time.go @@ -13,7 +13,7 @@ type Time struct { } // 创建一个空的时间对象,参数可以是标准库时间对象,可选 -func New (t...time.Time) *Time { +func New(t...time.Time) *Time { if len(t) > 0 { return NewFromTime(t[0]) } @@ -30,14 +30,14 @@ func Now() *Time { } // 标准时间对象转换为自定义的时间对象 -func NewFromTime (t time.Time) *Time { +func NewFromTime(t time.Time) *Time { return &Time{ t, } } // 从字符串转换为时间对象,复杂的时间字符串需要给定格式 -func NewFromStr (str string) *Time { +func NewFromStr(str string) *Time { if t, err := StrToTime(str); err == nil { return t } @@ -45,7 +45,7 @@ func NewFromStr (str string) *Time { } // 从字符串转换为时间对象,指定字符串时间格式,format格式形如:Y-m-d H:i:s -func NewFromStrFormat (str string, format string) *Time { +func NewFromStrFormat(str string, format string) *Time { if t, err := StrToTimeFormat(str, format); err == nil { return t } @@ -53,7 +53,7 @@ func NewFromStrFormat (str string, format string) *Time { } // 从字符串转换为时间对象,通过标准库layout格式进行解析,layout格式形如:2006-01-02 15:04:05 -func NewFromStrLayout (str string, layout string) *Time { +func NewFromStrLayout(str string, layout string) *Time { if t, err := StrToTimeLayout(str, layout); err == nil { return t } @@ -61,7 +61,7 @@ func NewFromStrLayout (str string, layout string) *Time { } // 时间戳转换为时间对象,时间戳支持到纳秒的数值 -func NewFromTimeStamp (timestamp int64) *Time { +func NewFromTimeStamp(timestamp int64) *Time { if timestamp == 0 { return &Time {} } @@ -98,7 +98,8 @@ func (t *Time) String() string { return t.Format("Y-m-d H:i:s") } -// 转换为标准库日期对象 +// Deprecated. +// Directly use t.Time instead. func (t *Time) ToTime() time.Time { return t.Time } @@ -121,13 +122,12 @@ func (t *Time) ToLocation(location *time.Location) *Time { } // 时区转换为指定的时区(通过时区名称,如:Asia/Shanghai) -func (t *Time) ToZone(zone string) *Time { +func (t *Time) ToZone(zone string) (*Time, error) { if l, err := time.LoadLocation(zone); err == nil { t.Time = t.Time.In(l) - return t + return t, nil } else { - //panic(err) - return nil + return nil, err } } diff --git a/g/os/gtime/gtime_z_unit_format_test.go b/g/os/gtime/gtime_z_unit_format_test.go index 6a67df146..d4abb1b11 100644 --- a/g/os/gtime/gtime_z_unit_format_test.go +++ b/g/os/gtime/gtime_z_unit_format_test.go @@ -85,9 +85,24 @@ func Test_Format(t *testing.T) { }) } +func Test_FormatTo(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.FormatTo("Y-m-01 00:00:01"), timeTemp.Time.Format("2006-01") + "-01 00:00:01") + }) +} + + func Test_Layout(t *testing.T) { gtest.Case(t, func() { timeTemp := gtime.Now() gtest.Assert(timeTemp.Layout("2006-01-02 15:04:05"), timeTemp.Time.Format("2006-01-02 15:04:05")) }) } + +func Test_LayoutTo(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.LayoutTo("2006-01-02 00:00:00"), timeTemp.Time.Format("2006-01-02 00:00:00")) + }) +} diff --git a/g/os/gtime/gtime_z_unit_time_test.go b/g/os/gtime/gtime_z_unit_time_test.go index 22dbe5e46..f00b2d760 100644 --- a/g/os/gtime/gtime_z_unit_time_test.go +++ b/g/os/gtime/gtime_z_unit_time_test.go @@ -137,7 +137,7 @@ func Test_ToZone(t *testing.T) { timeTemp.ToLocation(loc) gtest.Assert(timeTemp.Time.Location().String(), "Asia/Shanghai") - timeTemp1 := timeTemp.ToZone("errZone") + timeTemp1, _ := timeTemp.ToZone("errZone") if timeTemp1 != nil { t.Error("test fail") } diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index 8b1b6a325..e84455496 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -40,10 +40,8 @@ func Struct(params interface{}, pointer interface{}, mapping...map[string]string } // Using reflect to do the converting, // it also supports type of reflect.Value for (always in internal usage). - elem := reflect.Value{} - if v, ok := pointer.(reflect.Value); ok { - elem = v - } else { + elem, ok := pointer.(reflect.Value) + if !ok { rv := reflect.ValueOf(pointer) if kind := rv.Kind(); kind != reflect.Ptr { return fmt.Errorf("object pointer should be type of: %v", kind) diff --git a/geg/container/gpool/gpool_expire.go b/geg/container/gpool/gpool_expire.go new file mode 100644 index 000000000..1a047a625 --- /dev/null +++ b/geg/container/gpool/gpool_expire.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/container/gpool" + "time" +) + +func main() { + p := gpool.New(60000, nil, func(i interface{}) { + fmt.Println("expired") + }) + p.Put(1) + time.Sleep(10000*time.Second) + fmt.Println(p.Get()) +} diff --git a/geg/other/test.go b/geg/other/test.go index 4f5b4d622..0d9688c87 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,32 +1,17 @@ package main import ( - "fmt" - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/util/gconv" - "log" - "os" + "github.com/gogf/gf/g/os/glog" + "github.com/gogf/gf/g/os/gtime" ) func main() { - var mylog = log.New(os.Stdout, "[Api] ", log.LstdFlags|log.Lshortfile) - mylog.Println(123) - return - a := []int{1,2,3} - fmt.Println(a[:0]) - return - type Person struct{ - Name string - } - type Staff struct{ - Person - StaffId int - } - staff := &Staff{} - params := g.Map{ - "Name" : "john", - "StaffId" : "10000", - } - gconv.Struct(params, staff) - fmt.Println(staff) + Time := gtime.Now().AddDate(0, -1, 0).Format("Y-m") + glog.Debug(Time) + Time = gtime.Now().AddDate(0, -2, 0).Format("Y-m") + glog.Debug(Time) + Time = gtime.Now().AddDate(0, -3, 0).Format("Y-m") + glog.Debug(Time) + Time = gtime.Now().AddDate(0, -4, 0).Format("Y-m") + glog.Debug(Time) } \ No newline at end of file From 945dd71251a7efb290dd78d831ada5a0b7fe16bd Mon Sep 17 00:00:00 2001 From: John Date: Wed, 29 May 2019 11:53:10 +0800 Subject: [PATCH 22/50] TODO updates --- TODO.MD | 9 ++- g/database/gdb/gdb_mssql.go | 110 ++++++++++++++++---------------- g/database/gdb/gdb_oracle.go | 110 ++++++++++++++++---------------- g/database/gdb/gdb_structure.go | 52 +++++++-------- 4 files changed, 143 insertions(+), 138 deletions(-) diff --git a/TODO.MD b/TODO.MD index 1a4d09dc1..f68a8f50e 100644 --- a/TODO.MD +++ b/TODO.MD @@ -6,7 +6,6 @@ 1. orm增加sqlite对Save方法的支持(去掉触发器语句); 1. ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps); 1. ghttp增加返回数据压缩机制; -1. gview中的template标签失效问题; 1. ghttp.Server增加proxy功能特性,本地proxy和远程proxy,本地即将路由规则映射;远程即反向代理; 1. gjson对大json数据的解析效率问题; 1. ghttp增加route name特性,并同时支持backend和template(提供内置函数)引用,可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上; @@ -46,6 +45,11 @@ 1. gset.Add/Remove/Contains方法增加批量操作支持; 1. gmlock增加手动清理机制:当内存锁不再使用时,由调用端决定是否清理内存锁; 1. gtimer增加DelayAdd*方法返回Entry对象,以便DelayAdd*的定时任务也能进行状态控制;gcron同理需要改进; +1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式; +1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现; + + + # DONE 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; @@ -119,4 +123,5 @@ 1. gfile对于文件的读写强行使用了gfpool,在某些场景下不合适,需要考虑剥离开,并为开发者提供单独的指针池文件操作特性; 1. ghttp.Client自动Close机制; 1. ghttp路由功能增加分组路由特性; -1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射; \ No newline at end of file +1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射; +1. gview中的template标签失效问题; \ No newline at end of file diff --git a/g/database/gdb/gdb_mssql.go b/g/database/gdb/gdb_mssql.go index 7ad243643..bd3de9e08 100644 --- a/g/database/gdb/gdb_mssql.go +++ b/g/database/gdb/gdb_mssql.go @@ -67,13 +67,13 @@ func (db *dbMssql) parseSql(sql string) string { //下面的正则表达式匹配出SELECT和INSERT的关键字后分别做不同的处理,如有LIMIT则将LIMIT的关键字也匹配出 patten := `^\s*(?i)(SELECT)|(LIMIT\s*(\d+)\s*,\s*(\d+))` if gregex.IsMatchString(patten, sql) == false { - fmt.Println("not matched..") + //fmt.Println("not matched..") return sql } res, err := gregex.MatchAllString(patten, sql) if err != nil { - fmt.Println("MatchString error.", err) + //fmt.Println("MatchString error.", err) return "" } @@ -83,69 +83,69 @@ func (db *dbMssql) parseSql(sql string) string { index++ switch keyword { - case "SELECT": - //不含LIMIT关键字则不处理 - if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) { - break - } - - //不含LIMIT则不处理 - if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false { - break - } - - //判断SQL中是否含有order by - selectStr := "" - orderbyStr := "" - haveOrderby := gregex.IsMatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql) - if haveOrderby { - //取order by 前面的字符串 - queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql) - - if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false { + case "SELECT": + //不含LIMIT关键字则不处理 + if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) { break } - selectStr = queryExpr[2] - //取order by表达式的值 - orderbyExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", sql) - if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false { + //不含LIMIT则不处理 + if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false { break } - orderbyStr = orderbyExpr[2] - } else { - queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) - if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false { - break - } - selectStr = queryExpr[2] - } - //取limit后面的取值范围 - first, limit := 0, 0 - for i := 1; i < len(res[index]); i++ { - if len(strings.TrimSpace(res[index][i])) == 0 { - continue - } + //判断SQL中是否含有order by + selectStr := "" + orderbyStr := "" + haveOrderby := gregex.IsMatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql) + if haveOrderby { + //取order by 前面的字符串 + queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql) - if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") { - first, _ = strconv.Atoi(res[index][i+1]) - limit, _ = strconv.Atoi(res[index][i+2]) - break - } - } + if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false { + break + } + selectStr = queryExpr[2] - if haveOrderby { - sql = fmt.Sprintf("SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d", orderbyStr, selectStr, first, limit) - } else { - if first == 0 { - first = limit + //取order by表达式的值 + orderbyExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", sql) + if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false { + break + } + orderbyStr = orderbyExpr[2] } else { - first = limit - first + queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) + if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false { + break + } + selectStr = queryExpr[2] } - sql = fmt.Sprintf("SELECT * FROM (SELECT TOP %d * FROM (SELECT TOP %d %s) as TMP1_ ) as TMP2_ ", first, limit, selectStr) - } - default: + + //取limit后面的取值范围 + first, limit := 0, 0 + for i := 1; i < len(res[index]); i++ { + if len(strings.TrimSpace(res[index][i])) == 0 { + continue + } + + if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") { + first, _ = strconv.Atoi(res[index][i+1]) + limit, _ = strconv.Atoi(res[index][i+2]) + break + } + } + + if haveOrderby { + sql = fmt.Sprintf("SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d", orderbyStr, selectStr, first, limit) + } else { + if first == 0 { + first = limit + } else { + first = limit - first + } + sql = fmt.Sprintf("SELECT * FROM (SELECT TOP %d * FROM (SELECT TOP %d %s) as TMP1_ ) as TMP2_ ", first, limit, selectStr) + } + default: } return sql } diff --git a/g/database/gdb/gdb_oracle.go b/g/database/gdb/gdb_oracle.go index 5df9057c1..17c7d48fc 100644 --- a/g/database/gdb/gdb_oracle.go +++ b/g/database/gdb/gdb_oracle.go @@ -65,80 +65,80 @@ func (db *dbOracle) parseSql(sql string) string { //下面的正则表达式匹配出SELECT和INSERT的关键字后分别做不同的处理,如有LIMIT则将LIMIT的关键字也匹配出 patten := `^\s*(?i)(SELECT)|(INSERT)|(LIMIT\s*(\d+)\s*,\s*(\d+))` if gregex.IsMatchString(patten, sql) == false { - fmt.Println("not matched..") + //fmt.Println("not matched..") return sql } res, err := gregex.MatchAllString(patten, sql) if err != nil { - fmt.Println("MatchString error.", err) + //fmt.Println("MatchString error.", err) return "" } - index := 0 + index := 0 keyword := strings.TrimSpace(res[index][0]) - keyword = strings.ToUpper(keyword) + keyword = strings.ToUpper(keyword) index++ switch keyword { - case "SELECT": - //不含LIMIT关键字则不处理 - if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) { - break - } - - //取limit前面的字符串 - if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false { - break - } - - queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) - if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false { - break - } - - //取limit后面的取值范围 - first, limit := 0, 0 - for i := 1; i < len(res[index]); i++ { - if len(strings.TrimSpace(res[index][i])) == 0 { - continue - } - - if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") { - first, _ = strconv.Atoi(res[index][i+1]) - limit, _ = strconv.Atoi(res[index][i+2]) + case "SELECT": + //不含LIMIT关键字则不处理 + if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) { break } - } - //也可以使用between,据说这种写法的性能会比between好点,里层SQL中的ROWNUM_ >= limit可以缩小查询后的数据集规模 - sql = fmt.Sprintf("SELECT * FROM (SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d) WHERE ROWNUM_ >= %d", queryExpr[1], queryExpr[2], limit, first) - case "INSERT": - //获取VALUE的值,匹配所有带括号的值,会将INSERT INTO后的值匹配到,所以下面的判断语句会判断数组长度是否小于3 - valueExpr, err := gregex.MatchAllString(`(\s*\(([^\(\)]*)\))`, sql) - if err != nil { - return sql - } + //取limit前面的字符串 + if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false { + break + } - //判断VALUE后的值是否有多个,只有在批量插入的时候才需要做转换,如只有1个VALUE则不需要做转换 - if len(valueExpr) < 3 { - break - } + queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) + if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false { + break + } - //获取INTO后面的值 - tableExpr, err := gregex.MatchString(`(?i)\s*(INTO\s+\w+\(([^\(\)]*)\))`, sql) - if err != nil { - return sql - } - tableExpr[0] = strings.TrimSpace(tableExpr[0]) + //取limit后面的取值范围 + first, limit := 0, 0 + for i := 1; i < len(res[index]); i++ { + if len(strings.TrimSpace(res[index][i])) == 0 { + continue + } - sql = "INSERT ALL" - for i := 1; i < len(valueExpr); i++ { - sql += fmt.Sprintf(" %s VALUES%s", tableExpr[0], strings.TrimSpace(valueExpr[i][0])) - } - sql += " SELECT 1 FROM DUAL" + if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") { + first, _ = strconv.Atoi(res[index][i+1]) + limit, _ = strconv.Atoi(res[index][i+2]) + break + } + } - default: + //也可以使用between,据说这种写法的性能会比between好点,里层SQL中的ROWNUM_ >= limit可以缩小查询后的数据集规模 + sql = fmt.Sprintf("SELECT * FROM (SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d) WHERE ROWNUM_ >= %d", queryExpr[1], queryExpr[2], limit, first) + case "INSERT": + //获取VALUE的值,匹配所有带括号的值,会将INSERT INTO后的值匹配到,所以下面的判断语句会判断数组长度是否小于3 + valueExpr, err := gregex.MatchAllString(`(\s*\(([^\(\)]*)\))`, sql) + if err != nil { + return sql + } + + //判断VALUE后的值是否有多个,只有在批量插入的时候才需要做转换,如只有1个VALUE则不需要做转换 + if len(valueExpr) < 3 { + break + } + + //获取INTO后面的值 + tableExpr, err := gregex.MatchString(`(?i)\s*(INTO\s+\w+\(([^\(\)]*)\))`, sql) + if err != nil { + return sql + } + tableExpr[0] = strings.TrimSpace(tableExpr[0]) + + sql = "INSERT ALL" + for i := 1; i < len(valueExpr); i++ { + sql += fmt.Sprintf(" %s VALUES%s", tableExpr[0], strings.TrimSpace(valueExpr[i][0])) + } + sql += " SELECT 1 FROM DUAL" + + default: } return sql } diff --git a/g/database/gdb/gdb_structure.go b/g/database/gdb/gdb_structure.go index 2530f5255..709b114cb 100644 --- a/g/database/gdb/gdb_structure.go +++ b/g/database/gdb/gdb_structure.go @@ -28,42 +28,42 @@ func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interfa t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType) t = strings.ToLower(t) switch t { - case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob": - return gconv.Bytes(fieldValue) + case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob": + return gconv.Bytes(fieldValue) - case "bit", "int", "tinyint", "small_int", "medium_int": - return gconv.Int(fieldValue) + case "bit", "int", "tinyint", "small_int", "medium_int": + return gconv.Int(fieldValue) - case "big_int": - return gconv.Int64(fieldValue) + case "big_int": + return gconv.Int64(fieldValue) - case "float", "double", "decimal": - return gconv.Float64(fieldValue) + case "float", "double", "decimal": + return gconv.Float64(fieldValue) - case "bool": - return gconv.Bool(fieldValue) + case "bool": + return gconv.Bool(fieldValue) - default: - // 自动识别类型, 以便默认支持更多数据库类型 - switch { - case strings.Contains(t, "int"): - return gconv.Int(fieldValue) + default: + // 自动识别类型, 以便默认支持更多数据库类型 + switch { + case strings.Contains(t, "int"): + return gconv.Int(fieldValue) - case strings.Contains(t, "text") || strings.Contains(t, "char"): - return gconv.String(fieldValue) + case strings.Contains(t, "text") || strings.Contains(t, "char"): + return gconv.String(fieldValue) - case strings.Contains(t, "float") || strings.Contains(t, "double"): - return gconv.Float64(fieldValue) + case strings.Contains(t, "float") || strings.Contains(t, "double"): + return gconv.Float64(fieldValue) - case strings.Contains(t, "bool"): - return gconv.Bool(fieldValue) + case strings.Contains(t, "bool"): + return gconv.Bool(fieldValue) - case strings.Contains(t, "binary") || strings.Contains(t, "blob"): - return gconv.Bytes(fieldValue) + case strings.Contains(t, "binary") || strings.Contains(t, "blob"): + return gconv.Bytes(fieldValue) - default: - return gconv.String(fieldValue) - } + default: + return gconv.String(fieldValue) + } } } From 733512606410501cb8eb24853138bda93acf7caa Mon Sep 17 00:00:00 2001 From: John Date: Wed, 29 May 2019 15:18:12 +0800 Subject: [PATCH 23/50] mark deprecated for *fln functions of glog; fix issue in gfile.MainPkgPath in Windows --- g/os/gfile/gfile.go | 6 ++- g/os/glog/glog_api.go | 48 ++++++++++++---------- g/os/glog/glog_logger.go | 11 +++-- g/os/glog/glog_logger_api.go | 78 +++++++++++++++++++----------------- geg/other/test.go | 17 ++++---- 5 files changed, 83 insertions(+), 77 deletions(-) diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go index 0097d0468..a5111de0c 100644 --- a/g/os/gfile/gfile.go +++ b/g/os/gfile/gfile.go @@ -417,14 +417,16 @@ func MainPkgPath() string { } for i := 1; i < 10000; i++ { if _, file, _, ok := runtime.Caller(i); ok { + // is separated by '/' if gstr.Contains(file, "/gf/g/") { continue } if Ext(file) != ".go" { continue } - path = file - for path != "/" && gstr.Contains(path, "/") { + // separator of '/' will be converted to Separator. + path = Dir(file) + for path != "/" && gstr.Contains(path, Separator) { files, _ := ScanDir(path, "*.go") for _, v := range files { if gregex.IsMatchString(`package\s+main`, GetContents(v)) { diff --git a/g/os/glog/glog_api.go b/g/os/glog/glog_api.go index 1381b7e63..d7700bfe8 100644 --- a/g/os/glog/glog_api.go +++ b/g/os/glog/glog_api.go @@ -23,8 +23,8 @@ func Println(v ...interface{}) { logger.Println(v ...) } -// Printf prints with newline and format using fmt.Sprintf. -// The param can be multiple variables. +// Deprecated. +// Use Printf instead. func Printfln(format string, v ...interface{}) { logger.Printfln(format, v ...) } @@ -34,12 +34,13 @@ func Fatal(v ...interface{}) { logger.Fatal(v ...) } -// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. +// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. func Fatalf(format string, v ...interface{}) { logger.Fatalf(format, v ...) } -// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. +// Deprecated. +// Use Fatalf instead. func Fatalfln(format string, v ...interface{}) { logger.Fatalfln(format, v ...) } @@ -49,12 +50,13 @@ func Panic(v ...interface{}) { logger.Panic(v ...) } -// Panicf prints the logging content with [PANI] header and custom format, then panics. +// Panicf prints the logging content with [PANI] header, custom format and newline, then panics. func Panicf(format string, v ...interface{}) { logger.Panicf(format, v ...) } -// Panicfln prints the logging content with [PANI] header, newline and custom format, then panics. +// Deprecated. +// Use Panicf instead. func Panicfln(format string, v ...interface{}) { logger.Panicfln(format, v ...) } @@ -64,12 +66,13 @@ func Info(v ...interface{}) { logger.Info(v...) } -// Infof prints the logging content with [INFO] header and custom format. +// Infof prints the logging content with [INFO] header, custom format and newline. func Infof(format string, v ...interface{}) { logger.Infof(format, v...) } -// Infofln prints the logging content with [INFO] header, newline and custom format. +// Deprecated. +// Use Infof instead. func Infofln(format string, v ...interface{}) { logger.Infofln(format, v...) } @@ -79,12 +82,13 @@ func Debug(v ...interface{}) { logger.Debug(v...) } -// Debugf prints the logging content with [DEBU] header and custom format. +// Debugf prints the logging content with [DEBU] header, custom format and newline. func Debugf(format string, v ...interface{}) { logger.Debugf(format, v...) } -// Debugfln prints the logging content with [DEBU] header, newline and custom format. +// Deprecated. +// Use Debugf instead. func Debugfln(format string, v ...interface{}) { logger.Debugfln(format, v...) } @@ -95,14 +99,14 @@ func Notice(v ...interface{}) { logger.Notice(v...) } -// Noticef prints the logging content with [NOTI] header and custom format. +// Noticef prints the logging content with [NOTI] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func Noticef(format string, v ...interface{}) { logger.Noticef(format, v...) } -// Noticefln prints the logging content with [NOTI] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Noticef instead. func Noticefln(format string, v ...interface{}) { logger.Noticefln(format, v...) } @@ -113,14 +117,14 @@ func Warning(v ...interface{}) { logger.Warning(v...) } -// Warningf prints the logging content with [WARN] header and custom format. +// Warningf prints the logging content with [WARN] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func Warningf(format string, v ...interface{}) { logger.Warningf(format, v...) } -// Warningfln prints the logging content with [WARN] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Warningf instead. func Warningfln(format string, v ...interface{}) { logger.Warningfln(format, v...) } @@ -131,14 +135,14 @@ func Error(v ...interface{}) { logger.Error(v...) } -// Errorf prints the logging content with [ERRO] header and custom format. +// Errorf prints the logging content with [ERRO] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func Errorf(format string, v ...interface{}) { logger.Errorf(format, v...) } -// Errorfln prints the logging content with [ERRO] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Errorf instead. func Errorfln(format string, v ...interface{}) { logger.Errorfln(format, v...) } @@ -149,14 +153,14 @@ func Critical(v ...interface{}) { logger.Critical(v...) } -// Criticalf prints the logging content with [CRIT] header and custom format. +// Criticalf prints the logging content with [CRIT] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func Criticalf(format string, v ...interface{}) { logger.Criticalf(format, v...) } -// Criticalfln prints the logging content with [CRIT] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Criticalf instead. func Criticalfln(format string, v ...interface{}) { logger.Criticalfln(format, v...) } diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 4332eb4af..6a9379d59 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -299,7 +299,7 @@ func (l *Logger) GetBacktrace(skip...int) string { backtrace := "" from := 0 // Find the caller position exclusive of the glog file. - for i := 0; i < 100; i++ { + for i := 0; i < 1000; i++ { if _, file, _, ok := runtime.Caller(i); ok { if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { from = i @@ -310,7 +310,7 @@ func (l *Logger) GetBacktrace(skip...int) string { // Find the true caller file path using custom skip. index := 1 goRoot := runtime.GOROOT() - for i := from + customSkip + l.btSkip; i < 10000; i++ { + for i := from + customSkip + l.btSkip; i < 1000; i++ { if _, file, cline, ok := runtime.Caller(i); ok && file != "" { if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, ln) @@ -327,18 +327,17 @@ func (l *Logger) GetBacktrace(skip...int) string { func (l *Logger) getLongFile() string { from := 0 // Find the caller position exclusive of the glog file. - for i := 0; i < 100; i++ { - if _, file, line, ok := runtime.Caller(i); ok { + for i := 0; i < 1000; i++ { + if _, file, _, ok := runtime.Caller(i); ok { if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { from = i break - return fmt.Sprintf(`%s:%d`, file, line) } } } // Find the true caller file path using custom skip. goRoot := runtime.GOROOT() - for i := from + l.btSkip; i < 10000; i++ { + for i := from + l.btSkip; i < 1000; i++ { if _, file, line, ok := runtime.Caller(i); ok && file != "" { if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { return fmt.Sprintf(`%s:%d`, file, line) diff --git a/g/os/glog/glog_logger_api.go b/g/os/glog/glog_logger_api.go index 08327bc78..9ef5c0c2a 100644 --- a/g/os/glog/glog_logger_api.go +++ b/g/os/glog/glog_logger_api.go @@ -20,7 +20,7 @@ func (l *Logger) Print(v ...interface{}) { // Printf prints with format using fmt.Sprintf. // The param can be multiple variables. func (l *Logger) Printf(format string, v ...interface{}) { - l.printStd(fmt.Sprintf(format, v...)) + l.printStd(fmt.Sprintf(format + ln, v...)) } // See Print. @@ -28,8 +28,8 @@ func (l *Logger) Println(v ...interface{}) { l.Print(v...) } -// Printfln prints with newline and format using fmt.Sprintf. -// The param can be multiple variables. +// Deprecated. +// Use Printf instead. func (l *Logger) Printfln(format string, v ...interface{}) { l.printStd(fmt.Sprintf(format + ln, v...)) } @@ -40,13 +40,14 @@ func (l *Logger) Fatal(v ...interface{}) { os.Exit(1) } -// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. +// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. func (l *Logger) Fatalf(format string, v ...interface{}) { - l.printErr("[FATA] " + fmt.Sprintf(format, v...)) + l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) os.Exit(1) } -// Fatalfln prints the logging content with [FATA] header, custom format and newline, then exit the current process. +// Deprecated. +// Use Fatalf instead. func (l *Logger) Fatalfln(format string, v ...interface{}) { l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) os.Exit(1) @@ -59,14 +60,15 @@ func (l *Logger) Panic(v ...interface{}) { panic(s) } -// Panicf prints the logging content with [PANI] header and custom format, then panics. +// Panicf prints the logging content with [PANI] header, custom format and newline, then panics. func (l *Logger) Panicf(format string, v ...interface{}) { - s := fmt.Sprintf(format, v...) + s := fmt.Sprintf(format + ln, v...) l.printErr("[PANI] " + s) panic(s) } -// Panicfln prints the logging content with [PANI] header, newline and custom format, then panics. +// Deprecated. +// Use Panicf instead. func (l *Logger) Panicfln(format string, v ...interface{}) { s := fmt.Sprintf(format + ln, v...) l.printErr("[PANI] " + s) @@ -80,17 +82,18 @@ func (l *Logger) Info(v ...interface{}) { } } -// Infof prints the logging content with [INFO] header and custom format. +// Infof prints the logging content with [INFO] header, custom format and newline. func (l *Logger) Infof(format string, v ...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintf(format, v...)) + l.printStd("[INFO] " + fmt.Sprintf(format + ln, v...)) } } -// Infofln prints the logging content with [INFO] header, newline and custom format. +// Deprecated. +// Use Infof instead. func (l *Logger) Infofln(format string, v ...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintf(format, v...) + ln) + l.printStd("[INFO] " + fmt.Sprintf(format + ln, v...) + ln) } } @@ -101,17 +104,18 @@ func (l *Logger) Debug(v ...interface{}) { } } -// Debugf prints the logging content with [DEBU] header and custom format. +// Debugf prints the logging content with [DEBU] header, custom format and newline. func (l *Logger) Debugf(format string, v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintf(format, v...)) + l.printStd("[DEBU] " + fmt.Sprintf(format + ln, v...)) } } -// Debugfln prints the logging content with [DEBU] header, newline and custom format. +// Deprecated. +// Use Debugf instead. func (l *Logger) Debugfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintf(format, v...) + ln) + l.printStd("[DEBU] " + fmt.Sprintf(format + ln, v...) + ln) } } @@ -123,19 +127,19 @@ func (l *Logger) Notice(v ...interface{}) { } } -// Noticef prints the logging content with [NOTI] header and custom format. +// Noticef prints the logging content with [NOTI] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Noticef(format string, v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintf(format, v...)) + l.printErr("[NOTI] " + fmt.Sprintf(format + ln, v...)) } } -// Noticefln prints the logging content with [NOTI] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Noticef instead. func (l *Logger) Noticefln(format string, v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[NOTI] " + fmt.Sprintf(format + ln, v...) + ln) } } @@ -147,19 +151,19 @@ func (l *Logger) Warning(v ...interface{}) { } } -// Warningf prints the logging content with [WARN] header and custom format. +// Warningf prints the logging content with [WARN] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Warningf(format string, v ...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintf(format, v...)) + l.printErr("[WARN] " + fmt.Sprintf(format + ln, v...)) } } -// Warningfln prints the logging content with [WARN] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Warningf instead. func (l *Logger) Warningfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[WARN] " + fmt.Sprintf(format + ln, v...) + ln) } } @@ -171,19 +175,19 @@ func (l *Logger) Error(v ...interface{}) { } } -// Errorf prints the logging content with [ERRO] header and custom format. +// Errorf prints the logging content with [ERRO] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Errorf(format string, v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintf(format, v...)) + l.printErr("[ERRO] " + fmt.Sprintf(format + ln, v...)) } } -// Errorfln prints the logging content with [ERRO] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Errorf instead. func (l *Logger) Errorfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[ERRO] " + fmt.Sprintf(format + ln, v...) + ln) } } @@ -195,19 +199,19 @@ func (l *Logger) Critical(v ...interface{}) { } } -// Criticalf prints the logging content with [CRIT] header and custom format. +// Criticalf prints the logging content with [CRIT] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Criticalf(format string, v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintf(format, v...)) + l.printErr("[CRIT] " + fmt.Sprintf(format + ln, v...)) } } -// Criticalfln prints the logging content with [CRIT] header, newline and custom format. -// It also prints caller backtrace info if backtrace feature is enabled. +// Deprecated. +// Use Criticalf instead. func (l *Logger) Criticalfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintf(format, v...) + ln) + l.printErr("[CRIT] " + fmt.Sprintf(format + ln, v...) + ln) } } diff --git a/geg/other/test.go b/geg/other/test.go index 0d9688c87..abd15f7ed 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,17 +1,14 @@ package main import ( - "github.com/gogf/gf/g/os/glog" - "github.com/gogf/gf/g/os/gtime" + "log" ) func main() { - Time := gtime.Now().AddDate(0, -1, 0).Format("Y-m") - glog.Debug(Time) - Time = gtime.Now().AddDate(0, -2, 0).Format("Y-m") - glog.Debug(Time) - Time = gtime.Now().AddDate(0, -3, 0).Format("Y-m") - glog.Debug(Time) - Time = gtime.Now().AddDate(0, -4, 0).Format("Y-m") - glog.Debug(Time) + log.Printf("111") + log.Printf("111") + log.Print("222") + log.Print("222") + log.Println("333") + log.Println("333") } \ No newline at end of file From d9aa9e4480e62e043ae933392538f703dd3538f4 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 31 May 2019 22:54:57 +0800 Subject: [PATCH 24/50] improve grpool --- g/os/grpool/grpool.go | 72 +++++++++++------------- g/os/grpool/grpool_unit_test.go | 57 +++++++++++++++++++ geg/container/gpool/gpool_expire.go | 16 ------ geg/container/gpool/gpool_expirefunc.go | 32 +++++++++++ geg/other/test.go | 73 ++++++++++++++++++++++--- 5 files changed, 187 insertions(+), 63 deletions(-) create mode 100644 g/os/grpool/grpool_unit_test.go delete mode 100644 geg/container/gpool/gpool_expire.go create mode 100644 geg/container/gpool/gpool_expirefunc.go diff --git a/g/os/grpool/grpool.go b/g/os/grpool/grpool.go index 82a0c6318..cb4c9866a 100644 --- a/g/os/grpool/grpool.go +++ b/g/os/grpool/grpool.go @@ -19,10 +19,10 @@ import ( // goroutine池对象 type Pool struct { - workerChan chan struct{} // 使用channel限制最大的goroutine数量 - workerNum *gtype.Int // 当前正在运行的worker/goroutine数量 - jobQueue *glist.List // 待处理任务操作队列 - jobEvents chan struct{} // 任务添加事件(jobQueue+jobEvents结合使用) + limit int // 最大的goroutine数量限制 + count *gtype.Int // 当前正在运行的goroutine数量 + list *glist.List // 待处理任务操作列表 + events chan struct{} // 任务添加事件 closed *gtype.Bool } @@ -30,18 +30,18 @@ type Pool struct { // 该对象与进程同生命周期,无需Close var defaultPool = New() -// 创建goroutine池管理对象, 参数用于限制限制最大的goroutine数量/线程数/worker数量,非必需参数,默认不做限制 +// 创建goroutine池管理对象,参数用于限制限制最大的goroutine数量,非必需参数,默认不做限制 func New(size...int) *Pool { - s := 0 + limit := -1 if len(size) > 0 { - s = size[0] + limit = size[0] } p := &Pool { - workerNum : gtype.NewInt(), - jobQueue : glist.New(), - jobEvents : make(chan struct{}, math.MaxInt32), - workerChan : make(chan struct{}, s), - closed : gtype.NewBool(), + limit : limit, + count : gtype.NewInt(), + list : glist.New(), + events : make(chan struct{}, math.MaxInt32), + closed : gtype.NewBool(), } return p } @@ -53,65 +53,57 @@ func Add(f func()) error { // 查询当前goroutine总数 func Size() int { - return defaultPool.workerNum.Val() + return defaultPool.count.Val() } // 查询当前等待处理的任务总数 func Jobs() int { - return len(defaultPool.jobEvents) + return len(defaultPool.events) } // 添加异步任务 func (p *Pool) Add(f func()) error { - p.jobQueue.PushBack(f) - p.jobEvents <- struct{}{} + p.list.PushBack(f) + p.events <- struct{}{} // 判断是否创建新的worker - if p.Jobs() > 1 || p.workerNum.Val() == 0 { - p.ForkWorker() + if p.list.Len() > 1 || p.count.Val() == 0 { + p.fork() } return nil } -// 查询当前goroutine worker总数 +// 查询当前goroutine总数 func (p *Pool) Size() int { - return p.workerNum.Val() + return p.count.Val() } // 查询当前等待处理的任务总数 func (p *Pool) Jobs() int { - return p.jobQueue.Len() + return p.list.Len() } // 创建新的worker执行任务 -func (p *Pool) ForkWorker() { - if cap(p.workerChan) > 0 { - // 如果worker数量已经达到限制,那么不创建新worker,直接返回 - if p.workerNum.Val() == cap(p.workerChan) { - return - } - p.workerNum.Add(1) - p.workerChan <- struct{}{} - } else { - p.workerNum.Add(1) +func (p *Pool) fork() { + // 如果worker数量已经达到限制,那么不创建新worker,直接返回 + if p.count.Val() == p.limit { + return } + p.count.Add(1) go func() { for !p.closed.Val() { select { - case <- p.jobEvents: - if job := p.jobQueue.PopFront(); job != nil { + case <- p.events: + if job := p.list.PopFront(); job != nil { job.(func())() } else { - goto WorkerDone + p.count.Add(-1) + return } default: - goto WorkerDone + p.count.Add(-1) + return } } -WorkerDone: - p.workerNum.Add(-1) - if cap(p.workerChan) > 0 { - <- p.workerChan - } }() } diff --git a/g/os/grpool/grpool_unit_test.go b/g/os/grpool/grpool_unit_test.go new file mode 100644 index 000000000..799e027cc --- /dev/null +++ b/g/os/grpool/grpool_unit_test.go @@ -0,0 +1,57 @@ +// 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=".*" -count=1 + +package grpool_test + +import ( + "github.com/gogf/gf/g/container/garray" + "github.com/gogf/gf/g/os/grpool" + "github.com/gogf/gf/g/test/gtest" + "sync" + "testing" + "time" +) + + +func Test_Basic(t *testing.T) { + gtest.Case(t, func() { + wg := sync.WaitGroup{} + array := garray.NewArray() + size := 100000 + wg.Add(size) + for i := 0; i < size; i++ { + grpool.Add(func() { + array.Append(1) + wg.Done() + }) + } + wg.Wait() + gtest.Assert(array.Len(), size) + }) + + gtest.Case(t, func() { + array := garray.NewArray() + size := 100000 + pool := grpool.New(10000) + for i := 0; i < size; i++ { + pool.Add(func() { + array.Append(1) + time.Sleep(2*time.Second) + }) + } + time.Sleep(time.Second) + gtest.Assert(pool.Size(), 10000) + gtest.Assert(pool.Jobs(), 90000) + gtest.Assert(array.Len(), 10000) + pool.Close() + time.Sleep(2*time.Second) + gtest.Assert(pool.Size(), 10000) + gtest.Assert(pool.Jobs(), 90000) + gtest.Assert(array.Len(), 10000) + }) +} \ No newline at end of file diff --git a/geg/container/gpool/gpool_expire.go b/geg/container/gpool/gpool_expire.go deleted file mode 100644 index 1a047a625..000000000 --- a/geg/container/gpool/gpool_expire.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "fmt" - "github.com/gogf/gf/g/container/gpool" - "time" -) - -func main() { - p := gpool.New(60000, nil, func(i interface{}) { - fmt.Println("expired") - }) - p.Put(1) - time.Sleep(10000*time.Second) - fmt.Println(p.Get()) -} diff --git a/geg/container/gpool/gpool_expirefunc.go b/geg/container/gpool/gpool_expirefunc.go new file mode 100644 index 000000000..4341ef271 --- /dev/null +++ b/geg/container/gpool/gpool_expirefunc.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/container/gpool" + "github.com/gogf/gf/g/net/gtcp" + "github.com/gogf/gf/g/os/glog" + "time" +) + +func main() { + // 创建对象复用池,对象过期时间为3000毫秒,并给定创建及销毁方法 + p := gpool.New(3000, func() (interface{}, error) { + return gtcp.NewConn("www.baidu.com:80") + }, func(i interface{}) { + glog.Println("expired") + i.(*gtcp.Conn).Close() + }) + conn, err := p.Get() + if err != nil { + panic(err) + } + result, err := conn.(*gtcp.Conn).SendRecv([]byte("HEAD / HTTP/1.1\n\n"), -1) + if err != nil { + panic(err) + } + fmt.Println(string(result)) + // 丢回池中以便重复使用 + p.Put(conn) + // 等待一定时间观察过期方法调用 + time.Sleep(4*time.Second) +} diff --git a/geg/other/test.go b/geg/other/test.go index abd15f7ed..224ca34a3 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,14 +1,73 @@ package main import ( - "log" + "fmt" + "github.com/gogf/gf/g/container/gmap" ) func main() { - log.Printf("111") - log.Printf("111") - log.Print("222") - log.Print("222") - log.Println("333") - log.Println("333") + // 创建一个默认的gmap对象, + // 默认情况下该gmap对象支持并发安全特性, + // 初始化时可以给定true参数关闭并发安全特性,当做一个普通的map使用。 + m := gmap.New() + + // 设置键值对 + for i := 0; i < 10; i++ { + m.Set(i, i) + } + // 查询大小 + fmt.Println(m.Size()) + // 批量设置键值对(不同的数据类型对象参数不同) + m.Sets(map[interface{}]interface{}{ + 10 : 10, + 11 : 11, + }) + fmt.Println(m.Size()) + + // 查询是否存在 + fmt.Println(m.Contains(1)) + + // 查询键值 + fmt.Println(m.Get(1)) + + // 删除数据项 + m.Remove(9) + fmt.Println(m.Size()) + + // 批量删除 + m.Removes([]interface{}{10, 11}) + fmt.Println(m.Size()) + + // 当前键名列表(随机排序) + fmt.Println(m.Keys()) + // 当前键值列表(随机排序) + fmt.Println(m.Values()) + + // 查询键名,当键值不存在时,写入给定的默认值 + fmt.Println(m.GetOrSet(100, 100)) + + // 删除键值对,并返回对应的键值 + fmt.Println(m.Remove(100)) + + // 遍历map + m.Iterator(func(k interface{}, v interface{}) bool { + fmt.Printf("%v:%v ", k, v) + return true + }) + + // 自定义写锁操作 + m.LockFunc(func(m map[interface{}]interface{}) { + m[99] = 99 + }) + + // 自定义读锁操作 + m.RLockFunc(func(m map[interface{}]interface{}) { + fmt.Println(m[99]) + }) + + // 清空map + m.Clear() + + // 判断map是否为空 + fmt.Println(m.IsEmpty()) } \ No newline at end of file From 6e8a900f250a170ca24d50361b1f4516843151c3 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 00:02:05 +0800 Subject: [PATCH 25/50] fix issue in gjson/gparser in loading xml content; add *time.Time support for gdb --- g/database/gdb/gdb_func.go | 18 +++++- g/database/gdb/gdb_unit_method_test.go | 49 ++++++++++++++ g/database/gdb/gdb_unit_model_test.go | 5 +- g/encoding/gjson/gjson_api_new_load.go | 2 +- g/encoding/gjson/gjson_z_unit_load_test.go | 18 ++++++ g/encoding/gparser/gparser_api_new_load.go | 2 +- g/encoding/gparser/gparser_unit_load_test.go | 28 ++++++-- g/encoding/gxml/gxml.go | 15 +---- g/util/gconv/gconv_z_unit_struct_test.go | 67 +++++++++++++++++++- g/util/gconv/gconv_z_unit_time_test.go | 10 +-- geg/encoding/{ => gparser}/gparser.go | 0 geg/encoding/gparser/gparser_xml.go | 21 ++++++ 12 files changed, 205 insertions(+), 30 deletions(-) rename geg/encoding/{ => gparser}/gparser.go (100%) create mode 100644 geg/encoding/gparser/gparser_xml.go diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go index 374def4ee..f010d23b4 100644 --- a/g/database/gdb/gdb_func.go +++ b/g/database/gdb/gdb_func.go @@ -142,10 +142,19 @@ func convertParam(value interface{}) interface{} { } switch kind { case reflect.Struct: - // 底层数据库引擎支持 time.Time 类型 - if _, ok := value.(time.Time); ok { + // 底层数据库引擎支持 time.Time/*time.Time 类型 + if v, ok := value.(time.Time); ok { + if v.IsZero() { + return "null" + } return value } + if v, ok := value.(*time.Time); ok { + if v.IsZero() { + return "" + } + return value + } return gconv.String(value) } return value @@ -206,10 +215,13 @@ func structToMap(obj interface{}) map[string]interface{} { } switch kind { case reflect.Struct: - // 底层数据库引擎支持 time.Time 类型 + // 底层数据库引擎支持 time.Time/*time.Time 类型 if _, ok := value.(time.Time); ok { continue } + if _, ok := value.(*time.Time); ok { + continue + } // 如果执行String方法,那么执行字符串转换 if s, ok := value.(apiString); ok { data[key] = s.String() diff --git a/g/database/gdb/gdb_unit_method_test.go b/g/database/gdb/gdb_unit_method_test.go index 8bc04ca87..a620bd8c9 100644 --- a/g/database/gdb/gdb_unit_method_test.go +++ b/g/database/gdb/gdb_unit_method_test.go @@ -11,6 +11,7 @@ import ( "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" "testing" + "time" ) func TestDbBase_Ping(t *testing.T) { @@ -488,3 +489,51 @@ func TestDbBase_Delete(t *testing.T) { } } +func TestDbBase_Time(t *testing.T) { + gtest.Case(t, func() { + result, err := db.Insert("user", g.Map{ + "id" : 200, + "passport" : "t200", + "password" : "123456", + "nickname" : "T200", + "create_time" : time.Now(), + }) + if err != nil { + gtest.Fatal(err) + } + n, _ := result.RowsAffected() + gtest.Assert(n, 1) + value, err := db.GetValue("select `passport` from `user` where id=?", 200) + gtest.Assert(err, nil) + gtest.Assert(value.String(), "t200") + }) + + gtest.Case(t, func() { + t := time.Now() + result, err := db.Insert("user", g.Map{ + "id" : 300, + "passport" : "t300", + "password" : "123456", + "nickname" : "T300", + "create_time" : &t, + }) + if err != nil { + gtest.Fatal(err) + } + n, _ := result.RowsAffected() + gtest.Assert(n, 1) + value, err := db.GetValue("select `passport` from `user` where id=?", 300) + gtest.Assert(err, nil) + gtest.Assert(value.String(), "t300") + }) + + if result, err := db.Delete("user", nil); err != nil { + gtest.Fatal(err) + } else { + n, _ := result.RowsAffected() + gtest.Assert(n, 2) + } +} + + + diff --git a/g/database/gdb/gdb_unit_model_test.go b/g/database/gdb/gdb_unit_model_test.go index 504fd8c89..41e5d3294 100644 --- a/g/database/gdb/gdb_unit_model_test.go +++ b/g/database/gdb/gdb_unit_model_test.go @@ -10,7 +10,8 @@ import ( "github.com/gogf/gf/g" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" - "testing" + "os" + "testing" ) // 基本测试 @@ -28,7 +29,7 @@ func TestModel_Insert(t *testing.T) { } n, _ := result.LastInsertId() gtest.Assert(n, 1) - +os.Exit(1) result, err = db.Table("user").Filter().Data(map[interface{}]interface{} { "id" : "2", "uid" : "2", diff --git a/g/encoding/gjson/gjson_api_new_load.go b/g/encoding/gjson/gjson_api_new_load.go index 72a0df0ec..e1d32340d 100644 --- a/g/encoding/gjson/gjson_api_new_load.go +++ b/g/encoding/gjson/gjson_api_new_load.go @@ -136,7 +136,7 @@ func LoadContent(data interface{}, unsafe...bool) (*Json, error) { // auto check data type if json.Valid(b) { t = "json" - } else if gregex.IsMatch(`^<.+>.*$`, b) { + } else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, b) { t = "xml" } else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) { t = "yml" diff --git a/g/encoding/gjson/gjson_z_unit_load_test.go b/g/encoding/gjson/gjson_z_unit_load_test.go index 7c096428e..6717ea124 100644 --- a/g/encoding/gjson/gjson_z_unit_load_test.go +++ b/g/encoding/gjson/gjson_z_unit_load_test.go @@ -67,6 +67,24 @@ func Test_Load_XML(t *testing.T) { gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3}) gtest.Assert(j.Get("doc.a.1"), 2) }) + + // XML + gtest.Case(t, func() { + xml := ` + + + 0 + 1 + 2 + GF框架 + + + ` + j, err := gjson.LoadContent(xml) + gtest.Assert(err, nil) + gtest.Assert(j.Get("Output.ipageIndex"), "2") + gtest.Assert(j.Get("Output.itotalRecords"), "GF框架") + }) } func Test_Load_YAML1(t *testing.T) { diff --git a/g/encoding/gparser/gparser_api_new_load.go b/g/encoding/gparser/gparser_api_new_load.go index 434d05bd1..0cf99c054 100644 --- a/g/encoding/gparser/gparser_api_new_load.go +++ b/g/encoding/gparser/gparser_api_new_load.go @@ -40,7 +40,7 @@ func Load(path string, unsafe...bool) (*Parser, error) { // LoadContent creates a Parser object from given content, // it checks the data type of automatically, // supporting JSON, XML, YAML and TOML types of data. -func LoadContent(data []byte, unsafe...bool) (*Parser, error) { +func LoadContent(data interface{}, unsafe...bool) (*Parser, error) { if j, e := gjson.LoadContent(data, unsafe...); e == nil { return &Parser{j}, nil } else { diff --git a/g/encoding/gparser/gparser_unit_load_test.go b/g/encoding/gparser/gparser_unit_load_test.go index 930693a72..2702ca05f 100644 --- a/g/encoding/gparser/gparser_unit_load_test.go +++ b/g/encoding/gparser/gparser_unit_load_test.go @@ -7,11 +7,11 @@ package gparser_test import ( - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/encoding/gparser" - "github.com/gogf/gf/g/os/gfile" - "github.com/gogf/gf/g/test/gtest" - "testing" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/encoding/gparser" + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" + "testing" ) @@ -67,6 +67,24 @@ func Test_Load_XML(t *testing.T) { gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3}) gtest.Assert(j.Get("doc.a.1"), 2) }) + + // XML + gtest.Case(t, func() { + xml := ` + + + 0 + 1 + 2 + GF框架 + + + ` + j, err := gparser.LoadContent(xml) + gtest.Assert(err, nil) + gtest.Assert(j.Get("Output.ipageIndex"), "2") + gtest.Assert(j.Get("Output.itotalRecords"), "GF框架") + }) } func Test_Load_YAML1(t *testing.T) { diff --git a/g/encoding/gxml/gxml.go b/g/encoding/gxml/gxml.go index 828d7f8ca..6d4b1ea97 100644 --- a/g/encoding/gxml/gxml.go +++ b/g/encoding/gxml/gxml.go @@ -5,8 +5,6 @@ // You can obtain one at https://github.com/gogf/gf. // Package gxml provides accessing and converting for XML content. -// -// XML数据格式解析。 package gxml import ( @@ -52,33 +50,26 @@ func ToJson(content []byte) ([]byte, error) { } // XML字符集预处理 -// @author wenzi1 -// @date 20180604 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换 -func convert(xmlbyte []byte) (res []byte, err error) { +func convert(xml []byte) (res []byte, err error) { patten := `<\?xml.*encoding\s*=\s*['|"](.*?)['|"].*\?>` - matchStr, err := gregex.MatchString(patten, string(xmlbyte)) + matchStr, err := gregex.MatchString(patten, string(xml)) if err != nil { return nil, err } - xmlEncode := "UTF-8" if len(matchStr) == 2 { xmlEncode = matchStr[1] } - s := mahonia.GetCharset(xmlEncode) if s == nil { return nil, fmt.Errorf("not support charset:%s\n", xmlEncode) } - - res, err = gregex.Replace(patten, []byte(""), []byte(xmlbyte)) + res, err = gregex.Replace(patten, []byte(""), xml) if err != nil { return nil, err } - if !strings.EqualFold(s.Name, "UTF-8") { res = []byte(s.NewDecoder().ConvertString(string(res))) } - return res, nil } diff --git a/g/util/gconv/gconv_z_unit_struct_test.go b/g/util/gconv/gconv_z_unit_struct_test.go index c2426083a..35d4274f8 100644 --- a/g/util/gconv/gconv_z_unit_struct_test.go +++ b/g/util/gconv/gconv_z_unit_struct_test.go @@ -8,9 +8,11 @@ package gconv_test import ( "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gconv" "testing" + "time" ) func Test_Struct_Basic1(t *testing.T) { @@ -347,4 +349,67 @@ func Test_Struct_Deep(t *testing.T) { gtest.Assert(user.Nickname, "T1") gtest.Assert(user.CreateTime, "2019") }) -} \ No newline at end of file +} + +func Test_Struct_Time(t *testing.T) { + gtest.Case(t, func() { + type User struct { + CreateTime time.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time" : now, + }, user) + gtest.Assert(user.CreateTime.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime *time.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time" : &now, + }, user) + gtest.Assert(user.CreateTime.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime *gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time" : &now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time" : &now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time" : now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) +} + diff --git a/g/util/gconv/gconv_z_unit_time_test.go b/g/util/gconv/gconv_z_unit_time_test.go index 7195f89f6..994b7856e 100644 --- a/g/util/gconv/gconv_z_unit_time_test.go +++ b/g/util/gconv/gconv_z_unit_time_test.go @@ -17,9 +17,9 @@ import ( func Test_Time(t *testing.T) { gtest.Case(t, func() { - t1 := "2011-10-10 01:02:03.456" - gtest.AssertEQ(gconv.GTime(t1), gtime.NewFromStr(t1)) - gtest.AssertEQ(gconv.Time(t1), gtime.NewFromStr(t1).Time) - gtest.AssertEQ(gconv.Duration(100), 100*time.Nanosecond) - }) + t1 := "2011-10-10 01:02:03.456" + gtest.AssertEQ(gconv.GTime(t1), gtime.NewFromStr(t1)) + gtest.AssertEQ(gconv.Time(t1), gtime.NewFromStr(t1).Time) + gtest.AssertEQ(gconv.Duration(100), 100*time.Nanosecond) + }) } diff --git a/geg/encoding/gparser.go b/geg/encoding/gparser/gparser.go similarity index 100% rename from geg/encoding/gparser.go rename to geg/encoding/gparser/gparser.go diff --git a/geg/encoding/gparser/gparser_xml.go b/geg/encoding/gparser/gparser_xml.go new file mode 100644 index 000000000..375600951 --- /dev/null +++ b/geg/encoding/gparser/gparser_xml.go @@ -0,0 +1,21 @@ +package main + +import "github.com/gogf/gf/g/encoding/gparser" + +func main() { + xml := ` + + + 0 + 1 + 2 + 3 + + + ` + p, err := gparser.LoadContent([]byte(xml)) + if err != nil { + panic(err) + } + p.Dump() +} From 9ad94eccad50bfd99202542617d0125c7bef36fd Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 11:01:57 +0800 Subject: [PATCH 26/50] add json output support for glog --- g/os/glog/glog_logger.go | 117 +++++++++++++++++++--------------- g/os/glog/glog_logger_api.go | 118 +++++++++++++++++------------------ g/util/gconv/gconv.go | 1 + geg/os/glog/glog_category.go | 2 +- geg/os/glog/glog_debug.go | 3 +- geg/os/glog/glog_json.go | 16 +++++ 6 files changed, 145 insertions(+), 112 deletions(-) create mode 100644 geg/os/glog/glog_json.go diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 6a9379d59..f0620fe11 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -3,8 +3,6 @@ // 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. -// -// @author john, zseeker package glog @@ -16,6 +14,7 @@ import ( "github.com/gogf/gf/g/os/gfpool" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/text/gregex" + "github.com/gogf/gf/g/util/gconv" "io" "os" "runtime" @@ -80,19 +79,10 @@ func New() *Logger { // Clone returns a new logger, which is the clone the current logger. func (l *Logger) Clone() *Logger { - logger := &Logger { - parent : l, - path : l.path, - file : l.file, - level : l.level, - flags : l.flags, - prefix : l.prefix, - btSkip : l.btSkip, - btStatus : l.btStatus, - headerPrint : l.headerPrint, - stdoutPrint : l.stdoutPrint, - } - return logger + logger := Logger{} + logger = *l + logger.parent = l + return &logger } // SetLevel sets the logging level. @@ -226,67 +216,98 @@ func (l *Logger) SetPrefix(prefix string) { } // print prints to defined writer, logging file or passed . -func (l *Logger) print(std io.Writer, s string) { - // Custom writer has the most high priority. +func (l *Logger) print(std io.Writer, level string, format string, value...interface{}) { + buffer := bytes.NewBuffer(nil) if l.headerPrint { - s = l.format(s) + // Time. + timeFormat := "" + if l.flags & F_TIME_DATE > 0 { + timeFormat += "2006-01-02 " + } + if l.flags & F_TIME_TIME > 0 { + timeFormat += "15:04:05 " + } + if l.flags & F_TIME_MILLI > 0 { + timeFormat += "15:04:05.000 " + } + if len(timeFormat) > 0 { + buffer.WriteString(time.Now().Format(timeFormat)) + } + // Caller path. + callerPath := "" + if l.flags & F_FILE_LONG > 0 { + callerPath = l.getLongFile() + ": " + } + if l.flags & F_FILE_SHORT > 0 { + callerPath = gfile.Basename(l.getLongFile()) + ": " + } + if len(callerPath) > 0 { + buffer.WriteString(callerPath) + } + // Prefix. + if len(l.prefix) > 0 { + buffer.WriteString(l.prefix + " ") + } } + if len(level) > 0 { + buffer.WriteString(level + " ") + } + if len(format) > 0 { + buffer.WriteString(fmt.Sprintf(format, value...)) + } else { + for k, v := range value { + if k > 0 { + buffer.WriteByte(' ') + } + buffer.WriteString(gconv.String(v)) + } + } + buffer.WriteString(ln) + if l.writer == nil { if f := l.getFilePointer(); f != nil { defer f.Close() - if _, err := io.WriteString(f, s); err != nil { + if _, err := io.WriteString(f, buffer.String()); err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } // Allow output to stdout? if l.stdoutPrint { - if _, err := std.Write([]byte(s)); err != nil { + if _, err := std.Write(buffer.Bytes()); err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } } else { - if _, err := l.writer.Write([]byte(s)); err != nil { + if _, err := l.writer.Write(buffer.Bytes()); err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } } // printStd prints content without backtrace. -func (l *Logger) printStd(s string) { - l.print(os.Stdout, s) +func (l *Logger) printStd(level string, format string, value...interface{}) { + l.print(os.Stdout, level, "", value...) } // printStd prints content with backtrace check. -func (l *Logger) printErr(s string) { +func (l *Logger) printErr(level string, format string, value...interface{}) { if l.btStatus == 1 { - s = l.appendBacktrace(s) + if s := l.GetBacktrace(); s != "" { + value = append(value, ln + "Backtrace:" + ln + s) + } } // In matter of sequence, do not use stderr here, but use the same stdout. - l.print(os.Stdout, s) -} - -// appendBacktrace appends backtrace to the . -func (l *Logger) appendBacktrace(s string, skip...int) string { - trace := l.GetBacktrace(skip...) - if trace != "" { - backtrace := "Backtrace:" + ln + trace - if len(s) > 0 { - if s[len(s)-1] == byte('\n') { - s = s + backtrace + ln - } else { - s = s + ln + backtrace + ln - } - } else { - s = backtrace - } - } - return s + l.print(os.Stdout, level, format, value...) } // PrintBacktrace prints the caller backtrace, // the optional parameter specify the skipped backtrace offset from the end point. func (l *Logger) PrintBacktrace(skip...int) { - l.Println(l.appendBacktrace("", skip...)) + if s := l.GetBacktrace(skip...); s != "" { + l.Println("Backtrace:" + ln + s) + } else { + l.Println() + } } // GetBacktrace returns the caller backtrace content, @@ -350,8 +371,8 @@ func (l *Logger) getLongFile() string { } -// format formats the content according the flags. -func (l *Logger) format(content string) string { +// header returns the header according logger's settings. +func (l *Logger) header() string { buffer := bytes.NewBuffer(nil) // Time. timeFormat := "" @@ -382,8 +403,6 @@ func (l *Logger) format(content string) string { if len(l.prefix) > 0 { buffer.WriteString(l.prefix + " ") } - // Content. - buffer.WriteString(content) return buffer.String() } diff --git a/g/os/glog/glog_logger_api.go b/g/os/glog/glog_logger_api.go index 9ef5c0c2a..a77bd1442 100644 --- a/g/os/glog/glog_logger_api.go +++ b/g/os/glog/glog_logger_api.go @@ -13,205 +13,201 @@ import ( // Print prints with newline using fmt.Sprintln. // The param can be multiple variables. -func (l *Logger) Print(v ...interface{}) { - l.printStd(fmt.Sprintln(v...)) +func (l *Logger) Print(v...interface{}) { + l.printStd("", "", v...) } // Printf prints with format using fmt.Sprintf. // The param can be multiple variables. -func (l *Logger) Printf(format string, v ...interface{}) { - l.printStd(fmt.Sprintf(format + ln, v...)) +func (l *Logger) Printf(format string, v...interface{}) { + l.printStd("", format, v...) } // See Print. -func (l *Logger) Println(v ...interface{}) { +func (l *Logger) Println(v...interface{}) { l.Print(v...) } // Deprecated. // Use Printf instead. -func (l *Logger) Printfln(format string, v ...interface{}) { - l.printStd(fmt.Sprintf(format + ln, v...)) +func (l *Logger) Printfln(format string, v...interface{}) { + l.printStd("", format, v...) } // Fatal prints the logging content with [FATA] header and newline, then exit the current process. -func (l *Logger) Fatal(v ...interface{}) { - l.printErr("[FATA] " + fmt.Sprintln(v...)) +func (l *Logger) Fatal(v...interface{}) { + l.printErr("[FATA]", "", v...) os.Exit(1) } // Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. -func (l *Logger) Fatalf(format string, v ...interface{}) { - l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) +func (l *Logger) Fatalf(format string, v...interface{}) { + l.printErr("[FATA]", format, v...) os.Exit(1) } // Deprecated. // Use Fatalf instead. -func (l *Logger) Fatalfln(format string, v ...interface{}) { - l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) +func (l *Logger) Fatalfln(format string, v...interface{}) { + l.Fatalf(format, v...) os.Exit(1) } // Panic prints the logging content with [PANI] header and newline, then panics. -func (l *Logger) Panic(v ...interface{}) { - s := fmt.Sprintln(v...) - l.printErr("[PANI] " + s) - panic(s) +func (l *Logger) Panic(v...interface{}) { + l.printErr("[PANI]", "", v...) + panic(fmt.Sprint(v...)) } // Panicf prints the logging content with [PANI] header, custom format and newline, then panics. -func (l *Logger) Panicf(format string, v ...interface{}) { - s := fmt.Sprintf(format + ln, v...) - l.printErr("[PANI] " + s) - panic(s) +func (l *Logger) Panicf(format string, v...interface{}) { + l.printErr("[PANI]", format, v...) + panic(fmt.Sprintf(format, v...)) } // Deprecated. // Use Panicf instead. -func (l *Logger) Panicfln(format string, v ...interface{}) { - s := fmt.Sprintf(format + ln, v...) - l.printErr("[PANI] " + s) - panic(s) +func (l *Logger) Panicfln(format string, v...interface{}) { + l.Panicf(format, v...) } // Info prints the logging content with [INFO] header and newline. -func (l *Logger) Info(v ...interface{}) { +func (l *Logger) Info(v...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintln(v...)) + l.printStd("[INFO]", "", v...) } } // Infof prints the logging content with [INFO] header, custom format and newline. -func (l *Logger) Infof(format string, v ...interface{}) { +func (l *Logger) Infof(format string, v...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintf(format + ln, v...)) + l.printStd("[INFO]", format, v...) } } // Deprecated. // Use Infof instead. -func (l *Logger) Infofln(format string, v ...interface{}) { +func (l *Logger) Infofln(format string, v...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO] " + fmt.Sprintf(format + ln, v...) + ln) + l.Infof(format, v...) } } // Debug prints the logging content with [DEBU] header and newline. -func (l *Logger) Debug(v ...interface{}) { +func (l *Logger) Debug(v...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintln(v...)) + l.printStd("[DEBU]", "", v...) } } // Debugf prints the logging content with [DEBU] header, custom format and newline. -func (l *Logger) Debugf(format string, v ...interface{}) { +func (l *Logger) Debugf(format string, v...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintf(format + ln, v...)) + l.printStd("[DEBU]", format, v...) } } // Deprecated. // Use Debugf instead. -func (l *Logger) Debugfln(format string, v ...interface{}) { +func (l *Logger) Debugfln(format string, v...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU] " + fmt.Sprintf(format + ln, v...) + ln) + l.Debugf(format, v...) } } // Notice prints the logging content with [NOTI] header and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Notice(v ...interface{}) { +func (l *Logger) Notice(v...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintln(v...)) + l.printErr("[NOTI]", "", v...) } } // Noticef prints the logging content with [NOTI] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Noticef(format string, v ...interface{}) { +func (l *Logger) Noticef(format string, v...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintf(format + ln, v...)) + l.printErr("[NOTI]", format, v...) } } // Deprecated. // Use Noticef instead. -func (l *Logger) Noticefln(format string, v ...interface{}) { +func (l *Logger) Noticefln(format string, v...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI] " + fmt.Sprintf(format + ln, v...) + ln) + l.Noticef(format, v...) } } // Warning prints the logging content with [WARN] header and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Warning(v ...interface{}) { +func (l *Logger) Warning(v...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintln(v...)) + l.printErr("[WARN]", "", v...) } } // Warningf prints the logging content with [WARN] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Warningf(format string, v ...interface{}) { +func (l *Logger) Warningf(format string, v...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintf(format + ln, v...)) + l.printErr("[WARN]", format, v...) } } // Deprecated. // Use Warningf instead. -func (l *Logger) Warningfln(format string, v ...interface{}) { +func (l *Logger) Warningfln(format string, v...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN] " + fmt.Sprintf(format + ln, v...) + ln) + l.Warningf(format, v...) } } // Error prints the logging content with [ERRO] header and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Error(v ...interface{}) { +func (l *Logger) Error(v...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintln(v...)) + l.printErr("[ERRO]", "", v...) } } // Errorf prints the logging content with [ERRO] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Errorf(format string, v ...interface{}) { +func (l *Logger) Errorf(format string, v...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintf(format + ln, v...)) + l.printErr("[ERRO]", format, v...) } } // Deprecated. // Use Errorf instead. -func (l *Logger) Errorfln(format string, v ...interface{}) { +func (l *Logger) Errorfln(format string, v...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO] " + fmt.Sprintf(format + ln, v...) + ln) + l.Errorf(format, v...) } } // Critical prints the logging content with [CRIT] header and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Critical(v ...interface{}) { +func (l *Logger) Critical(v...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintln(v...)) + l.printErr("[CRIT]", "", v...) } } // Criticalf prints the logging content with [CRIT] header, custom format and newline. // It also prints caller backtrace info if backtrace feature is enabled. -func (l *Logger) Criticalf(format string, v ...interface{}) { +func (l *Logger) Criticalf(format string, v...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintf(format + ln, v...)) + l.printErr("[CRIT]", format, v...) } } // Deprecated. // Use Criticalf instead. -func (l *Logger) Criticalfln(format string, v ...interface{}) { +func (l *Logger) Criticalfln(format string, v...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT] " + fmt.Sprintf(format + ln, v...) + ln) + l.Criticalf(format, v...) } } diff --git a/g/util/gconv/gconv.go b/g/util/gconv/gconv.go index af64db5c3..6b517ca37 100644 --- a/g/util/gconv/gconv.go +++ b/g/util/gconv/gconv.go @@ -137,6 +137,7 @@ func String(i interface{}) string { case bool: return strconv.FormatBool(value) case string: return value case []byte: return string(value) + case []rune: return string(value) default: if f, ok := value.(apiString); ok { // If the variable implements the String() interface, diff --git a/geg/os/glog/glog_category.go b/geg/os/glog/glog_category.go index 8a0d0d9c0..3df48a275 100644 --- a/geg/os/glog/glog_category.go +++ b/geg/os/glog/glog_category.go @@ -9,7 +9,7 @@ import ( func main() { path := "/tmp/glog-cat" glog.SetPath(path) - glog.StdPrint(false).Cat("cat1").Cat("cat2").Println("test") + glog.Stdout(false).Cat("cat1").Cat("cat2").Println("test") list, err := gfile.ScanDir(path, "*", true) g.Dump(err) g.Dump(list) diff --git a/geg/os/glog/glog_debug.go b/geg/os/glog/glog_debug.go index 259f0f8b4..faa4f1c9e 100644 --- a/geg/os/glog/glog_debug.go +++ b/geg/os/glog/glog_debug.go @@ -3,11 +3,12 @@ package main import ( "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/os/gtimer" "time" ) func main() { - gtime.SetTimeout(3*time.Second, func() { + gtimer.SetTimeout(3*time.Second, func() { glog.SetDebug(false) }) for { diff --git a/geg/os/glog/glog_json.go b/geg/os/glog/glog_json.go new file mode 100644 index 000000000..75ceeb6dc --- /dev/null +++ b/geg/os/glog/glog_json.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/glog" +) + +func main() { + glog.Debug(g.Map{"uid" : 100, "name" : "john"}) + + type User struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + glog.Debug(User{100, "john"}) +} From 2bf2f1b82266be0269dbb89e1a7eaf2d3a0007cd Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 15:11:32 +0800 Subject: [PATCH 27/50] improve grpool --- g/container/glist/glist.go | 5 +++ g/os/grpool/grpool.go | 79 +++++++++++++-------------------- g/os/grpool/grpool_unit_test.go | 42 +++++++++++++++++- geg/container/gqueue/gqueue3.go | 12 ++--- geg/os/grpool/grpool1.go | 6 +-- geg/os/grpool/grpool2.go | 3 +- geg/os/grpool/grpool3.go | 7 ++- geg/os/grpool/grpool4.go | 18 ++++++++ 8 files changed, 109 insertions(+), 63 deletions(-) create mode 100644 geg/os/grpool/grpool4.go diff --git a/g/container/glist/glist.go b/g/container/glist/glist.go index 868136571..cef9b7c01 100644 --- a/g/container/glist/glist.go +++ b/g/container/glist/glist.go @@ -209,6 +209,11 @@ func (l *List) Len() (length int) { return } +// Alias of Len. +func (l *List) Size() int { + return l.Len() +} + // MoveBefore moves element to its new position before

. // If or

is not an element of , or ==

, the list is not modified. // The element and

must not be nil. diff --git a/g/os/grpool/grpool.go b/g/os/grpool/grpool.go index cb4c9866a..2e2eda86b 100644 --- a/g/os/grpool/grpool.go +++ b/g/os/grpool/grpool.go @@ -5,50 +5,42 @@ // You can obtain one at https://github.com/gogf/gf. // Package grpool implements a goroutine reusable pool. -// -// Goroutine池, -// 用于goroutine复用,提升异步操作执行效率(避免goroutine限制,并节约内存开销). -// 需要注意的是,grpool提供给的公共池不提供关闭方法,自创建的池可以手动关闭掉。 package grpool import ( - "github.com/gogf/gf/g/container/glist" - "github.com/gogf/gf/g/container/gtype" - "math" + "github.com/gogf/gf/g/container/glist" + "github.com/gogf/gf/g/container/gtype" ) -// goroutine池对象 +// Goroutine Pool type Pool struct { - limit int // 最大的goroutine数量限制 - count *gtype.Int // 当前正在运行的goroutine数量 - list *glist.List // 待处理任务操作列表 - events chan struct{} // 任务添加事件 - closed *gtype.Bool + limit int // 最大的goroutine数量限制 + count *gtype.Int // 当前正在运行的goroutine数量 + list *glist.List // 待处理任务操作列表 + closed *gtype.Bool // 是否关闭 } -// 默认的goroutine池管理对象 +// 默认的goroutine池管理对象, // 该对象与进程同生命周期,无需Close var defaultPool = New() // 创建goroutine池管理对象,参数用于限制限制最大的goroutine数量,非必需参数,默认不做限制 -func New(size...int) *Pool { - limit := -1 - if len(size) > 0 { - limit = size[0] - } +func New(limit...int) *Pool { p := &Pool { - limit : limit, + limit : -1, count : gtype.NewInt(), list : glist.New(), - events : make(chan struct{}, math.MaxInt32), closed : gtype.NewBool(), } + if len(limit) > 0 { + p.limit = limit[0] + } return p } // 添加异步任务(使用默认的池对象) -func Add(f func()) error { - return defaultPool.Add(f) +func Add(f func()) { + defaultPool.Add(f) } // 查询当前goroutine总数 @@ -58,18 +50,16 @@ func Size() int { // 查询当前等待处理的任务总数 func Jobs() int { - return len(defaultPool.events) + return defaultPool.list.Len() } // 添加异步任务 -func (p *Pool) Add(f func()) error { - p.list.PushBack(f) - p.events <- struct{}{} - // 判断是否创建新的worker - if p.list.Len() > 1 || p.count.Val() == 0 { +func (p *Pool) Add(f func()) { + p.list.PushFront(f) + // 判断是否创建新的goroutine + if p.count.Val() != p.limit { p.fork() } - return nil } // 查询当前goroutine总数 @@ -79,35 +69,26 @@ func (p *Pool) Size() int { // 查询当前等待处理的任务总数 func (p *Pool) Jobs() int { - return p.list.Len() + return p.list.Size() } -// 创建新的worker执行任务 +// 检查并创建新的goroutine执行任务 func (p *Pool) fork() { - // 如果worker数量已经达到限制,那么不创建新worker,直接返回 - if p.count.Val() == p.limit { - return - } p.count.Add(1) go func() { + defer p.count.Add(-1) + job := (interface{})(nil) for !p.closed.Val() { - select { - case <- p.events: - if job := p.list.PopFront(); job != nil { - job.(func())() - } else { - p.count.Add(-1) - return - } - default: - p.count.Add(-1) - return - } + if job = p.list.PopBack(); job != nil { + job.(func())() + } else { + return + } } }() } // 关闭池,所有的任务将会停止,此后继续添加的任务将不会被执行 func (p *Pool) Close() { - p.closed.Set(true) + p.closed.Set(true) } \ No newline at end of file diff --git a/g/os/grpool/grpool_unit_test.go b/g/os/grpool/grpool_unit_test.go index 799e027cc..ec87b5291 100644 --- a/g/os/grpool/grpool_unit_test.go +++ b/g/os/grpool/grpool_unit_test.go @@ -22,7 +22,7 @@ func Test_Basic(t *testing.T) { gtest.Case(t, func() { wg := sync.WaitGroup{} array := garray.NewArray() - size := 100000 + size := 10000 wg.Add(size) for i := 0; i < size; i++ { grpool.Add(func() { @@ -33,7 +33,45 @@ func Test_Basic(t *testing.T) { wg.Wait() gtest.Assert(array.Len(), size) }) +} +func Test_Limit1(t *testing.T) { + gtest.Case(t, func() { + wg := sync.WaitGroup{} + array := garray.NewArray() + size := 10000 + pool := grpool.New(10) + wg.Add(size) + for i := 0; i < size; i++ { + pool.Add(func() { + array.Append(1) + wg.Done() + }) + } + wg.Wait() + gtest.Assert(array.Len(), size) + }) +} + +func Test_Limit2(t *testing.T) { + gtest.Case(t, func() { + wg := sync.WaitGroup{} + array := garray.NewArray() + size := 10000 + pool := grpool.New(1) + wg.Add(size) + for i := 0; i < size; i++ { + pool.Add(func() { + array.Append(1) + wg.Done() + }) + } + wg.Wait() + gtest.Assert(array.Len(), size) + }) +} + +func Test_Limit3(t *testing.T) { gtest.Case(t, func() { array := garray.NewArray() size := 100000 @@ -50,7 +88,7 @@ func Test_Basic(t *testing.T) { gtest.Assert(array.Len(), 10000) pool.Close() time.Sleep(2*time.Second) - gtest.Assert(pool.Size(), 10000) + gtest.Assert(pool.Size(), 0) gtest.Assert(pool.Jobs(), 90000) gtest.Assert(array.Len(), 10000) }) diff --git a/geg/container/gqueue/gqueue3.go b/geg/container/gqueue/gqueue3.go index 87ac9468d..5e25a6339 100644 --- a/geg/container/gqueue/gqueue3.go +++ b/geg/container/gqueue/gqueue3.go @@ -18,12 +18,12 @@ func main() { // 消费者,不停读取队列数据并输出到终端 for { select { - case v := <-queue.C: - if v != nil { - fmt.Println(v) - } else { - return - } + case v := <-queue.C: + if v != nil { + fmt.Println(v) + } else { + return + } } } } diff --git a/geg/os/grpool/grpool1.go b/geg/os/grpool/grpool1.go index 969098184..4aec3d922 100644 --- a/geg/os/grpool/grpool1.go +++ b/geg/os/grpool/grpool1.go @@ -3,7 +3,7 @@ package main import ( "fmt" "github.com/gogf/gf/g/os/grpool" - "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/os/gtimer" "time" ) @@ -18,11 +18,11 @@ func main() { } fmt.Println("worker:", pool.Size()) fmt.Println(" jobs:", pool.Jobs()) - gtime.SetInterval(time.Second, func() bool { + gtimer.SetInterval(time.Second, func() { fmt.Println("worker:", pool.Size()) fmt.Println(" jobs:", pool.Jobs()) fmt.Println() - return true + gtimer.Exit() }) select {} diff --git a/geg/os/grpool/grpool2.go b/geg/os/grpool/grpool2.go index b2eb2d881..72bfbb0b5 100644 --- a/geg/os/grpool/grpool2.go +++ b/geg/os/grpool/grpool2.go @@ -10,8 +10,9 @@ func main() { wg := sync.WaitGroup{} for i := 0; i < 10; i++ { wg.Add(1) + v := i grpool.Add(func() { - fmt.Println(i) + fmt.Println(v) wg.Done() }) } diff --git a/geg/os/grpool/grpool3.go b/geg/os/grpool/grpool3.go index 4a2fd6773..343700108 100644 --- a/geg/os/grpool/grpool3.go +++ b/geg/os/grpool/grpool3.go @@ -2,17 +2,20 @@ package main import ( "fmt" + "github.com/gogf/gf/g/os/grpool" "sync" ) func main() { + p := grpool.New(1) wg := sync.WaitGroup{} for i := 0; i < 10; i++ { wg.Add(1) - go func(v int) { + v := i + p.Add(func() { fmt.Println(v) wg.Done() - }(i) + }) } wg.Wait() } diff --git a/geg/os/grpool/grpool4.go b/geg/os/grpool/grpool4.go new file mode 100644 index 000000000..4a2fd6773 --- /dev/null +++ b/geg/os/grpool/grpool4.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "sync" +) + +func main() { + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func(v int) { + fmt.Println(v) + wg.Done() + }(i) + } + wg.Wait() +} From d67b95c593a5a6367062b2805edb007d2ef1ddeb Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 19:34:03 +0800 Subject: [PATCH 28/50] add async logging feature for glog --- g/os/gfpool/gfpool.go | 25 ++++---- g/os/glog/glog.go | 8 +++ g/os/glog/glog_chaining.go | 6 ++ g/os/glog/glog_logger.go | 99 +++++++++++++------------------ g/os/glog/glog_logger_chaining.go | 32 ++++++++-- g/os/grpool/grpool.go | 45 +++++++------- geg/os/glog/glog_async.go | 13 ++++ 7 files changed, 132 insertions(+), 96 deletions(-) create mode 100644 geg/os/glog/glog_async.go diff --git a/g/os/gfpool/gfpool.go b/g/os/gfpool/gfpool.go index 034c43e42..e904a79e2 100644 --- a/g/os/gfpool/gfpool.go +++ b/g/os/gfpool/gfpool.go @@ -1,12 +1,10 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2017-2019 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. // Package gfpool provides io-reusable pool for file pointer. -// -// 文件指针池. package gfpool import ( @@ -19,11 +17,11 @@ import ( "sync" ) -// 文件指针池 +// File pointer pool. type Pool struct { - id *gtype.Int // 指针池ID,用以识别指针池是否重建 + id *gtype.Int // 指针池ID,用以识别指针池是否需要重建 pool *gpool.Pool // 底层对象池 - inited *gtype.Bool // 是否初始化(在执行第一次File方法后初始化,主要用于监听的添加,但是只能添加一次) + inited *gtype.Bool // 是否初始化(在执行第一次执行File方法后初始化,主要用于文件监听的添加,但是只能添加一次) expire int // 过期时间 } @@ -38,8 +36,11 @@ type File struct { path string // 绝对路径 } -// 全局指针池,expire < 0表示不过期,expire = 0表示使用完立即回收,expire > 0表示超时回收 -var pools = gmap.NewStrAnyMap() + +var ( + // 全局文件指针池Map, 不过期 + pools = gmap.NewStrAnyMap() +) // 获得文件对象,并自动创建指针池(过期时间单位:毫秒) func Open(path string, flag int, perm os.FileMode, expire...int) (file *File, err error) { @@ -54,12 +55,14 @@ func Open(path string, flag int, perm os.FileMode, expire...int) (file *File, er return pool.File() } +// Deprecated. +// See Open. func OpenFile(path string, flag int, perm os.FileMode, expire...int) (file *File, err error) { return Open(path, flag, perm, expire...) } -// 创建一个文件指针池,expire = 0表示不过期,expire < 0表示使用完立即回收,expire > 0表示超时回收,默认值为0不过期 -// 过期时间单位:毫秒 +// 创建一个文件指针池,expire = 0表示不过期,expire < 0表示使用完立即回收,expire > 0表示超时回收,默认值为0表示不过期。 +// 注意过期时间单位为:毫秒。 func New(path string, flag int, perm os.FileMode, expire...int) *Pool { fpExpire := 0 if len(expire) > 0 { @@ -130,7 +133,7 @@ func (p *Pool) File() (*File, error) { return nil, err } } - // !p.inited.Val() 使用原子读取操作判断,保证该操作判断的效率; + // 优先使用 !p.inited.Val() 原子读取操作判断,保证判断操作的效率; // p.inited.Set(true) == false 使用原子写入操作,保证该操作的原子性; if !p.inited.Val() && p.inited.Set(true) == false { gfsnotify.Add(f.path, func(event *gfsnotify.Event) { diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index 7dd62c664..ad59cad70 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -11,6 +11,7 @@ package glog import ( "github.com/gogf/gf/g/internal/cmdenv" + "github.com/gogf/gf/g/os/grpool" "io" ) @@ -29,6 +30,8 @@ const ( var ( // Default logger object, for package method usage logger = New() + // Goroutine pool for async logging output. + asyncPool = grpool.New(1) ) func init() { @@ -83,6 +86,11 @@ func SetDebug(debug bool) { logger.SetDebug(debug) } +// SetAsync enables/disables async logging output feature for default logger. +func SetAsync(enabled bool) { + logger.SetAsync(enabled) +} + // SetStdoutPrint sets whether ouptput the logging contents to stdout, which is false in default. func SetStdoutPrint(enabled bool) { logger.SetStdoutPrint(enabled) diff --git a/g/os/glog/glog_chaining.go b/g/os/glog/glog_chaining.go index 537210a17..471c96701 100644 --- a/g/os/glog/glog_chaining.go +++ b/g/os/glog/glog_chaining.go @@ -73,3 +73,9 @@ func Header(enabled...bool) *Logger { func Line(long...bool) *Logger { return logger.Line(long...) } + +// Async is a chaining function, +// which enables/disables async logging output feature. +func Async(enabled...bool) *Logger { + return logger.Async(enabled...) +} diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index f0620fe11..84358c4bd 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -44,7 +44,8 @@ const ( ) const ( - F_FILE_LONG = 1 << iota // Print full file name and line number: /a/b/c/d.go:23. + F_ASYNC = 1 << iota // Print logging content asynchronously。 + F_FILE_LONG // Print full file name and line number: /a/b/c/d.go:23. F_FILE_SHORT // Print final file name element and line number: d.go:23. overrides F_FILE_LONG. F_TIME_DATE // Print the date in the local time zone: 2009-01-23. F_TIME_TIME // Print the time in the local time zone: 01:23:23. @@ -105,6 +106,15 @@ func (l *Logger) SetDebug(debug bool) { } } +// SetAsync enables/disables async logging output feature. +func (l *Logger) SetAsync(enabled bool) { + if enabled { + l.flags = l.flags | F_ASYNC + } else { + l.flags = l.flags & ^F_ASYNC + } +} + // SetFlags sets extra flags for logging output features. func (l *Logger) SetFlags(flags int) { l.flags = flags @@ -263,25 +273,35 @@ func (l *Logger) print(std io.Writer, level string, format string, value...inter } } buffer.WriteString(ln) + if l.flags & F_ASYNC > 0 { + asyncPool.Add(func() { + l.printToWriter(std, buffer) + }) + } else { + l.printToWriter(std, buffer) + } +} - if l.writer == nil { - if f := l.getFilePointer(); f != nil { - defer f.Close() - if _, err := io.WriteString(f, buffer.String()); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - } - // Allow output to stdout? - if l.stdoutPrint { - if _, err := std.Write(buffer.Bytes()); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - } - } else { - if _, err := l.writer.Write(buffer.Bytes()); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - } +// printToWriter writes buffer to writer. +func (l *Logger) printToWriter(std io.Writer, buffer *bytes.Buffer) { + if l.writer == nil { + if f := l.getFilePointer(); f != nil { + defer f.Close() + if _, err := io.WriteString(f, buffer.String()); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } + } + // Allow output to stdout? + if l.stdoutPrint { + if _, err := std.Write(buffer.Bytes()); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } + } + } else { + if _, err := l.writer.Write(buffer.Bytes()); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } + } } // printStd prints content without backtrace. @@ -332,7 +352,7 @@ func (l *Logger) GetBacktrace(skip...int) string { index := 1 goRoot := runtime.GOROOT() for i := from + customSkip + l.btSkip; i < 1000; i++ { - if _, file, cline, ok := runtime.Caller(i); ok && file != "" { + if _, file, cline, ok := runtime.Caller(i); ok && len(file) > 2 { if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, ln) index++ @@ -359,7 +379,7 @@ func (l *Logger) getLongFile() string { // Find the true caller file path using custom skip. goRoot := runtime.GOROOT() for i := from + l.btSkip; i < 1000; i++ { - if _, file, line, ok := runtime.Caller(i); ok && file != "" { + if _, file, line, ok := runtime.Caller(i); ok && len(file) > 2 { if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { return fmt.Sprintf(`%s:%d`, file, line) } @@ -369,40 +389,3 @@ func (l *Logger) getLongFile() string { } return "" } - - -// header returns the header according logger's settings. -func (l *Logger) header() string { - buffer := bytes.NewBuffer(nil) - // Time. - timeFormat := "" - if l.flags & F_TIME_DATE > 0 { - timeFormat += "2006-01-02 " - } - if l.flags & F_TIME_TIME > 0 { - timeFormat += "15:04:05 " - } - if l.flags & F_TIME_MILLI > 0 { - timeFormat += "15:04:05.000 " - } - if len(timeFormat) > 0 { - buffer.WriteString(time.Now().Format(timeFormat)) - } - // Caller path. - callerPath := "" - if l.flags & F_FILE_LONG > 0 { - callerPath = l.getLongFile() + ": " - } - if l.flags & F_FILE_SHORT > 0 { - callerPath = gfile.Basename(l.getLongFile()) + ": " - } - if len(callerPath) > 0 { - buffer.WriteString(callerPath) - } - // Prefix. - if len(l.prefix) > 0 { - buffer.WriteString(l.prefix + " ") - } - return buffer.String() -} - diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index fbef51b04..1070e8882 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -121,10 +121,11 @@ func (l *Logger) Stdout(enabled...bool) *Logger { } else { logger = l } - if len(enabled) > 0 && enabled[0] { - logger.stdoutPrint = true - } else { + // stdout printing is enabled if is not passed. + if len(enabled) > 0 && !enabled[0] { logger.stdoutPrint = false + } else { + logger.stdoutPrint = true } return logger } @@ -145,10 +146,11 @@ func (l *Logger) Header(enabled...bool) *Logger { } else { logger = l } - if len(enabled) > 0 && enabled[0] { - logger.SetHeaderPrint(true) - } else { + // header is enabled if is not passed. + if len(enabled) > 0 && !enabled[0] { logger.SetHeaderPrint(false) + } else { + logger.SetHeaderPrint(true) } return logger } @@ -170,4 +172,22 @@ func (l *Logger) Line(long...bool) *Logger { logger.flags |= F_FILE_SHORT } return logger +} + +// Async is a chaining function, +// which enables/disables async logging output feature. +func (l *Logger) Async(enabled...bool) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + // async feature is enabled if is not passed. + if len(enabled) > 0 && !enabled[0] { + logger.SetAsync(false) + } else { + logger.SetAsync(true) + } + return logger } \ No newline at end of file diff --git a/g/os/grpool/grpool.go b/g/os/grpool/grpool.go index 2e2eda86b..1de07ea32 100644 --- a/g/os/grpool/grpool.go +++ b/g/os/grpool/grpool.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2017-2019 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, @@ -14,17 +14,18 @@ import ( // Goroutine Pool type Pool struct { - limit int // 最大的goroutine数量限制 - count *gtype.Int // 当前正在运行的goroutine数量 - list *glist.List // 待处理任务操作列表 - closed *gtype.Bool // 是否关闭 + limit int // Max goroutine count limit. + count *gtype.Int // Current running goroutine count. + list *glist.List // Job list. + closed *gtype.Bool // Is pool closed or not. } -// 默认的goroutine池管理对象, -// 该对象与进程同生命周期,无需Close -var defaultPool = New() +// Default goroutine pool. +var pool = New() -// 创建goroutine池管理对象,参数用于限制限制最大的goroutine数量,非必需参数,默认不做限制 +// New creates and returns a new goroutine pool object. +// The param is used to limit the max goroutine count, +// which is not limited in default. func New(limit...int) *Pool { p := &Pool { limit : -1, @@ -38,41 +39,43 @@ func New(limit...int) *Pool { return p } -// 添加异步任务(使用默认的池对象) +// Add pushes a new job to the pool using default goroutine pool. +// The job will be executed asynchronously. func Add(f func()) { - defaultPool.Add(f) + pool.Add(f) } -// 查询当前goroutine总数 +// Size returns current goroutine count of default goroutine pool. func Size() int { - return defaultPool.count.Val() + return pool.count.Val() } -// 查询当前等待处理的任务总数 +// Jobs returns current job count of default goroutine pool. func Jobs() int { - return defaultPool.list.Len() + return pool.list.Len() } -// 添加异步任务 +// Add pushes a new job to the pool. +// The job will be executed asynchronously. func (p *Pool) Add(f func()) { p.list.PushFront(f) - // 判断是否创建新的goroutine + // checking whether to create a new goroutine or not. if p.count.Val() != p.limit { p.fork() } } -// 查询当前goroutine总数 +// Size returns current goroutine count of the pool. func (p *Pool) Size() int { return p.count.Val() } -// 查询当前等待处理的任务总数 +// Jobs returns current job count of the pool. func (p *Pool) Jobs() int { return p.list.Size() } -// 检查并创建新的goroutine执行任务 +// fork creates a new goroutine pool. func (p *Pool) fork() { p.count.Add(1) go func() { @@ -88,7 +91,7 @@ func (p *Pool) fork() { }() } -// 关闭池,所有的任务将会停止,此后继续添加的任务将不会被执行 +// Close closes the goroutine pool, which makes all goroutines exit. func (p *Pool) Close() { p.closed.Set(true) } \ No newline at end of file diff --git a/geg/os/glog/glog_async.go b/geg/os/glog/glog_async.go new file mode 100644 index 000000000..0825f2f39 --- /dev/null +++ b/geg/os/glog/glog_async.go @@ -0,0 +1,13 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" + "time" +) + +func main() { + for i := 0; i < 10; i++ { + glog.Async().Print("async log", i) + } + time.Sleep(time.Second) +} From 9c6f54131f5d7d5117e0035b03928d4d3d81e7e7 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 19:37:00 +0800 Subject: [PATCH 29/50] updates unit test cases for gdb,grpool --- g/database/gdb/gdb_unit_model_test.go | 9 ++++----- g/os/grpool/grpool_unit_test.go | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/g/database/gdb/gdb_unit_model_test.go b/g/database/gdb/gdb_unit_model_test.go index 41e5d3294..efd1d35a5 100644 --- a/g/database/gdb/gdb_unit_model_test.go +++ b/g/database/gdb/gdb_unit_model_test.go @@ -7,10 +7,9 @@ package gdb_test import ( - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/os/gtime" - "github.com/gogf/gf/g/test/gtest" - "os" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" "testing" ) @@ -29,7 +28,7 @@ func TestModel_Insert(t *testing.T) { } n, _ := result.LastInsertId() gtest.Assert(n, 1) -os.Exit(1) + result, err = db.Table("user").Filter().Data(map[interface{}]interface{} { "id" : "2", "uid" : "2", diff --git a/g/os/grpool/grpool_unit_test.go b/g/os/grpool/grpool_unit_test.go index ec87b5291..2131a4683 100644 --- a/g/os/grpool/grpool_unit_test.go +++ b/g/os/grpool/grpool_unit_test.go @@ -22,7 +22,7 @@ func Test_Basic(t *testing.T) { gtest.Case(t, func() { wg := sync.WaitGroup{} array := garray.NewArray() - size := 10000 + size := 100 wg.Add(size) for i := 0; i < size; i++ { grpool.Add(func() { @@ -39,7 +39,7 @@ func Test_Limit1(t *testing.T) { gtest.Case(t, func() { wg := sync.WaitGroup{} array := garray.NewArray() - size := 10000 + size := 100 pool := grpool.New(10) wg.Add(size) for i := 0; i < size; i++ { @@ -57,7 +57,7 @@ func Test_Limit2(t *testing.T) { gtest.Case(t, func() { wg := sync.WaitGroup{} array := garray.NewArray() - size := 10000 + size := 100 pool := grpool.New(1) wg.Add(size) for i := 0; i < size; i++ { @@ -74,8 +74,8 @@ func Test_Limit2(t *testing.T) { func Test_Limit3(t *testing.T) { gtest.Case(t, func() { array := garray.NewArray() - size := 100000 - pool := grpool.New(10000) + size := 1000 + pool := grpool.New(100) for i := 0; i < size; i++ { pool.Add(func() { array.Append(1) @@ -83,13 +83,13 @@ func Test_Limit3(t *testing.T) { }) } time.Sleep(time.Second) - gtest.Assert(pool.Size(), 10000) - gtest.Assert(pool.Jobs(), 90000) - gtest.Assert(array.Len(), 10000) + gtest.Assert(pool.Size(), 100) + gtest.Assert(pool.Jobs(), 900) + gtest.Assert(array.Len(), 100) pool.Close() time.Sleep(2*time.Second) gtest.Assert(pool.Size(), 0) - gtest.Assert(pool.Jobs(), 90000) - gtest.Assert(array.Len(), 10000) + gtest.Assert(pool.Jobs(), 900) + gtest.Assert(array.Len(), 100) }) } \ No newline at end of file From 9206574bae3b923b538ba9ff1ac8b49447b3f4a4 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 20:01:45 +0800 Subject: [PATCH 30/50] add json tag most priority for gjson.New --- g/encoding/gjson/gjson_api_new_load.go | 6 ++- geg/other/test.go | 75 ++++---------------------- 2 files changed, 16 insertions(+), 65 deletions(-) diff --git a/g/encoding/gjson/gjson_api_new_load.go b/g/encoding/gjson/gjson_api_new_load.go index e1d32340d..04a4b3166 100644 --- a/g/encoding/gjson/gjson_api_new_load.go +++ b/g/encoding/gjson/gjson_api_new_load.go @@ -43,6 +43,10 @@ func New(data interface{}, unsafe...bool) *Json { default: rv := reflect.ValueOf(data) kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } switch kind { case reflect.Slice: fallthrough case reflect.Array: @@ -56,7 +60,7 @@ func New(data interface{}, unsafe...bool) *Json { case reflect.Map: fallthrough case reflect.Struct: i := interface{}(nil) - i = gconv.Map(data) + i = gconv.Map(data, "json") j = &Json { p : &i, c : byte(gDEFAULT_SPLIT_CHAR), diff --git a/geg/other/test.go b/geg/other/test.go index 224ca34a3..93993139e 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,73 +1,20 @@ package main import ( + "encoding/json" "fmt" - "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/encoding/gparser" ) +type User struct { + Id int `json:"id" gconv:"i_d"` +} + func main() { - // 创建一个默认的gmap对象, - // 默认情况下该gmap对象支持并发安全特性, - // 初始化时可以给定true参数关闭并发安全特性,当做一个普通的map使用。 - m := gmap.New() + user := User{100} + jsonBytes, _ := json.Marshal(user) + fmt.Println(string(jsonBytes)) - // 设置键值对 - for i := 0; i < 10; i++ { - m.Set(i, i) - } - // 查询大小 - fmt.Println(m.Size()) - // 批量设置键值对(不同的数据类型对象参数不同) - m.Sets(map[interface{}]interface{}{ - 10 : 10, - 11 : 11, - }) - fmt.Println(m.Size()) - - // 查询是否存在 - fmt.Println(m.Contains(1)) - - // 查询键值 - fmt.Println(m.Get(1)) - - // 删除数据项 - m.Remove(9) - fmt.Println(m.Size()) - - // 批量删除 - m.Removes([]interface{}{10, 11}) - fmt.Println(m.Size()) - - // 当前键名列表(随机排序) - fmt.Println(m.Keys()) - // 当前键值列表(随机排序) - fmt.Println(m.Values()) - - // 查询键名,当键值不存在时,写入给定的默认值 - fmt.Println(m.GetOrSet(100, 100)) - - // 删除键值对,并返回对应的键值 - fmt.Println(m.Remove(100)) - - // 遍历map - m.Iterator(func(k interface{}, v interface{}) bool { - fmt.Printf("%v:%v ", k, v) - return true - }) - - // 自定义写锁操作 - m.LockFunc(func(m map[interface{}]interface{}) { - m[99] = 99 - }) - - // 自定义读锁操作 - m.RLockFunc(func(m map[interface{}]interface{}) { - fmt.Println(m[99]) - }) - - // 清空map - m.Clear() - - // 判断map是否为空 - fmt.Println(m.IsEmpty()) + b, _ := gparser.VarToJson(user) + fmt.Println(string(b)) } \ No newline at end of file From 35d860427e439a9b6be988390368827654d21f25 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 20:31:29 +0800 Subject: [PATCH 31/50] add GetLastSql function for gdb --- g/database/gdb/gdb.go | 1 + g/database/gdb/gdb_base.go | 11 +++++++++++ geg/database/orm/mysql/gdb_debug.go | 5 ++++- geg/database/orm/mysql/gdb_update_union.go | 16 ++++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 geg/database/orm/mysql/gdb_update_union.go diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index f1c2b2e2b..68a069344 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -84,6 +84,7 @@ type DB interface { SetDebug(debug bool) SetSchema(schema string) GetQueriedSqls() []*Sql + GetLastSql() *Sql PrintQueriedSqls() SetMaxIdleConns(n int) SetMaxOpenConns(n int) diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index ebc745c49..bf50a1b7f 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -24,6 +24,17 @@ const ( gDEFAULT_DEBUG_SQL_LENGTH = 1000 // 默认调试模式下记录的SQL条数 ) +// 获取最近一条执行的sql +func (bs *dbBase) GetLastSql() *Sql { + if bs.sqls == nil { + return nil + } + if v := bs.sqls.Val(); v != nil { + return v.(*Sql) + } + return nil +} + // 获取已经执行的SQL列表(仅在debug=true时有效) func (bs *dbBase) GetQueriedSqls() []*Sql { if bs.sqls == nil { diff --git a/geg/database/orm/mysql/gdb_debug.go b/geg/database/orm/mysql/gdb_debug.go index 6aee83cb7..f171f68a4 100644 --- a/geg/database/orm/mysql/gdb_debug.go +++ b/geg/database/orm/mysql/gdb_debug.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "github.com/gogf/gf/g" "github.com/gogf/gf/g/database/gdb" "github.com/gogf/gf/g/os/glog" @@ -32,5 +33,7 @@ func main() { db.Table("user").Data(g.Map{"name": "smith"}).Where("uid=?", 1).Save() - //db.PrintQueriedSqls() + db.PrintQueriedSqls() + + fmt.Println(db.GetLastSql()) } diff --git a/geg/database/orm/mysql/gdb_update_union.go b/geg/database/orm/mysql/gdb_update_union.go new file mode 100644 index 000000000..907c66b0d --- /dev/null +++ b/geg/database/orm/mysql/gdb_update_union.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g" +) + +func main() { + db := g.DB() + db.SetDebug(true) + result, err := db.Table("pw_passageway m,pw_template t").Data("t.status", 99).Where("m.templateId=t.id AND m.status = 0").Update() + if err != nil { + panic(err) + } + fmt.Println(result.RowsAffected()) +} From 216af6a662adfba888181e5e29d9ebe105b06e03 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 20:34:57 +0800 Subject: [PATCH 32/50] updates glog --- g/os/glog/glog_logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 84358c4bd..03f91010c 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -306,7 +306,7 @@ func (l *Logger) printToWriter(std io.Writer, buffer *bytes.Buffer) { // printStd prints content without backtrace. func (l *Logger) printStd(level string, format string, value...interface{}) { - l.print(os.Stdout, level, "", value...) + l.print(os.Stdout, level, format, value...) } // printStd prints content with backtrace check. From d62ef17290ac4f28f65c04905b47790cc7a95180 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 1 Jun 2019 22:36:12 +0800 Subject: [PATCH 33/50] improve glog --- g/os/glog/glog_logger.go | 36 +++++++++++++++++--------------- g/os/glog/glog_logger_api.go | 40 ++++++++++++++++++------------------ geg/os/glog/glog_error.go | 1 + 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index 03f91010c..7b9329c5f 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -226,7 +226,7 @@ func (l *Logger) SetPrefix(prefix string) { } // print prints to defined writer, logging file or passed . -func (l *Logger) print(std io.Writer, level string, format string, value...interface{}) { +func (l *Logger) print(std io.Writer, lead string, value...interface{}) { buffer := bytes.NewBuffer(nil) if l.headerPrint { // Time. @@ -259,19 +259,18 @@ func (l *Logger) print(std io.Writer, level string, format string, value...inter buffer.WriteString(l.prefix + " ") } } - if len(level) > 0 { - buffer.WriteString(level + " ") - } - if len(format) > 0 { - buffer.WriteString(fmt.Sprintf(format, value...)) - } else { - for k, v := range value { - if k > 0 { - buffer.WriteByte(' ') - } - buffer.WriteString(gconv.String(v)) + if len(lead) > 0 { + buffer.WriteString(lead) + if len(value) > 0 { + buffer.WriteByte(' ') } } + for k, v := range value { + if k > 0 { + buffer.WriteByte(' ') + } + buffer.WriteString(gconv.String(v)) + } buffer.WriteString(ln) if l.flags & F_ASYNC > 0 { asyncPool.Add(func() { @@ -305,19 +304,24 @@ func (l *Logger) printToWriter(std io.Writer, buffer *bytes.Buffer) { } // printStd prints content without backtrace. -func (l *Logger) printStd(level string, format string, value...interface{}) { - l.print(os.Stdout, level, format, value...) +func (l *Logger) printStd(lead string, value...interface{}) { + l.print(os.Stdout, lead, value...) } // printStd prints content with backtrace check. -func (l *Logger) printErr(level string, format string, value...interface{}) { +func (l *Logger) printErr(lead string, value...interface{}) { if l.btStatus == 1 { if s := l.GetBacktrace(); s != "" { value = append(value, ln + "Backtrace:" + ln + s) } } // In matter of sequence, do not use stderr here, but use the same stdout. - l.print(os.Stdout, level, format, value...) + l.print(os.Stdout, lead, value...) +} + +// format formats using fmt.Sprintf. +func (l *Logger) format(format string, value...interface{}) string { + return fmt.Sprintf(format, value...) } // PrintBacktrace prints the caller backtrace, diff --git a/g/os/glog/glog_logger_api.go b/g/os/glog/glog_logger_api.go index a77bd1442..0c75f1336 100644 --- a/g/os/glog/glog_logger_api.go +++ b/g/os/glog/glog_logger_api.go @@ -14,13 +14,13 @@ import ( // Print prints with newline using fmt.Sprintln. // The param can be multiple variables. func (l *Logger) Print(v...interface{}) { - l.printStd("", "", v...) + l.printStd("", v...) } // Printf prints with format using fmt.Sprintf. // The param can be multiple variables. func (l *Logger) Printf(format string, v...interface{}) { - l.printStd("", format, v...) + l.printStd(l.format(format, v...)) } // See Print. @@ -31,18 +31,18 @@ func (l *Logger) Println(v...interface{}) { // Deprecated. // Use Printf instead. func (l *Logger) Printfln(format string, v...interface{}) { - l.printStd("", format, v...) + l.printStd(l.format(format, v...)) } // Fatal prints the logging content with [FATA] header and newline, then exit the current process. func (l *Logger) Fatal(v...interface{}) { - l.printErr("[FATA]", "", v...) + l.printErr("[FATA]", v...) os.Exit(1) } // Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. func (l *Logger) Fatalf(format string, v...interface{}) { - l.printErr("[FATA]", format, v...) + l.printErr("[FATA]", l.format(format, v...)) os.Exit(1) } @@ -55,14 +55,14 @@ func (l *Logger) Fatalfln(format string, v...interface{}) { // Panic prints the logging content with [PANI] header and newline, then panics. func (l *Logger) Panic(v...interface{}) { - l.printErr("[PANI]", "", v...) + l.printErr("[PANI]", v...) panic(fmt.Sprint(v...)) } // Panicf prints the logging content with [PANI] header, custom format and newline, then panics. func (l *Logger) Panicf(format string, v...interface{}) { - l.printErr("[PANI]", format, v...) - panic(fmt.Sprintf(format, v...)) + l.printErr("[PANI]", l.format(format, v...)) + panic(l.format(format, v...)) } // Deprecated. @@ -74,14 +74,14 @@ func (l *Logger) Panicfln(format string, v...interface{}) { // Info prints the logging content with [INFO] header and newline. func (l *Logger) Info(v...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO]", "", v...) + l.printStd("[INFO]", v...) } } // Infof prints the logging content with [INFO] header, custom format and newline. func (l *Logger) Infof(format string, v...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd("[INFO]", format, v...) + l.printStd("[INFO]", l.format(format, v...)) } } @@ -96,14 +96,14 @@ func (l *Logger) Infofln(format string, v...interface{}) { // Debug prints the logging content with [DEBU] header and newline. func (l *Logger) Debug(v...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU]", "", v...) + l.printStd("[DEBU]", v...) } } // Debugf prints the logging content with [DEBU] header, custom format and newline. func (l *Logger) Debugf(format string, v...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd("[DEBU]", format, v...) + l.printStd("[DEBU]", l.format(format, v...)) } } @@ -119,7 +119,7 @@ func (l *Logger) Debugfln(format string, v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Notice(v...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI]", "", v...) + l.printErr("[NOTI]", v...) } } @@ -127,7 +127,7 @@ func (l *Logger) Notice(v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Noticef(format string, v...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printErr("[NOTI]", format, v...) + l.printErr("[NOTI]", l.format(format, v...)) } } @@ -143,7 +143,7 @@ func (l *Logger) Noticefln(format string, v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Warning(v...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN]", "", v...) + l.printErr("[WARN]", v...) } } @@ -151,7 +151,7 @@ func (l *Logger) Warning(v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Warningf(format string, v...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printErr("[WARN]", format, v...) + l.printErr("[WARN]", l.format(format, v...)) } } @@ -167,7 +167,7 @@ func (l *Logger) Warningfln(format string, v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Error(v...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO]", "", v...) + l.printErr("[ERRO]", v...) } } @@ -175,7 +175,7 @@ func (l *Logger) Error(v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Errorf(format string, v...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr("[ERRO]", format, v...) + l.printErr("[ERRO]", l.format(format, v...)) } } @@ -191,7 +191,7 @@ func (l *Logger) Errorfln(format string, v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Critical(v...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT]", "", v...) + l.printErr("[CRIT]", v...) } } @@ -199,7 +199,7 @@ func (l *Logger) Critical(v...interface{}) { // It also prints caller backtrace info if backtrace feature is enabled. func (l *Logger) Criticalf(format string, v...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr("[CRIT]", format, v...) + l.printErr("[CRIT]", l.format(format, v...)) } } diff --git a/geg/os/glog/glog_error.go b/geg/os/glog/glog_error.go index a2273b458..18738726c 100644 --- a/geg/os/glog/glog_error.go +++ b/geg/os/glog/glog_error.go @@ -7,4 +7,5 @@ import ( func main() { //glog.SetPath("/tmp/") glog.Error("This is error!") + glog.Errorf("This is error, %d!", 2) } From c52640c6726c0459efc173d0fe63e6fa662b7c92 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 2 Jun 2019 13:32:03 +0800 Subject: [PATCH 34/50] README updates --- .github/FUNDING.yml | 8 -------- DONATOR.MD | 17 +++++++++++++++++ README.MD | 37 +++---------------------------------- README_ZH.MD | 42 ++++-------------------------------------- 4 files changed, 24 insertions(+), 80 deletions(-) delete mode 100644 .github/FUNDING.yml create mode 100644 DONATOR.MD diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index f53dc3b9c..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,8 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -custom: # Replace with a single custom sponsorship URL diff --git a/DONATOR.MD b/DONATOR.MD new file mode 100644 index 000000000..36b92beff --- /dev/null +++ b/DONATOR.MD @@ -0,0 +1,17 @@ +# Donators + + + +| Name | Channel | Amount +|---|---|--- +|[hailaz](https://gitee.com/hailaz)|gitee|¥20.00 +|[ireadx](https://github.com/ireadx)|alipay|¥201.00 +|[mg91](https://gitee.com/mg91)|gitee|¥10.00 +|[pibigstar](https://github.com/pibigstar)|alipay|¥10.00 +|[tiangenglan](https://gitee.com/tiangenglan)|gitee|¥30.00 +|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00 +|[zhuhuan12](https://gitee.com/zhuhuan12)|gitee|¥50.00 +|[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00 +|潘兄|wechat|¥100.00 +|土豆相公|alipay|¥66.60 +|上海金保证|bank|¥2000.00 \ No newline at end of file diff --git a/README.MD b/README.MD index 96b60d09b..8ef473078 100644 --- a/README.MD +++ b/README.MD @@ -68,43 +68,12 @@ func main() { `GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever. -# Contributors - -- [aloncn](https://github.com/aloncn) -- [chenyang351](https://github.com/chenyang351) -- [garfieldkwong](https://gitee.com/garfieldkwong) -- [hailaz](https://gitee.com/hailaz) -- [johng](https://johng.cn) -- [jroam](https://github.com/jroam) -- [pibigstar](https://github.com/pibigstar) -- [qq1054000800](https://gitee.com/qq1054000800) -- [qq976739120](https://github.com/qq976739120) -- [touzijiao](https://github.com/touzijiao) -- [wenzi1](https://gitee.com/wenzi1) -- [wxkj001](https://github.com/wxkj001) -- [ymrjqyy](https://gitee.com/ymrjqyy) -- [youyixiao](https://github.com/youyixiao) -- [zhangjinfu](https://gitee.com/zhangjinfu) -- [zhaopengme](https://github.com/zhaopengme) -- [zseeker](https://gitee.com/zseeker) - # Donators -We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](https://goframe.org/images/donate.png)? - -- [flyke-xu](https://gitee.com/flyke-xu) -- [hailaz](https://gitee.com/hailaz) -- [ireadx](https://github.com/ireadx) -- [mg91](https://gitee.com/mg91) -- [pibigstar](https://github.com/pibigstar) -- [tiangenglan](https://gitee.com/tiangenglan) -- [wxkj](https://gitee.com/wxkj) -- [zhuhuan12](https://gitee.com/zhuhuan12) -- [zfan_codes](https://gitee.com/zfan_codes) - - - +We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](DONATOR.MD)? +# Thanks +JetBrains