mirror of https://github.com/gin-gonic/gin.git
Merge branch 'master' into features/form-binding-time-improve
This commit is contained in:
commit
9c168e578a
|
@ -7,6 +7,7 @@ go:
|
|||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- master
|
||||
|
||||
matrix:
|
||||
|
@ -14,6 +15,8 @@ matrix:
|
|||
include:
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=on
|
||||
- go: 1.12.x
|
||||
env: GO111MODULE=on
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
|
5
Makefile
5
Makefile
|
@ -21,7 +21,10 @@ test:
|
|||
exit 1; \
|
||||
elif grep -q "build failed" tmp.out; then \
|
||||
rm tmp.out; \
|
||||
exit; \
|
||||
exit 1; \
|
||||
elif grep -q "setup failed" tmp.out; then \
|
||||
rm tmp.out; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
if [ -f profile.out ]; then \
|
||||
cat profile.out | grep -v "mode:" >> coverage.out; \
|
||||
|
|
56
README.md
56
README.md
|
@ -215,9 +215,6 @@ $ go build -tags=jsoniter .
|
|||
|
||||
```go
|
||||
func main() {
|
||||
// Disable Console Color
|
||||
// gin.DisableConsoleColor()
|
||||
|
||||
// Creates a gin router with default middleware:
|
||||
// logger and recovery (crash-free) middleware
|
||||
router := gin.Default()
|
||||
|
@ -570,6 +567,48 @@ func main() {
|
|||
::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "
|
||||
```
|
||||
|
||||
### Controlling Log output coloring
|
||||
|
||||
By default, logs output on console should be colorized depending on the detected TTY.
|
||||
|
||||
Never colorize logs:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// Disable log's color
|
||||
gin.DisableConsoleColor()
|
||||
|
||||
// Creates a gin router with default middleware:
|
||||
// logger and recovery (crash-free) middleware
|
||||
router := gin.Default()
|
||||
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong")
|
||||
})
|
||||
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
Always colorize logs:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// Force log's color
|
||||
gin.ForceConsoleColor()
|
||||
|
||||
// Creates a gin router with default middleware:
|
||||
// logger and recovery (crash-free) middleware
|
||||
router := gin.Default()
|
||||
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong")
|
||||
})
|
||||
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
### Model binding and validation
|
||||
|
||||
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
|
||||
|
@ -1633,6 +1672,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -1660,7 +1700,10 @@ func main() {
|
|||
// Wait for interrupt signal to gracefully shutdown the server with
|
||||
// a timeout of 5 seconds.
|
||||
quit := make(chan os.Signal)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
// kill (no param) default send syscanll.SIGTERM
|
||||
// kill -2 is syscall.SIGINT
|
||||
// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
log.Println("Shutdown Server ...")
|
||||
|
||||
|
@ -1669,6 +1712,11 @@ func main() {
|
|||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Fatal("Server Shutdown:", err)
|
||||
}
|
||||
// catching ctx.Done(). timeout of 5 seconds.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("timeout of 5 seconds.")
|
||||
}
|
||||
log.Println("Server exiting")
|
||||
}
|
||||
```
|
||||
|
|
|
@ -61,6 +61,10 @@ type FooStructForMapType struct {
|
|||
MapFoo map[string]interface{} `form:"map_foo"`
|
||||
}
|
||||
|
||||
type FooStructForIgnoreFormTag struct {
|
||||
Foo *string `form:"-"`
|
||||
}
|
||||
|
||||
type InvalidNameType struct {
|
||||
TestName string `invalid_name:"test_name"`
|
||||
}
|
||||
|
@ -278,6 +282,12 @@ func TestBindingFormForTime2(t *testing.T) {
|
|||
"", "")
|
||||
}
|
||||
|
||||
func TestFormBindingIgnoreField(t *testing.T) {
|
||||
testFormBindingIgnoreField(t, "POST",
|
||||
"/", "/",
|
||||
"-=bar", "")
|
||||
}
|
||||
|
||||
func TestBindingFormInvalidName(t *testing.T) {
|
||||
testFormBindingInvalidName(t, "POST",
|
||||
"/", "/",
|
||||
|
@ -860,6 +870,21 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBody string) {
|
||||
b := Form
|
||||
assert.Equal(t, "form", b.Name())
|
||||
|
||||
obj := FooStructForIgnoreFormTag{}
|
||||
req := requestWithBody(method, path, body)
|
||||
if method == "POST" {
|
||||
req.Header.Add("Content-Type", MIMEPOSTForm)
|
||||
}
|
||||
err := b.Bind(req, &obj)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Nil(t, obj.Foo)
|
||||
}
|
||||
|
||||
func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBody string) {
|
||||
b := Form
|
||||
assert.Equal(t, "form", b.Name())
|
||||
|
|
|
@ -41,6 +41,9 @@ func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
|
|||
defaultValue = defaultList[1]
|
||||
}
|
||||
}
|
||||
if inputFieldName == "-" {
|
||||
continue
|
||||
}
|
||||
if inputFieldName == "" {
|
||||
inputFieldName = typeField.Name
|
||||
|
||||
|
|
14
context.go
14
context.go
|
@ -82,6 +82,10 @@ func (c *Context) Copy() *Context {
|
|||
cp.Writer = &cp.writermem
|
||||
cp.index = abortIndex
|
||||
cp.handlers = nil
|
||||
cp.Keys = map[string]interface{}{}
|
||||
for k, v := range c.Keys {
|
||||
cp.Keys[k] = v
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
|
@ -91,6 +95,16 @@ func (c *Context) HandlerName() string {
|
|||
return nameOfFunction(c.handlers.Last())
|
||||
}
|
||||
|
||||
// HandlerNames returns a list of all registered handlers for this context in descending order,
|
||||
// following the semantics of HandlerName()
|
||||
func (c *Context) HandlerNames() []string {
|
||||
hn := make([]string, 0, len(c.handlers))
|
||||
for _, val := range c.handlers {
|
||||
hn = append(hn, nameOfFunction(val))
|
||||
}
|
||||
return hn
|
||||
}
|
||||
|
||||
// Handler returns the main handler.
|
||||
func (c *Context) Handler() HandlerFunc {
|
||||
return c.handlers.Last()
|
||||
|
|
|
@ -331,6 +331,8 @@ func TestContextCopy(t *testing.T) {
|
|||
assert.Equal(t, cp.Keys, c.Keys)
|
||||
assert.Equal(t, cp.engine, c.engine)
|
||||
assert.Equal(t, cp.Params, c.Params)
|
||||
cp.Set("foo", "notBar")
|
||||
assert.False(t, cp.Keys["foo"] == c.Keys["foo"])
|
||||
}
|
||||
|
||||
func TestContextHandlerName(t *testing.T) {
|
||||
|
@ -340,10 +342,26 @@ func TestContextHandlerName(t *testing.T) {
|
|||
assert.Regexp(t, "^(.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest$", c.HandlerName())
|
||||
}
|
||||
|
||||
func TestContextHandlerNames(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest, func(c *Context) {}, handlerNameTest2}
|
||||
|
||||
names := c.HandlerNames()
|
||||
|
||||
assert.True(t, len(names) == 4)
|
||||
for _, name := range names {
|
||||
assert.Regexp(t, `^(.*/vendor/)?(github\.com/gin-gonic/gin\.){1}(TestContextHandlerNames\.func.*){0,1}(handlerNameTest.*){0,1}`, name)
|
||||
}
|
||||
}
|
||||
|
||||
func handlerNameTest(c *Context) {
|
||||
|
||||
}
|
||||
|
||||
func handlerNameTest2(c *Context) {
|
||||
|
||||
}
|
||||
|
||||
var handlerTest HandlerFunc = func(c *Context) {
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ func TestDebugPrint(t *testing.T) {
|
|||
SetMode(TestMode)
|
||||
debugPrint("DEBUG this!")
|
||||
SetMode(DebugMode)
|
||||
debugPrint("these are %d %s\n", 2, "error messages")
|
||||
debugPrint("these are %d %s", 2, "error messages")
|
||||
SetMode(TestMode)
|
||||
})
|
||||
assert.Equal(t, "[GIN-debug] these are 2 error messages\n", re)
|
||||
|
|
|
@ -24,7 +24,9 @@ func TestBindWith(t *testing.T) {
|
|||
Foo string `form:"foo"`
|
||||
Bar string `form:"bar"`
|
||||
}
|
||||
assert.NoError(t, c.BindWith(&obj, binding.Form))
|
||||
captureOutput(t, func() {
|
||||
assert.NoError(t, c.BindWith(&obj, binding.Form))
|
||||
})
|
||||
assert.Equal(t, "foo", obj.Bar)
|
||||
assert.Equal(t, "bar", obj.Foo)
|
||||
assert.Equal(t, 0, w.Body.Len())
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -35,7 +36,10 @@ func main() {
|
|||
// Wait for interrupt signal to gracefully shutdown the server with
|
||||
// a timeout of 5 seconds.
|
||||
quit := make(chan os.Signal)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
// kill (no param) default send syscanll.SIGTERM
|
||||
// kill -2 is syscall.SIGINT
|
||||
// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
log.Println("Shutdown Server ...")
|
||||
|
||||
|
@ -44,5 +48,10 @@ func main() {
|
|||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Fatal("Server Shutdown:", err)
|
||||
}
|
||||
// catching ctx.Done(). timeout of 5 seconds.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("timeout of 5 seconds.")
|
||||
}
|
||||
log.Println("Server exiting")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
The [New Relic Go Agent](https://github.com/newrelic/go-agent) provides a nice middleware for the stdlib handler signature.
|
||||
The following is an adaptation of that middleware for Gin.
|
||||
|
||||
```golang
|
||||
const (
|
||||
// NewRelicTxnKey is the key used to retrieve the NewRelic Transaction from the context
|
||||
NewRelicTxnKey = "NewRelicTxnKey"
|
||||
)
|
||||
|
||||
// NewRelicMonitoring is a middleware that starts a newrelic transaction, stores it in the context, then calls the next handler
|
||||
func NewRelicMonitoring(app newrelic.Application) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
txn := app.StartTransaction(ctx.Request.URL.Path, ctx.Writer, ctx.Request)
|
||||
defer txn.End()
|
||||
ctx.Set(NewRelicTxnKey, txn)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
and in `main.go` or equivalent...
|
||||
```golang
|
||||
router := gin.Default()
|
||||
cfg := newrelic.NewConfig(os.Getenv("APP_NAME"), os.Getenv("NEW_RELIC_API_KEY"))
|
||||
app, err := newrelic.NewApplication(cfg)
|
||||
if err != nil {
|
||||
log.Printf("failed to make new_relic app: %v", err)
|
||||
} else {
|
||||
router.Use(adapters.NewRelicMonitoring(app))
|
||||
}
|
||||
```
|
|
@ -0,0 +1,42 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/newrelic/go-agent"
|
||||
)
|
||||
|
||||
const (
|
||||
// NewRelicTxnKey is the key used to retrieve the NewRelic Transaction from the context
|
||||
NewRelicTxnKey = "NewRelicTxnKey"
|
||||
)
|
||||
|
||||
// NewRelicMonitoring is a middleware that starts a newrelic transaction, stores it in the context, then calls the next handler
|
||||
func NewRelicMonitoring(app newrelic.Application) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
txn := app.StartTransaction(ctx.Request.URL.Path, ctx.Writer, ctx.Request)
|
||||
defer txn.End()
|
||||
ctx.Set(NewRelicTxnKey, txn)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
cfg := newrelic.NewConfig(os.Getenv("APP_NAME"), os.Getenv("NEW_RELIC_API_KEY"))
|
||||
app, err := newrelic.NewApplication(cfg)
|
||||
if err != nil {
|
||||
log.Printf("failed to make new_relic app: %v", err)
|
||||
} else {
|
||||
router.Use(NewRelicMonitoring(app))
|
||||
}
|
||||
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "Hello World!\n")
|
||||
})
|
||||
router.Run()
|
||||
}
|
4
gin.go
4
gin.go
|
@ -318,6 +318,7 @@ func (engine *Engine) RunUnix(file string) (err error) {
|
|||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
os.Chmod(file, 0777)
|
||||
err = http.Serve(listener, engine)
|
||||
return
|
||||
}
|
||||
|
@ -355,8 +356,11 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
// This can be done by setting c.Request.URL.Path to your new target.
|
||||
// Disclaimer: You can loop yourself to death with this, use wisely.
|
||||
func (engine *Engine) HandleContext(c *Context) {
|
||||
oldIndexValue := c.index
|
||||
c.reset()
|
||||
engine.handleHTTPRequest(c)
|
||||
|
||||
c.index = oldIndexValue
|
||||
}
|
||||
|
||||
func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||
|
|
|
@ -188,15 +188,12 @@ func TestConcurrentHandleContext(t *testing.T) {
|
|||
})
|
||||
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
iterations := 200
|
||||
wg.Add(iterations)
|
||||
for i := 0; i < iterations; i++ {
|
||||
go func() {
|
||||
testRequest(t, ts.URL+"/")
|
||||
testGetRequestHandler(t, router, "/")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
@ -217,3 +214,14 @@ func TestConcurrentHandleContext(t *testing.T) {
|
|||
|
||||
// testRequest(t, "http://localhost:8033/example")
|
||||
// }
|
||||
|
||||
func testGetRequestHandler(t *testing.T, h http.Handler, url string) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, "it worked", w.Body.String(), "resp body should match")
|
||||
assert.Equal(t, 200, w.Code, "should get a 200")
|
||||
}
|
||||
|
|
68
gin_test.go
68
gin_test.go
|
@ -12,6 +12,8 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -25,18 +27,23 @@ func formatAsDate(t time.Time) string {
|
|||
|
||||
func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine)) *httptest.Server {
|
||||
SetMode(mode)
|
||||
router := New()
|
||||
router.Delims("{[{", "}]}")
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
})
|
||||
loadMethod(router)
|
||||
router.GET("/test", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
||||
defer SetMode(TestMode)
|
||||
|
||||
var router *Engine
|
||||
captureOutput(t, func() {
|
||||
router = New()
|
||||
router.Delims("{[{", "}]}")
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
})
|
||||
loadMethod(router)
|
||||
router.GET("/test", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -488,6 +495,43 @@ func TestEngineHandleContext(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestEngineHandleContextManyReEntries(t *testing.T) {
|
||||
expectValue := 10000
|
||||
|
||||
var handlerCounter, middlewareCounter int64
|
||||
|
||||
r := New()
|
||||
r.Use(func(c *Context) {
|
||||
atomic.AddInt64(&middlewareCounter, 1)
|
||||
})
|
||||
r.GET("/:count", func(c *Context) {
|
||||
countStr := c.Param("count")
|
||||
count, err := strconv.Atoi(countStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n, err := c.Writer.Write([]byte("."))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, n)
|
||||
|
||||
switch {
|
||||
case count > 0:
|
||||
c.Request.URL.Path = "/" + strconv.Itoa(count-1)
|
||||
r.HandleContext(c)
|
||||
}
|
||||
}, func(c *Context) {
|
||||
atomic.AddInt64(&handlerCounter, 1)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
w := performRequest(r, "GET", "/"+strconv.Itoa(expectValue-1)) // include 0 value
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, expectValue, w.Body.Len())
|
||||
})
|
||||
|
||||
assert.Equal(t, int64(expectValue), handlerCounter)
|
||||
assert.Equal(t, int64(expectValue), middlewareCounter)
|
||||
}
|
||||
|
||||
func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) {
|
||||
for _, gotRoute := range gotRoutes {
|
||||
if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method {
|
||||
|
|
|
@ -287,7 +287,7 @@ var githubAPI = []route{
|
|||
|
||||
func TestShouldBindUri(t *testing.T) {
|
||||
DefaultWriter = os.Stdout
|
||||
router := Default()
|
||||
router := New()
|
||||
|
||||
type Person struct {
|
||||
Name string `uri:"name" binding:"required"`
|
||||
|
@ -309,7 +309,7 @@ func TestShouldBindUri(t *testing.T) {
|
|||
|
||||
func TestBindUri(t *testing.T) {
|
||||
DefaultWriter = os.Stdout
|
||||
router := Default()
|
||||
router := New()
|
||||
|
||||
type Person struct {
|
||||
Name string `uri:"name" binding:"required"`
|
||||
|
@ -331,7 +331,7 @@ func TestBindUri(t *testing.T) {
|
|||
|
||||
func TestBindUriError(t *testing.T) {
|
||||
DefaultWriter = os.Stdout
|
||||
router := Default()
|
||||
router := New()
|
||||
|
||||
type Member struct {
|
||||
Number string `uri:"num" binding:"required,uuid"`
|
||||
|
@ -346,6 +346,29 @@ func TestBindUriError(t *testing.T) {
|
|||
assert.Equal(t, http.StatusBadRequest, w1.Code)
|
||||
}
|
||||
|
||||
func TestRaceContextCopy(t *testing.T) {
|
||||
DefaultWriter = os.Stdout
|
||||
router := Default()
|
||||
router.GET("/test/copy/race", func(c *Context) {
|
||||
c.Set("1", 0)
|
||||
c.Set("2", 0)
|
||||
|
||||
// Sending a copy of the Context to two separate routines
|
||||
go readWriteKeys(c.Copy())
|
||||
go readWriteKeys(c.Copy())
|
||||
c.String(http.StatusOK, "run OK, no panics")
|
||||
})
|
||||
w := performRequest(router, "GET", "/test/copy/race")
|
||||
assert.Equal(t, "run OK, no panics", w.Body.String())
|
||||
}
|
||||
|
||||
func readWriteKeys(c *Context) {
|
||||
for {
|
||||
c.Set("1", rand.Int())
|
||||
c.Set("2", c.Value("1"))
|
||||
}
|
||||
}
|
||||
|
||||
func githubConfigRouter(router *Engine) {
|
||||
for _, route := range githubAPI {
|
||||
router.Handle(route.method, route.path, func(c *Context) {
|
||||
|
@ -361,7 +384,7 @@ func githubConfigRouter(router *Engine) {
|
|||
|
||||
func TestGithubAPI(t *testing.T) {
|
||||
DefaultWriter = os.Stdout
|
||||
router := Default()
|
||||
router := New()
|
||||
githubConfigRouter(router)
|
||||
|
||||
for _, route := range githubAPI {
|
||||
|
@ -436,7 +459,7 @@ func BenchmarkParallelGithub(b *testing.B) {
|
|||
|
||||
func BenchmarkParallelGithubDefault(b *testing.B) {
|
||||
DefaultWriter = os.Stdout
|
||||
router := Default()
|
||||
router := New()
|
||||
githubConfigRouter(router)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/repos/manucorporat/sse/git/blobs", nil)
|
||||
|
|
40
go.mod
40
go.mod
|
@ -1,30 +1,32 @@
|
|||
module github.com/gin-gonic/gin
|
||||
|
||||
require (
|
||||
github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296
|
||||
github.com/client9/misspell v0.3.4
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7
|
||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b
|
||||
github.com/gin-contrib/sse v0.0.0-20190124093953-61b50c2ef482
|
||||
github.com/golang/protobuf v1.2.0
|
||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15
|
||||
github.com/json-iterator/go v1.1.5
|
||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227
|
||||
github.com/mattn/go-isatty v0.0.4
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/thinkerou/favicon v0.1.0
|
||||
github.com/ugorji/go v1.1.1
|
||||
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e
|
||||
golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
|
||||
golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba // indirect
|
||||
google.golang.org/grpc v1.15.0
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2
|
||||
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e // indirect
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
||||
|
||||
exclude (
|
||||
github.com/campoy/embedmd v0.0.0-20181127031020-97c13d6e4160
|
||||
github.com/client9/misspell v0.3.4
|
||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66
|
||||
github.com/gin-gonic/autotls v0.0.0-20190119125636-0b5f4fc15768
|
||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15
|
||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227
|
||||
github.com/newrelic/go-agent v2.5.0+incompatible
|
||||
github.com/thinkerou/favicon v0.1.0
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b
|
||||
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1
|
||||
google.golang.org/grpc v1.18.0
|
||||
)
|
||||
|
|
64
go.sum
64
go.sum
|
@ -1,72 +1,54 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296 h1:tRsilif6pbtt+PX6uRoyGd+qR+4ZPucFZLHlc3Ak6z8=
|
||||
github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296/go.mod h1:/dBk8ICkslPCmyRdn4azP+QvBxL6Eg3EYxUGI9xMMFw=
|
||||
github.com/campoy/embedmd v0.0.0-20181127031020-97c13d6e4160 h1:HJpuhXOHC4EkXDARsLjmXAV9FhlY6qFDnKI/MJM6eoE=
|
||||
github.com/campoy/embedmd v0.0.0-20181127031020-97c13d6e4160/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
|
||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 h1:QnnoVdChKs+GeTvN4rPYTW6b5U6M3HMEvQ/+x4IGtfY=
|
||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66/go.mod h1:kTEh6M2J/mh7nsskr28alwLCXm/DSG5OSA/o31yy2XU=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b h1:dm/NYytoj7p8Jc6zMvyRz3PCQrTTCXnVRvEzyBcM890=
|
||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gin-contrib/sse v0.0.0-20190124093953-61b50c2ef482 h1:iOz5sIQUvuOlpiC7Q6+MmJQpWnlneYX98QIGf+2m50Y=
|
||||
github.com/gin-contrib/sse v0.0.0-20190124093953-61b50c2ef482/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 h1:cW/amwGEJK5MSKntPXRjX4dxs/nGxGT8gXKIsKFmHGc=
|
||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15/go.mod h1:Fdm/oWRW+CH8PRbLntksCNtmcCBximKPkVQYvmMl80k=
|
||||
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 h1:KIaAZ/V+/0/6BOULrmBQ9T1ed8BkKqGIjIKW923nJuo=
|
||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227/go.mod h1:ruMr5t05gVho4tuDv0PbI0Bb8nOxc/5Y6JzRHe/yfA0=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/thinkerou/favicon v0.1.0 h1:eWMISKTpHq2G8HOuKn7ydD55j5DDehx94b0C2y8ABMs=
|
||||
github.com/thinkerou/favicon v0.1.0/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s=
|
||||
github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w=
|
||||
github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e h1:IzypfodbhbnViNUO/MEh0FzCUooG97cIGfdggUrUSyU=
|
||||
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd h1:cgsAvzdqkDKdI02tIvDjO225vDPHMDCgfKqx5KEVI7U=
|
||||
golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 h1:EICbibRW4JNKMcY+LsWmuwob+CRS1BmdRdjphAm9mH4=
|
||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1 h1:Y/KGZSOdz/2r0WJ9Mkmz6NJBusp0kiNx1Cn82lzJQ6w=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e h1:MDa3fSUp6MdYHouVmCCNz/zaH2a6CRcxY3VhT/K3C5Q=
|
||||
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba h1:nZJIJPGow0Kf9bU9QTc1U6OXbs/7Hu4e+cNv+hxH+Zc=
|
||||
golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e h1:3GIlrlVLfkoipSReOMNAgApI0ajnalyLa/EZHHca/XI=
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.15.0 h1:Az/KuahOM4NAidTEuJCv/RonAA7rYsTPkqXVjr+8OOw=
|
||||
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
99
logger.go
99
logger.go
|
@ -24,6 +24,7 @@ var (
|
|||
cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
|
||||
reset = string([]byte{27, 91, 48, 109})
|
||||
disableColor = false
|
||||
forceColor = false
|
||||
)
|
||||
|
||||
// LoggerConfig defines the config for Logger middleware.
|
||||
|
@ -63,15 +64,62 @@ type LogFormatterParams struct {
|
|||
ErrorMessage string
|
||||
// IsTerm shows whether does gin's output descriptor refers to a terminal.
|
||||
IsTerm bool
|
||||
// BodySize is the size of the Response Body
|
||||
BodySize int
|
||||
}
|
||||
|
||||
// StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
|
||||
func (p *LogFormatterParams) StatusCodeColor() string {
|
||||
code := p.StatusCode
|
||||
|
||||
switch {
|
||||
case code >= http.StatusOK && code < http.StatusMultipleChoices:
|
||||
return green
|
||||
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
|
||||
return white
|
||||
case code >= http.StatusBadRequest && code < http.StatusInternalServerError:
|
||||
return yellow
|
||||
default:
|
||||
return red
|
||||
}
|
||||
}
|
||||
|
||||
// MethodColor is the ANSI color for appropriately logging http method to a terminal.
|
||||
func (p *LogFormatterParams) MethodColor() string {
|
||||
method := p.Method
|
||||
|
||||
switch method {
|
||||
case "GET":
|
||||
return blue
|
||||
case "POST":
|
||||
return cyan
|
||||
case "PUT":
|
||||
return yellow
|
||||
case "DELETE":
|
||||
return red
|
||||
case "PATCH":
|
||||
return green
|
||||
case "HEAD":
|
||||
return magenta
|
||||
case "OPTIONS":
|
||||
return white
|
||||
default:
|
||||
return reset
|
||||
}
|
||||
}
|
||||
|
||||
// ResetColor resets all escape attributes.
|
||||
func (p *LogFormatterParams) ResetColor() string {
|
||||
return reset
|
||||
}
|
||||
|
||||
// defaultLogFormatter is the default log format function Logger middleware uses.
|
||||
var defaultLogFormatter = func(param LogFormatterParams) string {
|
||||
var statusColor, methodColor, resetColor string
|
||||
if param.IsTerm {
|
||||
statusColor = colorForStatus(param.StatusCode)
|
||||
methodColor = colorForMethod(param.Method)
|
||||
resetColor = reset
|
||||
statusColor = param.StatusCodeColor()
|
||||
methodColor = param.MethodColor()
|
||||
resetColor = param.ResetColor()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",
|
||||
|
@ -90,6 +138,11 @@ func DisableConsoleColor() {
|
|||
disableColor = true
|
||||
}
|
||||
|
||||
// ForceConsoleColor force color output in the console.
|
||||
func ForceConsoleColor() {
|
||||
forceColor = true
|
||||
}
|
||||
|
||||
// ErrorLogger returns a handlerfunc for any error type.
|
||||
func ErrorLogger() HandlerFunc {
|
||||
return ErrorLoggerT(ErrorTypeAny)
|
||||
|
@ -144,9 +197,9 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
|
|||
|
||||
isTerm := true
|
||||
|
||||
if w, ok := out.(*os.File); !ok ||
|
||||
if w, ok := out.(*os.File); (!ok ||
|
||||
(os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) ||
|
||||
disableColor {
|
||||
disableColor) && !forceColor {
|
||||
isTerm = false
|
||||
}
|
||||
|
||||
|
@ -185,6 +238,8 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
|
|||
param.StatusCode = c.Writer.Status()
|
||||
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
|
||||
|
||||
param.BodySize = c.Writer.Size()
|
||||
|
||||
if raw != "" {
|
||||
path = path + "?" + raw
|
||||
}
|
||||
|
@ -195,37 +250,3 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func colorForStatus(code int) string {
|
||||
switch {
|
||||
case code >= http.StatusOK && code < http.StatusMultipleChoices:
|
||||
return green
|
||||
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
|
||||
return white
|
||||
case code >= http.StatusBadRequest && code < http.StatusInternalServerError:
|
||||
return yellow
|
||||
default:
|
||||
return red
|
||||
}
|
||||
}
|
||||
|
||||
func colorForMethod(method string) string {
|
||||
switch method {
|
||||
case "GET":
|
||||
return blue
|
||||
case "POST":
|
||||
return cyan
|
||||
case "PUT":
|
||||
return yellow
|
||||
case "DELETE":
|
||||
return red
|
||||
case "PATCH":
|
||||
return green
|
||||
case "HEAD":
|
||||
return magenta
|
||||
case "OPTIONS":
|
||||
return white
|
||||
default:
|
||||
return reset
|
||||
}
|
||||
}
|
||||
|
|
|
@ -257,6 +257,13 @@ func TestDefaultLogFormatter(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestColorForMethod(t *testing.T) {
|
||||
colorForMethod := func(method string) string {
|
||||
p := LogFormatterParams{
|
||||
Method: method,
|
||||
}
|
||||
return p.MethodColor()
|
||||
}
|
||||
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 52, 109}), colorForMethod("GET"), "get should be blue")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 54, 109}), colorForMethod("POST"), "post should be cyan")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForMethod("PUT"), "put should be yellow")
|
||||
|
@ -268,12 +275,24 @@ func TestColorForMethod(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestColorForStatus(t *testing.T) {
|
||||
colorForStatus := func(code int) string {
|
||||
p := LogFormatterParams{
|
||||
StatusCode: code,
|
||||
}
|
||||
return p.StatusCodeColor()
|
||||
}
|
||||
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForStatus(http.StatusOK), "2xx should be green")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), colorForStatus(http.StatusMovedPermanently), "3xx should be white")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForStatus(http.StatusNotFound), "4xx should be yellow")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForStatus(2), "other things should be red")
|
||||
}
|
||||
|
||||
func TestResetColor(t *testing.T) {
|
||||
p := LogFormatterParams{}
|
||||
assert.Equal(t, string([]byte{27, 91, 48, 109}), p.ResetColor())
|
||||
}
|
||||
|
||||
func TestErrorLogger(t *testing.T) {
|
||||
router := New()
|
||||
router.Use(ErrorLogger())
|
||||
|
@ -340,3 +359,10 @@ func TestDisableConsoleColor(t *testing.T) {
|
|||
DisableConsoleColor()
|
||||
assert.True(t, disableColor)
|
||||
}
|
||||
|
||||
func TestForceConsoleColor(t *testing.T) {
|
||||
New()
|
||||
assert.False(t, forceColor)
|
||||
ForceConsoleColor()
|
||||
assert.True(t, forceColor)
|
||||
}
|
||||
|
|
6
mode.go
6
mode.go
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
// ENV_GIN_MODE indicates environment name for gin mode.
|
||||
const ENV_GIN_MODE = "GIN_MODE"
|
||||
// EnvGinMode indicates environment name for gin mode.
|
||||
const EnvGinMode = "GIN_MODE"
|
||||
|
||||
const (
|
||||
// DebugMode indicates gin mode is debug.
|
||||
|
@ -44,7 +44,7 @@ var ginMode = debugCode
|
|||
var modeName = DebugMode
|
||||
|
||||
func init() {
|
||||
mode := os.Getenv(ENV_GIN_MODE)
|
||||
mode := os.Getenv(EnvGinMode)
|
||||
SetMode(mode)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv(ENV_GIN_MODE, TestMode)
|
||||
os.Setenv(EnvGinMode, TestMode)
|
||||
}
|
||||
|
||||
func TestSetMode(t *testing.T) {
|
||||
assert.Equal(t, testCode, ginMode)
|
||||
assert.Equal(t, TestMode, Mode())
|
||||
os.Unsetenv(ENV_GIN_MODE)
|
||||
os.Unsetenv(EnvGinMode)
|
||||
|
||||
SetMode("")
|
||||
assert.Equal(t, debugCode, ginMode)
|
||||
|
|
|
@ -43,6 +43,7 @@ func TestPanicInHandler(t *testing.T) {
|
|||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
assert.Contains(t, buffer.String(), "GET /recovery")
|
||||
|
||||
SetMode(TestMode)
|
||||
}
|
||||
|
||||
// TestPanicWithAbort assert that panic has been recovered even if context.Abort was used.
|
||||
|
|
|
@ -36,8 +36,8 @@ func (r Reader) WriteContentType(w http.ResponseWriter) {
|
|||
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
|
||||
header := w.Header()
|
||||
for k, v := range headers {
|
||||
if val := header[k]; len(val) == 0 {
|
||||
header[k] = []string{v}
|
||||
if header.Get(k) == "" {
|
||||
header.Set(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -470,6 +470,7 @@ func TestRenderReader(t *testing.T) {
|
|||
body := "#!PNG some raw data"
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Disposition"] = `attachment; filename="filename.png"`
|
||||
headers["x-request-id"] = "requestId"
|
||||
|
||||
err := (Reader{
|
||||
ContentLength: int64(len(body)),
|
||||
|
@ -483,4 +484,5 @@ func TestRenderReader(t *testing.T) {
|
|||
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length"))
|
||||
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
|
||||
assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2018 Gin Core Team. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build tools
|
||||
|
||||
// This package exists to cause `go mod` and `go get` to believe these tools
|
||||
// are dependencies, even though they are not runtime dependencies of any
|
||||
// gin package. This means they will appear in `go.mod` file, but will not
|
||||
// be a part of the build.
|
||||
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "github.com/campoy/embedmd"
|
||||
_ "github.com/client9/misspell/cmd/misspell"
|
||||
_ "github.com/dustin/go-broadcast"
|
||||
_ "github.com/gin-gonic/autotls"
|
||||
_ "github.com/jessevdk/go-assets"
|
||||
_ "github.com/manucorporat/stats"
|
||||
_ "github.com/thinkerou/favicon"
|
||||
_ "golang.org/x/crypto/acme/autocert"
|
||||
_ "golang.org/x/lint/golint"
|
||||
_ "google.golang.org/grpc"
|
||||
)
|
Loading…
Reference in New Issue