Skip to content

Commit

Permalink
Merge pull request #84 for v0.7 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm authored Aug 1, 2017
2 parents 981ec7a + 971e067 commit 34d34a8
Show file tree
Hide file tree
Showing 34 changed files with 1,663 additions and 382 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ go:

go_import_path: aahframework.org/aah.v0

install:
- git config --global http.https://aahframework.org.followRedirects true
- go get -t -v ./...

script:
- bash <(curl -s https://aahframework.org/go-test)

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# aah web framework for Go
[![Build Status](https://travis-ci.org/go-aah/aah.svg?branch=master)](https://travis-ci.org/go-aah/aah) [![codecov](https://codecov.io/gh/go-aah/aah/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/aah/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/aah.v0)](https://goreportcard.com/report/aahframework.org/aah.v0)
[![Powered by Go](https://img.shields.io/badge/powered_by-go-blue.svg)](https://golang.org)
[![Version](https://img.shields.io/badge/version-0.6-blue.svg)](https://github.com/go-aah/aah/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/aah.v0?status.svg)](https://godoc.org/aahframework.org/aah.v0)
[![License](https://img.shields.io/github/license/go-aah/aah.svg)](LICENSE)
[![Version](https://img.shields.io/badge/version-0.7-blue.svg)](https://github.com/go-aah/aah/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/aah.v0?status.svg)](https://godoc.org/aahframework.org/aah.v0)
[![License](https://img.shields.io/github/license/go-aah/aah.svg)](LICENSE) [![Twitter](https://img.shields.io/badge/[email protected])](https://twitter.com/aahframework)

***Release [v0.6](https://github.com/go-aah/aah/releases/latest) tagged on Jun 07, 2017***
***Release [v0.7](https://github.com/go-aah/aah/releases/latest) tagged on Aug 01, 2017***

aah framework - A scalable, performant, rapid development Web framework for Go.

Expand Down
25 changes: 11 additions & 14 deletions aah.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
)

// Version no. of aah framework
const Version = "0.6"
const Version = "0.7"

// aah application variables
var (
Expand Down Expand Up @@ -49,6 +49,7 @@ var (
appDefaultHTTPPort = "8080"
appDefaultDateFormat = "2006-01-02"
appDefaultDateTimeFormat = "2006-01-02 15:04:05"
appLogFatal = log.Fatal

goPath string
goSrcDir string
Expand All @@ -63,7 +64,7 @@ type BuildInfo struct {
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Global methods
// Package methods
//___________________________________

// AppName method returns aah application name from app config otherwise app name
Expand Down Expand Up @@ -105,16 +106,9 @@ func AppHTTPAddress() string {
// AppHTTPPort method returns aah application HTTP port number based on `server.port`
// value. Possible outcomes are user-defined port, `80`, `443` and `8080`.
func AppHTTPPort() string {
port := AppConfig().StringDefault("server.port", appDefaultHTTPPort)
if !ess.IsStrEmpty(port) {
return port
}

if AppIsSSLEnabled() {
return "443"
}

return "80"
port := firstNonEmpty(AppConfig().StringDefault("server.proxyport", ""),
AppConfig().StringDefault("server.port", appDefaultHTTPPort))
return parsePort(port)
}

// AppDateFormat method returns aah application date format
Expand Down Expand Up @@ -200,8 +194,11 @@ func Init(importPath string) {
logAsFatal(initLogs(appLogsDir(), AppConfig()))
logAsFatal(initI18n(appI18nDir()))
logAsFatal(initRoutes(appConfigDir(), AppConfig()))
logAsFatal(initSecurity(appConfigDir(), AppConfig()))
logAsFatal(initSecurity(AppConfig()))
logAsFatal(initViewEngine(appViewsDir(), AppConfig()))
if AppConfig().BoolDefault("server.access_log.enable", false) {
logAsFatal(initAccessLog(appLogsDir(), AppConfig()))
}
}

appInitialized = true
Expand All @@ -228,7 +225,7 @@ func appLogsDir() string {

func logAsFatal(err error) {
if err != nil {
log.Fatal(err)
appLogFatal(err)
}
}

Expand Down
30 changes: 30 additions & 0 deletions aah_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package aah

import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
Expand Down Expand Up @@ -172,6 +173,35 @@ func TestAahLogDir(t *testing.T) {
cfg, _ := config.ParseString("")
logger, _ := log.New(cfg)
log.SetDefaultLogger(logger)

// relative path filename
cfgRelativeFile, _ := config.ParseString(`
log {
receiver = "file"
file = "my-test-file.log"
}
`)
err = initLogs(logsDir, cfgRelativeFile)
assert.Nil(t, err)

// no filename mentioned
cfgNoFile, _ := config.ParseString(`
log {
receiver = "file"
}
`)
SetAppBuildInfo(&BuildInfo{
BinaryName: "testapp",
Date: time.Now().Format(time.RFC3339),
Version: "1.0.0",
})
err = initLogs(logsDir, cfgNoFile)
assert.Nil(t, err)
appBuildInfo = nil

appLogFatal = func(v ...interface{}) { fmt.Println(v) }
logAsFatal(errors.New("test msg"))

}

func TestWritePID(t *testing.T) {
Expand Down
240 changes: 240 additions & 0 deletions access_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm)
// go-aah/aah source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package aah

import (
"fmt"
"net/http"
"path/filepath"
"strings"
"sync"
"time"

"aahframework.org/ahttp.v0"
"aahframework.org/config.v0"
"aahframework.org/essentials.v0"
"aahframework.org/log.v0"
)

const (
fmtFlagClientIP ess.FmtFlag = iota
fmtFlagRequestTime
fmtFlagRequestURL
fmtFlagRequestMethod
fmtFlagRequestProto
fmtFlagRequestID
fmtFlagRequestHeader
fmtFlagQueryString
fmtFlagResponseStatus
fmtFlagResponseSize
fmtFlagResponseHeader
fmtFlagResponseTime
fmtFlagCustom
)

var (
accessLogFmtFlags = map[string]ess.FmtFlag{
"clientip": fmtFlagClientIP,
"reqtime": fmtFlagRequestTime,
"requrl": fmtFlagRequestURL,
"reqmethod": fmtFlagRequestMethod,
"reqproto": fmtFlagRequestProto,
"reqid": fmtFlagRequestID,
"reqhdr": fmtFlagRequestHeader,
"querystr": fmtFlagQueryString,
"resstatus": fmtFlagResponseStatus,
"ressize": fmtFlagResponseSize,
"reshdr": fmtFlagResponseHeader,
"restime": fmtFlagResponseTime,
"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{} }}
)

type (
//accessLog contains data about the current request
accessLog struct {
StartTime time.Time
ElapsedDuration time.Duration
Request *ahttp.Request
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
//___________________________________

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

cfg.SetString("log.receiver", "file")
if ess.IsStrEmpty(file) {
cfg.SetString("log.file", filepath.Join(logsDir, getBinaryFileName()+"-access.log"))
} else {
abspath, err := filepath.Abs(file)
if err != nil {
return err
}
cfg.SetString("log.file", abspath)
}

cfg.SetString("log.pattern", "%message")

var err error

// initialize request access log file
appAccessLog, err = log.New(cfg)
if err != nil {
return err
}

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

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

appReqIDHdrKey = cfg.StringDefault("request.id.header", ahttp.HeaderXRequestID)

return nil
}

func listenForAccessLog() {
for {
appAccessLog.Print(accessLogFormatter(<-appAccessLogChan))
}
}

func sendToAccessLog(ctx *Context) {
al := acquireAccessLog()
al.StartTime = ctx.values[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.ResStatus = ctx.Res.Status()
al.ResBytes = ctx.Res.BytesWritten()
al.ResHdr = ctx.Res.Header()

appAccessLogChan <- al
}

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

for _, part := range appAccessLogFmtFlags {
switch part.Flag {
case fmtFlagClientIP:
buf.WriteString(al.Request.ClientIP)
case fmtFlagRequestTime:
buf.WriteString(al.FmtRequestTime(part.Format))
case fmtFlagRequestURL:
buf.WriteString(al.Request.Path)
case fmtFlagRequestMethod:
buf.WriteString(al.Request.Method)
case fmtFlagRequestProto:
buf.WriteString(al.Request.Unwrap().Proto)
case fmtFlagRequestID:
buf.WriteString(al.GetRequestHdr(appReqIDHdrKey))
case fmtFlagRequestHeader:
buf.WriteString(al.GetRequestHdr(part.Format))
case fmtFlagQueryString:
buf.WriteString(al.GetQueryString())
case fmtFlagResponseStatus:
buf.WriteString(fmt.Sprintf(part.Format, al.ResStatus))
case fmtFlagResponseSize:
buf.WriteString(fmt.Sprintf(part.Format, al.ResBytes))
case fmtFlagResponseHeader:
buf.WriteString(al.GetResponseHdr(part.Format))
case fmtFlagResponseTime:
buf.WriteString(fmt.Sprintf("%.4f", al.ElapsedDuration.Seconds()*1e3))
case fmtFlagCustom:
buf.WriteString(part.Format)
}
buf.WriteByte(' ')
}
return strings.TrimSpace(buf.String())
}

func acquireAccessLog() *accessLog {
return accessLogPool.Get().(*accessLog)
}

func releaseAccessLog(al *accessLog) {
if al != nil {
al.Reset()
accessLogPool.Put(al)
}
}
Loading

0 comments on commit 34d34a8

Please sign in to comment.