ghttp.Server增加access & error log功能

This commit is contained in:
John
2018-04-19 19:11:10 +08:00
parent 179a4b2710
commit 9406b02fa5
9 changed files with 165 additions and 60 deletions

View File

@ -39,6 +39,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
Server : s,
Request : *r,
Response : &Response {
status : http.StatusOK,
ResponseWriter : w,
},
}

View File

@ -21,6 +21,7 @@ type Response struct {
bufmu sync.RWMutex // 缓冲区互斥锁
buffer []byte // 每个请求的返回数据缓冲区
request *Request // 关联的Request请求对象
status int // 返回状态码
}
// 返回信息任何变量自动转换为bytes
@ -98,6 +99,7 @@ func (r *Response) SetAllowCrossDomainRequest(allowOrigin string, allowMethods s
// 返回HTTP Code状态码
func (r *Response) WriteStatus(code int, content...string) {
r.status = code
r.Header().Set("Content-Type", "text/plain; charset=utf-8")
r.Header().Set("X-Content-Type-Options", "nosniff")
if len(content) > 0 {

View File

@ -32,27 +32,32 @@ const (
// http server结构体
type Server struct {
hmmu sync.RWMutex // handler互斥锁
hhmu sync.RWMutex // hooks互斥锁
hmcmu sync.RWMutex // handlerCache互斥锁
hhcmu sync.RWMutex // hooksCache互斥锁
name string // 服务名称,方便识别
server http.Server // 底层http server对象
config ServerConfig // 配置对象
status int8 // 当前服务器状态(0未启动1运行中)
methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充)
handlerMap HandlerMap // 所有注册的回调函数(静态匹配)
handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配)
hooksTree map[string]interface{} // 所有注册的事件回调函数(动态匹配,树型+链表优先级匹配)
handlerCache *gcache.Cache // 服务注册路由内存缓存
hooksCache *gcache.Cache // 回调事件注册路由内存缓存
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)
cookieMaxAge *gtype.Int // Cookie有效期
sessionMaxAge *gtype.Int // Session有效期
sessionIdName *gtype.String // SessionId名称
cookies *gmap.IntInterfaceMap // 当前服务器正在服务(请求正在执行)的Cookie(每个请求一个Cookie对象)
sessions *gcache.Cache // Session内存缓存
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
hmmu sync.RWMutex // handler互斥锁
hhmu sync.RWMutex // hooks互斥锁
hmcmu sync.RWMutex // handlerCache互斥锁
hhcmu sync.RWMutex // hooksCache互斥锁
name string // 服务名称,方便识别
server http.Server // 底层http server对象
config ServerConfig // 配置对象
status int8 // 当前服务器状态(0未启动1运行中)
methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充)
handlerMap HandlerMap // 所有注册的回调函数(静态匹配)
handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配)
hooksTree map[string]interface{} // 所有注册的事件回调函数(动态匹配,树型+链表优先级匹配)
handlerCache *gcache.Cache // 服务注册路由内存缓存
hooksCache *gcache.Cache // 回调事件注册路由内存缓存
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)
cookieMaxAge *gtype.Int // Cookie有效期
sessionMaxAge *gtype.Int // Session有效期
sessionIdName *gtype.String // SessionId名称
cookies *gmap.IntInterfaceMap // 当前服务器正在服务(请求正在执行)的Cookie(每个请求一个Cookie对象)
sessions *gcache.Cache // Session内存缓存
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
logPath *gtype.String // 存放日志的目录路径
errorLogEnabled *gtype.Bool // 是否开启error log
accessLogEnabled *gtype.Bool // 是否开启access log
logger *glog.Logger // 日志对象
logHandler *gtype.Interface // 自定义的日志处理回调方法
}
// 域名、URI与回调函数的绑定记录表
@ -86,20 +91,25 @@ func GetServer(names...string) (*Server) {
return s.(*Server)
}
s := &Server {
name : name,
methodsMap : make(map[string]bool),
handlerMap : make(HandlerMap),
handlerTree : make(map[string]interface{}),
hooksTree : make(map[string]interface{}),
handlerCache : gcache.New(),
hooksCache : gcache.New(),
cookies : gmap.NewIntInterfaceMap(),
sessions : gcache.New(),
cookieMaxAge : gtype.NewInt(gDEFAULT_COOKIE_MAX_AGE),
sessionMaxAge : gtype.NewInt(gDEFAULT_SESSION_MAX_AGE),
sessionIdName : gtype.NewString(gDEFAULT_SESSION_ID_NAME),
servedCount : gtype.NewInt(),
closeQueue : gqueue.New(),
name : name,
methodsMap : make(map[string]bool),
handlerMap : make(HandlerMap),
handlerTree : make(map[string]interface{}),
hooksTree : make(map[string]interface{}),
handlerCache : gcache.New(),
hooksCache : gcache.New(),
cookies : gmap.NewIntInterfaceMap(),
sessions : gcache.New(),
cookieMaxAge : gtype.NewInt(gDEFAULT_COOKIE_MAX_AGE),
sessionMaxAge : gtype.NewInt(gDEFAULT_SESSION_MAX_AGE),
sessionIdName : gtype.NewString(gDEFAULT_SESSION_ID_NAME),
servedCount : gtype.NewInt(),
closeQueue : gqueue.New(),
logPath : gtype.NewString(),
accessLogEnabled : gtype.NewBool(),
errorLogEnabled : gtype.NewBool(),
logger : glog.New(),
logHandler : gtype.NewInterface(),
}
// 设置路由解析缓存上限使用LRU进行缓存淘汰
s.hooksCache.SetCap(10000)

View File

