mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
ghttp.Server增加access & error log功能
This commit is contained in:
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
54
g/net/ghttp/http_server_log.go
Normal file
54
g/net/ghttp/http_server_log.go
Normal 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"
|
||||
)
|
||||
|
||||
// 处理服务错误信息,主要是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)
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
)
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
Reference in New Issue
Block a user