mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,4 +15,3 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
|
||||
|
||||
73
RELEASE.MD
73
RELEASE.MD
@ -1,4 +1,75 @@
|
||||
# `v1.7.0`
|
||||
# `v1.8.0`
|
||||
|
||||
## 新功能改进
|
||||
1. 框架目前 `69` 个开发模块(不包括内部模块),原生代码 `65302` 行(不包含第三包依赖包),单元测试覆盖率达到`77%`;
|
||||
1. 新增`gerror`错误处理模块:https://goframe.org/errors/gerror/index
|
||||
1. 改进`gcharset`字符编码转换模块,支持更多的字符集:https://goframe.org/encoding/gcharset/index
|
||||
1. 新增`gmutex`模块,基于`channel`实现的高级互斥锁模块,支持更丰富的互斥锁特性:https://goframe.org/os/gmutex/index
|
||||
1. 改进`glog`日志模块:
|
||||
- 新增日志异步输出特性:https://goframe.org/os/glog/async
|
||||
- 新增`Flags`额外功能特性:https://goframe.org/os/glog/flags
|
||||
- 新增`Json`数据格式输出:https://goframe.org/os/glog/json
|
||||
- 新增自定义`Writer`接口特性:https://goframe.org/os/glog/writer
|
||||
- **修改`Backtrace`名称为`Stack`,并改进调用堆栈输出格式;**
|
||||
- 新增`Expose`方法暴露内部默认`Logger`对象;
|
||||
1. 改进`gdb`数据库ORM模块:
|
||||
- 改进错误处理,当数据库操作没有查询到数据时,`error`返回`sql.ErrNoRows`:https://goframe.org/database/gdb/error
|
||||
- 改进`Update`/`Delete`方法支持`Order BY`及`LIMIT`特性;
|
||||
- 数据库链式操作及方法操作中,预处理变量参数支持`slice`参数:https://goframe.org/database/gdb/chaining/model
|
||||
- **修改`Priority`权重配置名称为`Weight`;**
|
||||
- 新增`Debug`配置,可配置开启/关闭调试特性:https://goframe.org/database/gdb/config
|
||||
- 新增`Offset`方法,该方法为可选链式操作方法,`pgsql`数据库可直接通过`Limit`方法第二个参数自动识别为`Offset`语法;
|
||||
- 改进数据库动态切换特性,支持不同数据库类型的当前操作数据库切换;
|
||||
- 改进简化配置文件结构:https://goframe.org/database/gdb/config
|
||||
1. 改进`gconv`数据转换模块:
|
||||
- 对结构体对象转换时支持更多的标签:`gconv/c/json`;
|
||||
- 支持`*struct/[]struct/[]*struct`自动初始化创建对象/数组:https://goframe.org/util/gconv/struct
|
||||
- 新增`Strusts/StrctsDeep`方法,用于结构体数组的转换即递归转换;
|
||||
- 新增`StructDeep`方法,用于对结构体对象的递归转换;
|
||||
- 新增`MapDeep`方法,用于对结构体属性的递归转换;
|
||||
1. 改进`ghttp`模块:
|
||||
- 改进`ghttp`模块的分组路由功能,完善逻辑处理细节,程序更加稳健;
|
||||
- 改进`ghttp.Request.Get*ToStruct`方法,支持`params/param/p`标签,支持结构体递归转换,并且支持`**struct`参数的对象自动初始化;
|
||||
- 改进`ghttp.CORSDefault`的跨域设置参数,`AllowOrigin`参数调整为`*`;
|
||||
1. 改进`gvalid`数据校验模块:
|
||||
- 增加对校验标签`gvalid/valid/v`的支持;
|
||||
- 改进`CheckStruct`支持对结构体对象的递归校验:https://goframe.org/util/gvalid/checkstruct
|
||||
1. 改进`gtcp`TCP通信模块:
|
||||
- 改进通信包协议设计,更加轻量级高效:https://goframe.org/net/gtcp/conn/pkg
|
||||
- 改进`TCP Server`增加对`TLS`的支持:https://goframe.org/net/gtcp/tls
|
||||
- 增加`Server.Cloce`服务端关闭方法;
|
||||
1. 改进`gproc`模块的通信数据结构,并使用`gtcp`的轻量级包协议重构消息发送逻辑;
|
||||
1. 改进`gqueue`模块增加数据同步缓冲机制,解决大数据量下的内存占用及延迟问题;
|
||||
1. 改进`gmlock`模块,使用`gmutex`模块替换内部的互斥锁,增加更多的操作方法;
|
||||
1. 改进`gaes`加密模块,增加`CBC`模式的加密/解密方法:
|
||||
1. 改进`garray.Range/SubSlice`方法,改进设计,提高性能;
|
||||
1. 改进`gjson`/`gparser`模块实现`MarshalJSON`接口以实现自定义的`JSON`数据格式转换;
|
||||
1. **改进`crypto`分类下模块的方法返回值,增加`error`错误变量返回,以保证更严谨的接口设计风格;**
|
||||
1. 改进`gflock`修改方法名`UnLock`为`Unlock`,新增`IsRLocked`方法;
|
||||
1. 新增`gfile.CopyFile/CopyDir`方法,用于文件及目录的复制;
|
||||
1. 改进`gjson/gparser/gvar/gcfg`模块增加更多的类型转换方法;
|
||||
1. 改进`gcache`模块,过期时间参数支持`time.Duration`类型;
|
||||
1. 新增`internal/structs`包,强大且便捷的结构体解析,并改进框架中所有涉及到结构体反射处理的模块;
|
||||
1. 改进`gbinary`增加封装方法对`BigEndian`的支持;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`garray.Search`返回值问题;
|
||||
1. 修复`garray.Contains`, `garray.New*ArrayFromCopy`方法逻辑问题;
|
||||
1. 修复`gjson.Remove`删除`slice`参数问题;
|
||||
1. 修复`gtree.AVLTree.Remove`方法返回值问题;
|
||||
1. 修复`gqueue.Size`不准确的大小问题;
|
||||
1. 修复`queue.Close`问题;
|
||||
1. 修复`gcache.GetOrSetLockFunc`当回调函数返回`nil`结果时的死锁问题;
|
||||
1. 修复`gfsnotify.Add`方法默认递归监控添加失效问题;
|
||||
1. 修复`gdb.Model.Scan`在某些参数类型下的失效问题;
|
||||
|
||||
## 注意事项
|
||||
|
||||
以上粗体文字部分为不兼容调整,升级时请注意。
|
||||
|
||||
|
||||
|
||||
# `v1.7.0` (2019-06-10)
|
||||
## 新功能/改进
|
||||
1. 重构改进`glog`模块:
|
||||
- 去掉日志模块所有的锁机制,改为无锁设计,执行性能更加高效
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
@ -40,7 +40,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(f.Close(), "file closing error")
|
||||
err = gerror.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
h := md5.New()
|
||||
_, err = io.Copy(h, f)
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
@ -37,7 +37,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(f.Close(), "file closing error")
|
||||
err = gerror.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
h := sha1.New()
|
||||
_, err = io.Copy(h, f)
|
||||
|
||||
@ -87,14 +87,15 @@ type DB interface {
|
||||
GetQueriedSqls() []*Sql
|
||||
GetLastSql() *Sql
|
||||
PrintQueriedSqls()
|
||||
SetMaxIdleConns(n int)
|
||||
SetMaxOpenConns(n int)
|
||||
SetConnMaxLifetime(n int)
|
||||
SetMaxIdleConnCount(n int)
|
||||
SetMaxOpenConnCount(n int)
|
||||
SetMaxConnLifetime(n int)
|
||||
|
||||
// 内部方法接口
|
||||
getCache() *gcache.Cache
|
||||
getChars() (charLeft string, charRight string)
|
||||
getDebug() bool
|
||||
setSchema(sqlDb *sql.DB, schema string) error
|
||||
filterFields(table string, data map[string]interface{}) map[string]interface{}
|
||||
convertValue(fieldValue interface{}, fieldType string) interface{}
|
||||
getTableFields(table string) (map[string]string, error)
|
||||
@ -248,9 +249,9 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
slaveList = masterList
|
||||
}
|
||||
if master {
|
||||
return getConfigNodeByPriority(masterList), nil
|
||||
return getConfigNodeByWeight(masterList), nil
|
||||
} else {
|
||||
return getConfigNodeByPriority(slaveList), nil
|
||||
return getConfigNodeByWeight(slaveList), nil
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
||||
@ -263,19 +264,19 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
// 2、那么节点1的权重范围为[0, 99],节点2的权重范围为[100, 199],比例为1:1;
|
||||
// 3、假如计算出的随机数为99;
|
||||
// 4、那么选择的配置为节点1;
|
||||
func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode {
|
||||
func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
|
||||
if len(cg) < 2 {
|
||||
return &cg[0]
|
||||
}
|
||||
var total int
|
||||
for i := 0; i < len(cg); i++ {
|
||||
total += cg[i].Priority * 100
|
||||
total += cg[i].Weight * 100
|
||||
}
|
||||
// 如果total为0表示所有连接都没有配置priority属性,那么默认都是1
|
||||
if total == 0 {
|
||||
for i := 0; i < len(cg); i++ {
|
||||
cg[i].Priority = 1
|
||||
total += cg[i].Priority * 100
|
||||
cg[i].Weight = 1
|
||||
total += cg[i].Weight * 100
|
||||
}
|
||||
}
|
||||
// 不能取到末尾的边界点
|
||||
@ -286,7 +287,7 @@ func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode {
|
||||
min := 0
|
||||
max := 0
|
||||
for i := 0; i < len(cg); i++ {
|
||||
max = min + cg[i].Priority*100
|
||||
max = min + cg[i].Weight*100
|
||||
//fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max)
|
||||
if r >= min && r < max {
|
||||
return &cg[i]
|
||||
@ -308,12 +309,14 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
|
||||
if node.Charset == "" {
|
||||
node.Charset = "utf8"
|
||||
}
|
||||
// 缓存连接对象(该对象其实是一个连接池对象)
|
||||
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
|
||||
sqlDb, err = bs.db.Open(node)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 接口对象可能会覆盖这些连接参数,所以这里优先判断有误设置连接池属性。
|
||||
// 若无设置则使用配置节点的连接池参数
|
||||
if n := bs.maxIdleConnCount.Val(); n > 0 {
|
||||
sqlDb.SetMaxIdleConns(n)
|
||||
} else if node.MaxIdleConnCount > 0 {
|
||||
@ -336,9 +339,13 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
|
||||
if v != nil && sqlDb == nil {
|
||||
sqlDb = v.(*sql.DB)
|
||||
}
|
||||
// 是否开启调试模式
|
||||
bs.db.SetDebug(node.Debug)
|
||||
// 是否手动选择数据库
|
||||
if v := bs.schema.Val(); v != "" {
|
||||
sqlDb.Exec("USE " + v)
|
||||
if e := bs.db.setSchema(sqlDb, v); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -22,7 +22,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_DEBUG_SQL_LENGTH = 1000 // 默认调试模式下记录的SQL条数
|
||||
// 默认调试模式下记录的SQL条数
|
||||
gDEFAULT_DEBUG_SQL_LENGTH = 1000
|
||||
)
|
||||
|
||||
// 获取最近一条执行的sql
|
||||
@ -78,6 +79,7 @@ func (bs *dbBase) Query(query string, args ...interface{}) (rows *sql.Rows, err
|
||||
|
||||
// 数据库sql查询操作,主要执行查询
|
||||
func (bs *dbBase) doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
query, args = formatQuery(query, args)
|
||||
query = bs.db.handleSqlBeforeExec(query)
|
||||
if bs.db.getDebug() {
|
||||
mTime1 := gtime.Millisecond()
|
||||
@ -114,6 +116,7 @@ func (bs *dbBase) Exec(query string, args ...interface{}) (result sql.Result, er
|
||||
|
||||
// 执行一条sql,并返回执行情况,主要用于非查询操作
|
||||
func (bs *dbBase) doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error) {
|
||||
query, args = formatQuery(query, args)
|
||||
query = bs.db.handleSqlBeforeExec(query)
|
||||
if bs.db.getDebug() {
|
||||
mTime1 := gtime.Millisecond()
|
||||
@ -178,28 +181,28 @@ func (bs *dbBase) GetOne(query string, args ...interface{}) (Record, error) {
|
||||
}
|
||||
|
||||
// 数据库查询,查询单条记录,自动映射数据到给定的struct对象中
|
||||
func (bs *dbBase) GetStruct(objPointer interface{}, query string, args ...interface{}) error {
|
||||
func (bs *dbBase) GetStruct(pointer interface{}, query string, args ...interface{}) error {
|
||||
one, err := bs.GetOne(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return one.ToStruct(objPointer)
|
||||
return one.ToStruct(pointer)
|
||||
}
|
||||
|
||||
// 数据库查询,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (bs *dbBase) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error {
|
||||
func (bs *dbBase) GetStructs(pointer interface{}, query string, args ...interface{}) error {
|
||||
all, err := bs.GetAll(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ToStructs(objPointerSlice)
|
||||
return all.ToStructs(pointer)
|
||||
}
|
||||
|
||||
// 将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (bs *dbBase) GetScan(objPointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
func (bs *dbBase) GetScan(pointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(pointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
@ -207,9 +210,9 @@ func (bs *dbBase) GetScan(objPointer interface{}, query string, args ...interfac
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array, reflect.Slice:
|
||||
return bs.db.GetStructs(objPointer, query, args...)
|
||||
return bs.db.GetStructs(pointer, query, args...)
|
||||
case reflect.Struct:
|
||||
return bs.db.GetStruct(objPointer, query, args...)
|
||||
return bs.db.GetStruct(pointer, query, args...)
|
||||
}
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
@ -567,6 +570,9 @@ func (bs *dbBase) getCache() *gcache.Cache {
|
||||
|
||||
// 将数据查询的列表数据*sql.Rows转换为Result类型
|
||||
func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
if !rows.Next() {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
// 列信息列表, 名称与类型
|
||||
columnTypes, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
@ -585,7 +591,7 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
for i := range values {
|
||||
scanArgs[i] = &values[i]
|
||||
}
|
||||
for rows.Next() {
|
||||
for {
|
||||
if err := rows.Scan(scanArgs...); err != nil {
|
||||
return records, err
|
||||
}
|
||||
@ -603,6 +609,15 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
}
|
||||
}
|
||||
records = append(records, row)
|
||||
if !rows.Next() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// 动态切换数据库
|
||||
func (bs *dbBase) setSchema(sqlDb *sql.DB, schema string) error {
|
||||
_, err := sqlDb.Exec("USE " + schema)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -9,8 +9,9 @@ package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
"sync"
|
||||
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -30,10 +31,11 @@ type ConfigNode struct {
|
||||
User string // 账号
|
||||
Pass string // 密码
|
||||
Name string // 数据库名称
|
||||
Type string // 数据库类型:mysql, sqlite, mssql, pgsql, oracle(目前仅支持mysql)
|
||||
Type string // 数据库类型:mysql, sqlite, mssql, pgsql, oracle
|
||||
Role string // (可选,默认为master)数据库的角色,用于主从操作分离,至少需要有一个master,参数值:master, slave
|
||||
Debug bool // (可选)开启调试模式
|
||||
Weight int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
|
||||
Charset string // (可选,默认为 utf8)编码,默认为 utf8
|
||||
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
|
||||
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
|
||||
MaxIdleConnCount int // (可选)连接池最大限制的连接数
|
||||
MaxOpenConnCount int // (可选)连接池最大打开的连接数
|
||||
@ -47,37 +49,6 @@ var configs struct {
|
||||
defaultGroup string // 默认数据库分组名称
|
||||
}
|
||||
|
||||
// 数据库集群配置示例,支持主从处理,多数据库集群支持
|
||||
/*
|
||||
var DatabaseConfiguration = Config {
|
||||
// 数据库集群配置名称
|
||||
"default" : ConfigGroup {
|
||||
{
|
||||
Host : "192.168.1.100",
|
||||
Port : "3306",
|
||||
User : "root",
|
||||
Pass : "123456",
|
||||
Name : "test",
|
||||
Type : "mysql",
|
||||
Role : "master",
|
||||
Charset : "utf8",
|
||||
Priority : 100,
|
||||
},
|
||||
{
|
||||
Host : "192.168.1.101",
|
||||
Port : "3306",
|
||||
User : "root",
|
||||
Pass : "123456",
|
||||
Name : "test",
|
||||
Type : "mysql",
|
||||
Role : "slave",
|
||||
Charset : "utf8",
|
||||
Priority : 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
*/
|
||||
|
||||
// 包初始化
|
||||
func init() {
|
||||
configs.config = make(Config)
|
||||
@ -136,24 +107,24 @@ func SetDefaultGroup(name string) {
|
||||
// 获取默认链接的数据库链接配置项(默认是 default)
|
||||
func GetDefaultGroup() string {
|
||||
defer instances.Clear()
|
||||
configs.Lock()
|
||||
defer configs.Unlock()
|
||||
configs.RLock()
|
||||
defer configs.RUnlock()
|
||||
return configs.defaultGroup
|
||||
}
|
||||
|
||||
// 设置数据库连接池中空闲链接的大小
|
||||
func (bs *dbBase) SetMaxIdleConns(n int) {
|
||||
func (bs *dbBase) SetMaxIdleConnCount(n int) {
|
||||
bs.maxIdleConnCount.Set(n)
|
||||
}
|
||||
|
||||
// 设置数据库连接池最大打开的链接数量
|
||||
func (bs *dbBase) SetMaxOpenConns(n int) {
|
||||
func (bs *dbBase) SetMaxOpenConnCount(n int) {
|
||||
bs.maxOpenConnCount.Set(n)
|
||||
}
|
||||
|
||||
// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃
|
||||
// 如果 d <= 0 表示该链接会一直重复利用
|
||||
func (bs *dbBase) SetConnMaxLifetime(n int) {
|
||||
func (bs *dbBase) SetMaxConnLifetime(n int) {
|
||||
bs.maxConnLifetime.Set(n)
|
||||
}
|
||||
|
||||
@ -162,8 +133,8 @@ func (node *ConfigNode) String() string {
|
||||
if node.LinkInfo != "" {
|
||||
return node.LinkInfo
|
||||
}
|
||||
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
|
||||
node.Name, node.Type, node.Role, node.Charset,
|
||||
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d`, node.User, node.Host, node.Port,
|
||||
node.Name, node.Type, node.Role, node.Charset, node.Debug,
|
||||
node.MaxIdleConnCount, node.MaxOpenConnCount, node.MaxConnLifetime,
|
||||
)
|
||||
}
|
||||
|
||||
@ -27,7 +27,49 @@ type apiString interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// 格式化Where查询条件
|
||||
// 格式化SQL语句。
|
||||
// 1. 支持参数只传一个slice;
|
||||
// 2. 支持占位符号数量自动扩展;
|
||||
func formatQuery(query string, args []interface{}) (newQuery string, newArgs []interface{}) {
|
||||
newQuery = query
|
||||
// 查询条件参数处理,主要处理slice参数类型
|
||||
if len(args) > 0 {
|
||||
for index, arg := range args {
|
||||
rv := reflect.ValueOf(arg)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// '?'占位符支持slice类型, 这里会将slice参数拆散,并更新原有占位符'?'为多个'?',使用','符号连接。
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
newArgs = append(newArgs, rv.Index(i).Interface())
|
||||
}
|
||||
// 如果参数直接传递slice,并且占位符数量与slice长度相等,
|
||||
// 那么不用替换扩展占位符数量,直接使用该slice作为查询参数
|
||||
if len(args) == 1 && gstr.Count(newQuery, "?") == rv.Len() {
|
||||
break
|
||||
}
|
||||
// counter用于匹配该参数的位置(与index对应)
|
||||
counter := 0
|
||||
newQuery, _ = gregex.ReplaceStringFunc(`\?`, newQuery, func(s string) string {
|
||||
counter++
|
||||
if counter == index+1 {
|
||||
return "?" + strings.Repeat(",?", rv.Len()-1)
|
||||
}
|
||||
return s
|
||||
})
|
||||
default:
|
||||
newArgs = append(newArgs, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 格式化Where查询条件。
|
||||
func formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
|
||||
// 条件字符串处理
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
@ -38,7 +80,6 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
tmpArgs := []interface{}(nil)
|
||||
switch kind {
|
||||
// map/struct类型
|
||||
case reflect.Map:
|
||||
@ -57,19 +98,20 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
|
||||
count := gstr.Count(key, "?")
|
||||
if count == 0 {
|
||||
buffer.WriteString(key + " IN(?)")
|
||||
tmpArgs = append(tmpArgs, value)
|
||||
newArgs = append(newArgs, value)
|
||||
} else if count != rv.Len() {
|
||||
buffer.WriteString(key)
|
||||
tmpArgs = append(tmpArgs, value)
|
||||
newArgs = append(newArgs, value)
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
|
||||
tmpArgs = append(tmpArgs, gconv.Interfaces(value)...)
|
||||
newArgs = append(newArgs, gconv.Interfaces(value)...)
|
||||
}
|
||||
default:
|
||||
if value == nil {
|
||||
buffer.WriteString(key)
|
||||
} else {
|
||||
// 支持key带操作符号
|
||||
if gstr.Pos(key, "?") == -1 {
|
||||
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
|
||||
buffer.WriteString(key + "=?")
|
||||
@ -79,7 +121,7 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
}
|
||||
tmpArgs = append(tmpArgs, value)
|
||||
newArgs = append(newArgs, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,45 +133,16 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
|
||||
if buffer.Len() == 0 {
|
||||
return "", args
|
||||
}
|
||||
newArgs = append(newArgs, args...)
|
||||
newWhere = buffer.String()
|
||||
tmpArgs = append(tmpArgs, args...)
|
||||
// 查询条件参数处理,主要处理slice参数类型
|
||||
if len(tmpArgs) > 0 {
|
||||
for index, arg := range tmpArgs {
|
||||
rv := reflect.ValueOf(arg)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// '?'占位符支持slice类型,
|
||||
// 这里会将slice参数拆散,并更新原有占位符'?'为多个'?',使用','符号连接。
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
newArgs = append(newArgs, rv.Index(i).Interface())
|
||||
}
|
||||
// counter用于匹配该参数的位置(与index对应)
|
||||
counter := 0
|
||||
newWhere, _ = gregex.ReplaceStringFunc(`\?`, newWhere, func(s string) string {
|
||||
counter++
|
||||
if counter == index+1 {
|
||||
return "?" + strings.Repeat(",?", rv.Len()-1)
|
||||
}
|
||||
return s
|
||||
})
|
||||
default:
|
||||
// 支持例如 Where/And/Or("uid", 1) 这种格式
|
||||
if gstr.Pos(newWhere, "?") == -1 {
|
||||
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
|
||||
newWhere += "=?"
|
||||
} else {
|
||||
newWhere += "?"
|
||||
}
|
||||
}
|
||||
newArgs = append(newArgs, arg)
|
||||
if len(newArgs) > 0 {
|
||||
// 支持例如 Where/And/Or("uid", 1) 这种格式
|
||||
if gstr.Pos(newWhere, "?") == -1 {
|
||||
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
|
||||
newWhere += "=?"
|
||||
} else {
|
||||
newWhere += "?"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -175,7 +188,7 @@ func printSql(v *Sql) {
|
||||
)
|
||||
if v.Error != nil {
|
||||
s += "\nError: " + v.Error.Error()
|
||||
glog.Backtrace(true, 2).Error(s)
|
||||
glog.Stack(true, 2).Error(s)
|
||||
} else {
|
||||
glog.Debug(s)
|
||||
}
|
||||
|
||||
@ -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, ymrjqyy
|
||||
|
||||
package gdb
|
||||
|
||||
@ -30,6 +28,7 @@ type Model struct {
|
||||
orderBy string // 排序语句
|
||||
start int // 分页开始
|
||||
limit int // 分页条数
|
||||
offset int // 查询偏移量(OFFSET语法)
|
||||
data interface{} // 操作数据(注意仅支持Map/List/string类型)
|
||||
batch int // 批量操作条数
|
||||
filter bool // 是否按照表字段过滤data参数
|
||||
@ -47,6 +46,7 @@ func (bs *dbBase) Table(tables string) *Model {
|
||||
tables: tables,
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
safe: false,
|
||||
}
|
||||
}
|
||||
@ -214,6 +214,14 @@ func (md *Model) Limit(limit ...int) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,OFFSET语法(部分数据库支持)。
|
||||
// 注意:可以使用Limit方法调用替换该方法特性,底层不同数据库将会自动替换LIMIT语法为OFFSET语法。
|
||||
func (md *Model) Offset(offset int) *Model {
|
||||
model := md.getModel()
|
||||
model.offset = offset
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,翻页,注意分页页码从1开始,而Limit方法从0开始。
|
||||
func (md *Model) ForPage(page, limit int) *Model {
|
||||
model := md.getModel()
|
||||
@ -605,11 +613,13 @@ func (md *Model) getConditionSql() string {
|
||||
}
|
||||
if md.limit != 0 {
|
||||
if md.start >= 0 {
|
||||
s += fmt.Sprintf(" LIMIT %d, %d", md.start, md.limit)
|
||||
s += fmt.Sprintf(" LIMIT %d,%d", md.start, md.limit)
|
||||
} else {
|
||||
s += fmt.Sprintf(" LIMIT %d", md.limit)
|
||||
}
|
||||
|
||||
}
|
||||
if md.offset >= 0 {
|
||||
s += fmt.Sprintf(" OFFSET %d", md.offset)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@ -3,23 +3,21 @@
|
||||
// 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<liyz23@qq.com>
|
||||
@date 20181109
|
||||
说明:
|
||||
1.需要导入sqlserver驱动: github.com/denisenkom/go-mssqldb
|
||||
2.不支持save/replace方法
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
// 说明:
|
||||
// 1.需要导入sqlserver驱动: github.com/denisenkom/go-mssqldb
|
||||
// 2.不支持save/replace方法
|
||||
// 3.不支持LastInsertId方法
|
||||
//
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
)
|
||||
|
||||
// 数据库链接对象
|
||||
|
||||
@ -3,23 +3,20 @@
|
||||
// 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<liyz23@qq.com>
|
||||
@date 20181026
|
||||
说明:
|
||||
1.需要导入oracle驱动: github.com/mattn/go-oci8
|
||||
2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究)
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
// 说明:
|
||||
// 1.需要导入oracle驱动: github.com/mattn/go-oci8
|
||||
// 2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究)
|
||||
// 3.不支持LastInsertId方法
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
)
|
||||
|
||||
// 数据库链接对象
|
||||
|
||||
@ -9,12 +9,16 @@ package gdb
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
)
|
||||
|
||||
// PostgreSQL的适配.
|
||||
//
|
||||
// 使用时需要import:
|
||||
//
|
||||
// _ "github.com/gogf/gf/third/github.com/lib/pq"
|
||||
//
|
||||
// @todo 需要完善replace和save的操作覆盖
|
||||
|
||||
// 数据库链接对象
|
||||
@ -37,6 +41,12 @@ func (db *dbPgsql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 动态切换数据库
|
||||
func (db *dbPgsql) setSchema(sqlDb *sql.DB, schema string) error {
|
||||
_, err := sqlDb.Exec("SET search_path TO " + schema)
|
||||
return err
|
||||
}
|
||||
|
||||
// 获得关键字操作符
|
||||
func (db *dbPgsql) getChars() (charLeft string, charRight string) {
|
||||
return "\"", "\""
|
||||
@ -44,11 +54,12 @@ func (db *dbPgsql) getChars() (charLeft string, charRight string) {
|
||||
|
||||
// 在执行sql之前对sql进行进一步处理
|
||||
func (db *dbPgsql) handleSqlBeforeExec(query string) string {
|
||||
reg := regexp.MustCompile("\\?")
|
||||
index := 0
|
||||
str := reg.ReplaceAllStringFunc(query, func(s string) string {
|
||||
query, _ = gregex.ReplaceStringFunc("\\?", query, func(s string) string {
|
||||
index++
|
||||
return fmt.Sprintf("$%d", index)
|
||||
})
|
||||
return str
|
||||
// 分页语法替换
|
||||
query, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $1 OFFSET $2`, query)
|
||||
return query
|
||||
}
|
||||
|
||||
@ -3,7 +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 wxkj<wxscz@qq.com>
|
||||
|
||||
package gdb
|
||||
|
||||
@ -41,7 +40,7 @@ func (db *dbSqlite) getChars() (charLeft string, charRight string) {
|
||||
return "`", "`"
|
||||
}
|
||||
|
||||
// 在执行sql之前对sql进行进一步处理
|
||||
// 在执行sql之前对sql进行进一步处理。
|
||||
// @todo 需要增加对Save方法的支持,可使用正则来实现替换,
|
||||
// @todo 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE)
|
||||
func (db *dbSqlite) handleSqlBeforeExec(query string) string {
|
||||
|
||||
@ -32,15 +32,15 @@ var (
|
||||
// 测试前需要修改连接参数。
|
||||
func init() {
|
||||
node := gdb.ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: "root",
|
||||
Pass: "",
|
||||
Name: "",
|
||||
Type: "mysql",
|
||||
Role: "master",
|
||||
Charset: "utf8",
|
||||
Priority: 1,
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: "root",
|
||||
Pass: "",
|
||||
Name: "",
|
||||
Type: "mysql",
|
||||
Role: "master",
|
||||
Charset: "utf8",
|
||||
Weight: 1,
|
||||
}
|
||||
hostname, _ := os.Hostname()
|
||||
// 本地测试hack
|
||||
|
||||
@ -7,11 +7,12 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDbBase_Ping(t *testing.T) {
|
||||
@ -24,12 +25,17 @@ func TestDbBase_Ping(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDbBase_Query(t *testing.T) {
|
||||
if _, err := db.Query("SELECT ?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := db.Query("ERROR"); err == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
_, err := db.Query("SELECT ?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = db.Query("SELECT ?+?", 1, 2)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = db.Query("SELECT ?+?", g.Slice{1, 2})
|
||||
gtest.Assert(err, nil)
|
||||
_, err = db.Query("ERROR")
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestDbBase_Exec(t *testing.T) {
|
||||
@ -290,11 +296,50 @@ func TestDbBase_Update(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDbBase_GetAll(t *testing.T) {
|
||||
if result, err := db.GetAll("SELECT * FROM user WHERE id=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll("SELECT * FROM user WHERE id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 1)
|
||||
}
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll("SELECT * FROM user WHERE id=?", g.Slice{1})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll("SELECT * FROM user WHERE id in(?)", g.Slice{1, 2, 3})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll("SELECT * FROM user WHERE id in(?,?,?)", g.Slice{1, 2, 3})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll("SELECT * FROM user WHERE id in(?,?,?)", g.Slice{1, 2, 3}...)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll("SELECT * FROM user WHERE id>=? AND id <=?", g.Slice{1, 3})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_GetOne(t *testing.T) {
|
||||
|
||||
@ -280,33 +280,45 @@ func TestModel_Safe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModel_All(t *testing.T) {
|
||||
result, err := db.Table("user").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id<0").All()
|
||||
gtest.Assert(result, nil)
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_One(t *testing.T) {
|
||||
record, err := db.Table("user").Where("id", 1).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if record == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
gtest.Assert(record["nickname"].String(), "T111")
|
||||
gtest.Case(t, func() {
|
||||
record, err := db.Table("user").Where("id", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(record["nickname"].String(), "T111")
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
record, err := db.Table("user").Where("id", 0).One()
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
gtest.Assert(record, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Value(t *testing.T) {
|
||||
value, err := db.Table("user").Fields("nickname").Where("id", 1).Value()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if value == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
gtest.Assert(value.String(), "T111")
|
||||
gtest.Case(t, func() {
|
||||
value, err := db.Table("user").Fields("nickname").Where("id", 1).Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "T111")
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
value, err := db.Table("user").Fields("nickname").Where("id", 0).Value()
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
gtest.Assert(value, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Count(t *testing.T) {
|
||||
@ -613,6 +625,23 @@ func TestModel_Where(t *testing.T) {
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// slice parameter
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id=? and nickname=?", g.Slice{3, "T3"}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id=?", g.Slice{3}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id", 3).One()
|
||||
if err != nil {
|
||||
@ -661,7 +690,14 @@ func TestModel_Where(t *testing.T) {
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("passport like ? and nickname like ?", g.Slice{"t3", "T3"}...).One()
|
||||
result, err := db.Table("user").Where("id=? AND nickname=?", g.Slice{3, "T3"}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("passport like ? and nickname like ?", g.Slice{"t3", "T3"}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
@ -7,10 +7,11 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTX_Query(t *testing.T) {
|
||||
@ -23,6 +24,16 @@ func TestTX_Query(t *testing.T) {
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
if rows, err := tx.Query("SELECT ?+?", 1, 2); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
if rows, err := tx.Query("SELECT ?+?", g.Slice{1, 2}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
if _, err := tx.Query("ERROR"); err == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
@ -39,6 +50,12 @@ func TestTX_Exec(t *testing.T) {
|
||||
if _, err := tx.Exec("SELECT ?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Exec("SELECT ?+?", 1, 2); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Exec("SELECT ?+?", g.Slice{1, 2}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Exec("ERROR"); err == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
|
||||
@ -7,11 +7,12 @@
|
||||
package gredis_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/database/gredis"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
redis2 "github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
81
g/errors/gerror/gerror.go
Normal file
81
g/errors/gerror/gerror.go
Normal file
@ -0,0 +1,81 @@
|
||||
// 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 errors provides simple functions to manipulate errors.
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// New returns an error that formats as the given value.
|
||||
func New(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(gconv.String(value))
|
||||
}
|
||||
|
||||
// NewText returns an error that formats as the given text.
|
||||
func NewText(text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps error with text.
|
||||
// It returns nil if given err is nil.
|
||||
func Wrap(err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// It returns nil if given err is nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the root cause error.
|
||||
func Cause(err error) error {
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.Cause()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string id the <err> does not support stacks.
|
||||
func Stack(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
if e, ok := err.(*Error); !ok {
|
||||
return e.Stack()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
139
g/errors/gerror/gerror_error.go
Normal file
139
g/errors/gerror/gerror_error.go
Normal file
@ -0,0 +1,139 @@
|
||||
// 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 gerror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error is custom error for additional features.
|
||||
type Error struct {
|
||||
error error // Wrapped error.
|
||||
stack stack // Stack array, which records the stack information when this error is created or wrapped.
|
||||
text string // Error text, which is created by New* functions.
|
||||
}
|
||||
|
||||
const (
|
||||
gFILTER_KEY = "/g/errors/gerror/gerror"
|
||||
)
|
||||
|
||||
var (
|
||||
// goRootForFilter is used for stack filtering purpose.
|
||||
goRootForFilter = runtime.GOROOT()
|
||||
)
|
||||
|
||||
func init() {
|
||||
if goRootForFilter != "" {
|
||||
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements the interface of Error, it returns the error as string.
|
||||
func (err *Error) Error() string {
|
||||
if err.text != "" {
|
||||
if err.error != nil {
|
||||
return err.text + ": " + err.error.Error()
|
||||
}
|
||||
return err.text
|
||||
}
|
||||
return err.error.Error()
|
||||
}
|
||||
|
||||
// Cause returns the root cause error.
|
||||
func (err *Error) Cause() error {
|
||||
loop := err
|
||||
for loop != nil {
|
||||
if loop.error != nil {
|
||||
if e, ok := loop.error.(*Error); ok {
|
||||
loop = e
|
||||
} else {
|
||||
return loop.error
|
||||
}
|
||||
} else {
|
||||
return loop
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %v, %s : Print the error string;
|
||||
// %-v, %-s : Print current error string;
|
||||
// %+s : Print full stack error list;
|
||||
// %+v : Print the error string and full stack error list;
|
||||
func (err *Error) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's', 'v':
|
||||
switch {
|
||||
case s.Flag('-'):
|
||||
if err.text != "" {
|
||||
io.WriteString(s, err.text)
|
||||
} else {
|
||||
io.WriteString(s, err.Error())
|
||||
}
|
||||
case s.Flag('+'):
|
||||
if verb == 's' {
|
||||
io.WriteString(s, err.Stack())
|
||||
} else {
|
||||
io.WriteString(s, err.Error()+"\n"+err.Stack())
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string id the <err> does not support stacks.
|
||||
func (err *Error) Stack() string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
loop := err
|
||||
index := 1
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for loop != nil {
|
||||
buffer.WriteString(fmt.Sprintf("%d.\t%-v\n", index, loop))
|
||||
index++
|
||||
formatSubStack(loop.stack, buffer)
|
||||
if loop.error != nil {
|
||||
if e, ok := loop.error.(*Error); ok {
|
||||
loop = e
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%d.\t%s\n", index, loop.error.Error()))
|
||||
index++
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// formatSubStack formats the stack for error.
|
||||
func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
index := 1
|
||||
for _, p := range st {
|
||||
if fn := runtime.FuncForPC(p - 1); fn != nil {
|
||||
file, line := fn.FileLine(p - 1)
|
||||
if strings.Contains(file, gFILTER_KEY) {
|
||||
continue
|
||||
}
|
||||
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("\t%d).\t%s\n\t\t%s:%d\n", index, fn.Name(), file, line))
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
22
g/errors/gerror/gerror_stack.go
Normal file
22
g/errors/gerror/gerror_stack.go
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 gerror
|
||||
|
||||
import "runtime"
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
const (
|
||||
gMAX_STACK_DEPTH = 32
|
||||
)
|
||||
|
||||
func callers() stack {
|
||||
var pcs [gMAX_STACK_DEPTH]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
return pcs[0:n]
|
||||
}
|
||||
133
g/errors/gerror/gerror_test.go
Normal file
133
g/errors/gerror/gerror_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
// 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 gerror_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func interfaceNil() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nilError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_Nil(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gerror.New(interfaceNil()), nil)
|
||||
gtest.Assert(gerror.Wrap(nilError(), "test"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Wrap(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Cause(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
gtest.Assert(gerror.Cause(err), err)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.Assert(gerror.Cause(err), "1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
gtest.Assert(gerror.Cause(err), "1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.Assert(gerror.Cause(err), "1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Format(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(fmt.Sprintf("%s", err), "3: 2: 1")
|
||||
gtest.Assert(fmt.Sprintf("%v", err), "3: 2: 1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(fmt.Sprintf("%s", err), "3: 2: 1")
|
||||
gtest.Assert(fmt.Sprintf("%v", err), "3: 2: 1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(fmt.Sprintf("%-s", err), "3")
|
||||
gtest.Assert(fmt.Sprintf("%-v", err), "3")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Stack(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
gtest.Assert(fmt.Sprintf("%+v", err), "1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
//fmt.Printf("%+v", err)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
gtest.AssertNE(fmt.Sprintf("%+v", err), "1")
|
||||
//fmt.Printf("%+v", err)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
//fmt.Printf("%+v", err)
|
||||
})
|
||||
}
|
||||
@ -162,8 +162,8 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
|
||||
if value, ok := nodeMap["charset"]; ok {
|
||||
node.Charset = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["priority"]; ok {
|
||||
node.Priority = gconv.Int(value)
|
||||
if value, ok := nodeMap["weight"]; ok {
|
||||
node.Weight = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["linkinfo"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
|
||||
@ -8,11 +8,12 @@ package gins_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/frame/gins"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Database(t *testing.T) {
|
||||
@ -31,8 +32,8 @@ test = "v=2"
|
||||
name = "test"
|
||||
type = "mysql"
|
||||
role = "master"
|
||||
weight = "1"
|
||||
charset = "utf8"
|
||||
priority = "1"
|
||||
[[database.test]]
|
||||
host = "127.0.0.1"
|
||||
port = "3306"
|
||||
@ -42,8 +43,8 @@ test = "v=2"
|
||||
name = "test"
|
||||
type = "mysql"
|
||||
role = "master"
|
||||
weight = "1"
|
||||
charset = "utf8"
|
||||
priority = "1"
|
||||
# Redis数据库配置
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
|
||||
117
g/internal/debug/stack.go
Normal file
117
g/internal/debug/stack.go
Normal file
@ -0,0 +1,117 @@
|
||||
// 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 debug contains facilities for programs to debug themselves while
|
||||
// they are running.
|
||||
package debug
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
gMAX_DEPTH = 1000
|
||||
gFILTER_KEY = "/g/internal/debug/stack.go"
|
||||
)
|
||||
|
||||
var (
|
||||
// goRootForFilter is used for stack filtering purpose.
|
||||
goRootForFilter = runtime.GOROOT()
|
||||
)
|
||||
|
||||
func init() {
|
||||
if goRootForFilter != "" {
|
||||
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintStack prints to standard error the stack trace returned by runtime.Stack.
|
||||
func PrintStack(skip ...int) {
|
||||
fmt.Print(Stack(skip...))
|
||||
}
|
||||
|
||||
// Stack returns a formatted stack trace of the goroutine that calls it.
|
||||
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
|
||||
func Stack(skip ...int) string {
|
||||
return StackWithFilter("", skip...)
|
||||
}
|
||||
|
||||
// StackWithFilter returns a formatted stack trace of the goroutine that calls it.
|
||||
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
|
||||
//
|
||||
// The parameter <filter> is used to filter the path of the caller.
|
||||
func StackWithFilter(filter string, skip ...int) string {
|
||||
number := 0
|
||||
if len(skip) > 0 {
|
||||
number = skip[0]
|
||||
}
|
||||
name := ""
|
||||
index := 1
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for i := callerFromIndex() + number; i < gMAX_DEPTH; i++ {
|
||||
if pc, file, line, ok := runtime.Caller(i); ok {
|
||||
if filter != "" && strings.Contains(file, filter) {
|
||||
continue
|
||||
}
|
||||
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
if fn := runtime.FuncForPC(pc); fn == nil {
|
||||
name = "unknown"
|
||||
} else {
|
||||
name = fn.Name()
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%d.\t%s\n\t%s:%d\n", index, name, file, line))
|
||||
index++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CallerPath returns the absolute file path along with its line number of the caller.
|
||||
func Caller(skip ...int) string {
|
||||
return CallerWithFilter("", skip...)
|
||||
}
|
||||
|
||||
// CallerPathWithFilter returns the absolute file path along with its line number of the caller.
|
||||
//
|
||||
// The parameter <filter> is used to filter the path of the caller.
|
||||
func CallerWithFilter(filter string, skip ...int) string {
|
||||
number := 0
|
||||
if len(skip) > 0 {
|
||||
number = skip[0]
|
||||
}
|
||||
for i := callerFromIndex() + number; i < gMAX_DEPTH; i++ {
|
||||
if _, file, line, ok := runtime.Caller(i); ok {
|
||||
if filter != "" && strings.Contains(file, filter) {
|
||||
continue
|
||||
}
|
||||
return fmt.Sprintf(`%s:%d`, file, line)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// callerFromIndex returns the caller position exclusive of the debug package.
|
||||
func callerFromIndex() int {
|
||||
for i := 0; i < gMAX_DEPTH; i++ {
|
||||
if _, file, _, ok := runtime.Caller(i); ok {
|
||||
if strings.Contains(file, gFILTER_KEY) {
|
||||
continue
|
||||
}
|
||||
// exclude the depth from the function of current package.
|
||||
return i - 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@ -18,7 +18,6 @@ func IsEmpty(value interface{}) bool {
|
||||
if value == nil {
|
||||
return true
|
||||
}
|
||||
// 优先通过断言来进行常用类型判断
|
||||
switch value := value.(type) {
|
||||
case int:
|
||||
return value == 0
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
// 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 errors provides simple functions to manipulate errors.
|
||||
//
|
||||
// This package can be scalable due to https://go.googlesource.com/proposal/+/master/design/go2draft.md.
|
||||
package errors
|
||||
|
||||
import "github.com/gogf/gf/g/util/gconv"
|
||||
|
||||
// errorWrapper is a simple wrapper for errors.
|
||||
type errorWrapper struct {
|
||||
s string
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given value.
|
||||
func New(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(gconv.String(value))
|
||||
}
|
||||
|
||||
// NewText returns an error that formats as the given text.
|
||||
func NewText(text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &errorWrapper{
|
||||
s: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps error with text.
|
||||
func Wrap(err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(text + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Error implements interface Error.
|
||||
func (e *errorWrapper) Error() string {
|
||||
return e.s
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
// 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 errors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func interfaceNil() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nilError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_Nil(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(errors.New(interfaceNil()), nil)
|
||||
gtest.Assert(errors.Wrap(nilError(), "test"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Wrap(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = errors.Wrap(err, "func2 error")
|
||||
err = errors.Wrap(err, "func3 error")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(err.Error(), "func3 error: func2 error: 1")
|
||||
})
|
||||
}
|
||||
@ -9,6 +9,7 @@ package ghttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
)
|
||||
|
||||
@ -32,7 +33,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).Stdout(s.config.LogStdout).Println(content)
|
||||
s.logger.Cat("access").Stack(false, 2).Stdout(s.config.LogStdout).Println(content)
|
||||
}
|
||||
|
||||
// 处理服务错误信息,主要是panic,http请求的status由access log进行管理
|
||||
@ -60,5 +61,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).Stdout(s.config.LogStdout).Error(content)
|
||||
s.logger.Cat("error").Stack(true, 2).Stdout(s.config.LogStdout).Error(content)
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
)
|
||||
|
||||
// 封装的链接对象
|
||||
@ -209,7 +209,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
@ -221,7 +221,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
|
||||
@ -11,20 +11,23 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
)
|
||||
|
||||
const (
|
||||
// 默认允许最大的简单协议包大小(byte), 65535 byte
|
||||
gPKG_MAX_DATA_SIZE = 65535
|
||||
// 简单协议包头大小
|
||||
gPKG_HEADER_SIZE = 3
|
||||
gPKG_DEFAULT_MAX_DATA_SIZE = 65535
|
||||
// 默认简单协议包头大小
|
||||
gPKG_DEFAULT_HEADER_SIZE = 2
|
||||
// 协议头最大大小
|
||||
gPKG_MAX_HEADER_SIZE = 4
|
||||
)
|
||||
|
||||
// 数据读取选项
|
||||
type PkgOption struct {
|
||||
MaxSize int // (byte)数据读取的最大包大小,最大不能超过3字节(0xFFFFFF,15MB),默认为65535byte
|
||||
Retry Retry // 失败重试
|
||||
HeaderSize int // 自定义头大小(默认为2字节,最大不能超过4字节)
|
||||
MaxDataSize int // (byte)数据读取的最大包大小,默认最大不能超过2字节(65535 byte)
|
||||
Retry Retry // 失败重试
|
||||
}
|
||||
|
||||
// getPkgOption wraps and returns the PkgOption.
|
||||
@ -34,10 +37,13 @@ func getPkgOption(option ...PkgOption) (*PkgOption, error) {
|
||||
if len(option) > 0 {
|
||||
pkgOption = option[0]
|
||||
}
|
||||
if pkgOption.MaxSize == 0 {
|
||||
pkgOption.MaxSize = gPKG_MAX_DATA_SIZE
|
||||
} else if pkgOption.MaxSize > 0xFFFFFF {
|
||||
return nil, fmt.Errorf(`package size %d exceeds allowed max size %d`, pkgOption.MaxSize, 0xFFFFFF)
|
||||
if pkgOption.HeaderSize == 0 {
|
||||
pkgOption.HeaderSize = gPKG_DEFAULT_HEADER_SIZE
|
||||
}
|
||||
if pkgOption.MaxDataSize == 0 {
|
||||
pkgOption.MaxDataSize = gPKG_DEFAULT_MAX_DATA_SIZE
|
||||
} else if pkgOption.MaxDataSize > 0xFFFFFF {
|
||||
return nil, fmt.Errorf(`package size %d exceeds allowed max size %d`, pkgOption.MaxDataSize, 0xFFFFFF)
|
||||
}
|
||||
return &pkgOption, nil
|
||||
}
|
||||
@ -55,17 +61,18 @@ func (c *Conn) SendPkg(data []byte, option ...PkgOption) error {
|
||||
return err
|
||||
}
|
||||
length := len(data)
|
||||
if length > pkgOption.MaxSize {
|
||||
return fmt.Errorf(`data size %d exceeds max pkg size %d`, length, gPKG_MAX_DATA_SIZE)
|
||||
if length > pkgOption.MaxDataSize {
|
||||
return fmt.Errorf(`data size %d exceeds max pkg size %d`, length, pkgOption.MaxDataSize)
|
||||
}
|
||||
buffer := make([]byte, gPKG_HEADER_SIZE+1+len(data))
|
||||
offset := gPKG_MAX_HEADER_SIZE - pkgOption.HeaderSize
|
||||
buffer := make([]byte, gPKG_MAX_HEADER_SIZE+len(data))
|
||||
binary.BigEndian.PutUint32(buffer[0:], uint32(length))
|
||||
copy(buffer[gPKG_HEADER_SIZE+1:], data)
|
||||
copy(buffer[gPKG_MAX_HEADER_SIZE:], data)
|
||||
if pkgOption.Retry.Count > 0 {
|
||||
return c.Send(buffer[1:], pkgOption.Retry)
|
||||
return c.Send(buffer[offset:], pkgOption.Retry)
|
||||
}
|
||||
//fmt.Println("SendPkg:", buffer[1:])
|
||||
return c.Send(buffer[1:])
|
||||
//fmt.Println("SendPkg:", buffer[offset:])
|
||||
return c.Send(buffer[offset:])
|
||||
}
|
||||
|
||||
// 简单协议: 带超时时间的数据发送
|
||||
@ -74,7 +81,7 @@ func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.SendPkg(data, option...)
|
||||
return
|
||||
@ -109,26 +116,38 @@ func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error) {
|
||||
for {
|
||||
// 先根据对象的缓冲区数据进行计算
|
||||
for {
|
||||
if len(c.buffer) >= gPKG_HEADER_SIZE {
|
||||
// 注意"数据长度"为3个字节,不满足4个字节的uint32类型,因此这里"低位"补0
|
||||
length = int(binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}))
|
||||
if len(c.buffer) >= pkgOption.HeaderSize {
|
||||
// 不满足4个字节的uint32类型,因此这里"低位"补0
|
||||
if length <= 0 {
|
||||
switch pkgOption.HeaderSize {
|
||||
case 1:
|
||||
length = int(binary.BigEndian.Uint32([]byte{0, 0, 0, c.buffer[0]}))
|
||||
case 2:
|
||||
length = int(binary.BigEndian.Uint32([]byte{0, 0, c.buffer[0], c.buffer[1]}))
|
||||
case 3:
|
||||
length = int(binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}))
|
||||
default:
|
||||
length = int(binary.BigEndian.Uint32([]byte{c.buffer[0], c.buffer[1], c.buffer[2], c.buffer[3]}))
|
||||
}
|
||||
}
|
||||
// 解析的大小是否符合规范,清空从该连接接收到的所有数据包
|
||||
if length < 0 || length > pkgOption.MaxSize {
|
||||
if length < 0 || length > pkgOption.MaxDataSize {
|
||||
c.buffer = c.buffer[:0]
|
||||
return nil, fmt.Errorf(`invalid package size %d`, length)
|
||||
}
|
||||
// 不满足包大小,需要继续读取
|
||||
if len(c.buffer) < length+gPKG_HEADER_SIZE {
|
||||
if len(c.buffer) < length+pkgOption.HeaderSize {
|
||||
break
|
||||
}
|
||||
result = c.buffer[gPKG_HEADER_SIZE : gPKG_HEADER_SIZE+length]
|
||||
c.buffer = c.buffer[gPKG_HEADER_SIZE+length:]
|
||||
result = c.buffer[pkgOption.HeaderSize : pkgOption.HeaderSize+length]
|
||||
c.buffer = c.buffer[pkgOption.HeaderSize+length:]
|
||||
length = 0
|
||||
return
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
// 读取系统socket缓冲区的完整数据
|
||||
// 读取系统socket当前缓冲区的数据
|
||||
temp, err = c.Recv(0, pkgOption.Retry)
|
||||
if err != nil {
|
||||
break
|
||||
@ -147,7 +166,7 @@ func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (d
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.RecvPkg(option...)
|
||||
return
|
||||
|
||||
@ -9,10 +9,9 @@ package gtcp
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/container/gpool"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
)
|
||||
|
||||
// 链接池链接对象
|
||||
@ -122,7 +121,7 @@ func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...R
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
@ -134,7 +133,7 @@ func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
|
||||
@ -9,7 +9,7 @@ package gtcp
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
)
|
||||
|
||||
// 简单协议: (方法覆盖)发送数据
|
||||
@ -47,7 +47,7 @@ func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.RecvPkg(option...)
|
||||
return
|
||||
@ -59,7 +59,7 @@ func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.SendPkg(data, option...)
|
||||
return
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
)
|
||||
|
||||
// 封装的UDP链接对象
|
||||
@ -182,7 +182,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
@ -194,7 +194,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
|
||||
@ -12,21 +12,21 @@ var cache = New()
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func Set(key interface{}, value interface{}, expire int) {
|
||||
cache.Set(key, value, expire)
|
||||
func Set(key interface{}, value interface{}, duration interface{}) {
|
||||
cache.Set(key, value, duration)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
|
||||
// which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func SetIfNotExist(key interface{}, value interface{}, expire int) bool {
|
||||
return cache.SetIfNotExist(key, value, expire)
|
||||
func SetIfNotExist(key interface{}, value interface{}, duration interface{}) bool {
|
||||
return cache.SetIfNotExist(key, value, duration)
|
||||
}
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func Sets(data map[interface{}]interface{}, expire int) {
|
||||
cache.Sets(data, expire)
|
||||
func Sets(data map[interface{}]interface{}, duration interface{}) {
|
||||
cache.Sets(data, duration)
|
||||
}
|
||||
|
||||
// Get returns the value of <key>.
|
||||
@ -39,8 +39,8 @@ func Get(key interface{}) interface{} {
|
||||
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
|
||||
return cache.GetOrSet(key, value, expire)
|
||||
func GetOrSet(key interface{}, value interface{}, duration interface{}) interface{} {
|
||||
return cache.GetOrSet(key, value, duration)
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value of <key>,
|
||||
@ -48,8 +48,8 @@ func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
|
||||
// if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
|
||||
return cache.GetOrSetFunc(key, f, expire)
|
||||
func GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) interface{} {
|
||||
return cache.GetOrSetFunc(key, f, duration)
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value of <key>,
|
||||
@ -59,8 +59,8 @@ func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{}
|
||||
// If <expire> <=0 means it does not expire.
|
||||
//
|
||||
// Note that the function <f> is executed within writing mutex lock.
|
||||
func GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} {
|
||||
return cache.GetOrSetFuncLock(key, f, expire)
|
||||
func GetOrSetFuncLock(key interface{}, f func() interface{}, duration interface{}) interface{} {
|
||||
return cache.GetOrSetFuncLock(key, f, duration)
|
||||
}
|
||||
|
||||
// Contains returns true if <key> exists in the cache, or else returns false.
|
||||
|
||||
@ -9,6 +9,7 @@ package gcache
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/glist"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
@ -103,9 +104,21 @@ func (c *memCache) getOrNewExpireSet(expire int64) (expireSet *gset.Set) {
|
||||
return
|
||||
}
|
||||
|
||||
// getMilliExpire converts parameter <duration> to int type in milliseconds.
|
||||
//
|
||||
// Note that there's some performance cost in type assertion here, but it's valuable.
|
||||
func (c *memCache) getMilliExpire(duration interface{}) int {
|
||||
if d, ok := duration.(time.Duration); ok {
|
||||
return int(d.Nanoseconds() / 1000000)
|
||||
} else {
|
||||
return duration.(int)
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func (c *memCache) Set(key interface{}, value interface{}, expire int) {
|
||||
func (c *memCache) Set(key interface{}, value interface{}, duration interface{}) {
|
||||
expire := c.getMilliExpire(duration)
|
||||
expireTime := c.getInternalExpire(expire)
|
||||
c.dataMu.Lock()
|
||||
c.data[key] = memCacheItem{v: value, e: expireTime}
|
||||
@ -119,7 +132,8 @@ func (c *memCache) Set(key interface{}, value interface{}, expire int) {
|
||||
//
|
||||
// It doubly checks the <key> whether exists in the cache using mutex writing lock
|
||||
// before setting it to the cache.
|
||||
func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, expire int) interface{} {
|
||||
func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, duration interface{}) interface{} {
|
||||
expire := c.getMilliExpire(duration)
|
||||
expireTimestamp := c.getInternalExpire(expire)
|
||||
c.dataMu.Lock()
|
||||
defer c.dataMu.Unlock()
|
||||
@ -149,7 +163,8 @@ func (c *memCache) getInternalExpire(expire int) int64 {
|
||||
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
|
||||
// which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func (c *memCache) SetIfNotExist(key interface{}, value interface{}, expire int) bool {
|
||||
func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration interface{}) bool {
|
||||
expire := c.getMilliExpire(duration)
|
||||
if !c.Contains(key) {
|
||||
c.doSetWithLockCheck(key, value, expire)
|
||||
return true
|
||||
@ -159,7 +174,8 @@ func (c *memCache) SetIfNotExist(key interface{}, value interface{}, expire int)
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func (c *memCache) Sets(data map[interface{}]interface{}, expire int) {
|
||||
func (c *memCache) Sets(data map[interface{}]interface{}, duration interface{}) {
|
||||
expire := c.getMilliExpire(duration)
|
||||
expireTime := c.getInternalExpire(expire)
|
||||
for k, v := range data {
|
||||
c.dataMu.Lock()
|
||||
@ -189,9 +205,9 @@ func (c *memCache) Get(key interface{}) interface{} {
|
||||
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func (c *memCache) GetOrSet(key interface{}, value interface{}, expire int) interface{} {
|
||||
func (c *memCache) GetOrSet(key interface{}, value interface{}, duration interface{}) interface{} {
|
||||
if v := c.Get(key); v == nil {
|
||||
return c.doSetWithLockCheck(key, value, expire)
|
||||
return c.doSetWithLockCheck(key, value, duration)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
@ -202,9 +218,9 @@ func (c *memCache) GetOrSet(key interface{}, value interface{}, expire int) inte
|
||||
// if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
|
||||
func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) interface{} {
|
||||
if v := c.Get(key); v == nil {
|
||||
return c.doSetWithLockCheck(key, f(), expire)
|
||||
return c.doSetWithLockCheck(key, f(), duration)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
@ -217,9 +233,9 @@ func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, expire in
|
||||
// If <expire> <=0 means it does not expire.
|
||||
//
|
||||
// Note that the function <f> is executed within writing mutex lock.
|
||||
func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} {
|
||||
func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, duration interface{}) interface{} {
|
||||
if v := c.Get(key); v == nil {
|
||||
return c.doSetWithLockCheck(key, f, expire)
|
||||
return c.doSetWithLockCheck(key, f, duration)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
|
||||
@ -9,13 +9,14 @@
|
||||
package gcache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/os/grpool"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
//clear 用于清除全局缓存,因gcache api 暂未暴露 Clear 方法
|
||||
@ -49,6 +50,14 @@ func TestCache_Set_Expire(t *testing.T) {
|
||||
gtest.Assert(cache.Size(), 0)
|
||||
cache.Close()
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.Set(1, 11, 100*time.Millisecond)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
gtest.Assert(cache.Get(1), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_Keys_Values(t *testing.T) {
|
||||
@ -205,7 +214,7 @@ func TestCache_SetConcurrency(t *testing.T) {
|
||||
}()
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Log("first part end")
|
||||
//t.Log("first part end")
|
||||
}
|
||||
|
||||
go func() {
|
||||
@ -217,7 +226,7 @@ func TestCache_SetConcurrency(t *testing.T) {
|
||||
}()
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Log("second part end")
|
||||
//t.Log("second part end")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
package gfcache
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/cmdenv"
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
@ -27,18 +29,18 @@ var (
|
||||
// GetContents returns string content of given file by <path> from cache.
|
||||
// If there's no content in the cache, it will read it from disk file specified by <path>.
|
||||
// The parameter <expire> specifies the caching time for this file content in seconds.
|
||||
func GetContents(path string, expire ...int) string {
|
||||
return string(GetBinContents(path, expire...))
|
||||
func GetContents(path string, duration ...interface{}) string {
|
||||
return string(GetBinContents(path, duration...))
|
||||
}
|
||||
|
||||
// GetBinContents returns []byte content of given file by <path> from cache.
|
||||
// If there's no content in the cache, it will read it from disk file specified by <path>.
|
||||
// The parameter <expire> specifies the caching time for this file content in seconds.
|
||||
func GetBinContents(path string, expire ...int) []byte {
|
||||
func GetBinContents(path string, duration ...interface{}) []byte {
|
||||
k := cacheKey(path)
|
||||
e := cacheExpire
|
||||
if len(expire) > 0 {
|
||||
e = expire[0]
|
||||
if len(duration) > 0 {
|
||||
e = getSecondExpire(duration[0])
|
||||
}
|
||||
r := gcache.GetOrSetFuncLock(k, func() interface{} {
|
||||
b := gfile.GetBinContents(path)
|
||||
@ -58,7 +60,18 @@ func GetBinContents(path string, expire ...int) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 生成缓存键名
|
||||
// getSecondExpire converts parameter <duration> to int type in seconds.
|
||||
//
|
||||
// Note that there's some performance cost in type assertion here, but it's valuable.
|
||||
func getSecondExpire(duration interface{}) int {
|
||||
if d, ok := duration.(time.Duration); ok {
|
||||
return int(d.Nanoseconds() / 1000000000)
|
||||
} else {
|
||||
return duration.(int)
|
||||
}
|
||||
}
|
||||
|
||||
// cacheKey produces the cache key for gcache.
|
||||
func cacheKey(path string) string {
|
||||
return "gf.gfcache:" + path
|
||||
}
|
||||
|
||||
@ -8,9 +8,10 @@
|
||||
package glog
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gogf/gf/g/internal/cmdenv"
|
||||
"github.com/gogf/gf/g/os/grpool"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -115,19 +116,19 @@ func GetFlags() int {
|
||||
return logger.GetFlags()
|
||||
}
|
||||
|
||||
// PrintBacktrace prints the caller backtrace,
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func PrintBacktrace(skip ...int) {
|
||||
logger.PrintBacktrace(skip...)
|
||||
// PrintStack prints the caller stack,
|
||||
// the optional parameter <skip> specify the skipped stack offset from the end point.
|
||||
func PrintStack(skip ...int) {
|
||||
logger.PrintStack(skip...)
|
||||
}
|
||||
|
||||
// GetBacktrace returns the caller backtrace content,
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func GetBacktrace(skip ...int) string {
|
||||
return logger.GetBacktrace(skip...)
|
||||
// GetStack returns the caller stack content,
|
||||
// the optional parameter <skip> specify the skipped stack offset from the end point.
|
||||
func GetStack(skip ...int) string {
|
||||
return logger.GetStack(skip...)
|
||||
}
|
||||
|
||||
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
|
||||
func SetBacktrace(enabled bool) {
|
||||
logger.SetBacktrace(enabled)
|
||||
// SetStack enables/disables the stack feature in failure logging outputs.
|
||||
func SetStack(enabled bool) {
|
||||
logger.SetStack(enabled)
|
||||
}
|
||||
|
||||
@ -94,13 +94,13 @@ func Debugfln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Notice prints the logging content with [NOTI] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Notice(v ...interface{}) {
|
||||
logger.Notice(v...)
|
||||
}
|
||||
|
||||
// Noticef prints the logging content with [NOTI] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Noticef(format string, v ...interface{}) {
|
||||
logger.Noticef(format, v...)
|
||||
}
|
||||
@ -112,13 +112,13 @@ func Noticefln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Warning prints the logging content with [WARN] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Warning(v ...interface{}) {
|
||||
logger.Warning(v...)
|
||||
}
|
||||
|
||||
// Warningf prints the logging content with [WARN] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Warningf(format string, v ...interface{}) {
|
||||
logger.Warningf(format, v...)
|
||||
}
|
||||
@ -130,13 +130,13 @@ func Warningfln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Error prints the logging content with [ERRO] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Error(v ...interface{}) {
|
||||
logger.Error(v...)
|
||||
}
|
||||
|
||||
// Errorf prints the logging content with [ERRO] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Errorf(format string, v ...interface{}) {
|
||||
logger.Errorf(format, v...)
|
||||
}
|
||||
@ -148,13 +148,13 @@ func Errorfln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Critical prints the logging content with [CRIT] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Critical(v ...interface{}) {
|
||||
logger.Critical(v...)
|
||||
}
|
||||
|
||||
// Criticalf prints the logging content with [CRIT] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func Criticalf(format string, v ...interface{}) {
|
||||
logger.Criticalf(format, v...)
|
||||
}
|
||||
|
||||
@ -46,16 +46,16 @@ func Level(level int) *Logger {
|
||||
}
|
||||
|
||||
// Skip is a chaining function,
|
||||
// which sets backtrace skip for the current logging content output.
|
||||
// which sets stack 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 {
|
||||
return logger.Backtrace(enabled, skip...)
|
||||
// Stack is a chaining function,
|
||||
// which sets stack options for the current logging content output .
|
||||
func Stack(enabled bool, skip ...int) *Logger {
|
||||
return logger.Stack(enabled, skip...)
|
||||
}
|
||||
|
||||
// StdPrint is a chaining function,
|
||||
|
||||
@ -10,17 +10,18 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/debug"
|
||||
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"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"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
@ -31,8 +32,8 @@ type Logger struct {
|
||||
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)
|
||||
stSkip int // Skip count for stack.
|
||||
stStatus int // Stack 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).
|
||||
}
|
||||
@ -42,6 +43,7 @@ const (
|
||||
gDEFAULT_FILE_POOL_FLAGS = os.O_CREATE | os.O_WRONLY | os.O_APPEND
|
||||
gDEFAULT_FPOOL_PERM = os.FileMode(0666)
|
||||
gDEFAULT_FPOOL_EXPIRE = 60000
|
||||
gPATH_FILTER_KEY = "/g/os/glog/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -54,25 +56,13 @@ const (
|
||||
F_TIME_STD = F_TIME_DATE | F_TIME_MILLI
|
||||
)
|
||||
|
||||
var (
|
||||
// Default line break.
|
||||
ln = "\n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Initialize log line breaks depending on underlying os.
|
||||
if runtime.GOOS == "windows" {
|
||||
ln = "\r\n"
|
||||
}
|
||||
}
|
||||
|
||||
// New creates and returns a custom logger.
|
||||
func New() *Logger {
|
||||
logger := &Logger{
|
||||
file: gDEFAULT_FILE_FORMAT,
|
||||
flags: F_TIME_STD,
|
||||
level: LEVEL_ALL,
|
||||
btStatus: 1,
|
||||
stStatus: 1,
|
||||
headerPrint: true,
|
||||
stdoutPrint: true,
|
||||
}
|
||||
@ -126,18 +116,18 @@ func (l *Logger) GetFlags() int {
|
||||
return l.flags
|
||||
}
|
||||
|
||||
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
|
||||
func (l *Logger) SetBacktrace(enabled bool) {
|
||||
// SetStack enables/disables the stack feature in failure logging outputs.
|
||||
func (l *Logger) SetStack(enabled bool) {
|
||||
if enabled {
|
||||
l.btStatus = 1
|
||||
l.stStatus = 1
|
||||
} else {
|
||||
l.btStatus = 0
|
||||
l.stStatus = 0
|
||||
}
|
||||
}
|
||||
|
||||
// SetBacktraceSkip sets the backtrace offset from the end point.
|
||||
func (l *Logger) SetBacktraceSkip(skip int) {
|
||||
l.btSkip = skip
|
||||
// SetStackSkip sets the stack offset from the end point.
|
||||
func (l *Logger) SetStackSkip(skip int) {
|
||||
l.stSkip = skip
|
||||
}
|
||||
|
||||
// SetWriter sets the customized logging <writer> for logging.
|
||||
@ -254,10 +244,10 @@ func (l *Logger) print(std io.Writer, lead string, value ...interface{}) {
|
||||
// Caller path.
|
||||
callerPath := ""
|
||||
if l.flags&F_FILE_LONG > 0 {
|
||||
callerPath = l.getLongFile() + ": "
|
||||
callerPath = debug.CallerWithFilter(gPATH_FILTER_KEY) + ": "
|
||||
}
|
||||
if l.flags&F_FILE_SHORT > 0 {
|
||||
callerPath = gfile.Basename(l.getLongFile()) + ": "
|
||||
callerPath = gfile.Basename(debug.CallerWithFilter(gPATH_FILTER_KEY)) + ": "
|
||||
}
|
||||
if len(callerPath) > 0 {
|
||||
buffer.WriteString(callerPath)
|
||||
@ -267,13 +257,30 @@ func (l *Logger) print(std io.Writer, lead string, value ...interface{}) {
|
||||
buffer.WriteString(l.prefix + " ")
|
||||
}
|
||||
}
|
||||
for k, v := range value {
|
||||
if k > 0 {
|
||||
buffer.WriteByte(' ')
|
||||
// Convert value to string.
|
||||
tempStr := ""
|
||||
valueStr := ""
|
||||
for _, v := range value {
|
||||
if err, ok := v.(error); ok {
|
||||
tempStr = fmt.Sprintf("%+v", err)
|
||||
} else {
|
||||
tempStr = gconv.String(v)
|
||||
}
|
||||
if len(valueStr) > 0 {
|
||||
if valueStr[len(valueStr)-1] == '\n' {
|
||||
if tempStr[0] == '\n' {
|
||||
valueStr += tempStr[1:]
|
||||
} else {
|
||||
valueStr += tempStr
|
||||
}
|
||||
} else {
|
||||
valueStr += " " + tempStr
|
||||
}
|
||||
} else {
|
||||
valueStr = tempStr
|
||||
}
|
||||
buffer.WriteString(gconv.String(v))
|
||||
}
|
||||
buffer.WriteString(ln)
|
||||
buffer.WriteString(valueStr + "\n")
|
||||
if l.flags&F_ASYNC > 0 {
|
||||
asyncPool.Add(func() {
|
||||
l.printToWriter(std, buffer)
|
||||
@ -305,16 +312,16 @@ func (l *Logger) printToWriter(std io.Writer, buffer *bytes.Buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
// printStd prints content <s> without backtrace.
|
||||
// printStd prints content <s> without stack.
|
||||
func (l *Logger) printStd(lead string, value ...interface{}) {
|
||||
l.print(os.Stdout, lead, value...)
|
||||
}
|
||||
|
||||
// printStd prints content <s> with backtrace check.
|
||||
// printStd prints content <s> with stack check.
|
||||
func (l *Logger) printErr(lead string, value ...interface{}) {
|
||||
if l.btStatus == 1 {
|
||||
if s := l.GetBacktrace(); s != "" {
|
||||
value = append(value, ln+"Backtrace:"+ln+s)
|
||||
if l.stStatus == 1 {
|
||||
if s := l.GetStack(); s != "" {
|
||||
value = append(value, "\nStack:\n"+s)
|
||||
}
|
||||
}
|
||||
// In matter of sequence, do not use stderr here, but use the same stdout.
|
||||
@ -326,76 +333,22 @@ func (l *Logger) format(format string, value ...interface{}) string {
|
||||
return fmt.Sprintf(format, value...)
|
||||
}
|
||||
|
||||
// PrintBacktrace prints the caller backtrace,
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func (l *Logger) PrintBacktrace(skip ...int) {
|
||||
if s := l.GetBacktrace(skip...); s != "" {
|
||||
l.Println("Backtrace:" + ln + s)
|
||||
// PrintStack prints the caller stack,
|
||||
// the optional parameter <skip> specify the skipped stack offset from the end point.
|
||||
func (l *Logger) PrintStack(skip ...int) {
|
||||
if s := l.GetStack(skip...); s != "" {
|
||||
l.Println("Stack:\n" + s)
|
||||
} else {
|
||||
l.Println()
|
||||
}
|
||||
}
|
||||
|
||||
// GetBacktrace returns the caller backtrace content,
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func (l *Logger) GetBacktrace(skip ...int) string {
|
||||
customSkip := 0
|
||||
// GetStack returns the caller stack content,
|
||||
// the optional parameter <skip> specify the skipped stack offset from the end point.
|
||||
func (l *Logger) GetStack(skip ...int) string {
|
||||
number := 1
|
||||
if len(skip) > 0 {
|
||||
customSkip = skip[0]
|
||||
number = skip[0] + 1
|
||||
}
|
||||
backtrace := ""
|
||||
from := 0
|
||||
// Find the caller position exclusive of the glog file.
|
||||
for i := 0; i < 1000; i++ {
|
||||
if _, file, _, ok := runtime.Caller(i); ok {
|
||||
if !gregex.IsMatchString("/g/os/glog/glog.+$", file) {
|
||||
from = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// Find the true caller file path using custom skip.
|
||||
index := 1
|
||||
goRoot := runtime.GOROOT()
|
||||
if goRoot != "" {
|
||||
goRoot = strings.Replace(goRoot, "\\", "/", -1)
|
||||
goRoot = regexp.QuoteMeta(goRoot)
|
||||
}
|
||||
for i := from + customSkip + l.btSkip; i < 1000; i++ {
|
||||
if _, file, cline, ok := runtime.Caller(i); ok && len(file) > 2 {
|
||||
if (goRoot == "" || !gregex.IsMatchString("^"+goRoot, file)) && !gregex.IsMatchString(`<autogenerated>`, file) {
|
||||
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, ln)
|
||||
index++
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
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 < 1000; i++ {
|
||||
if _, file, _, ok := runtime.Caller(i); ok {
|
||||
if !gregex.IsMatchString("/g/os/glog/glog.+$", file) {
|
||||
from = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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 && len(file) > 2 {
|
||||
if (goRoot == "" || !gregex.IsMatchString("^"+goRoot, file)) && !gregex.IsMatchString(`<autogenerated>`, file) {
|
||||
return fmt.Sprintf(`%s:%d`, file, line)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return debug.StackWithFilter(gPATH_FILTER_KEY, number)
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ func (l *Logger) Debugfln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Notice prints the logging content with [NOTI] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Notice(v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_NOTI) {
|
||||
l.printErr("[NOTI]", v...)
|
||||
@ -124,7 +124,7 @@ func (l *Logger) Notice(v ...interface{}) {
|
||||
}
|
||||
|
||||
// Noticef prints the logging content with [NOTI] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Noticef(format string, v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_NOTI) {
|
||||
l.printErr("[NOTI]", l.format(format, v...))
|
||||
@ -140,7 +140,7 @@ func (l *Logger) Noticefln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Warning prints the logging content with [WARN] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Warning(v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_WARN) {
|
||||
l.printErr("[WARN]", v...)
|
||||
@ -148,7 +148,7 @@ func (l *Logger) Warning(v ...interface{}) {
|
||||
}
|
||||
|
||||
// Warningf prints the logging content with [WARN] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Warningf(format string, v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_WARN) {
|
||||
l.printErr("[WARN]", l.format(format, v...))
|
||||
@ -164,7 +164,7 @@ func (l *Logger) Warningfln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Error prints the logging content with [ERRO] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Error(v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_ERRO) {
|
||||
l.printErr("[ERRO]", v...)
|
||||
@ -172,7 +172,7 @@ func (l *Logger) Error(v ...interface{}) {
|
||||
}
|
||||
|
||||
// Errorf prints the logging content with [ERRO] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Errorf(format string, v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_ERRO) {
|
||||
l.printErr("[ERRO]", l.format(format, v...))
|
||||
@ -188,7 +188,7 @@ func (l *Logger) Errorfln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// Critical prints the logging content with [CRIT] header and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Critical(v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_CRIT) {
|
||||
l.printErr("[CRIT]", v...)
|
||||
@ -196,7 +196,7 @@ func (l *Logger) Critical(v ...interface{}) {
|
||||
}
|
||||
|
||||
// Criticalf prints the logging content with [CRIT] header, custom format and newline.
|
||||
// It also prints caller backtrace info if backtrace feature is enabled.
|
||||
// It also prints caller stack info if stack feature is enabled.
|
||||
func (l *Logger) Criticalf(format string, v ...interface{}) {
|
||||
if l.checkLevel(LEVEL_CRIT) {
|
||||
l.printErr("[CRIT]", l.format(format, v...))
|
||||
|
||||
@ -7,8 +7,9 @@
|
||||
package glog
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"io"
|
||||
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
)
|
||||
|
||||
// To is a chaining function,
|
||||
@ -82,7 +83,7 @@ func (l *Logger) Level(level int) *Logger {
|
||||
}
|
||||
|
||||
// Skip is a chaining function,
|
||||
// which sets backtrace skip for the current logging content output.
|
||||
// which sets stack 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)
|
||||
@ -91,22 +92,22 @@ func (l *Logger) Skip(skip int) *Logger {
|
||||
} else {
|
||||
logger = l
|
||||
}
|
||||
logger.SetBacktraceSkip(skip)
|
||||
logger.SetStackSkip(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 {
|
||||
// Stack is a chaining function,
|
||||
// which sets stack options for the current logging content output .
|
||||
func (l *Logger) Stack(enabled bool, skip ...int) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.parent == nil {
|
||||
logger = l.Clone()
|
||||
} else {
|
||||
logger = l
|
||||
}
|
||||
logger.SetBacktrace(enabled)
|
||||
logger.SetStack(enabled)
|
||||
if len(skip) > 0 {
|
||||
logger.SetBacktraceSkip(skip[0])
|
||||
logger.SetStackSkip(skip[0])
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
@ -115,6 +115,16 @@ func (t *Time) Add(d time.Duration) *Time {
|
||||
return t
|
||||
}
|
||||
|
||||
// 当前时间加上指定时间段(使用字符串格式)
|
||||
func (t *Time) AddStr(duration string) error {
|
||||
if d, err := time.ParseDuration(duration); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.Time = t.Time.Add(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 时区转换为指定的时区(通过time.Location)
|
||||
func (t *Time) ToLocation(location *time.Location) *Time {
|
||||
t.Time = t.Time.In(location)
|
||||
|
||||
@ -25,7 +25,7 @@ import (
|
||||
func Case(t *testing.T, f func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n%s", err, getBacktrace())
|
||||
fmt.Fprintf(os.Stderr, "%v\n%s", err, getStack())
|
||||
t.Fail()
|
||||
}
|
||||
}()
|
||||
@ -256,7 +256,7 @@ func Error(message ...interface{}) {
|
||||
|
||||
// Fatal prints <message> to stderr and exit the process.
|
||||
func Fatal(message ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), getBacktrace())
|
||||
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), getStack())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@ -299,14 +299,14 @@ func compareMap(value, expect interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBacktrace returns the caller backtrace content from getBacktrace.
|
||||
// The parameter <skip> indicates the skip count of the caller backtrace from getBacktrace.
|
||||
func getBacktrace(skip ...int) string {
|
||||
// getStack returns the caller stack content from getStack.
|
||||
// The parameter <skip> indicates the skip count of the caller stack from getStack.
|
||||
func getStack(skip ...int) string {
|
||||
customSkip := 0
|
||||
if len(skip) > 0 {
|
||||
customSkip = skip[0]
|
||||
}
|
||||
backtrace := ""
|
||||
stack := ""
|
||||
index := 1
|
||||
from := 0
|
||||
// Ignore current gtest lines and find the beginning index of caller file.
|
||||
@ -337,13 +337,13 @@ func getBacktrace(skip ...int) string {
|
||||
continue
|
||||
}
|
||||
}
|
||||
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, "\n")
|
||||
stack += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, "\n")
|
||||
index++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return backtrace
|
||||
return stack
|
||||
}
|
||||
|
||||
// isNil checks whether <value> is nil.
|
||||
|
||||
@ -11,10 +11,10 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/g/internal/empty"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Dump prints variables <i...> to stdout with more manually readable.
|
||||
@ -46,21 +46,6 @@ func Export(i ...interface{}) string {
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// PrintBacktrace prints the caller backtrace to stdout.
|
||||
func PrintBacktrace() {
|
||||
index := 1
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for i := 1; i < 10000; i++ {
|
||||
if _, path, line, ok := runtime.Caller(i); ok {
|
||||
buffer.WriteString(fmt.Sprintf(`%d. %s:%d%s`, index, path, line, "\n"))
|
||||
index++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Print(buffer.String())
|
||||
}
|
||||
|
||||
// Throw throws out an exception, which can be caught be TryCatch or recover.
|
||||
func Throw(exception interface{}) {
|
||||
panic(exception)
|
||||
|
||||
30
g/util/gutil/gutil_debug.go
Normal file
30
g/util/gutil/gutil_debug.go
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 gutil
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/debug"
|
||||
)
|
||||
|
||||
// PrintStack prints to standard error the stack trace returned by runtime.Stack.
|
||||
func PrintStack(skip ...int) {
|
||||
number := 1
|
||||
if len(skip) > 0 {
|
||||
number = skip[0] + 1
|
||||
}
|
||||
debug.PrintStack(number)
|
||||
}
|
||||
|
||||
// Stack returns a formatted stack trace of the goroutine that calls it.
|
||||
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
|
||||
func Stack(skip ...int) string {
|
||||
number := 1
|
||||
if len(skip) > 0 {
|
||||
number = skip[0] + 1
|
||||
}
|
||||
return debug.Stack(number)
|
||||
}
|
||||
@ -23,9 +23,9 @@ func Test_Dump(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PrintBacktrace(t *testing.T) {
|
||||
func Test_PrintStack(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gutil.PrintBacktrace()
|
||||
gutil.PrintStack()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -237,7 +237,7 @@ func Test_CheckStruct_With_Inherit(t *testing.T) {
|
||||
Pass Pass
|
||||
}
|
||||
user := &User{
|
||||
Name: "",
|
||||
Name: "john",
|
||||
Pass: Pass{
|
||||
Pass1: "1",
|
||||
Pass2: "2",
|
||||
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
//_ "github.com/denisenkom/go-mssqldb"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
@ -49,7 +50,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.2",
|
||||
@ -59,7 +60,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.3",
|
||||
@ -69,7 +70,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.4",
|
||||
@ -79,7 +80,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// },
|
||||
//})
|
||||
|
||||
@ -9,5 +9,6 @@
|
||||
name = "test"
|
||||
type = "mysql"
|
||||
role = "master"
|
||||
debug = "true"
|
||||
weight = "1"
|
||||
charset = "utf8"
|
||||
priority = "1"
|
||||
|
||||
@ -2,9 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 本文件用于gf框架的mysql数据库操作示例,不作为单元测试使用
|
||||
@ -48,7 +49,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.2",
|
||||
@ -58,7 +59,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.3",
|
||||
@ -68,7 +69,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.4",
|
||||
@ -78,7 +79,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// },
|
||||
//})
|
||||
|
||||
20
geg/database/gdb/mysql/gdb_args_slice.go
Normal file
20
geg/database/gdb/mysql/gdb_args_slice.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
|
||||
db.Table("user").Where("nickname like ? and passport like ?", g.Slice{"T3", "t3"}).OrderBy("id asc").All()
|
||||
|
||||
conditions := g.Map{
|
||||
"nickname like ?": "%T%",
|
||||
"id between ? and ?": g.Slice{1, 3},
|
||||
"id >= ?": 1,
|
||||
"create_time > ?": 0,
|
||||
"id in(?)": g.Slice{1, 2, 3},
|
||||
}
|
||||
db.Table("user").Where(conditions).OrderBy("id asc").All()
|
||||
}
|
||||
@ -24,8 +24,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
db.SetDebug(true)
|
||||
db.Table("user").Limit(2).Delete()
|
||||
return
|
||||
|
||||
glog.SetPath("/tmp")
|
||||
|
||||
// 执行3条SQL查询
|
||||
17
geg/database/gdb/mysql/gdb_debug2.go
Normal file
17
geg/database/gdb/mysql/gdb_debug2.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
// 执行3条SQL查询
|
||||
for i := 1; i <= 3; i++ {
|
||||
db.Table("user").Where("id=?", i).One()
|
||||
}
|
||||
// 构造一条错误查询
|
||||
db.Table("user").Where("no_such_field=?", "just_test").One()
|
||||
|
||||
db.Table("user").Data(g.Map{"name": "smith"}).Where("uid=?", 1).Save()
|
||||
}
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
//_ "github.com/mattn/go-oci8"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
@ -48,7 +49,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.2",
|
||||
@ -58,7 +59,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.3",
|
||||
@ -68,7 +69,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// gdb.ConfigNode {
|
||||
// Host : "127.0.0.4",
|
||||
@ -78,7 +79,7 @@ func init() {
|
||||
// Name : "test",
|
||||
// Type : "mysql",
|
||||
// Role : "master",
|
||||
// Priority : 100,
|
||||
// Weight : 100,
|
||||
// },
|
||||
// },
|
||||
//})
|
||||
|
||||
25
geg/errors/gerror/gerror1.go
Normal file
25
geg/errors/gerror/gerror1.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
)
|
||||
|
||||
func Error1() error {
|
||||
return errors.New("test")
|
||||
}
|
||||
|
||||
func Error2() error {
|
||||
return gerror.New("test")
|
||||
}
|
||||
|
||||
func main() {
|
||||
err1 := Error1()
|
||||
err2 := Error2()
|
||||
fmt.Printf("%s, %-s, %+s\n", err1, err1, err1)
|
||||
fmt.Printf("%v, %-v, %+v\n", err1, err1, err1)
|
||||
fmt.Printf("%s, %-s, %+s\n", err2, err2, err2)
|
||||
fmt.Printf("%v, %-v, %+v\n", err2, err2, err2)
|
||||
}
|
||||
26
geg/errors/gerror/gerror2.go
Normal file
26
geg/errors/gerror/gerror2.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
)
|
||||
|
||||
func OpenFile() error {
|
||||
return gerror.New("permission denied")
|
||||
}
|
||||
|
||||
func OpenConfig() error {
|
||||
return gerror.Wrap(OpenFile(), "configuration file opening failed")
|
||||
}
|
||||
|
||||
func ReadConfig() error {
|
||||
return gerror.Wrap(OpenConfig(), "reading configuration failed")
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := ReadConfig()
|
||||
glog.Printf("%s\n%+s", err, err)
|
||||
glog.Printf("%+v", err)
|
||||
glog.Error(err)
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
glog.PrintBacktrace()
|
||||
glog.New().PrintBacktrace()
|
||||
|
||||
fmt.Println(glog.GetBacktrace())
|
||||
fmt.Println(glog.New().GetBacktrace())
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
import "github.com/gogf/gf/g/os/glog"
|
||||
|
||||
func Test() {
|
||||
glog.Error("This is error!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
//glog.SetPath("/tmp/")
|
||||
glog.Error("This is error!")
|
||||
glog.Errorf("This is error, %d!", 2)
|
||||
Test()
|
||||
}
|
||||
|
||||
26
geg/os/glog/glog_gerror.go
Normal file
26
geg/os/glog/glog_gerror.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
|
||||
func MakeError() error {
|
||||
return errors.New("connection closed with normal error")
|
||||
}
|
||||
|
||||
func MakeGError() error {
|
||||
return gerror.New("connection closed with gerror")
|
||||
}
|
||||
|
||||
func TestGError() {
|
||||
err1 := MakeError()
|
||||
err2 := MakeGError()
|
||||
glog.Error(err1)
|
||||
glog.Error(err2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
TestGError()
|
||||
}
|
||||
@ -6,7 +6,7 @@ import (
|
||||
|
||||
func PrintLog(content string) {
|
||||
glog.Skip(1).Line().Println("line number with skip:", content)
|
||||
glog.Line().Println("line number without skip:", content)
|
||||
glog.Line(true).Println("line number without skip:", content)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
16
geg/os/glog/glog_stack.go
Normal file
16
geg/os/glog/glog_stack.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
glog.PrintStack()
|
||||
glog.New().PrintStack()
|
||||
|
||||
fmt.Println(glog.GetStack())
|
||||
fmt.Println(glog.New().GetStack())
|
||||
}
|
||||
@ -1,20 +1 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
)
|
||||
|
||||
func main() {
|
||||
v := math.MaxUint16
|
||||
//v := []byte{255, 127}
|
||||
//ve := gbinary.Encode(v)
|
||||
//ve1 := gbinary.BeEncodeByLength(len(ve), v)
|
||||
//fmt.Println(ve)
|
||||
//fmt.Println(ve1)
|
||||
|
||||
//fmt.Println(gbinary.LeDecodeToInt(gbinary.LeEncode(v)))
|
||||
fmt.Println(gbinary.BeDecodeToInt(gbinary.BeEncode(v)))
|
||||
}
|
||||
|
||||
@ -1,39 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
import "github.com/gogf/gf/g/os/glog"
|
||||
|
||||
type Schedule struct{}
|
||||
func Test() {
|
||||
|
||||
type Task struct{}
|
||||
|
||||
func (c *Schedule) ListDir(r *ghttp.Request) {
|
||||
r.Response.Writeln("ListDir")
|
||||
}
|
||||
|
||||
func (c *Task) Add(r *ghttp.Request) {
|
||||
r.Response.Writeln("Add")
|
||||
}
|
||||
|
||||
func (c *Task) Task(r *ghttp.Request) {
|
||||
r.Response.Writeln("Task")
|
||||
}
|
||||
|
||||
// 实现权限校验
|
||||
// 通过事件回调,类似于中间件机制,但是可控制的粒度更细,可以精准注册到路由规则
|
||||
func AuthHookHandler(r *ghttp.Request) {
|
||||
// 如果权限校验失败,调用 r.ExitAll() 退出执行流程
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/schedule").Bind([]ghttp.GroupItem{
|
||||
{"ALL", "*", AuthHookHandler, ghttp.HOOK_BEFORE_SERVE},
|
||||
{"POST", "/schedule", new(Schedule)},
|
||||
{"POST", "/task", new(Task)},
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
glog.Line().Println("123")
|
||||
}
|
||||
|
||||
14
geg/util/gutil/stack.go
Normal file
14
geg/util/gutil/stack.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
)
|
||||
|
||||
func Test(s *interface{}) {
|
||||
//debug.PrintStack()
|
||||
gutil.PrintStack()
|
||||
}
|
||||
|
||||
func main() {
|
||||
Test(nil)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.7.3"
|
||||
const VERSION = "v1.8.0"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user