Skip to content

Commit

Permalink
go-aah#157 Code, request and response optimization (go-aah#166)
Browse files Browse the repository at this point in the history
This PR creates many opportunities for framework future.
  • Loading branch information
jeevatkm authored Apr 12, 2018
1 parent 745f7a4 commit df6a802
Show file tree
Hide file tree
Showing 74 changed files with 5,681 additions and 5,021 deletions.
580 changes: 401 additions & 179 deletions aah.go

Large diffs are not rendered by default.

665 changes: 500 additions & 165 deletions aah_test.go

Large diffs are not rendered by default.

208 changes: 97 additions & 111 deletions access_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,86 +51,22 @@ var (
"custom": fmtFlagCustom,
}

appDefaultAccessLogPattern = "%clientip %custom:- %reqtime %reqmethod %requrl %reqproto %resstatus %ressize %restime %reqhdr:referer"
appReqStartTimeKey = "_appReqStartTimeKey"
appReqIDHdrKey = ahttp.HeaderXRequestID
appAccessLog *log.Logger
appAccessLogFmtFlags []ess.FmtFlagPart
appAccessLogChan chan *accessLog
accessLogPool = &sync.Pool{New: func() interface{} { return &accessLog{} }}
defaultAccessLogPattern = "%clientip %custom:- %reqtime %reqmethod %requrl %reqproto %resstatus %ressize %restime %reqhdr:referer"
reqStartTimeKey = "_appReqStartTimeKey"
)

type (
//accessLog contains data about the current request
accessLog struct {
StartTime time.Time
ElapsedDuration time.Duration
Request *ahttp.Request
RequestID string
ResStatus int
ResBytes int
ResHdr http.Header
}
)

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// accessLog methods
//___________________________________

// FmtRequestTime method returns the formatted request time. There are three
// possibilities to handle, `%reqtime`, `%reqtime:` and `%reqtime:<format>`.
func (al *accessLog) FmtRequestTime(format string) string {
if format == "%v" || ess.IsStrEmpty(format) {
return al.StartTime.Format(time.RFC3339)
}
return al.StartTime.Format(format)
}

func (al *accessLog) GetRequestHdr(hdrKey string) string {
hdrValues := al.Request.Header[http.CanonicalHeaderKey(hdrKey)]
if len(hdrValues) == 0 {
return "-"
}
return `"` + strings.Join(hdrValues, ", ") + `"`
}

func (al *accessLog) GetResponseHdr(hdrKey string) string {
hdrValues := al.ResHdr[http.CanonicalHeaderKey(hdrKey)]
if len(hdrValues) == 0 {
return "-"
}
return `"` + strings.Join(hdrValues, ", ") + `"`
}

func (al *accessLog) GetQueryString() string {
queryStr := al.Request.Raw.URL.Query().Encode()
if ess.IsStrEmpty(queryStr) {
return "-"
}
return `"` + queryStr + `"`
}

