Skip to content

Commit

Permalink
add abcsessions, abcrender, abcmiddleware
Browse files Browse the repository at this point in the history
  • Loading branch information
nullbio committed May 23, 2017
1 parent b0a8fcc commit 98cf752
Show file tree
Hide file tree
Showing 26 changed files with 4,223 additions and 0 deletions.
27 changes: 27 additions & 0 deletions abcmiddleware/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2016 The ABCWeb Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of VolatileTech nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12 changes: 12 additions & 0 deletions abcmiddleware/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# ABCMiddleware

[![License](https://img.shields.io/badge/license-BSD-blue.svg)](https://github.com/volatiletech/abcmiddleware/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/volatiletech/abcmiddleware?status.svg)](https://godoc.org/github.com/volatiletech/abcmiddleware)
[![Go Report Card](https://goreportcard.com/badge/volatiletech/abcmiddleware)](http://goreportcard.com/report/volatiletech/abcmiddleware)

## Available Middleware

* Zap - Zap middleware handles web request logging using Zap
* Recover - Recover middleware recovers panics that occur and gracefully logs their error

See GoDoc for API usage.
103 changes: 103 additions & 0 deletions abcmiddleware/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package abcmiddleware

import (
"context"
"fmt"
"net/http"
"time"

chimiddleware "github.com/pressly/chi/middleware"
"go.uber.org/zap"
)

// zapResponseWriter is a wrapper that includes that http status and size for logging
type zapResponseWriter struct {
http.ResponseWriter
status int
size int
}

// Zap middleware handles web request logging using Zap
func (m Middleware) Zap(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
zw := &zapResponseWriter{ResponseWriter: w}

// Serve the request
next.ServeHTTP(zw, r)

// Write the request log line
writeZap(m.Log, r, t, zw.status, zw.size)
}

return http.HandlerFunc(fn)
}

// RequestIDLogger middleware creates a derived logger to include logging of the
// Request ID, and inserts it into the context object
func (m Middleware) RequestIDLogger(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
requestID := chimiddleware.GetReqID(r.Context())
derivedLogger := m.Log.With(zap.String("request_id", requestID))
r = r.WithContext(context.WithValue(r.Context(), CtxLoggerKey, derivedLogger))
next.ServeHTTP(w, r)
}

return http.HandlerFunc(fn)
}

// Log returns the Request ID scoped logger from the request Context
// and panics if it cannot be found. This function is only ever used
// by your controllers if your app uses the RequestID middlewares,
// otherwise you should use the controller's receiver logger directly.
func Log(r *http.Request) *zap.Logger {
v := r.Context().Value(CtxLoggerKey)
log, ok := v.(*zap.Logger)
if !ok {
panic("cannot get derived request id logger from context object")
}
return log
}

func writeZap(log *zap.Logger, r *http.Request, t time.Time, status int, size int) {
elapsed := time.Now().Sub(t)
var protocol string
if r.TLS == nil {
protocol = "http"
} else {
protocol = "https"
}

v := r.Context().Value(CtxLoggerKey)
if v != nil {
var ok bool
log, ok = v.(*zap.Logger)
if !ok {
panic("cannot get derived request id logger from context object")
}
}

// log all the fields
log.Info(fmt.Sprintf("%s request", protocol),
zap.Int("status", status),
zap.String("method", r.Method),
zap.String("uri", r.RequestURI),
zap.Bool("tls", r.TLS != nil),
zap.String("protocol", r.Proto),
zap.String("host", r.Host),
zap.String("remote_addr", r.RemoteAddr),
zap.Int("size", size),
zap.Duration("elapsed", elapsed),
)
}

func (z *zapResponseWriter) WriteHeader(code int) {
z.status = code
z.ResponseWriter.WriteHeader(code)
}

func (z *zapResponseWriter) Write(b []byte) (int, error) {
size, err := z.ResponseWriter.Write(b)
z.size += size
return size, err
}
27 changes: 27 additions & 0 deletions abcmiddleware/log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package abcmiddleware

import (
"context"
"net/http"
"testing"

"go.uber.org/zap"
)

func TestLog(t *testing.T) {
t.Parallel()

ctx := context.Background()
z, err := zap.NewDevelopment()
if err != nil {
t.Error(err)
}

ctx = context.WithValue(ctx, CtxLoggerKey, z)
r := &http.Request{}
r = r.WithContext(ctx)

// Ensure log can be called successfully. Ignore response because we don't
// need to validate anything.
_ = Log(r)
}
13 changes: 13 additions & 0 deletions abcmiddleware/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package abcmiddleware

import "go.uber.org/zap"

// CtxLoggerKey is the http.Request Context lookup key for the request ID logger
const CtxLoggerKey = "request_id_logger"

// Middleware exposes useful variables to every abcmiddleware handler
type Middleware struct {
// Log is used for logging in your middleware and to
// create a derived logger that includes the request ID.
Log *zap.Logger
}
63 changes: 63 additions & 0 deletions abcmiddleware/recover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package abcmiddleware

import (
"fmt"
"net/http"

"go.uber.org/zap"
)

// Recover middleware recovers panics that occur and gracefully logs their error
func (m Middleware) Recover(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
var protocol string
if r.TLS == nil {
protocol = "http"
} else {
protocol = "https"
}

var log *zap.Logger
v := r.Context().Value(CtxLoggerKey)
if v != nil {
var ok bool
log, ok = v.(*zap.Logger)
if !ok {
panic("cannot get derived request id logger from context object")
}
// log with the request_id scoped logger
log.Error(fmt.Sprintf("%s request error", protocol),
zap.String("method", r.Method),
zap.String("uri", r.RequestURI),
zap.Bool("tls", r.TLS != nil),
zap.String("protocol", r.Proto),
zap.String("host", r.Host),
zap.String("remote_addr", r.RemoteAddr),
zap.String("error", fmt.Sprintf("%+v", err)),
)
} else {
// log with the logger attached to middleware struct if
// cannot find request_id scoped logger
m.Log.Error(fmt.Sprintf("%s request error", protocol),
zap.String("method", r.Method),
zap.String("uri", r.RequestURI),
zap.Bool("tls", r.TLS != nil),
zap.String("protocol", r.Proto),
zap.String("host", r.Host),
zap.String("remote_addr", r.RemoteAddr),
zap.String("error", fmt.Sprintf("%+v", err)),
)
}

// Return a http 500 with the HTTP body of "Internal Server Error"
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}()

next.ServeHTTP(w, r)
}

return http.HandlerFunc(fn)
}
27 changes: 27 additions & 0 deletions abcrender/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2016 The ABCWeb Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of VolatileTech nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 changes: 45 additions & 0 deletions abcrender/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package abcrender

