upd(vendor): upgrade sentimensrg ctx package
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
Generated
Vendored
+6
-39
@@ -1,39 +1,6 @@
|
||||
# DEPRECATION WARNING
|
||||
MIT License
|
||||
|
||||
`sigctx` is now part of [SentimensRG/ctx](https://github.com/SentimensRG/ctx).
|
||||
|
||||
This repository will no longer receive updates.
|
||||
|
||||
# sigctx
|
||||
Go contexts for graceful shutdown
|
||||
|
||||
## installation
|
||||
|
||||
```bash
|
||||
go get -u github.com/SentimensRG/sigctx
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
```go
|
||||
ctx := sigctx.New() // returns a regular context.Context
|
||||
|
||||
// With this simple pattern, your goroutines are guaranteed to terminate correctly
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
go someBlockingFunction(ctx)
|
||||
defer cancel()
|
||||
|
||||
<-ctx.Done() // will unblock on SIGINT and SIGTERM
|
||||
```
|
||||
|
||||
## RFC
|
||||
|
||||
If you find this useful, please let me know: <l.thibault@sentimens.com>
|
||||
|
||||
## License
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017 Sentimens Research Group, LLC
|
||||
Copyright (c) 2017 Sentimens Research Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,13 +9,13 @@ 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 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.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# ctx
|
||||
|
||||
Composable utilities for Go contexts.
|
||||
|
||||
[](https://godoc.org/github.com/SentimensRG/ctx)
|
||||
[](https://goreportcard.com/report/github.com/SentimensRG/ctx)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get -u github.com/SentimensRG/ctx
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
The `ctx` package provides utilites for working with data structures satisfying
|
||||
the `ctx.Doner` interface, most notably `context.Context`:
|
||||
|
||||
```go
|
||||
type Doner interface {
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
```
|
||||
|
||||
The functions in `ctx` are appropriate for operations that do not preserve the
|
||||
values in a context, e.g.: joining several contexts together.
|
||||
|
||||
## Subpackages
|
||||
|
||||
- [sigctx](https://github.com/SentimensRG/ctx/tree/master/sigctx): contexts for graceful shutdown
|
||||
- [refctx](https://github.com/SentimensRG/ctx/tree/master/refctx): contexts linked to a reference-counter
|
||||
- [mergectx](https://github.com/SentimensRG/ctx/tree/master/mergectx): utilities for merging `context.Context` instances while preserving values, errors and deadlines.
|
||||
|
||||
## RFC
|
||||
|
||||
If you find this useful please let me know: <l.thibault@sentimens.com>
|
||||
|
||||
Seriously, even if you just used it in your weekend project, I'd like to hear
|
||||
about it :)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017 Sentimens Research Group, LLC
|
||||
|
||||
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.
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Binder is the interface that wraps the basic Bind method.
|
||||
// Bind executes logic until the Doner completes. Implementations of Bind must
|
||||
// not return until the Doner has completed.
|
||||
type Binder interface {
|
||||
Bind(Doner)
|
||||
}
|
||||
|
||||
// BindFunc is an adapter to allow the use of ordinary functions as Binders.
|
||||
type BindFunc func(Doner)
|
||||
|
||||
// Bind executes logic until the Doner completes. It satisfies the Binder
|
||||
// interface.
|
||||
func (f BindFunc) Bind(d Doner) { f(d) }
|
||||
|
||||
// Doner can block until something is done
|
||||
type Doner interface {
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
|
||||
// C is a basic implementation of Doner
|
||||
type C <-chan struct{}
|
||||
|
||||
// Background is the ctx analog to context.Background(). It never fires.
|
||||
func Background() C {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Done returns a channel that receives when an action is complete
|
||||
func (dc C) Done() <-chan struct{} { return dc }
|
||||
|
||||
type ctx struct {
|
||||
Doner
|
||||
}
|
||||
|
||||
// Deadline returns the time when work done on behalf of this context
|
||||
// should be canceled. Deadline returns ok==false when no deadline is
|
||||
// set. Successive calls to Deadline return the same results.
|
||||
func (ctx) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c ctx) Err() error {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return context.Canceled
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c ctx) Value(interface{}) (v interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// AsContext creates a context that fires when the Doner fires
|
||||
func AsContext(d Doner) context.Context {
|
||||
return ctx{d}
|
||||
}
|
||||
|
||||
// After time time has elapsed, the Doner fires
|
||||
func After(d time.Duration) C {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
<-time.After(d)
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// WithCancel returns a new Doner that can be cancelled via the associated
|
||||
// function
|
||||
func WithCancel(d Doner) (C, func()) {
|
||||
var closer sync.Once
|
||||
cq := make(chan struct{})
|
||||
cancel := func() { closer.Do(func() { close(cq) }) }
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-cq:
|
||||
case <-d.Done():
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return cq, cancel
|
||||
}
|
||||
|
||||
// Tick returns a <-chan whose range ends when the underlying context cancels
|
||||
func Tick(d Doner) <-chan struct{} {
|
||||
c := make(chan struct{})
|
||||
cq := d.Done()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-cq:
|
||||
close(c)
|
||||
return
|
||||
default:
|
||||
select {
|
||||
case c <- struct{}{}:
|
||||
case <-cq:
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
// Defer guarantees that a function will be called after a context has cancelled
|
||||
func Defer(d Doner, cb func()) {
|
||||
go func() {
|
||||
<-d.Done()
|
||||
cb()
|
||||
}()
|
||||
}
|
||||
|
||||
// Link returns a channel that fires if ANY of the constituent Doners have fired
|
||||
func Link(doners ...Doner) C {
|
||||
c := make(chan struct{})
|
||||
cancel := func() { close(c) }
|
||||
|
||||
var once sync.Once
|
||||
for _, d := range doners {
|
||||
Defer(d, func() { once.Do(cancel) })
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Join returns a channel that receives when all constituent Doners have fired
|
||||
func Join(doners ...Doner) C {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(doners))
|
||||
for _, d := range doners {
|
||||
Defer(d, wg.Done)
|
||||
}
|
||||
|
||||
cq := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(cq)
|
||||
}()
|
||||
return cq
|
||||
}
|
||||
|
||||
// FTick calls a function in a loop until the Doner has fired
|
||||
func FTick(d Doner, f func()) {
|
||||
for range Tick(d) {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
// FTickInterval calls a function repeatedly at a given internval, until the Doner
|
||||
// has fired. Note that FTickInterval ignores the time spent executing a function,
|
||||
// and instead guarantees an interval of `t` between of return of the previous
|
||||
// function call and the invocation of the next function call.
|
||||
func FTickInterval(d Doner, t time.Duration, f func()) {
|
||||
for {
|
||||
select {
|
||||
case <-d.Done():
|
||||
return
|
||||
case <-time.After(t):
|
||||
f()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FDone returns a doner that fires when the function returns or panics
|
||||
func FDone(f func()) C {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
f()
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# sigctx
|
||||
|
||||
Package `sigctx` provides contexts for graceful shutdown.
|
||||
|
||||
The `sigctx` package provides a context that terminates when it receives a
|
||||
SIGINT or SIGTERM. This provides a convenient mechanism for triggering
|
||||
graceful application shutdown.
|
||||
|
||||
`sigctx.New` returns a `ctx.C`, which implements the ubiquitous `ctx.Doner`
|
||||
interface. It fires when either SIGINT or SIGTERM is caught.
|
||||
|
||||
## Examples
|
||||
|
||||
```go
|
||||
import (
|
||||
"log"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := sigctx.New() // returns a regular context.Context
|
||||
|
||||
<-ctx.Done() // will unblock on SIGINT and SIGTERM
|
||||
log.Println("exiting.")
|
||||
}
|
||||
```
|
||||
|
||||
`sigctx.Tick` can be used to react to streams of signals. For example, you can
|
||||
implement a graceful shutdown attempt, followed by a forced shutdown.
|
||||
|
||||
```go
|
||||
import (
|
||||
"log"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
"github.com/SentimensRG/ctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := sigctx.Tick()
|
||||
d, cancel := ctx.WithCancel(ctx.Background())
|
||||
|
||||
go func() {
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
// business logic goes here
|
||||
}()
|
||||
|
||||
<-t
|
||||
log.Println("attempting graceful shutdown - press Ctrl + c again to force quit")
|
||||
go func() {
|
||||
defer cancel()
|
||||
// cleanup logic goes here
|
||||
}()
|
||||
|
||||
<-t
|
||||
log.Println("forcing close")
|
||||
}()
|
||||
|
||||
<-d.Done()
|
||||
}
|
||||
|
||||
```
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Package sigctx provides a context that expires when a SIGINT or SIGTERM is
|
||||
// received.
|
||||
package sigctx
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/SentimensRG/ctx"
|
||||
)
|
||||
|
||||
var (
|
||||
c ctx.C
|
||||
sigCh chan os.Signal
|
||||
initC, initSig sync.Once
|
||||
)
|
||||
|
||||
func initSigCh() {
|
||||
sigCh = make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
}
|
||||
|
||||
// New signal-bound ctx.C that terminates when either SIGINT or SIGTERM
|
||||
// is caught.
|
||||
func New() ctx.C {
|
||||
initC.Do(func() {
|
||||
initSig.Do(initSigCh)
|
||||
|
||||
dc := make(chan struct{})
|
||||
c = dc
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-sigCh:
|
||||
close(dc)
|
||||
case <-c.Done():
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Tick returns a channel that recvs each time a either SIGINT or SIGTERM are
|
||||
// caught.
|
||||
func Tick() <-chan struct{} {
|
||||
initSig.Do(initSigCh)
|
||||
|
||||
dc := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
<-sigCh
|
||||
dc <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
return dc
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
package sigctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var ctx context.Context
|
||||
|
||||
func init() {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// New signal-bound context
|
||||
func New() context.Context {
|
||||
return ctx
|
||||
}
|
||||
+7
-1
@@ -4,6 +4,8 @@ go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
|
||||
install:
|
||||
- go get -u golang.org/x/tools/cmd/goimports
|
||||
@@ -15,4 +17,8 @@ script:
|
||||
- golint ./...
|
||||
- go test ./...
|
||||
- >
|
||||
goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :
|
||||
go_version=$(go version);
|
||||
if [ ${go_version:13:4} = "1.11" ]; then
|
||||
goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :;
|
||||
fi
|
||||
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ type Router interface {
|
||||
}
|
||||
|
||||
// Routes interface adds two methods for router traversal, which is also
|
||||
// used by the `docgen` subpackage to generation documentation for Routers.
|
||||
// used by the github.com/go-chi/docgen package to generate documentation for Routers.
|
||||
type Routes interface {
|
||||
// Routes returns the routing tree in an easily traversable structure.
|
||||
Routes() []Route
|
||||
|
||||
+7
-7
@@ -84,13 +84,13 @@ func (x *Context) URLParam(key string) string {
|
||||
//
|
||||
// For example,
|
||||
//
|
||||
// func Instrument(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// next.ServeHTTP(w, r)
|
||||
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
|
||||
// measure(w, r, routePattern)
|
||||
// })
|
||||
// }
|
||||
// func Instrument(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// next.ServeHTTP(w, r)
|
||||
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
|
||||
// measure(w, r, routePattern)
|
||||
// })
|
||||
// }
|
||||
func (x *Context) RoutePattern() string {
|
||||
routePattern := strings.Join(x.RoutePatterns, "")
|
||||
return strings.Replace(routePattern, "/*/", "/", -1)
|
||||
|
||||
+22
-18
@@ -16,7 +16,7 @@ var (
|
||||
// DefaultLogger is called by the Logger middleware handler to log each request.
|
||||
// Its made a package-level variable so that it can be reconfigured for custom
|
||||
// logging configurations.
|
||||
DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags)})
|
||||
DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false})
|
||||
)
|
||||
|
||||
// Logger is a middleware that logs the start and end of each request, along
|
||||
@@ -81,29 +81,32 @@ type LoggerInterface interface {
|
||||
|
||||
// DefaultLogFormatter is a simple logger that implements a LogFormatter.
|
||||
type DefaultLogFormatter struct {
|
||||
Logger LoggerInterface
|
||||
Logger LoggerInterface
|
||||
NoColor bool
|
||||
}
|
||||
|
||||
// NewLogEntry creates a new LogEntry for the request.
|
||||
func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry {
|
||||
useColor := !l.NoColor
|
||||
entry := &defaultLogEntry{
|
||||
DefaultLogFormatter: l,
|
||||
request: r,
|
||||
buf: &bytes.Buffer{},
|
||||
useColor: useColor,
|
||||
}
|
||||
|
||||
reqID := GetReqID(r.Context())
|
||||
if reqID != "" {
|
||||
cW(entry.buf, nYellow, "[%s] ", reqID)
|
||||
cW(entry.buf, useColor, nYellow, "[%s] ", reqID)
|
||||
}
|
||||
cW(entry.buf, nCyan, "\"")
|
||||
cW(entry.buf, bMagenta, "%s ", r.Method)
|
||||
cW(entry.buf, useColor, nCyan, "\"")
|
||||
cW(entry.buf, useColor, bMagenta, "%s ", r.Method)
|
||||
|
||||
scheme := "http"
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
cW(entry.buf, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto)
|
||||
cW(entry.buf, useColor, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto)
|
||||
|
||||
entry.buf.WriteString("from ")
|
||||
entry.buf.WriteString(r.RemoteAddr)
|
||||
@@ -114,33 +117,34 @@ func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry {
|
||||
|
||||
type defaultLogEntry struct {
|
||||
*DefaultLogFormatter
|
||||
request *http.Request
|
||||
buf *bytes.Buffer
|
||||
request *http.Request
|
||||
buf *bytes.Buffer
|
||||
useColor bool
|
||||
}
|
||||
|
||||
func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) {
|
||||
switch {
|
||||
case status < 200:
|
||||
cW(l.buf, bBlue, "%03d", status)
|
||||
cW(l.buf, l.useColor, bBlue, "%03d", status)
|
||||
case status < 300:
|
||||
cW(l.buf, bGreen, "%03d", status)
|
||||
cW(l.buf, l.useColor, bGreen, "%03d", status)
|
||||
case status < 400:
|
||||
cW(l.buf, bCyan, "%03d", status)
|
||||
cW(l.buf, l.useColor, bCyan, "%03d", status)
|
||||
case status < 500:
|
||||
cW(l.buf, bYellow, "%03d", status)
|
||||
cW(l.buf, l.useColor, bYellow, "%03d", status)
|
||||
default:
|
||||
cW(l.buf, bRed, "%03d", status)
|
||||
cW(l.buf, l.useColor, bRed, "%03d", status)
|
||||
}
|
||||
|
||||
cW(l.buf, bBlue, " %dB", bytes)
|
||||
cW(l.buf, l.useColor, bBlue, " %dB", bytes)
|
||||
|
||||
l.buf.WriteString(" in ")
|
||||
if elapsed < 500*time.Millisecond {
|
||||
cW(l.buf, nGreen, "%s", elapsed)
|
||||
cW(l.buf, l.useColor, nGreen, "%s", elapsed)
|
||||
} else if elapsed < 5*time.Second {
|
||||
cW(l.buf, nYellow, "%s", elapsed)
|
||||
cW(l.buf, l.useColor, nYellow, "%s", elapsed)
|
||||
} else {
|
||||
cW(l.buf, nRed, "%s", elapsed)
|
||||
cW(l.buf, l.useColor, nRed, "%s", elapsed)
|
||||
}
|
||||
|
||||
l.Logger.Print(l.buf.String())
|
||||
@@ -148,7 +152,7 @@ func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) {
|
||||
|
||||
func (l *defaultLogEntry) Panic(v interface{}, stack []byte) {
|
||||
panicEntry := l.NewLogEntry(l.request).(*defaultLogEntry)
|
||||
cW(panicEntry.buf, bRed, "panic: %+v", v)
|
||||
cW(panicEntry.buf, l.useColor, bRed, "panic: %+v", v)
|
||||
l.Logger.Print(panicEntry.buf.String())
|
||||
l.Logger.Print(string(stack))
|
||||
}
|
||||
|
||||
+3
-3
@@ -52,12 +52,12 @@ func init() {
|
||||
}
|
||||
|
||||
// colorWrite
|
||||
func cW(w io.Writer, color []byte, s string, args ...interface{}) {
|
||||
if isTTY {
|
||||
func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) {
|
||||
if isTTY && useColor {
|
||||
w.Write(color)
|
||||
}
|
||||
fmt.Fprintf(w, s, args...)
|
||||
if isTTY {
|
||||
if isTTY && useColor {
|
||||
w.Write(reset)
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -83,6 +83,8 @@ type flushWriter struct {
|
||||
}
|
||||
|
||||
func (f *flushWriter) Flush() {
|
||||
f.wroteHeader = true
|
||||
|
||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||
fl.Flush()
|
||||
}
|
||||
@@ -102,6 +104,8 @@ func (f *httpFancyWriter) CloseNotify() <-chan bool {
|
||||
return cn.CloseNotify()
|
||||
}
|
||||
func (f *httpFancyWriter) Flush() {
|
||||
f.wroteHeader = true
|
||||
|
||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||
fl.Flush()
|
||||
}
|
||||
@@ -140,6 +144,8 @@ func (f *http2FancyWriter) CloseNotify() <-chan bool {
|
||||
return cn.CloseNotify()
|
||||
}
|
||||
func (f *http2FancyWriter) Flush() {
|
||||
f.wroteHeader = true
|
||||
|
||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||
fl.Flush()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user