func (al *accessLog) Reset() {
al.StartTime = time.Time{}
al.ElapsedDuration = 0
al.Request = nil
al.ResStatus = 0
al.ResBytes = 0
al.ResHdr = nil
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Unexported methods
//___________________________________
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// app Unexported methods
//______________________________________________________________________________

func initAccessLog(logsDir string, appCfg *config.Config) error {
func (a *app) initAccessLog() error {
// log file configuration
cfg, _ := config.ParseString("")
file := appCfg.StringDefault("server.access_log.file", "")
file := a.Config().StringDefault("server.access_log.file", "")

cfg.SetString("log.receiver", "file")
if ess.IsStrEmpty(file) {
cfg.SetString("log.file", filepath.Join(logsDir, getBinaryFileName()+"-access.log"))
cfg.SetString("log.file", filepath.Join(a.logsDir(), a.binaryFilename()+"-access.log"))
} else {
abspath, err := filepath.Abs(file)
if err != nil {
Expand All @@ -146,61 +82,60 @@ func initAccessLog(logsDir string, appCfg *config.Config) error {
if err != nil {
return err
}
appAccessLog = aaLog

aaLogger := &accessLogger{
a: a,
e: a.engine,
logger: aaLog,
logPool: &sync.Pool{New: func() interface{} { return new(accessLog) }},
}

// parse request access log pattern
pattern := appCfg.StringDefault("server.access_log.pattern", appDefaultAccessLogPattern)
pattern := a.Config().StringDefault("server.access_log.pattern", defaultAccessLogPattern)
aaLogFmtFlags, err := ess.ParseFmtFlag(pattern, accessLogFmtFlags)
if err != nil {
return err
}
appAccessLogFmtFlags = aaLogFmtFlags
aaLogger.fmtFlags = aaLogFmtFlags

// initialize request access log channel
if appAccessLogChan == nil {
appAccessLogChan = make(chan *accessLog, cfg.IntDefault("server.access_log.channel_buffer_size", 500))
go listenForAccessLog()
}
aaLogger.logChan = make(chan *accessLog, a.Config().IntDefault("server.access_log.channel_buffer_size", 500))

appReqIDHdrKey = cfg.StringDefault("request.id.header", ahttp.HeaderXRequestID)
a.accessLog = aaLogger
go a.accessLog.listenToLogChan()

return nil
}

func listenForAccessLog() {
for {
al := <-appAccessLogChan
appAccessLog.Print(accessLogFormatter(al))
}
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// accessLogger
//______________________________________________________________________________

type accessLogger struct {
a *app
e *engine
logger *log.Logger
fmtFlags []ess.FmtFlagPart
logChan chan *accessLog
logPool *sync.Pool
}

func sendToAccessLog(ctx *Context) {
al := acquireAccessLog()
al.StartTime = ctx.Get(appReqStartTimeKey).(time.Time)

// All the bytes have been written on the wire
// so calculate elapsed time
al.ElapsedDuration = time.Since(al.StartTime)

req := *ctx.Req
al.Request = &req
al.RequestID = firstNonZeroString(req.Header.Get(appReqIDHdrKey), "-")
al.ResStatus = ctx.Res.Status()
al.ResBytes = ctx.Res.BytesWritten()
al.ResHdr = ctx.Res.Header()

appAccessLogChan <- al
func (aal *accessLogger) listenToLogChan() {
for {
al := <-aal.logChan
aal.logger.Print(aal.accessLogFormatter(al))
}
}

func accessLogFormatter(al *accessLog) string {
defer releaseAccessLog(al)
func (aal *accessLogger) accessLogFormatter(al *accessLog) string {
defer aal.releaseAccessLog(al)
buf := acquireBuffer()
defer releaseBuffer(buf)

for _, part := range appAccessLogFmtFlags {
for _, part := range aal.fmtFlags {
switch part.Flag {
case fmtFlagClientIP:
buf.WriteString(al.Request.ClientIP)
buf.WriteString(al.Request.ClientIP())
case fmtFlagRequestTime:
buf.WriteString(al.FmtRequestTime(part.Format))
case fmtFlagRequestURL:
Expand Down Expand Up @@ -231,13 +166,64 @@ func accessLogFormatter(al *accessLog) string {
return strings.TrimSpace(buf.String())
}

func acquireAccessLog() *accessLog {
return accessLogPool.Get().(*accessLog)
func (aal *accessLogger) releaseAccessLog(al *accessLog) {
al.Reset()
aal.logPool.Put(al)
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// accessLog
//______________________________________________________________________________

//accessLog contains data about the current request
type accessLog struct {
StartTime time.Time
ElapsedDuration time.Duration
Request *ahttp.Request
RequestID string
ResStatus int
ResBytes int
ResHdr http.Header
}

// FmtRequestTime method returns the formatted request time. There are three
// possibilities to handle, `%reqtime`, `%reqtime:` and `%reqtime:<format>`.
func (al *accessLog) FmtRequestTime(format string) string {
if format == "%v" || ess.IsStrEmpty(format) {
return al.StartTime.Format(time.RFC3339)
}
return al.StartTime.Format(format)
}

func releaseAccessLog(al *accessLog) {
if al != nil {
al.Reset()
accessLogPool.Put(al)
func (al *accessLog) GetRequestHdr(hdrKey string) string {
hdrValues := al.Request.Header[http.CanonicalHeaderKey(hdrKey)]
if len(hdrValues) == 0 {
return "-"
}
return `"` + strings.Join(hdrValues, ", ") + `"`
}

func (al *accessLog) GetResponseHdr(hdrKey string) string {
hdrValues := al.ResHdr[http.CanonicalHeaderKey(hdrKey)]
if len(hdrValues) == 0 {
return "-"
}
return `"` + strings.Join(hdrValues, ", ") + `"`
}

func (al *accessLog) GetQueryString() string {
queryStr := al.Request.URL().Query().Encode()
if ess.IsStrEmpty(queryStr) {
return "-"
}
return `"` + queryStr + `"`
}

func (al *accessLog) Reset() {
al.StartTime = time.Time{}
al.ElapsedDuration = 0
al.Request = nil
al.ResStatus = 0
al.ResBytes = 0
al.ResHdr = nil
}
Loading

0 comments on commit df6a802

Please sign in to comment.