From 9406b02fa56344aee44db474e81c697817a09fc0 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 19 Apr 2018 19:11:10 +0800 Subject: [PATCH] =?UTF-8?q?ghttp.Server=E5=A2=9E=E5=8A=A0access=20&=20erro?= =?UTF-8?q?r=20log=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/net/ghttp/http_request.go | 1 + g/net/ghttp/http_response.go | 2 + g/net/ghttp/http_server.go | 80 +++++++++++++++++------------- g/net/ghttp/http_server_auto.go | 5 +- g/net/ghttp/http_server_handler.go | 9 ++++ g/net/ghttp/http_server_log.go | 54 ++++++++++++++++++++ g/net/ghttp/http_server_options.go | 59 ++++++++++++++++++---- geg/net/ghttp/hello.go | 4 -- geg/other/test.go | 11 ++-- 9 files changed, 165 insertions(+), 60 deletions(-) create mode 100644 g/net/ghttp/http_server_log.go diff --git a/g/net/ghttp/http_request.go b/g/net/ghttp/http_request.go index 353ffac83..e46a5c9ca 100644 --- a/g/net/ghttp/http_request.go +++ b/g/net/ghttp/http_request.go @@ -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, }, } diff --git a/g/net/ghttp/http_response.go b/g/net/ghttp/http_response.go index 019e2a2bb..e071403ed 100644 --- a/g/net/ghttp/http_response.go +++ b/g/net/ghttp/http_response.go @@ -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 { diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index 20bf110a8..1516ced8c 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -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) diff --git a/g/net/ghttp/http_server_auto.go b/g/net/ghttp/http_server_auto.go index f8e9b0fad..4fc6fba27 100644 --- a/g/net/ghttp/http_server_auto.go +++ b/g/net/ghttp/http_server_auto.go @@ -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") } } diff --git a/g/net/ghttp/http_server_handler.go b/g/net/ghttp/http_server_handler.go index 4389ac6d6..7538dca51 100644 --- a/g/net/ghttp/http_server_handler.go +++ b/g/net/ghttp/http_server_handler.go @@ -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) } diff --git a/g/net/ghttp/http_server_log.go b/g/net/ghttp/http_server_log.go new file mode 100644 index 000000000..d3b45d0f3 --- /dev/null +++ b/g/net/ghttp/http_server_log.go @@ -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" +) + +// 处理服务错误信息,主要是panic,http请求的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) +} + +// 处理服务错误信息,主要是panic,http请求的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) +} diff --git a/g/net/ghttp/http_server_options.go b/g/net/ghttp/http_server_options.go index 7af1dc3f0..fffd508ba 100644 --- a/g/net/ghttp/http_server_options.go +++ b/g/net/ghttp/http_server_options.go @@ -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() } + diff --git a/geg/net/ghttp/hello.go b/geg/net/ghttp/hello.go index 5f2f88cc2..000a026f7 100644 --- a/geg/net/ghttp/hello.go +++ b/geg/net/ghttp/hello.go @@ -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" ) diff --git a/geg/other/test.go b/geg/other/test.go index 4494b20ca..1ae2f0de4 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -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() } \ No newline at end of file