Compare commits

..

15 Commits

32 changed files with 916 additions and 318 deletions

View File

@ -14,13 +14,13 @@
[![Code Helper](https://www.codetriage.com/gogf/gf/badges/users.svg)](https://www.codetriage.com/gogf/gf)
-->
`GF(GoFrame)` is a modular, lightweight, loosely coupled, high performance application development framework written in Go. Supporting graceful server, hot updates, multi-domain, multi-port, multi-service, HTTP/HTTPS, dynamic/hook routing and many more features. Providing a series of core components and dozens of practical modules.
`GF(GoFrame)` is a modular, loose-coupled and production-ready application development framework written in Go. Providing a series of core components and dozens of practical modules, such as: cache, logging, array/queue/set/map, timer/timing tasks, file/memory lock, object pool, validator, database ORM, etc. Supporting web server with graceful server, hot updates, multi-domain, multi-port, multi-service, HTTP/HTTPS, dynamic/hook routing, rewrite rules and many more features.
# Installation
```
go get -u github.com/gogf/gf
```
or use `go.mod`
or use `go.mod`:
```
require github.com/gogf/gf latest
```
@ -32,6 +32,7 @@ golang version >= 1.9.2
# Documentation
* [GoDoc](https://godoc.org/github.com/gogf/gf)
* [中文文档](https://goframe.org)
# Architecture
@ -92,6 +93,8 @@ func main() {
# Donators
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>

View File

@ -32,7 +32,7 @@
go get -u github.com/gogf/gf
```
或者
`go.mod`
`go.mod`:
```
require github.com/gogf/gf latest
```
@ -100,6 +100,8 @@ func main() {
# 捐赠者
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>

16
g/g_setting.go Normal file
View File

@ -0,0 +1,16 @@
// 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 g
import "github.com/gogf/gf/g/net/ghttp"
// SetServerGraceful enables/disables graceful reload feature of ghttp Web Server.
//
// 是否开启WebServer的平滑重启特性。
func SetServerGraceful(enabled bool) {
ghttp.SetGraceful(enabled)
}

View File

@ -9,44 +9,44 @@
package ghttp
func Get(url string) (*ClientResponse, error) {
return DoRequest("GET", url, []byte(""))
return DoRequest("GET", url)
}
func Put(url, data string) (*ClientResponse, error) {
return DoRequest("PUT", url, []byte(data))
func Put(url string, data...string) (*ClientResponse, error) {
return DoRequest("PUT", url, data...)
}
func Post(url, data string) (*ClientResponse, error) {
return DoRequest("POST", url, []byte(data))
func Post(url string, data...string) (*ClientResponse, error) {
return DoRequest("POST", url, data...)
}
func Delete(url, data string) (*ClientResponse, error) {
return DoRequest("DELETE", url, []byte(data))
func Delete(url string, data...string) (*ClientResponse, error) {
return DoRequest("DELETE", url, data...)
}
func Head(url, data string) (*ClientResponse, error) {
return DoRequest("HEAD", url, []byte(data))
func Head(url string, data...string) (*ClientResponse, error) {
return DoRequest("HEAD", url, data...)
}
func Patch(url, data string) (*ClientResponse, error) {
return DoRequest("PATCH", url, []byte(data))
func Patch(url string, data...string) (*ClientResponse, error) {
return DoRequest("PATCH", url, data...)
}
func Connect(url, data string) (*ClientResponse, error) {
return DoRequest("CONNECT", url, []byte(data))
func Connect(url string, data...string) (*ClientResponse, error) {
return DoRequest("CONNECT", url, data...)
}
func Options(url, data string) (*ClientResponse, error) {
return DoRequest("OPTIONS", url, []byte(data))
func Options(url string, data...string) (*ClientResponse, error) {
return DoRequest("OPTIONS", url, data...)
}
func Trace(url, data string) (*ClientResponse, error) {
return DoRequest("TRACE", url, []byte(data))
func Trace(url string, data...string) (*ClientResponse, error) {
return DoRequest("TRACE", url, data...)
}
// 该方法支持二进制提交数据
func DoRequest(method, url string, data []byte) (*ClientResponse, error) {
return NewClient().DoRequest(method, url, data)
func DoRequest(method, url string, data...string) (*ClientResponse, error) {
return NewClient().DoRequest(method, url, data...)
}
// GET请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)

View File

@ -24,11 +24,13 @@ import (
// http客户端
type Client struct {
http.Client // 底层http client对象
header map[string]string // HEADER信息Map
prefix string // 设置请求的URL前缀
authUser string // HTTP基本权限设置名称
authPass string // HTTP基本权限设置密码
http.Client // 底层http client对象
header map[string]string // HEADER信息Map
cookies map[string]string // 自定义COOKIE
prefix string // 设置请求的URL前缀
authUser string // HTTP基本权限设置名称
authPass string // HTTP基本权限设置密码
browserMode bool // 是否模拟浏览器模式(自动保存提交COOKIE)
}
// http客户端对象指针
@ -39,10 +41,16 @@ func NewClient() (*Client) {
DisableKeepAlives: true,
},
},
header : make(map[string]string),
header : make(map[string]string),
cookies : make(map[string]string),
}
}
// 是否模拟浏览器模式(自动保存提交COOKIE)
func (c *Client) SetBrowserMode(enabled bool) {
c.browserMode = enabled
}
// 设置HTTP Header
func (c *Client) SetHeader(key, value string) {
c.header[key] = value
@ -58,6 +66,18 @@ func (c *Client) SetHeaderRaw(header string) {
}
}
// 设置COOKIE
func (c *Client) SetCookie(key, value string) {
c.cookies[key] = value
}
// 使用Map设置COOKIE
func (c *Client) SetCookieMap(cookieMap map[string]string) {
for k, v := range cookieMap {
c.cookies[k] = v
}
}
// 设置请求的URL前缀
func (c *Client) SetPrefix(prefix string) {
c.prefix = prefix
@ -76,26 +96,30 @@ func (c *Client) SetBasicAuth(user, pass string) {
// GET请求
func (c *Client) Get(url string) (*ClientResponse, error) {
return c.DoRequest("GET", url, []byte(""))
return c.DoRequest("GET", url)
}
// PUT请求
func (c *Client) Put(url, data string) (*ClientResponse, error) {
return c.DoRequest("PUT", url, []byte(data))
func (c *Client) Put(url string, data...string) (*ClientResponse, error) {
return c.DoRequest("PUT", url, data...)
}
// POST请求提交数据默认使用表单方式提交数据(绝大部分场景下也是如此)。
// 如果服务端对Content-Type有要求可使用Client对象进行请求单独设置相关属性。
// 支持文件上传需要字段格式为FieldName=@file:
func (c *Client) Post(url, data string) (*ClientResponse, error) {
func (c *Client) Post(url string, data...string) (*ClientResponse, error) {
if len(c.prefix) > 0 {
url = c.prefix + url
}
param := ""
if len(data) > 0 {
param = data[0]
}
req := (*http.Request)(nil)
if strings.Contains(data, "@file:") {
if strings.Contains(param, "@file:") {
buffer := new(bytes.Buffer)
writer := multipart.NewWriter(buffer)
for _, item := range strings.Split(data, "&") {
for _, item := range strings.Split(param, "&") {
array := strings.Split(item, "=")
if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 {
path := array[1][6:]
@ -126,7 +150,7 @@ func (c *Client) Post(url, data string) (*ClientResponse, error) {
req.Header.Set("Content-Type", writer.FormDataContentType())
}
} else {
if r, err := http.NewRequest("POST", url, bytes.NewReader([]byte(data))); err != nil {
if r, err := http.NewRequest("POST", url, bytes.NewReader([]byte(param))); err != nil {
return nil, err
} else {
req = r
@ -139,6 +163,19 @@ func (c *Client) Post(url, data string) (*ClientResponse, error) {
req.Header.Set(k, v)
}
}
// COOKIE
if len(c.cookies) > 0 {
headerCookie := ""
for k, v := range c.cookies {
if len(headerCookie) > 0 {
headerCookie += ";"
}
headerCookie += k + "=" + v
}
if len(headerCookie) > 0 {
req.Header.Set("Cookie", headerCookie)
}
}
// HTTP账号密码
if len(c.authUser) > 0 {
req.SetBasicAuth(c.authUser, c.authPass)
@ -148,34 +185,36 @@ func (c *Client) Post(url, data string) (*ClientResponse, error) {
if err != nil {
return nil, err
}
r := &ClientResponse{}
r.Response = *resp
r := &ClientResponse{
cookies : make(map[string]string),
}
r.Response = resp
return r, nil
}
// DELETE请求
func (c *Client) Delete(url, data string) (*ClientResponse, error) {
return c.DoRequest("DELETE", url, []byte(data))
func (c *Client) Delete(url string, data...string) (*ClientResponse, error) {
return c.DoRequest("DELETE", url, data...)
}
func (c *Client) Head(url, data string) (*ClientResponse, error) {
return c.DoRequest("HEAD", url, []byte(data))
func (c *Client) Head(url string, data...string) (*ClientResponse, error) {
return c.DoRequest("HEAD", url, data...)
}
func (c *Client) Patch(url, data string) (*ClientResponse, error) {
return c.DoRequest("PATCH", url, []byte(data))
func (c *Client) Patch(url string, data...string) (*ClientResponse, error) {
return c.DoRequest("PATCH", url, data...)
}
func (c *Client) Connect(url, data string) (*ClientResponse, error) {
return c.DoRequest("CONNECT", url, []byte(data))
func (c *Client) Connect(url string, data...string) (*ClientResponse, error) {
return c.DoRequest("CONNECT", url, data...)
}
func (c *Client) Options(url, data string) (*ClientResponse, error) {
return c.DoRequest("OPTIONS", url, []byte(data))
func (c *Client) Options(url string, data...string) (*ClientResponse, error) {
return c.DoRequest("OPTIONS", url, data...)
}
func (c *Client) Trace(url, data string) (*ClientResponse, error) {
return c.DoRequest("TRACE", url, []byte(data))
func (c *Client) Trace(url string, data...string) (*ClientResponse, error) {
return c.DoRequest("TRACE", url, data...)
}
// GET请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
@ -220,11 +259,7 @@ func (c *Client) TraceContent(url string, data...string) string {
// 请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
func (c *Client) DoRequestContent(method string, url string, data...string) string {
content := ""
if len(data) > 0 {
content = data[0]
}
response, err := c.DoRequest(method, url, []byte(content))
response, err := c.DoRequest(method, url, data...)
if err != nil {
return ""
}
@ -233,14 +268,18 @@ func (c *Client) DoRequestContent(method string, url string, data...string) stri
}
// 请求并返回response对象该方法支持二进制提交数据
func (c *Client) DoRequest(method, url string, data []byte) (*ClientResponse, error) {
func (c *Client) DoRequest(method, url string, data...string) (*ClientResponse, error) {
if strings.EqualFold("POST", method) {
return c.Post(url, string(data))
return c.Post(url, data...)
}
if len(c.prefix) > 0 {
url = c.prefix + url
}
req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(data))
param := ""
if len(data) > 0 {
param = data[0]
}
req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader([]byte(param)))
if err != nil {
return nil, err
}
@ -250,13 +289,40 @@ func (c *Client) DoRequest(method, url string, data []byte) (*ClientResponse, er
req.Header.Set(k, v)
}
}
// COOKIE
if len(c.cookies) > 0 {
headerCookie := ""
for k, v := range c.cookies {
if len(headerCookie) > 0 {
headerCookie += ";"
}
headerCookie += k + "=" + v
}
if len(headerCookie) > 0 {
req.Header.Set("Cookie", headerCookie)
}
}
// 执行请求
resp, err := c.Do(req)
if err != nil {
return nil, err
}
r := &ClientResponse{}
r.Response = *resp
r := &ClientResponse{
cookies : make(map[string]string),
}
r.Response = resp
// 浏览器模式
if c.browserMode {
now := time.Now()
for _, v := range r.Cookies() {
if v.Expires.UnixNano() < now.UnixNano() {
delete(c.cookies, v.Name)
} else {
c.cookies[v.Name] = v.Value
}
}
}
//fmt.Println(url, c.cookies)
return r, nil
}

View File

@ -10,14 +10,30 @@ package ghttp
import (
"io/ioutil"
"net/http"
"time"
)
// 客户端请求结果对象
type ClientResponse struct {
http.Response
*http.Response
cookies map[string]string
}
// 获返回的数据
// 获返回的指定COOKIE值
func (r *ClientResponse) GetCookie(key string) string {
if r.cookies == nil {
now := time.Now()
for _, v := range r.Cookies() {
if v.Expires.UnixNano() < now.UnixNano() {
continue
}
r.cookies[v.Name] = v.Value
}
}
return r.cookies[key]
}
// 获取返回的数据(二进制).
func (r *ClientResponse) ReadAll() []byte {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
@ -26,6 +42,11 @@ func (r *ClientResponse) ReadAll() []byte {
return body
}
// 获取返回的数据(字符串).
func (r *ClientResponse) ReadAllString() string {
return string(r.ReadAll())
}
// 关闭返回的HTTP链接
func (r *ClientResponse) Close() {
r.Response.Close = true

View File

@ -32,7 +32,6 @@ func newResponse(s *Server, w http.ResponseWriter) *Response {
Server : s,
ResponseWriter : ResponseWriter {
ResponseWriter : w,
Status : http.StatusOK,
buffer : bytes.NewBuffer(nil),
},
}
@ -137,9 +136,8 @@ func (r *Response) WriteStatus(status int, content...string) {
if status != http.StatusOK {
if f := r.request.Server.getStatusHandler(status, r.request); f != nil {
f(r.request)
// 如果是http.StatusOK那么表示回调函数内部没有设置header status
// 那么这里就可以设置status防止多次设置(http: multiple response.WriteHeader calls)
if r.Status == http.StatusOK {
// 防止多次设置(http: multiple response.WriteHeader calls)
if r.Status == 0 {
r.WriteHeader(status)
}
return

View File

@ -140,17 +140,24 @@ var (
doneChan = make(chan struct{}, 1000)
// 用于服务进程初始化,只能初始化一次,采用“懒初始化”(在server运行时才初始化)
serverProcInited = gtype.NewBool()
serverProcessInited = gtype.NewBool()
// 是否开启WebServer平滑重启特性, 会开启额外的本地端口监听,用于进程管理通信
gracefulEnabled = true
)
// 是否开启平滑重启特性
func SetGraceful(enabled bool) {
gracefulEnabled = enabled
}
// Web Server进程初始化.
// 注意该方法不能放置于包初始化方法init中不使用ghttp.Server的功能便不能初始化对应的协程goroutine逻辑.
func serverProcInit() {
if serverProcInited.Val() {
func serverProcessInit() {
if serverProcessInited.Val() {
return
}
serverProcInited.Set(true)
serverProcessInited.Set(true)
// 如果是完整重启,那么需要等待主进程销毁后,才开始执行监听,防止端口冲突
if genv.Get(gADMIN_ACTION_RESTART_ENVKEY) != "" {
if p, e := os.FindProcess(gproc.PPid()); e == nil {
@ -164,7 +171,9 @@ func serverProcInit() {
// 信号量管理操作监听
go handleProcessSignal()
// 异步监听进程间消息
go handleProcessMessage()
if gracefulEnabled {
go handleProcessMessage()
}
}
// 获取/创建一个默认配置的HTTP Server(默认监听端口是80)
@ -207,7 +216,7 @@ func GetServer(name...interface{}) (*Server) {
// 需要结合Wait方式一起使用
func (s *Server) Start() error {
// 服务进程初始化,只会初始化一次
serverProcInit()
serverProcessInit()
// 当前Web Server状态判断
if s.Status() == SERVER_STATUS_RUNNING {
@ -253,7 +262,7 @@ func (s *Server) Start() error {
if gproc.IsChild() {
gtimer.SetTimeout(2*time.Second, func() {
if err := gproc.Send(gproc.PPid(), []byte("exit"), gADMIN_GPROC_COMM_GROUP); err != nil {
panic(err)
glog.Error("ghttp server error in process communication:", err)
}
})
}

View File

@ -42,6 +42,7 @@ func GetCookie(r *Request) *Cookie {
}
return &Cookie {
request : r,
server : r.Server,
}
}
@ -52,7 +53,6 @@ func (c *Cookie) init() {
c.path = c.request.Server.GetCookiePath()
c.domain = c.request.Server.GetCookieDomain()
c.maxage = c.request.Server.GetCookieMaxAge()
c.server = c.request.Server
c.response = c.request.Response
// 如果没有设置COOKIE有效域名那么设置HOST为默认有效域名
if c.domain == "" {
@ -115,6 +115,11 @@ func (c *Cookie) SetCookie(key, value, domain, path string, maxAge int, httpOnly
}
}
// 获得客户端提交的SessionId
func (c *Cookie) GetSessionId() string {
return c.Get(c.server.GetSessionIdName())
}
// 设置SessionId
func (c *Cookie) SetSessionId(id string) {
c.Set(c.server.GetSessionIdName(), id)
@ -133,9 +138,14 @@ func (c *Cookie) Get(key string) string {
return ""
}
// 删除COOKIE使用默认的domain&path
func (c *Cookie) Remove(key string) {
c.SetCookie(key, "", c.domain, c.path, -86400)
}
// 标记该cookie在对应的域名和路径失效
// 删除cookie的重点是需要通知浏览器客户端cookie已过期
func (c *Cookie) Remove(key, domain, path string) {
func (c *Cookie) RemoveCookie(key, domain, path string) {
c.SetCookie(key, "", domain, path, -86400)
}

View File

@ -46,14 +46,28 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
request := newRequest(s, r, w)
defer func() {
if request.LeaveTime == 0 {
request.LeaveTime = gtime.Microsecond()
// 设置请求完成时间
request.LeaveTime = gtime.Microsecond()
// 事件 - BeforeOutput
if !request.IsExited() {
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
}
// 输出Cookie
request.Cookie.Output()
// 输出缓冲区
request.Response.OutputBuffer()
// 事件 - AfterOutput
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
// 事件 - BeforeClose
s.callHookHandler(HOOK_BEFORE_CLOSE, request)
// access log
s.handleAccessLog(request)
// error log使用recover进行判断
if e := recover(); e != nil {
request.Response.WriteStatus(http.StatusInternalServerError)
s.handleErrorLog(e, request)
}
// 更新Session会话超时时间
@ -125,22 +139,6 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_SERVE, request)
}
// 设置请求完成时间
request.LeaveTime = gtime.Microsecond()
// 事件 - BeforeOutput
if !request.IsExited() {
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
}
// 输出Cookie
request.Cookie.Output()
// 输出缓冲区
request.Response.OutputBuffer()
// 事件 - AfterOutput
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
}
// 查找静态文件的绝对路径

View File

@ -10,7 +10,6 @@ package ghttp
import (
"fmt"
"github.com/gogf/gf/g/os/gfile"
"net/http"
)
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
@ -34,8 +33,6 @@ func (s *Server) handleAccessLog(r *Request) {
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
func (s *Server) handleErrorLog(error interface{}, r *Request) {
r.Response.WriteStatus(http.StatusInternalServerError)
// 错误输出默认是开启的
if !s.IsErrorLogEnabled() && gfile.MainPkgPath() == "" {
return

View File

@ -52,7 +52,7 @@ func (s *Session) init() {
}
}
// 获取SessionId
// 获取/创建SessionId
func (s *Session) Id() string {
s.init()
return s.id
@ -60,8 +60,11 @@ func (s *Session) Id() string {
// 获取当前session所有数据
func (s *Session) Data() map[string]interface{} {
s.init()
return s.data.Map()
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
return s.data.Map()
}
return nil
}
// 设置session
@ -84,70 +87,158 @@ func (s *Session) BatchSet(m map[string]interface{}) {
// 判断键名是否存在
func (s *Session) Contains (key string) bool {
s.init()
return s.data.Contains(key)
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
return s.data.Contains(key)
}
return false
}
// 获取SESSION
func (s *Session) Get (key string) interface{} {
s.init()
return s.data.Get(key)
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
return s.data.Get(key)
}
return nil
}
// 获取SESSION建议都用该方法获取参数
func (s *Session) GetVar(key string) gvar.VarRead {
s.init()
return gvar.NewRead(s.data.Get(key), true)
}
func (s *Session) GetString (key string) string { return gconv.String(s.Get(key)) }
func (s *Session) GetBool(key string) bool { return gconv.Bool(s.Get(key)) }
func (s *Session) GetInt(key string) int { return gconv.Int(s.Get(key)) }
func (s *Session) GetInt8(key string) int8 { return gconv.Int8(s.Get(key)) }
func (s *Session) GetInt16(key string) int16 { return gconv.Int16(s.Get(key)) }
func (s *Session) GetInt32(key string) int32 { return gconv.Int32(s.Get(key)) }
func (s *Session) GetInt64(key string) int64 { return gconv.Int64(s.Get(key)) }
func (s *Session) GetUint(key string) uint { return gconv.Uint(s.Get(key)) }
func (s *Session) GetUint8(key string) uint8 { return gconv.Uint8(s.Get(key)) }
func (s *Session) GetUint16(key string) uint16 { return gconv.Uint16(s.Get(key)) }
func (s *Session) GetUint32(key string) uint32 { return gconv.Uint32(s.Get(key)) }
func (s *Session) GetUint64(key string) uint64 { return gconv.Uint64(s.Get(key)) }
func (s *Session) GetFloat32 (key string) float32 { return gconv.Float32(s.Get(key)) }
func (s *Session) GetFloat64 (key string) float64 { return gconv.Float64(s.Get(key)) }
func (s *Session) GetBytes(key string) []byte { return gconv.Bytes(s.Get(key)) }
func (s *Session) GetInts(key string) []int { return gconv.Ints(s.Get(key)) }
func (s *Session) GetFloats(key string) []float64 { return gconv.Floats(s.Get(key)) }
func (s *Session) GetStrings(key string) []string { return gconv.Strings(s.Get(key)) }
func (s *Session) GetInterfaces(key string) []interface{} { return gconv.Interfaces(s.Get(key)) }
func (s *Session) GetTime(key string, format...string) time.Time { return gconv.Time(s.Get(key), format...) }
func (s *Session) GetTimeDuration(key string) time.Duration { return gconv.TimeDuration(s.Get(key)) }
// 将变量转换为对象,注意 objPointer 参数必须为struct指针
func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error {
return gconv.Struct(s.Get(key), objPointer, attrMapping...)
return gvar.NewRead(s.Get(key), true)
}
// 删除session
func (s *Session) Remove(key string) {
s.init()
s.data.Remove(key)
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
s.data.Remove(key)
}
}
// 清空session
func (s *Session) Clear() {
s.init()
s.data.Clear()
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
s.data.Clear()
}
}
// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除)
func (s *Session) UpdateExpire() {
if len(s.id) > 0 {
if len(s.id) > 0 && s.data.Size() > 0 {
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
}
}
}
// Deprecated, use GetVar instead.
func (s *Session) GetString(key string) string {
return gconv.String(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetBool(key string) bool {
return gconv.Bool(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetInt(key string) int {
return gconv.Int(s.Get(key)) }
// Deprecated, use GetVar instead.
func (s *Session) GetInt8(key string) int8 {
return gconv.Int8(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetInt16(key string) int16 {
return gconv.Int16(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetInt32(key string) int32 {
return gconv.Int32(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetInt64(key string) int64 {
return gconv.Int64(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetUint(key string) uint {
return gconv.Uint(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetUint8(key string) uint8 {
return gconv.Uint8(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetUint16(key string) uint16 {
return gconv.Uint16(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetUint32(key string) uint32 {
return gconv.Uint32(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetUint64(key string) uint64 {
return gconv.Uint64(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetFloat32(key string) float32 {
return gconv.Float32(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetFloat64(key string) float64 {
return gconv.Float64(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetBytes(key string) []byte {
return gconv.Bytes(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetInts(key string) []int {
return gconv.Ints(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetFloats(key string) []float64 {
return gconv.Floats(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetStrings(key string) []string {
return gconv.Strings(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetInterfaces(key string) []interface{} {
return gconv.Interfaces(s.Get(key))
}
// Deprecated, use GetVar instead.
func (s *Session) GetTime(key string, format...string) time.Time {
return gconv.Time(s.Get(key), format...)
}
// Deprecated, use GetVar instead.
func (s *Session) GetTimeDuration(key string) time.Duration {
return gconv.TimeDuration(s.Get(key))
}
// Deprecated, use GetVar instead.
// (已废弃, 请使用GetVar) 将变量转换为对象,注意 objPointer 参数必须为struct指针
func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error {
return gconv.Struct(s.Get(key), objPointer, attrMapping...)
}

View File

@ -17,6 +17,7 @@ import (
)
// 基本路由功能测试
func Test_Router_Basic(t *testing.T) {
s := g.Server(gtime.Nanosecond())
s.BindHandler("/:name", func(r *ghttp.Request){
@ -53,3 +54,155 @@ func Test_Router_Basic(t *testing.T) {
gtest.Assert(client.GetContent("/user/list/100.html"), "100")
})
}
// 测试HTTP Method注册.
func Test_Router_Method(t *testing.T) {
s := g.Server(gtime.Nanosecond())
s.BindHandler("GET:/get", func(r *ghttp.Request){
})
s.BindHandler("POST:/post", func(r *ghttp.Request){
})
s.SetPort(8105)
s.SetDumpRouteMap(false)
go s.Run()
defer func() {
s.Shutdown()
time.Sleep(time.Second)
}()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix("http://127.0.0.1:8105")
resp1, err := client.Get("/get")
defer resp1.Close()
gtest.Assert(err, nil)
gtest.Assert(resp1.StatusCode, 200)
resp2, err := client.Post("/get")
defer resp2.Close()
gtest.Assert(err, nil)
gtest.Assert(resp2.StatusCode, 404)
resp3, err := client.Get("/post")
defer resp3.Close()
gtest.Assert(err, nil)
gtest.Assert(resp3.StatusCode, 404)
resp4, err := client.Post("/post")
defer resp4.Close()
gtest.Assert(err, nil)
gtest.Assert(resp4.StatusCode, 200)
})
}
// 测试状态返回.
func Test_Router_Status(t *testing.T) {
s := g.Server(gtime.Nanosecond())
s.BindHandler("/200", func(r *ghttp.Request){
r.Response.WriteStatus(200)
})
s.BindHandler("/300", func(r *ghttp.Request){
r.Response.WriteStatus(300)
})
s.BindHandler("/400", func(r *ghttp.Request){
r.Response.WriteStatus(400)
})
s.BindHandler("/500", func(r *ghttp.Request){
r.Response.WriteStatus(500)
})
s.SetPort(8110)
s.SetDumpRouteMap(false)
go s.Run()
defer func() {
s.Shutdown()
time.Sleep(time.Second)
}()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix("http://127.0.0.1:8110")
resp1, err := client.Get("/200")
defer resp1.Close()
gtest.Assert(err, nil)
gtest.Assert(resp1.StatusCode, 200)
resp2, err := client.Get("/300")
defer resp2.Close()
gtest.Assert(err, nil)
gtest.Assert(resp2.StatusCode, 300)
resp3, err := client.Get("/400")
defer resp3.Close()
gtest.Assert(err, nil)
gtest.Assert(resp3.StatusCode, 400)
resp4, err := client.Get("/500")
defer resp4.Close()
gtest.Assert(err, nil)
gtest.Assert(resp4.StatusCode, 500)
})
}
// 自定义状态码处理.
func Test_Router_CustomStatusHandler(t *testing.T) {
s := g.Server(gtime.Nanosecond())
s.BindHandler("/", func(r *ghttp.Request){
r.Response.Write("hello")
})
s.BindStatusHandler(404, func(r *ghttp.Request){
r.Response.Write("404 page")
})
s.SetPort(8120)
s.SetDumpRouteMap(false)
go s.Run()
defer func() {
s.Shutdown()
time.Sleep(time.Second)
}()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix("http://127.0.0.1:8120")
gtest.Assert(client.GetContent("/"), "hello")
resp, err := client.Get("/ThisDoesNotExist")
defer resp.Close()
gtest.Assert(err, nil)
gtest.Assert(resp.StatusCode, 404)
gtest.Assert(resp.ReadAllString(), "404 page")
})
}
// 测试不存在的路由.
func Test_Router_404(t *testing.T) {
s := g.Server(gtime.Nanosecond())
s.BindHandler("/", func(r *ghttp.Request){
r.Response.Write("hello")
})
s.SetPort(8130)
s.SetDumpRouteMap(false)
go s.Run()
defer func() {
s.Shutdown()
time.Sleep(time.Second)
}()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix("http://127.0.0.1:8130")
gtest.Assert(client.GetContent("/"), "hello")
resp, err := client.Get("/ThisDoesNotExist")
defer resp.Close()
gtest.Assert(err, nil)
gtest.Assert(resp.StatusCode, 404)
})
}

View File

@ -82,6 +82,11 @@ func Test_Router_Group1(t *testing.T) {
gtest.Assert(client.GetContent ("/api/obj/show"), "Object Show")
gtest.Assert(client.DeleteContent("/api/obj/rest"), "Object REST Delete")
// 测试404
resp, err := client.Get("/ThisDoesNotExist")
defer resp.Close()
gtest.Assert(err, nil)
gtest.Assert(resp.StatusCode, 404)
})
}
@ -122,5 +127,11 @@ func Test_Router_Group2(t *testing.T) {
gtest.Assert(client.GetContent ("/api/obj/my-show"), "Object Show")
gtest.Assert(client.GetContent ("/api/obj/show"), "Object Show")
gtest.Assert(client.DeleteContent("/api/obj/rest"), "Object REST Delete")
// 测试404
resp, err := client.Get("/ThisDoesNotExist")
defer resp.Close()
gtest.Assert(err, nil)
gtest.Assert(resp.StatusCode, 404)
})
}

View File

@ -0,0 +1,62 @@
// Copyright 2018 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.
// COOKIE测试
package ghttp_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
)
func Test_Cookie(t *testing.T) {
s := g.Server(gtime.Nanosecond())
s.BindHandler("/set", func(r *ghttp.Request){
r.Cookie.Set(r.Get("k"), r.Get("v"))
})
s.BindHandler("/get", func(r *ghttp.Request){
//fmt.Println(r.Cookie.Map())
r.Response.Write(r.Cookie.Get(r.Get("k")))
})
s.BindHandler("/remove", func(r *ghttp.Request){
r.Cookie.Remove(r.Get("k"))
})
s.SetPort(8500)
s.SetDumpRouteMap(false)
go s.Run()
defer func() {
s.Shutdown()
time.Sleep(time.Second)
}()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetBrowserMode(true)
client.SetPrefix("http://127.0.0.1:8500")
r1, e1 := client.Get("/set?k=key1&v=100")
if r1 != nil {
defer r1.Close()
}
gtest.Assert(e1, nil)
gtest.Assert(r1.ReadAllString(), "")
gtest.Assert(client.GetContent("/set?k=key2&v=200"), "")
gtest.Assert(client.GetContent("/get?k=key1"), "100")
gtest.Assert(client.GetContent("/get?k=key2"), "200")
gtest.Assert(client.GetContent("/get?k=key3"), "")
gtest.Assert(client.GetContent("/remove?k=key1"), "")
gtest.Assert(client.GetContent("/remove?k=key3"), "")
gtest.Assert(client.GetContent("/remove?k=key4"), "")
gtest.Assert(client.GetContent("/get?k=key1"), "")
gtest.Assert(client.GetContent("/get?k=key2"), "200")
})
}

View File

@ -0,0 +1,66 @@
// Copyright 2018 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.
// SESSION测试
package ghttp_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
)
func Test_Session(t *testing.T) {
s := g.Server(gtime.Nanosecond())
s.BindHandler("/set", func(r *ghttp.Request){
r.Session.Set(r.Get("k"), r.Get("v"))
})
s.BindHandler("/get", func(r *ghttp.Request){
r.Response.Write(r.Session.Get(r.Get("k")))
})
s.BindHandler("/remove", func(r *ghttp.Request){
r.Session.Remove(r.Get("k"))
})
s.BindHandler("/clear", func(r *ghttp.Request){
r.Session.Clear()
})
s.SetPort(8600)
s.SetDumpRouteMap(false)
go s.Run()
defer func() {
s.Shutdown()
time.Sleep(time.Second)
}()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetBrowserMode(true)
client.SetPrefix("http://127.0.0.1:8600")
r1, e1 := client.Get("/set?k=key1&v=100")
if r1 != nil {
defer r1.Close()
}
gtest.Assert(e1, nil)
gtest.Assert(r1.ReadAllString(), "")
gtest.Assert(client.GetContent("/set?k=key2&v=200"), "")
gtest.Assert(client.GetContent("/get?k=key1"), "100")
gtest.Assert(client.GetContent("/get?k=key2"), "200")
gtest.Assert(client.GetContent("/get?k=key3"), "")
gtest.Assert(client.GetContent("/remove?k=key1"), "")
gtest.Assert(client.GetContent("/remove?k=key3"), "")
gtest.Assert(client.GetContent("/remove?k=key4"), "")
gtest.Assert(client.GetContent("/get?k=key1"), "")
gtest.Assert(client.GetContent("/get?k=key2"), "200")
gtest.Assert(client.GetContent("/clear"), "")
gtest.Assert(client.GetContent("/get?k=key2"), "")
})
}

View File

@ -117,23 +117,23 @@ func newSchedule(pattern string) (*cronSchedule, error) {
schedule.hour = m
}
// 天
if m, err := parseItem(match[4], 1, 31, false); err != nil {
if m, err := parseItem(match[4], 1, 31, true); err != nil {
return nil, err
} else {
schedule.day = m
}
// 周
if m, err := parseItem(match[5], 0, 6, false); err != nil {
return nil, err
} else {
schedule.week = m
}
// 月
if m, err := parseItem(match[6], 1, 12, false); err != nil {
if m, err := parseItem(match[5], 1, 12, false); err != nil {
return nil, err
} else {
schedule.month = m
}
// 周
if m, err := parseItem(match[6], 0, 6, true); err != nil {
return nil, err
} else {
schedule.week = m
}
return schedule, nil
} else {
return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern))
@ -200,14 +200,14 @@ func parseItemValue(value string, valueType byte) (int, error) {
} else {
// 英文字母
switch valueType {
case 'w':
if i, ok := weekMap[strings.ToLower(value)]; ok {
return int(i), nil
}
case 'm':
if i, ok := monthMap[strings.ToLower(value)]; ok {
return int(i), nil
}
case 'w':
if i, ok := weekMap[strings.ToLower(value)]; ok {
return int(i), nil
}
}
}
return 0, errors.New(fmt.Sprintf(`invalid pattern value: "%s"`, value))
@ -234,10 +234,10 @@ func (s *cronSchedule) meet(t time.Time) bool {
if _, ok := s.day[t.Day()]; !ok {
return false
}
if _, ok := s.week[int(t.Weekday())]; !ok {
if _, ok := s.month[int(t.Month())]; !ok {
return false
}
if _, ok := s.month[int(t.Month())]; !ok {
if _, ok := s.week[int(t.Weekday())]; !ok {
return false
}
return true

View File

@ -47,31 +47,16 @@ func (c *Cache) GetBinContents(path string) []byte {
return b
}
// 添加文件监控
// 添加文件监控,一旦文件有变化立即清除缓存,下一次读取的时候再执行缓存。
func (c *Cache) addMonitor(path string) {
// 防止多goroutine同时调用
if c.cache.Contains(path) {
return
}
gfsnotify.Add(path, func(event *gfsnotify.Event) {
//glog.Debug("gfcache:", event)
length := 0
if r := c.cache.Get(path); r != nil {
length = len(r.([]byte))
}
// 是否删除
if event.IsRemove() {
c.cache.Remove(path)
c.size.Add(-length)
return
}
// 更新缓存内容
if c.cap.Val() == 0 || c.size.Val() < c.cap.Val() {
b := gfile.GetBinContents(path)
if len(b) > 0 {
c.size.Add(len(b) - length)
c.cache.Set(path, b)
}
c.size.Add(-len(r.([]byte)))
}
})
}

View File

@ -130,7 +130,7 @@ func (p *Pool) File() (*File, error) {
return nil, err
}
}
if !p.inited.Val() || p.inited.Set(true) == false {
if p.inited.Set(true) == false {
gfsnotify.Add(f.path, func(event *gfsnotify.Event) {
// 如果文件被删除或者重命名,立即重建指针池
if event.IsRemove() || event.IsRename() {

View File

@ -7,8 +7,7 @@
// Package glog implements powerful and easy-to-use levelled logging functionality.
//
// 日志模块,
// 直接文件/输出操作,没有异步逻辑,没有使用缓存或者通道
// 日志模块, 直接文件/输出操作,没有异步逻辑,没有使用缓存或者通道
package glog
import (
@ -28,10 +27,10 @@ const (
)
var (
// glog默认的日志等级影响全局
// default level for log
defaultLevel = gtype.NewInt(LEVEL_ALL)
// 默认的日志对象
// default logger object, for package method usage
logger = New()
)
@ -39,101 +38,157 @@ func init() {
SetDebug(cmdenv.Get("gf.glog.debug", true).Bool())
}
// 日志日志目录绝对路径
// SetPath sets the directory path for file logging.
//
// 日志日志目录绝对路径.
func SetPath(path string) {
logger.SetPath(path)
}
// 日志文件名称
func SetFile(file string) {
logger.SetFile(file)
// SetFile sets the file name <pattern> for file logging.
// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log.
// The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log
//
// 日志文件名称.
func SetFile(pattern string) {
logger.SetFile(pattern)
}
// 设置全局的日志记录等级
// SetLevel sets the default logging level.
//
// 设置全局的日志记录等级.
func SetLevel(level int) {
logger.SetLevel(level)
defaultLevel.Set(level)
}
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出
// SetWriter sets the customed logging <writer> for logging.
// The <writer> object should implements the io.Writer interface.
// Developer can use customed logging <writer> to redirect logging output to another service,
// eg: kafka, mysql, mongodb, etc.
//
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出.
func SetWriter(writer io.Writer) {
logger.SetWriter(writer)
}
// 返回自定义的IO默认为nil
// GetWriter returns the customed writer object, which implements the io.Writer interface.
// It returns nil if no customed writer set.
//
// 返回自定义的IO默认为nil.
func GetWriter() io.Writer {
return logger.GetWriter()
}
// 获取全局的日志记录等级
// GetLevel returns the default logging level value.
//
// 获取全局的日志记录等级.
func GetLevel() int {
return defaultLevel.Val()
}
// 设置是否允许输出DEBUG信息
// SetDebug enables/disables the debug level for default logger.
// The debug level is enbaled in default.
//
// 设置是否允许输出DEBUG信息.
func SetDebug(debug bool) {
logger.SetDebug(debug)
}
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false indefault.
//
// 设置写日志的同时开启or关闭控制台打印默认是关闭的
func SetStdPrint(open bool) {
logger.SetStdPrint(open)
}
// GetPath returns the logging directory path for file logging.
// It returns empty string if no directory path set.
//
// 获取日志目录绝对路径
func GetPath() string {
return logger.GetPath()
}
// PrintBacktrace prints the caller backtrace,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 打印文件调用回溯信息
func PrintBacktrace(skip...int) {
logger.PrintBacktrace(skip...)
}
// 获取文件调用回溯信息
// GetBacktrace returns the caller backtrace content,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 获取文件调用回溯信息.
func GetBacktrace(skip...int) string {
return logger.GetBacktrace(skip...)
}
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
//
// 是否关闭全局的backtrace信息
func SetBacktrace(enabled bool) {
logger.SetBacktrace(enabled)
}
// To is a chaining function,
// which redirects current logging content output to the sepecified <writer>.
//
// 链式操作设置下一次写入日志内容的Writer
func To(writer io.Writer) *Logger {
return logger.To(writer)
}
// 设置下一次输出的分类,支持多级分类设置
// Cat is a chaining function,
// which sets the category to <category> for current logging content output.
//
// 设置下一次输出的分类,支持多级分类设置.
func Cat(category string) *Logger {
return logger.Cat(category)
}
// File is a chaining function,
// which sets file name <pattern> for the current logging content output.
//
// 设置日志输出文件名称格式
func File(file string) *Logger {
return logger.File(file)
func File(pattern string) *Logger {
return logger.File(pattern)
}
// 设置日志打印等级
// Level is a chaining function,
// which sets logging level for the current logging content output.
//
// 设置日志打印等级.
func Level(level int) *Logger {
return logger.Level(level)
}
// 设置文件调用回溯信息
// Backtrace is a chaining function,
// which sets backtrace options for the current logging content output .
//
// 设置文件调用回溯信息.
func Backtrace(enabled bool, skip...int) *Logger {
return logger.Backtrace(enabled, skip...)
}
// StdPrint is a chaining function,
// which enables/disables stdout for the current logging content output.
//
// 是否允许在设置输出文件时同时也输出到终端
func StdPrint(enabled bool) *Logger {
return logger.StdPrint(enabled)
}
// Header is a chaining function,
// which enables/disables log header for the current logging content output.
//
// 是否打印每行日志头信息(默认开启)
func Header(enabled bool) *Logger {
return logger.Header(enabled)
}
func Print(v ...interface{}) {
logger.Print(v ...)
}
@ -150,14 +205,17 @@ func Printfln(format string, v ...interface{}) {
logger.Printfln(format, v ...)
}
// Fatal prints the logging content with [FATA] header and newline, then exit the current process.
func Fatal(v ...interface{}) {
logger.Fatal(v ...)
}
// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process.
func Fatalf(format string, v ...interface{}) {
logger.Fatalf(format, v ...)
}
// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process.
func Fatalfln(format string, v ...interface{}) {
logger.Fatalfln(format, v ...)
}

View File

@ -58,6 +58,8 @@ func init() {
}
}
// New creates a custom logger.
//
// 新建自定义的日志操作对象
func New() *Logger {
return &Logger {
@ -72,7 +74,9 @@ func New() *Logger {
}
}
// Logger深拷贝
// Clone returns a new logger, which is the clone the current logger.
//
// Logger拷贝.
func (l *Logger) Clone() *Logger {
return &Logger {
pr : l,
@ -87,16 +91,23 @@ func (l *Logger) Clone() *Logger {
}
}
// SetLevel sets the logging level.
//
// 设置日志记录等级
func (l *Logger) SetLevel(level int) {
l.level.Set(level)
}
// GetLevel returns the logging level value.
//
// 获取日志记录等级
func (l *Logger) GetLevel() int {
return l.level.Val()
}
// SetDebug enables/disables the debug level for logger.
// The debug level is enbaled in default.
//
// 快捷方法打开或关闭DEBU日志信息
func (l *Logger) SetDebug(debug bool) {
if debug {
@ -106,6 +117,7 @@ func (l *Logger) SetDebug(debug bool) {
}
}
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
func (l *Logger) SetBacktrace(enabled bool) {
if enabled {
l.btStatus.Set(1)
@ -115,11 +127,16 @@ func (l *Logger) SetBacktrace(enabled bool) {
}
// 设置BacktraceSkip
// SetBacktraceSkip sets the backtrace offset from the end point.
func (l *Logger) SetBacktraceSkip(skip int) {
l.btSkip.Set(skip)
}
// SetWriter sets the customed logging <writer> for logging.
// The <writer> object should implements the io.Writer interface.
// Developer can use customed logging <writer> to redirect logging output to another service,
// eg: kafka, mysql, mongodb, etc.
//
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出
func (l *Logger) SetWriter(writer io.Writer) {
l.mu.Lock()
@ -127,6 +144,9 @@ func (l *Logger) SetWriter(writer io.Writer) {
l.mu.Unlock()
}
// GetWriter returns the customed writer object, which implements the io.Writer interface.
// It returns nil if no customed writer set.
//
// 返回自定义的IO默认为nil
func (l *Logger) GetWriter() io.Writer {
l.mu.RLock()
@ -135,7 +155,10 @@ func (l *Logger) GetWriter() io.Writer {
return r
}
// 获取默认的文件IO
// getFilePointer returns the file pinter for file logging.
// It returns nil if file logging disabled, or file open fails.
//
// 获取默认的文件IO.
func (l *Logger) getFilePointer() *gfpool.File {
if path := l.path.Val(); path != "" {
// 文件名称中使用"{}"包含的内容使用gtime格式化
@ -159,7 +182,9 @@ func (l *Logger) getFilePointer() *gfpool.File {
return nil
}
// 设置日志文件的存储目录路径
// SetPath sets the directory path for file logging.
//
// 设置日志文件的存储目录路径.
func (l *Logger) SetPath(path string) error {
// path必须有值
if path == "" {
@ -176,16 +201,25 @@ func (l *Logger) SetPath(path string) error {
return nil
}
// GetPath returns the logging directory path for file logging.
// It returns empty string if no directory path set.
//
// 获取设置的日志目录路径
func (l *Logger) GetPath() string {
return l.path.Val()
}
// 日志文件名称
func (l *Logger) SetFile(file string) {
l.file.Set(file)
// SetFile sets the file name <pattern> for file logging.
// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log.
// The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log
//
// 设置日志文件名称格式.
func (l *Logger) SetFile(pattern string) {
l.file.Set(pattern)
}
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false indefault.
//
// 设置写日志时开启or关闭控制台打印默认是关闭的
func (l *Logger) SetStdPrint(enabled bool) {
l.alsoStdPrint.Set(enabled)
@ -263,11 +297,17 @@ func (l *Logger) appendBacktrace(s string, skip...int) string {
return s
}
// PrintBacktrace prints the caller backtrace,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 直接打印回溯信息参数skip表示调用端往上多少级开始回溯
func (l *Logger) PrintBacktrace(skip...int) {
l.Println(l.appendBacktrace("", skip...))
}
// GetBacktrace returns the caller backtrace content,
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
//
// 获取文件调用回溯字符串参数skip表示调用端往上多少级开始回溯
func (l *Logger) GetBacktrace(skip...int) string {
customSkip := 0
@ -322,16 +362,19 @@ func (l *Logger) Printfln(format string, v ...interface{}) {
l.stdPrint(fmt.Sprintf(format + ln, v...))
}
// Fatal prints the logging content with [FATA] header and newline, then exit the current process.
func (l *Logger) Fatal(v ...interface{}) {
l.errPrint("[FATA] " + fmt.Sprintln(v...))
os.Exit(1)
}
// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process.
func (l *Logger) Fatalf(format string, v ...interface{}) {
l.errPrint("[FATA] " + fmt.Sprintf(format, v...))
os.Exit(1)
}
// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process.
func (l *Logger) Fatalfln(format string, v ...interface{}) {
l.errPrint("[FATA] " + fmt.Sprintf(format + ln, v...))
os.Exit(1)
@ -463,6 +506,8 @@ func (l *Logger) Criticalfln(format string, v ...interface{}) {
}
}
// checkLevel checks whether the given <level> could be output.
//
// 判断给定level是否满足
func (l *Logger) checkLevel(level int) bool {
return l.level.Val() & level > 0

View File

@ -11,6 +11,9 @@ import (
"io"
)
// To is a chaining function,
// which redirects current logging content output to the sepecified <writer>.
//
// 链式操作设置下一次写入日志内容的Writer
func (l *Logger) To(writer io.Writer) *Logger {
logger := (*Logger)(nil)
@ -23,6 +26,9 @@ func (l *Logger) To(writer io.Writer) *Logger {
return logger
}
// Cat is a chaining function,
// which sets the category to <category> for current logging content output.
//
// 链式操作,设置下一次输出的日志分类(可以按照文件目录层级设置)在当前logpath或者当前工作目录下创建category目录
// 这是一个链式操作,可以设置多个分类,将会创建层级的日志分类目录。
func (l *Logger) Cat(category string) *Logger {
@ -39,6 +45,9 @@ func (l *Logger) Cat(category string) *Logger {
return logger
}
// File is a chaining function,
// which sets file name <pattern> for the current logging content output.
//
// 日志文件格式
func (l *Logger) File(file string) *Logger {
logger := (*Logger)(nil)
@ -51,6 +60,9 @@ func (l *Logger) File(file string) *Logger {
return logger
}
// Level is a chaining function,
// which sets logging level for the current logging content output.
//
// 设置日志打印等级
func (l *Logger) Level(level int) *Logger {
logger := (*Logger)(nil)
@ -63,6 +75,9 @@ func (l *Logger) Level(level int) *Logger {
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 {
logger := (*Logger)(nil)
@ -78,6 +93,9 @@ func (l *Logger) Backtrace(enabled bool, skip...int) *Logger {
return logger
}
// StdPrint is a chaining function,
// which enables/disables stdout for the current logging content output.
//
// 是否允许在设置输出文件时同时也输出到终端
func (l *Logger) StdPrint(enabled bool) *Logger {
logger := (*Logger)(nil)
@ -90,6 +108,9 @@ func (l *Logger) StdPrint(enabled bool) *Logger {
return logger
}
// Header is a chaining function,
// which enables/disables log header for the current logging content output.
//
// 是否打印每行日志头信息(默认开启)
func (l *Logger) Header(enabled bool) *Logger {
logger := (*Logger)(nil)

View File

@ -27,8 +27,10 @@ const (
gPROC_TEMP_DIR_ENV_KEY = "GPROC_TEMP_DIR"
)
// 进程开始执行时间
var processStartTime = time.Now()
var (
// 进程开始执行时间
processStartTime = time.Now()
)
// 获取当前进程ID
func Pid() int {

View File

@ -3,6 +3,7 @@
// 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.
// "不要通过共享内存来通信,而应该通过通信来共享内存"
@ -26,16 +27,43 @@ const (
)
var (
// 是否已开启TCP端口监听服务(使用int而非bool以便于使用原子操作判断是否开启)
tcpListeningCount = gtype.NewInt()
// 是否已开启TCP端口监听服务
tcpListened = gtype.NewBool()
)
// 获取其他进程传递到当前进程的消息包,阻塞执行。
// 进程只有在执行该方法后才会打开请求端口,默认情况下不允许进程间通信。
func Receive(group...string) *Msg {
// 一个进程只能开启一个监听goroutine
if tcpListened.Set(true) == false {
go startTcpListening()
}
queue := (*gqueue.Queue)(nil)
groupName := gPROC_COMM_DEAFULT_GRUOP_NAME
if len(group) > 0 {
groupName = group[0]
}
if v := commReceiveQueues.Get(groupName); v == nil {
commReceiveQueues.LockFunc(func(m map[string]interface{}) {
if v, ok := m[groupName]; ok {
queue = v.(*gqueue.Queue)
} else {
queue = gqueue.New(gPROC_MSG_QUEUE_MAX_LENGTH)
m[groupName] = queue
}
})
} else {
queue = v.(*gqueue.Queue)
}
if v := queue.Pop(); v != nil {
return v.(*Msg)
}
return nil
}
// 创建本地进程TCP通信服务
func startTcpListening() {
// 一个进程只能开启一个监听goroutine
if tcpListeningCount.Add(1) != 1 {
return
}
var listen *net.TCPListener
for i := gPROC_DEFAULT_TCP_PORT; ; i++ {
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", i))
@ -48,7 +76,6 @@ func startTcpListening() {
}
// 将监听的端口保存到通信文件中(字符串类型存放)
gfile.PutContents(getCommFilePath(Pid()), gconv.String(i))
//glog.Printfln("%d: gproc listening on [%s]", Pid(), addr)
break
}
for {
@ -133,32 +160,3 @@ func bufferToMsgs(buffer []byte) []*Msg {
return msgs
}
// 获取其他进程传递到当前进程的消息包,阻塞执行。
func Receive(group...string) *Msg {
// 开启接收协程时才会开启端口监听
go startTcpListening()
var queue *gqueue.Queue
groupName := gPROC_COMM_DEAFULT_GRUOP_NAME
if len(group) > 0 {
groupName = group[0]
}
if v := commReceiveQueues.Get(groupName); v == nil {
commReceiveQueues.LockFunc(func(m map[string]interface{}) {
if v, ok := m[groupName]; ok {
queue = v.(*gqueue.Queue)
} else {
queue = gqueue.New(gPROC_MSG_QUEUE_MAX_LENGTH)
m[groupName] = queue
}
})
} else {
queue = v.(*gqueue.Queue)
}
if v := queue.Pop(); v != nil {
return v.(*Msg)
}
return nil
}

View File

@ -7,16 +7,16 @@
package gproc
import (
"github.com/gogf/gf/g/net/gtcp"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/encoding/gbinary"
"fmt"
"errors"
"time"
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/encoding/gbinary"
"github.com/gogf/gf/g/net/gtcp"
"github.com/gogf/gf/g/os/gfcache"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/util/gconv"
"io"
"time"
)
const (
@ -26,7 +26,7 @@ const (
gPROC_COMM_DEAFULT_GRUOP_NAME = "" // 默认分组名称
)
// 向指定gproc进程发送数据
// 向指定gproc进程发送数据.
// 数据格式:总长度(24bit)|发送进程PID(24bit)|接收进程PID(24bit)|分组长度(8bit)|分组名称(变长)|校验(32bit)|参数(变长)
func Send(pid int, data []byte, group...string) error {
groupName := gPROC_COMM_DEAFULT_GRUOP_NAME
@ -85,6 +85,6 @@ func getConnByPid(pid int) (*gtcp.Conn, error) {
// 获取指定进程监听的端口号
func getPortByPid(pid int) int {
path := getCommFilePath(pid)
content := gfile.GetContents(path)
content := gfcache.GetContents(path)
return gconv.Int(content)
}

View File

@ -13,17 +13,25 @@ import (
"regexp"
)
// Quote quotes <s> by replacing special chars in <s>
// to match the rules of regular expression pattern.
// And returns the copy.
//
// 转移正则规则字符串例如Quote(`[foo]`) 返回 `\[foo\]`
func Quote(s string) string {
return regexp.QuoteMeta(s)
}
// Validate checks whether given regular expression pattern <pattern> valid.
//
// 校验所给定的正则表达式是否符合规范
func Validate(pattern string) error {
_, err := getRegexp(pattern)
return err
}
// IsMatch checks whether given bytes <src> matches <pattern>.
//
// 正则表达式是否匹配
func IsMatch(pattern string, src []byte) bool {
if r, err := getRegexp(pattern); err == nil {
@ -32,10 +40,15 @@ func IsMatch(pattern string, src []byte) bool {
return false
}
// IsMatchString checks whether given string <src> matches <pattern>.
//
// 判断给定的字符串<src>是否满足正则表达式<pattern>.
func IsMatchString(pattern string, src string) bool {
return IsMatch(pattern, []byte(src))
}
// MatchString return bytes slice that matched <pattern>.
//
// 正则匹配,并返回匹配的列表(参数[]byte)
func Match(pattern string, src []byte) ([][]byte, error) {
if r, err := getRegexp(pattern); err == nil {
@ -45,6 +58,8 @@ func Match(pattern string, src []byte) ([][]byte, error) {
}
}
// MatchString return strings that matched <pattern>.
//
// 正则匹配,并返回匹配的列表(参数[]string)
func MatchString(pattern string, src string) ([]string, error) {
if r, err := getRegexp(pattern); err == nil {
@ -54,6 +69,8 @@ func MatchString(pattern string, src string) ([]string, error) {
}
}
// MatchAll return all bytes slices that matched <pattern>.
//
// 正则匹配,并返回所有匹配的列表(参数[]string)
func MatchAll(pattern string, src []byte) ([][][]byte, error) {
if r, err := getRegexp(pattern); err == nil {
@ -63,7 +80,9 @@ func MatchAll(pattern string, src []byte) ([][][]byte, error) {
}
}
// 正则匹配,并返回所有匹配的列表(参数[][]string)
// MatchAllString return all strings that matched <pattern>.
//
// 正则匹配,并返回所有匹配的列表(参数[][]string).
func MatchAllString(pattern string, src string) ([][]string, error) {
if r, err := getRegexp(pattern); err == nil {
return r.FindAllStringSubmatch(src, -1), nil
@ -72,7 +91,9 @@ func MatchAllString(pattern string, src string) ([][]string, error) {
}
}
// 正则替换(全部替换)
// ReplaceString replace all matched <pattern> in bytes <src> with bytes <replace>.
//
// 正则替换(全部替换).
func Replace(pattern string, replace, src []byte) ([]byte, error) {
if r, err := getRegexp(pattern); err == nil {
return r.ReplaceAll(src, replace), nil
@ -81,12 +102,17 @@ func Replace(pattern string, replace, src []byte) ([]byte, error) {
}
}
// ReplaceString replace all matched <pattern> in string <src> with string <replace>.
//
// 正则替换(全部替换),字符串
func ReplaceString(pattern, replace, src string) (string, error) {
r, e := Replace(pattern, []byte(replace), []byte(src))
return string(r), e
}
// ReplaceFunc replace all matched <pattern> in bytes <src>
// with custom replacement function <replaceFunc>.
//
// 正则替换(全部替换),给定自定义替换方法
func ReplaceFunc(pattern string, src []byte, replaceFunc func(b []byte) []byte) ([]byte, error) {
if r, err := getRegexp(pattern); err == nil {
@ -96,6 +122,9 @@ func ReplaceFunc(pattern string, src []byte, replaceFunc func(b []byte) []byte)
}
}
// ReplaceStringFunc replace all matched <pattern> in string <src>
// with custom replacement function <replaceFunc>.
//
// 正则替换(全部替换),给定自定义替换方法
func ReplaceStringFunc(pattern string, src string, replaceFunc func(s string) string) (string, error) {
bytes, err := ReplaceFunc(pattern, []byte(src), func(bytes []byte) []byte {

View File

@ -6,9 +6,10 @@
// go test *.go -bench=".*"
package gregex
package gregex_test
import (
"github.com/gogf/gf/g/text/gregex"
"testing"
)
@ -18,42 +19,42 @@ var replace = "johng.cn"
func BenchmarkValidate(b *testing.B) {
for i := 0; i < b.N; i++ {
Validate(pattern)
gregex.Validate(pattern)
}
}
func BenchmarkIsMatch(b *testing.B) {
for i := 0; i < b.N; i++ {
IsMatch(pattern, []byte(src))
gregex.IsMatch(pattern, []byte(src))
}
}
func BenchmarkIsMatchString(b *testing.B) {
for i := 0; i < b.N; i++ {
IsMatchString(pattern, src)
gregex.IsMatchString(pattern, src)
}
}
func BenchmarkMatchString(b *testing.B) {
for i := 0; i < b.N; i++ {
MatchString(pattern, src)
gregex.MatchString(pattern, src)
}
}
func BenchmarkMatchAllString(b *testing.B) {
for i := 0; i < b.N; i++ {
MatchAllString(pattern, src)
gregex.MatchAllString(pattern, src)
}
}
func BenchmarkReplace(b *testing.B) {
for i := 0; i < b.N; i++ {
Replace(pattern, []byte(replace), []byte(src))
gregex.Replace(pattern, []byte(replace), []byte(src))
}
}
func BenchmarkReplaceString(b *testing.B) {
for i := 0; i < b.N; i++ {
ReplaceString(pattern, replace, src)
gregex.ReplaceString(pattern, replace, src)
}
}

View File

@ -88,25 +88,6 @@ func UcWords(str string) string {
return strings.Title(str)
}
// Traverse the array to find the string index position, if not exist, return-1.
//
// 遍历数组查找字符串索引位置,如果不存在则返回-1使用完整遍历查找.
func SearchArray (a []string, s string) int {
for i, v := range a {
if s == v {
return i
}
}
return -1
}
// InArray tests whether the given string s is in string array a.
//
// 判断字符串是否在数组中
func InArray (a []string, s string) bool {
return SearchArray(a, s) != -1
}
// IsLetterLower tests whether the given byte b is in lower case.
//
// 判断给定字符是否小写
@ -490,15 +471,7 @@ func Explode(delimiter, str string) []string {
//
// 用glue将字符串数组pieces连接为一个字符串。
func Implode(glue string, pieces []string) string {
var buf bytes.Buffer
l := len(pieces)
for _, str := range pieces {
buf.WriteString(str)
if l--; l > 0 {
buf.WriteString(glue)
}
}
return buf.String()
return strings.Join(pieces, glue)
}
// Generate a single-byte string from a number.

View File

@ -68,26 +68,6 @@ func Test_UcWords(t *testing.T) {
})
}
func Test_SearchArray(t *testing.T) {
gtest.Case(t, func() {
array := []string{"a", "b", "c"}
gtest.Assert(gstr.SearchArray(array, "a"), 0)
gtest.Assert(gstr.SearchArray(array, "b"), 1)
gtest.Assert(gstr.SearchArray(array, "c"), 2)
gtest.Assert(gstr.SearchArray(array, "d"), -1)
})
}
func Test_InArray(t *testing.T) {
gtest.Case(t, func() {
array := []string{"a", "b", "c"}
gtest.Assert(gstr.InArray(array, "a"), true)
gtest.Assert(gstr.InArray(array, "b"), true)
gtest.Assert(gstr.InArray(array, "c"), true)
gtest.Assert(gstr.InArray(array, "d"), false)
})
}
func Test_IsLetterLower(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gstr.IsLetterLower('a'), true)

View File

@ -1,17 +1,18 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/os/gcron"
"github.com/gogf/gf/g/os/glog"
"time"
)
func main() {
cron := gcron.New()
glog.Println("start")
cron.DelayAddOnce(1, "* * * * * *", func() {
glog.Println("run")
})
func test() {
}
func main() {
_, err := gcron.Add("*/10 * * * * ?", test)
fmt.Println(err)
fmt.Println(gcron.Entries())
time.Sleep(10*time.Second)
}

View File

@ -2,11 +2,13 @@ package main
import (
"fmt"
"github.com/gogf/gf/g/os/gfile"
)
func main() {
f, e := gfile.Open("/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/third")
fmt.Println(e)
fmt.Println(f)
s := "abc我是中国人é"
fmt.Println(len(s))
for i := 0; i < len(s); i++ {
fmt.Println(s[i])
}
}

View File

@ -1,5 +1,5 @@
package gf
const VERSION = "v1.5.0"
const VERSION = "v1.5.4"
const AUTHORS = "john<john@goframe.org>"