import (
"io"

"github.com/unrolled/render"
)

// Renderer implements template rendering methods.
// If you'd like to create a renderer that uses a different rendering engine
// opposed to standard text/templates or html/templates you can do so by
// implementing this interface.
type Renderer interface {
Data(w io.Writer, status int, v []byte) error
JSON(w io.Writer, status int, v interface{}) error
Text(w io.Writer, status int, v string) error
// HTML renders a HTML template. Example:
// Assumes you have a template in ./templates called "home.tmpl"
// $ mkdir -p templates && echo "<h1>Hello {{.}}</h1>" > templates/home.tmpl
// HTML(w, http.StatusOK, "home", "World")
HTML(w io.Writer, status int, name string, binding interface{}) error
// HTMLWithLayout renders a HTML template using a different layout to the
// one specified in your renderer's configuration. Example:
// Example: HTMLWithLayout(w, http.StatusOK, "home", "World", "layout")
HTMLWithLayout(w io.Writer, status int, name string, binding interface{}, layout string) error
}

// Render implements the HTML and HTMLWithLayout functions on the Renderer
// interface and imbeds the unrolled Render type to satisfy the rest of the interface.
// The custom HTML/HTMLWithLayout implementation is required due to the Render
// HTML function having a package-specific type for the layout string (Render.HTMLOptions)
type Render struct {
*render.Render
}

// HTML renders a HTML template by calling unrolled Render package's HTML function
func (r *Render) HTML(w io.Writer, status int, name string, binding interface{}) error {
return r.Render.HTML(w, status, name, binding)
}

// HTMLWithLayout renders a HTML template using a specified layout file by calling
// unrolled Render package's HTML function with a HTMLOptions argument
func (r *Render) HTMLWithLayout(w io.Writer, status int, name string, binding interface{}, layout string) error {
return r.Render.HTML(w, status, name, binding, render.HTMLOptions{Layout: layout})
}
27 changes: 27 additions & 0 deletions abcsessions/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2016 The ABCWeb Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of VolatileTech nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Loading

0 comments on commit 98cf752

Please sign in to comment.