@ -13,13 +13,14 @@ func (s *Server) startCloseQueueLoop() {
for {
if v := s.closeQueue.PopFront(); v != nil {
r := v.(*Request)
s.callHookHandler(r, "BeforeClose")
s.handleAccessLog(r)
s.callHookHandler(r, "BeforeClose")
// 关闭当前会话的Cookie
r.Cookie.Close()
// 更新Session会话超时时间
r.Session.UpdateExpire()
s.callHookHandler(r, "AfterClose")
}
}

View File

@ -37,6 +37,14 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
// 创建请求处理对象
request := newRequest(s, r, w)
// 错误日志使用recover进行判断
defer func() {
if e := recover(); e != nil {
s.handleErrorLog(e, request)
}
}()
// 事件 - BeforeServe
s.callHookHandler(request, "BeforeServe")
if h := s.getHandler(request); h != nil {
@ -54,6 +62,7 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
request.Response.OutputBuffer()
// 事件 - AfterOutput
s.callHookHandler(request, "AfterOutput")
// 将Request对象指针丢到队列中异步处理
s.closeQueue.PushBack(request)
}

View File

@ -0,0 +1,54 @@
// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
// 默认错误日志封装.
package ghttp
import (
"fmt"
"strings"
"gitee.com/johng/gf/g/util/gconv"
)
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
func (s *Server) handleAccessLog(r *Request) {
if !s.IsAccessLogEnabled() {
return
}
// 自定义错误处理
if v := s.GetLogHandler(); v != nil {
v(r)
return
}
status := gconv.String(r.Response.status)
if v := r.Response.Header().Get("Status Code"); v != "" {
status = v
}
content := fmt.Sprintf(`%s %s %s`, r.Method, r.URL.String(), r.Proto)
content += fmt.Sprintf(", host: %s", r.Host)
content += fmt.Sprintf(", from: %s", strings.Split(r.RemoteAddr, ":")[0])
content += fmt.Sprintf(", refer: %s", r.Referer())
content += fmt.Sprintf(", status: %v", status)
s.logger.Info(content)
}
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
func (s *Server) handleErrorLog(error interface{}, r *Request) {
if !s.IsErrorLogEnabled() {
return
}
// 自定义错误处理
if v := s.GetLogHandler(); v != nil {
v(r, error)
return
}
content := fmt.Sprintf(`%s %s %s`, r.Method, r.URL.String(), r.Proto)
content += fmt.Sprintf(", host: %s", r.Host)
content += fmt.Sprintf(", from: %s", strings.Split(r.RemoteAddr, ":")[0])
content += fmt.Sprintf(", refer: %s", r.Referer())
content += fmt.Sprintf(", error: %v", error)
s.logger.Error(content)
}

View File

@ -8,11 +8,10 @@
package ghttp
import (
"strings"
"time"
"log"
"errors"
"strconv"
"strings"
"net/http"
"crypto/tls"
"path/filepath"
@ -105,15 +104,6 @@ func (s *Server)SetMaxHeaderBytes(b int) error {
return nil
}
// 设置http server参数 - ErrorLog
func (s *Server)SetErrorLog(logger *log.Logger) error {
if s.status == 1 {
return errors.New("server config cannot be changed while running")
}
s.config.ErrorLog = logger
return nil
}
// 设置http server参数 - IndexFiles
func (s *Server)SetIndexFiles(index []string) error {
if s.status == 1 {
@ -165,6 +155,52 @@ func (s *Server)SetSessionIdName(name string) {
s.sessionIdName.Set(name)
}
// 设置日志目录
func (s *Server)SetLogPath(path string) error {
if err := s.logger.SetPath(path); err != nil {
return err
}
s.logPath.Set(path)
return nil
}
// 设置是否开启access log日志功能
func (s *Server)SetAccessLogEnabled(enabled bool) {
s.accessLogEnabled.Set(enabled)
}
// 设置是否开启error log日志功能
func (s *Server)SetErrorLogEnabled(enabled bool) {
s.errorLogEnabled.Set(enabled)
}
// 设置日志写入的回调函数
func (s *Server) SetLogHandler(handler func(r *Request, error ... interface{})) {
s.logHandler.Set(handler)
}
// 获取日志写入的回调函数
func (s *Server) GetLogHandler() func(r *Request, error ... interface{}) {
if v := s.logHandler.Val(); v != nil {
return v.(func(r *Request, error ... interface{}))
}
return nil
}
// 获取日志目录
func (s *Server)GetLogPath() string {
return s.logPath.Val()
}
// access log日志功能是否开启
func (s *Server)IsAccessLogEnabled() bool {
return s.accessLogEnabled.Val()
}
// error log日志功能是否开启
func (s *Server)IsErrorLogEnabled() bool {
return s.errorLogEnabled.Val()
}
// 获取
func (s *Server) GetName() string {
@ -185,3 +221,4 @@ func (s *Server)GetSessionMaxAge() int {
func (s *Server)GetSessionIdName() string {
return s.sessionIdName.Val()
}

View File

@ -1,10 +1,6 @@
package main
import (
//"gitee.com/johng/gf/g/net/ghttp"
_"net/http/pprof"
"log"
"net/http"
"gitee.com/johng/gf/g/net/ghttp"
)

View File

@ -2,19 +2,14 @@ package main
import (
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/frame/gins"
)
func main() {
s := ghttp.GetServer()
s.BindHandler("/template2", func(r *ghttp.Request){
tplcontent := `id:{{.id}}, name:{{.name}}`
content, _ := gins.View().ParseContent(tplcontent, map[string]interface{}{
"id" : 123,
"name" : "john",
})
r.Response.Write(content)
//panic("123")
})
//s.SetPort(8199)
s.SetAccessLogEnabled(true)
s.SetPort(8199)
s.Run()
}