feat: switch glide to govendor. (#186)
* feat: switch glide to govendor. * fix: testing
This commit is contained in:
22
vendor/github.com/thoas/stats/LICENSE
generated
vendored
Normal file
22
vendor/github.com/thoas/stats/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Florent Messa
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
4
vendor/github.com/thoas/stats/Makefile
generated
vendored
Normal file
4
vendor/github.com/thoas/stats/Makefile
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
test: unit
|
||||
|
||||
unit:
|
||||
@(go test -v)
|
||||
251
vendor/github.com/thoas/stats/README.rst
generated
vendored
Normal file
251
vendor/github.com/thoas/stats/README.rst
generated
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
Go stats handler
|
||||
================
|
||||
|
||||
.. image:: https://secure.travis-ci.org/thoas/stats.png?branch=master
|
||||
:alt: Build Status
|
||||
:target: http://travis-ci.org/thoas/stats
|
||||
|
||||
stats is a ``net/http`` handler in golang reporting various metrics about
|
||||
your web application.
|
||||
|
||||
This middleware has been developed and required for the need of picfit_,
|
||||
an image resizing server written in Go.
|
||||
|
||||
Compatibility
|
||||
-------------
|
||||
|
||||
This handler supports the following frameworks at the moment:
|
||||
|
||||
* `negroni`_
|
||||
* `martini`_
|
||||
* `gocraft/web <https://github.com/gocraft/web>`_
|
||||
* `Gin <https://github.com/gin-gonic/gin>`_
|
||||
* `Goji <https://github.com/zenazn/goji>`_
|
||||
* `Beego <https://github.com/astaxie/beego>`_
|
||||
* `HTTPRouter <https://github.com/julienschmidt/httprouter>`_
|
||||
|
||||
We don't support your favorite Go framework? Send me a PR or
|
||||
create a new `issue <https://github.com/thoas/stats/issues>`_ and
|
||||
I will implement it :)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
1. Make sure you have a Go language compiler >= 1.3 (required) and git installed.
|
||||
2. Make sure you have the following go system dependencies in your $PATH: bzr, svn, hg, git
|
||||
3. Ensure your GOPATH_ is properly set.
|
||||
4. Download it:
|
||||
|
||||
::
|
||||
|
||||
go get github.com/thoas/stats
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Basic net/http
|
||||
..............
|
||||
|
||||
To use this handler directly with ``net/http``, you need to call the
|
||||
middleware with the handler itself:
|
||||
|
||||
.. code-block:: go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"github.com/thoas/stats"
|
||||
)
|
||||
|
||||
func main() {
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||
})
|
||||
|
||||
handler := stats.New().Handler(h)
|
||||
http.ListenAndServe(":8080", handler)
|
||||
}
|
||||
|
||||
Negroni
|
||||
.......
|
||||
|
||||
If you are using negroni_ you can implement the handler as
|
||||
a simple middleware in ``server.go``:
|
||||
|
||||
.. code-block:: go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"github.com/codegangsta/negroni"
|
||||
"github.com/thoas/stats"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func main() {
|
||||
middleware := stats.New()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||
})
|
||||
|
||||
mux.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
stats := middleware.Data()
|
||||
|
||||
b, _ := json.Marshal(stats)
|
||||
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
n := negroni.Classic()
|
||||
n.Use(middleware)
|
||||
n.UseHandler(mux)
|
||||
n.Run(":3000")
|
||||
}
|
||||
|
||||
HTTPRouter
|
||||
.......
|
||||
|
||||
If you are using HTTPRouter_ you need to call the middleware with the handler itself:
|
||||
|
||||
.. code-block:: go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/thoas/stats"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
router := httprouter.New()
|
||||
s := stats.New()
|
||||
router.GET("/stats", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
s, err := json.Marshal(s.Data())
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
w.Write(s)
|
||||
})
|
||||
http.ListenAndServe(":8080", s.Handler(router))
|
||||
}
|
||||
|
||||
|
||||
Martini
|
||||
.......
|
||||
|
||||
If you are using martini_, you can implement the handler as a wrapper of
|
||||
a ``Martini.Context`` in ``server.go``:
|
||||
|
||||
|
||||
.. code-block:: go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/go-martini/martini"
|
||||
"github.com/thoas/stats"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
middleware := stats.New()
|
||||
|
||||
m := martini.Classic()
|
||||
m.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||
})
|
||||
m.Get("/stats", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
stats := middleware.Data()
|
||||
|
||||
b, _ := json.Marshal(stats)
|
||||
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
m.Use(func(c martini.Context, w http.ResponseWriter, r *http.Request) {
|
||||
beginning, recorder := middleware.Begin(w)
|
||||
|
||||
c.Next()
|
||||
|
||||
middleware.End(beginning, recorder)
|
||||
})
|
||||
m.Run()
|
||||
}
|
||||
|
||||
Run it in a shell:
|
||||
|
||||
::
|
||||
|
||||
$ go run server.go
|
||||
|
||||
Then in another shell run:
|
||||
|
||||
::
|
||||
|
||||
$ curl http://localhost:3000/stats | python -m "json.tool"
|
||||
|
||||
Expect the following result:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"total_response_time": "1.907382ms",
|
||||
"average_response_time": "86.699\u00b5s",
|
||||
"average_response_time_sec": 8.6699e-05,
|
||||
"count": 1,
|
||||
"pid": 99894,
|
||||
"status_code_count": {
|
||||
"200": 1
|
||||
},
|
||||
"time": "2015-03-06 17:23:27.000677896 +0100 CET",
|
||||
"total_count": 22,
|
||||
"total_response_time_sec": 0.0019073820000000002,
|
||||
"total_status_code_count": {
|
||||
"200": 22
|
||||
},
|
||||
"unixtime": 1425659007,
|
||||
"uptime": "4m14.502271612s",
|
||||
"uptime_sec": 254.502271612
|
||||
}
|
||||
|
||||
See `examples <https://github.com/thoas/stats/blob/master/examples>`_ to
|
||||
test them.
|
||||
|
||||
|
||||
Inspiration
|
||||
-----------
|
||||
|
||||
`Antoine Imbert <https://github.com/ant0ine>`_ is the original author
|
||||
of this middleware.
|
||||
|
||||
Originally developed for `go-json-rest <https://github.com/ant0ine/go-json-rest>`_,
|
||||
it had been ported as a simple Golang handler by `Florent Messa <https://github.com/thoas>`_
|
||||
to be used in various frameworks.
|
||||
|
||||
This middleware implements a ticker which is launched every seconds to
|
||||
reset requests/sec and will implement new features in a near future :)
|
||||
|
||||
.. _GOPATH: http://golang.org/doc/code.html#GOPATH
|
||||
.. _StatusMiddleware: https://github.com/ant0ine/go-json-rest/blob/master/rest/status.go
|
||||
.. _go-json-rest: https://github.com/ant0ine/go-json-rest
|
||||
.. _negroni: https://github.com/codegangsta/negroni
|
||||
.. _martini: https://github.com/go-martini/martini
|
||||
.. _picfit: https://github.com/thoas/picfit
|
||||
.. _HTTPRouter: https://github.com/julienschmidt/httprouter
|
||||
90
vendor/github.com/thoas/stats/recorder.go
generated
vendored
Normal file
90
vendor/github.com/thoas/stats/recorder.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
// Status returns the status code of the response or 0 if the response has not been written.
|
||||
Status() int
|
||||
// Written returns whether or not the ResponseWriter has been written.
|
||||
Written() bool
|
||||
// Size returns the size of the response body.
|
||||
Size() int
|
||||
// Before allows for a function to be called before the ResponseWriter has been written to. This is
|
||||
// useful for setting headers or any other operations that must happen before a response has been written.
|
||||
Before(func(ResponseWriter))
|
||||
}
|
||||
|
||||
type beforeFunc func(ResponseWriter)
|
||||
|
||||
type recorderResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
size int
|
||||
beforeFuncs []beforeFunc
|
||||
}
|
||||
|
||||
func NewRecorderResponseWriter(w http.ResponseWriter, statusCode int) ResponseWriter {
|
||||
return &recorderResponseWriter{w, statusCode, 0, nil}
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) WriteHeader(code int) {
|
||||
r.ResponseWriter.WriteHeader(code)
|
||||
r.status = code
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Flush() {
|
||||
flusher, ok := r.ResponseWriter.(http.Flusher)
|
||||
if ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Status() int {
|
||||
return r.status
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Write(b []byte) (int, error) {
|
||||
if !r.Written() {
|
||||
// The status will be StatusOK if WriteHeader has not been called yet
|
||||
r.WriteHeader(http.StatusOK)
|
||||
}
|
||||
size, err := r.ResponseWriter.Write(b)
|
||||
r.size += size
|
||||
return size, err
|
||||
}
|
||||
|
||||
// Proxy method to Status to add support for gocraft
|
||||
func (r *recorderResponseWriter) StatusCode() int {
|
||||
return r.Status()
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Size() int {
|
||||
return r.size
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Written() bool {
|
||||
return r.StatusCode() != 0
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) CloseNotify() <-chan bool {
|
||||
return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hijacker, ok := r.ResponseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Before(before func(ResponseWriter)) {
|
||||
r.beforeFuncs = append(r.beforeFuncs, before)
|
||||
}
|
||||
168
vendor/github.com/thoas/stats/stats.go
generated
vendored
Normal file
168
vendor/github.com/thoas/stats/stats.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Stats data structure
|
||||
type Stats struct {
|
||||
mu sync.RWMutex
|
||||
Uptime time.Time
|
||||
Pid int
|
||||
ResponseCounts map[string]int
|
||||
TotalResponseCounts map[string]int
|
||||
TotalResponseTime time.Time
|
||||
}
|
||||
|
||||
// New constructs a new Stats structure
|
||||
func New() *Stats {
|
||||
stats := &Stats{
|
||||
Uptime: time.Now(),
|
||||
Pid: os.Getpid(),
|
||||
ResponseCounts: map[string]int{},
|
||||
TotalResponseCounts: map[string]int{},
|
||||
TotalResponseTime: time.Time{},
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
stats.ResetResponseCounts()
|
||||
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
}()
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
// ResetResponseCounts reset the response counts
|
||||
func (mw *Stats) ResetResponseCounts() {
|
||||
mw.mu.Lock()
|
||||
defer mw.mu.Unlock()
|
||||
mw.ResponseCounts = map[string]int{}
|
||||
}
|
||||
|
||||
// Handler is a MiddlewareFunc makes Stats implement the Middleware interface.
|
||||
func (mw *Stats) Handler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
beginning, recorder := mw.Begin(w)
|
||||
|
||||
h.ServeHTTP(recorder, r)
|
||||
|
||||
mw.End(beginning, recorder)
|
||||
})
|
||||
}
|
||||
|
||||
// Negroni compatible interface
|
||||
func (mw *Stats) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
beginning, recorder := mw.Begin(w)
|
||||
|
||||
next(recorder, r)
|
||||
|
||||
mw.End(beginning, recorder)
|
||||
}
|
||||
|
||||
// Begin starts a recorder
|
||||
func (mw *Stats) Begin(w http.ResponseWriter) (time.Time, ResponseWriter) {
|
||||
start := time.Now()
|
||||
|
||||
writer := NewRecorderResponseWriter(w, 200)
|
||||
|
||||
return start, writer
|
||||
}
|
||||
|
||||
// EndWithStatus closes the recorder with a specific status
|
||||
func (mw *Stats) EndWithStatus(start time.Time, status int) {
|
||||
end := time.Now()
|
||||
|
||||
responseTime := end.Sub(start)
|
||||
|
||||
mw.mu.Lock()
|
||||
|
||||
defer mw.mu.Unlock()
|
||||
|
||||
statusCode := fmt.Sprintf("%d", status)
|
||||
|
||||
mw.ResponseCounts[statusCode]++
|
||||
mw.TotalResponseCounts[statusCode]++
|
||||
mw.TotalResponseTime = mw.TotalResponseTime.Add(responseTime)
|
||||
}
|
||||
|
||||
// End closes the recorder with the recorder status
|
||||
func (mw *Stats) End(start time.Time, recorder ResponseWriter) {
|
||||
mw.EndWithStatus(start, recorder.Status())
|
||||
}
|
||||
|
||||
// Data serializable structure
|
||||
type Data struct {
|
||||
Pid int `json:"pid"`
|
||||
UpTime string `json:"uptime"`
|
||||
UpTimeSec float64 `json:"uptime_sec"`
|
||||
Time string `json:"time"`
|
||||
TimeUnix int64 `json:"unixtime"`
|
||||
StatusCodeCount map[string]int `json:"status_code_count"`
|
||||
TotalStatusCodeCount map[string]int `json:"total_status_code_count"`
|
||||
Count int `json:"count"`
|
||||
TotalCount int `json:"total_count"`
|
||||
TotalResponseTime string `json:"total_response_time"`
|
||||
TotalResponseTimeSec float64 `json:"total_response_time_sec"`
|
||||
AverageResponseTime string `json:"average_response_time"`
|
||||
AverageResponseTimeSec float64 `json:"average_response_time_sec"`
|
||||
}
|
||||
|
||||
// Data returns the data serializable structure
|
||||
func (mw *Stats) Data() *Data {
|
||||
|
||||
mw.mu.RLock()
|
||||
|
||||
responseCounts := make(map[string]int, len(mw.ResponseCounts))
|
||||
totalResponseCounts := make(map[string]int, len(mw.TotalResponseCounts))
|
||||
|
||||
now := time.Now()
|
||||
|
||||
uptime := now.Sub(mw.Uptime)
|
||||
|
||||
count := 0
|
||||
for code, current := range mw.ResponseCounts {
|
||||
responseCounts[code] = current
|
||||
count += current
|
||||
}
|
||||
|
||||
totalCount := 0
|
||||
for code, count := range mw.TotalResponseCounts {
|
||||
totalResponseCounts[code] = count
|
||||
totalCount += count
|
||||
}
|
||||
|
||||
totalResponseTime := mw.TotalResponseTime.Sub(time.Time{})
|
||||
|
||||
averageResponseTime := time.Duration(0)
|
||||
if totalCount > 0 {
|
||||
avgNs := int64(totalResponseTime) / int64(totalCount)
|
||||
averageResponseTime = time.Duration(avgNs)
|
||||
}
|
||||
|
||||
mw.mu.RUnlock()
|
||||
|
||||
r := &Data{
|
||||
Pid: mw.Pid,
|
||||
UpTime: uptime.String(),
|
||||
UpTimeSec: uptime.Seconds(),
|
||||
Time: now.String(),
|
||||
TimeUnix: now.Unix(),
|
||||
StatusCodeCount: responseCounts,
|
||||
TotalStatusCodeCount: totalResponseCounts,
|
||||
Count: count,
|
||||
TotalCount: totalCount,
|
||||
TotalResponseTime: totalResponseTime.String(),
|
||||
TotalResponseTimeSec: totalResponseTime.Seconds(),
|
||||
AverageResponseTime: averageResponseTime.String(),
|
||||
AverageResponseTimeSec: averageResponseTime.Seconds(),
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
Reference in New Issue
Block a user