Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
147
vendor/github.com/go-kit/kit/log/README.md
generated
vendored
Normal file
147
vendor/github.com/go-kit/kit/log/README.md
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
# package log
|
||||
|
||||
`package log` provides a minimal interface for structured logging in services.
|
||||
It may be wrapped to encode conventions, enforce type-safety, provide leveled
|
||||
logging, and so on. It can be used for both typical application log events,
|
||||
and log-structured data streams.
|
||||
|
||||
## Structured logging
|
||||
|
||||
Structured logging is, basically, conceding to the reality that logs are
|
||||
_data_, and warrant some level of schematic rigor. Using a stricter,
|
||||
key/value-oriented message format for our logs, containing contextual and
|
||||
semantic information, makes it much easier to get insight into the
|
||||
operational activity of the systems we build. Consequently, `package log` is
|
||||
of the strong belief that "[the benefits of structured logging outweigh the
|
||||
minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)".
|
||||
|
||||
Migrating from unstructured to structured logging is probably a lot easier
|
||||
than you'd expect.
|
||||
|
||||
```go
|
||||
// Unstructured
|
||||
log.Printf("HTTP server listening on %s", addr)
|
||||
|
||||
// Structured
|
||||
logger.Log("transport", "HTTP", "addr", addr, "msg", "listening")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Typical application logging
|
||||
|
||||
```go
|
||||
w := log.NewSyncWriter(os.Stderr)
|
||||
logger := log.NewLogfmtLogger(w)
|
||||
logger.Log("question", "what is the meaning of life?", "answer", 42)
|
||||
|
||||
// Output:
|
||||
// question="what is the meaning of life?" answer=42
|
||||
```
|
||||
|
||||
### Contextual Loggers
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var logger log.Logger
|
||||
logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
|
||||
logger = log.With(logger, "instance_id", 123)
|
||||
|
||||
logger.Log("msg", "starting")
|
||||
NewWorker(log.With(logger, "component", "worker")).Run()
|
||||
NewSlacker(log.With(logger, "component", "slacker")).Run()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// instance_id=123 msg=starting
|
||||
// instance_id=123 component=worker msg=running
|
||||
// instance_id=123 component=slacker msg=running
|
||||
```
|
||||
|
||||
### Interact with stdlib logger
|
||||
|
||||
Redirect stdlib logger to Go kit logger.
|
||||
|
||||
```go
|
||||
import (
|
||||
"os"
|
||||
stdlog "log"
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout))
|
||||
stdlog.SetOutput(kitlog.NewStdlibAdapter(logger))
|
||||
stdlog.Print("I sure like pie")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"}
|
||||
```
|
||||
|
||||
Or, if, for legacy reasons, you need to pipe all of your logging through the
|
||||
stdlib log package, you can redirect Go kit logger to the stdlib logger.
|
||||
|
||||
```go
|
||||
logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{})
|
||||
logger.Log("legacy", true, "msg", "at least it's something")
|
||||
|
||||
// Output:
|
||||
// 2016/01/01 12:34:56 legacy=true msg="at least it's something"
|
||||
```
|
||||
|
||||
### Timestamps and callers
|
||||
|
||||
```go
|
||||
var logger log.Logger
|
||||
logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
|
||||
logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
|
||||
|
||||
logger.Log("msg", "hello")
|
||||
|
||||
// Output:
|
||||
// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello
|
||||
```
|
||||
|
||||
## Supported output formats
|
||||
|
||||
- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write))
|
||||
- JSON
|
||||
|
||||
## Enhancements
|
||||
|
||||
`package log` is centered on the one-method Logger interface.
|
||||
|
||||
```go
|
||||
type Logger interface {
|
||||
Log(keyvals ...interface{}) error
|
||||
}
|
||||
```
|
||||
|
||||
This interface, and its supporting code like is the product of much iteration
|
||||
and evaluation. For more details on the evolution of the Logger interface,
|
||||
see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1),
|
||||
a talk by [Chris Hines](https://github.com/ChrisHines).
|
||||
Also, please see
|
||||
[#63](https://github.com/go-kit/kit/issues/63),
|
||||
[#76](https://github.com/go-kit/kit/pull/76),
|
||||
[#131](https://github.com/go-kit/kit/issues/131),
|
||||
[#157](https://github.com/go-kit/kit/pull/157),
|
||||
[#164](https://github.com/go-kit/kit/issues/164), and
|
||||
[#252](https://github.com/go-kit/kit/pull/252)
|
||||
to review historical conversations about package log and the Logger interface.
|
||||
|
||||
Value-add packages and suggestions,
|
||||
like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/level),
|
||||
are of course welcome. Good proposals should
|
||||
|
||||
- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/kit/log#With),
|
||||
- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped contextual loggers, and
|
||||
- Be friendly to packages that accept only an unadorned log.Logger.
|
||||
|
||||
## Benchmarks & comparisons
|
||||
|
||||
There are a few Go logging benchmarks and comparisons that include Go kit's package log.
|
||||
|
||||
- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log
|
||||
- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log
|
21
vendor/github.com/go-kit/kit/log/benchmark_test.go
generated
vendored
Normal file
21
vendor/github.com/go-kit/kit/log/benchmark_test.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) {
|
||||
lc := log.With(logger, "common_key", "common_value")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f(lc)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
baseMessage = func(logger log.Logger) { logger.Log("foo_key", "foo_value") }
|
||||
withMessage = func(logger log.Logger) { log.With(logger, "a", "b").Log("c", "d") }
|
||||
)
|
40
vendor/github.com/go-kit/kit/log/concurrency_test.go
generated
vendored
Normal file
40
vendor/github.com/go-kit/kit/log/concurrency_test.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
// These test are designed to be run with the race detector.
|
||||
|
||||
func testConcurrency(t *testing.T, logger log.Logger, total int) {
|
||||
n := int(math.Sqrt(float64(total)))
|
||||
share := total / n
|
||||
|
||||
errC := make(chan error, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
go func() {
|
||||
errC <- spam(logger, share)
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
err := <-errC
|
||||
if err != nil {
|
||||
t.Fatalf("concurrent logging error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func spam(logger log.Logger, count int) error {
|
||||
for i := 0; i < count; i++ {
|
||||
err := logger.Log("key", i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
127
vendor/github.com/go-kit/kit/log/deprecated_levels/levels.go
generated
vendored
Normal file
127
vendor/github.com/go-kit/kit/log/deprecated_levels/levels.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package levels
|
||||
|
||||
import "github.com/go-kit/kit/log"
|
||||
|
||||
// Levels provides a leveled logging wrapper around a logger. It has five
|
||||
// levels: debug, info, warning (warn), error, and critical (crit). If you
|
||||
// want a different set of levels, you can create your own levels type very
|
||||
// easily, and you can elide the configuration.
|
||||
type Levels struct {
|
||||
logger log.Logger
|
||||
levelKey string
|
||||
|
||||
// We have a choice between storing level values in string fields or
|
||||
// making a separate context for each level. When using string fields the
|
||||
// Log method must combine the base context, the level data, and the
|
||||
// logged keyvals; but the With method only requires updating one context.
|
||||
// If we instead keep a separate context for each level the Log method
|
||||
// must only append the new keyvals; but the With method would have to
|
||||
// update all five contexts.
|
||||
|
||||
// Roughly speaking, storing multiple contexts breaks even if the ratio of
|
||||
// Log/With calls is more than the number of levels. We have chosen to
|
||||
// make the With method cheap and the Log method a bit more costly because
|
||||
// we do not expect most applications to Log more than five times for each
|
||||
// call to With.
|
||||
|
||||
debugValue string
|
||||
infoValue string
|
||||
warnValue string
|
||||
errorValue string
|
||||
critValue string
|
||||
}
|
||||
|
||||
// New creates a new leveled logger, wrapping the passed logger.
|
||||
func New(logger log.Logger, options ...Option) Levels {
|
||||
l := Levels{
|
||||
logger: logger,
|
||||
levelKey: "level",
|
||||
|
||||
debugValue: "debug",
|
||||
infoValue: "info",
|
||||
warnValue: "warn",
|
||||
errorValue: "error",
|
||||
critValue: "crit",
|
||||
}
|
||||
for _, option := range options {
|
||||
option(&l)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// With returns a new leveled logger that includes keyvals in all log events.
|
||||
func (l Levels) With(keyvals ...interface{}) Levels {
|
||||
return Levels{
|
||||
logger: log.With(l.logger, keyvals...),
|
||||
levelKey: l.levelKey,
|
||||
debugValue: l.debugValue,
|
||||
infoValue: l.infoValue,
|
||||
warnValue: l.warnValue,
|
||||
errorValue: l.errorValue,
|
||||
critValue: l.critValue,
|
||||
}
|
||||
}
|
||||
|
||||
// Debug returns a debug level logger.
|
||||
func (l Levels) Debug() log.Logger {
|
||||
return log.WithPrefix(l.logger, l.levelKey, l.debugValue)
|
||||
}
|
||||
|
||||
// Info returns an info level logger.
|
||||
func (l Levels) Info() log.Logger {
|
||||
return log.WithPrefix(l.logger, l.levelKey, l.infoValue)
|
||||
}
|
||||
|
||||
// Warn returns a warning level logger.
|
||||
func (l Levels) Warn() log.Logger {
|
||||
return log.WithPrefix(l.logger, l.levelKey, l.warnValue)
|
||||
}
|
||||
|
||||
// Error returns an error level logger.
|
||||
func (l Levels) Error() log.Logger {
|
||||
return log.WithPrefix(l.logger, l.levelKey, l.errorValue)
|
||||
}
|
||||
|
||||
// Crit returns a critical level logger.
|
||||
func (l Levels) Crit() log.Logger {
|
||||
return log.WithPrefix(l.logger, l.levelKey, l.critValue)
|
||||
}
|
||||
|
||||
// Option sets a parameter for leveled loggers.
|
||||
type Option func(*Levels)
|
||||
|
||||
// Key sets the key for the field used to indicate log level. By default,
|
||||
// the key is "level".
|
||||
func Key(key string) Option {
|
||||
return func(l *Levels) { l.levelKey = key }
|
||||
}
|
||||
|
||||
// DebugValue sets the value for the field used to indicate the debug log
|
||||
// level. By default, the value is "debug".
|
||||
func DebugValue(value string) Option {
|
||||
return func(l *Levels) { l.debugValue = value }
|
||||
}
|
||||
|
||||
// InfoValue sets the value for the field used to indicate the info log level.
|
||||
// By default, the value is "info".
|
||||
func InfoValue(value string) Option {
|
||||
return func(l *Levels) { l.infoValue = value }
|
||||
}
|
||||
|
||||
// WarnValue sets the value for the field used to indicate the warning log
|
||||
// level. By default, the value is "warn".
|
||||
func WarnValue(value string) Option {
|
||||
return func(l *Levels) { l.warnValue = value }
|
||||
}
|
||||
|
||||
// ErrorValue sets the value for the field used to indicate the error log
|
||||
// level. By default, the value is "error".
|
||||
func ErrorValue(value string) Option {
|
||||
return func(l *Levels) { l.errorValue = value }
|
||||
}
|
||||
|
||||
// CritValue sets the value for the field used to indicate the critical log
|
||||
// level. By default, the value is "crit".
|
||||
func CritValue(value string) Option {
|
||||
return func(l *Levels) { l.critValue = value }
|
||||
}
|
65
vendor/github.com/go-kit/kit/log/deprecated_levels/levels_test.go
generated
vendored
Normal file
65
vendor/github.com/go-kit/kit/log/deprecated_levels/levels_test.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package levels_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
levels "github.com/go-kit/kit/log/deprecated_levels"
|
||||
)
|
||||
|
||||
func TestDefaultLevels(t *testing.T) {
|
||||
buf := bytes.Buffer{}
|
||||
logger := levels.New(log.NewLogfmtLogger(&buf))
|
||||
|
||||
logger.Debug().Log("msg", "résumé") // of course you'd want to do this
|
||||
if want, have := "level=debug msg=résumé\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
logger.Info().Log("msg", "Åhus")
|
||||
if want, have := "level=info msg=Åhus\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
logger.Error().Log("msg", "© violation")
|
||||
if want, have := "level=error msg=\"© violation\"\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
logger.Crit().Log("msg", " ")
|
||||
if want, have := "level=crit msg=\"\\t\"\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifiedLevels(t *testing.T) {
|
||||
buf := bytes.Buffer{}
|
||||
logger := levels.New(
|
||||
log.NewJSONLogger(&buf),
|
||||
levels.Key("l"),
|
||||
levels.DebugValue("dbg"),
|
||||
levels.InfoValue("nfo"),
|
||||
levels.WarnValue("wrn"),
|
||||
levels.ErrorValue("err"),
|
||||
levels.CritValue("crt"),
|
||||
)
|
||||
logger.With("easter_island", "176°").Debug().Log("msg", "moai")
|
||||
if want, have := `{"easter_island":"176°","l":"dbg","msg":"moai"}`+"\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleLevels() {
|
||||
logger := levels.New(log.NewLogfmtLogger(os.Stdout))
|
||||
logger.Debug().Log("msg", "hello")
|
||||
logger.With("context", "foo").Warn().Log("err", "error")
|
||||
|
||||
// Output:
|
||||
// level=debug msg=hello
|
||||
// level=warn context=foo err=error
|
||||
}
|
116
vendor/github.com/go-kit/kit/log/doc.go
generated
vendored
Normal file
116
vendor/github.com/go-kit/kit/log/doc.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// Package log provides a structured logger.
|
||||
//
|
||||
// Structured logging produces logs easily consumed later by humans or
|
||||
// machines. Humans might be interested in debugging errors, or tracing
|
||||
// specific requests. Machines might be interested in counting interesting
|
||||
// events, or aggregating information for off-line processing. In both cases,
|
||||
// it is important that the log messages are structured and actionable.
|
||||
// Package log is designed to encourage both of these best practices.
|
||||
//
|
||||
// Basic Usage
|
||||
//
|
||||
// The fundamental interface is Logger. Loggers create log events from
|
||||
// key/value data. The Logger interface has a single method, Log, which
|
||||
// accepts a sequence of alternating key/value pairs, which this package names
|
||||
// keyvals.
|
||||
//
|
||||
// type Logger interface {
|
||||
// Log(keyvals ...interface{}) error
|
||||
// }
|
||||
//
|
||||
// Here is an example of a function using a Logger to create log events.
|
||||
//
|
||||
// func RunTask(task Task, logger log.Logger) string {
|
||||
// logger.Log("taskID", task.ID, "event", "starting task")
|
||||
// ...
|
||||
// logger.Log("taskID", task.ID, "event", "task complete")
|
||||
// }
|
||||
//
|
||||
// The keys in the above example are "taskID" and "event". The values are
|
||||
// task.ID, "starting task", and "task complete". Every key is followed
|
||||
// immediately by its value.
|
||||
//
|
||||
// Keys are usually plain strings. Values may be any type that has a sensible
|
||||
// encoding in the chosen log format. With structured logging it is a good
|
||||
// idea to log simple values without formatting them. This practice allows
|
||||
// the chosen logger to encode values in the most appropriate way.
|
||||
//
|
||||
// Contextual Loggers
|
||||
//
|
||||
// A contextual logger stores keyvals that it includes in all log events.
|
||||
// Building appropriate contextual loggers reduces repetition and aids
|
||||
// consistency in the resulting log output. With and WithPrefix add context to
|
||||
// a logger. We can use With to improve the RunTask example.
|
||||
//
|
||||
// func RunTask(task Task, logger log.Logger) string {
|
||||
// logger = log.With(logger, "taskID", task.ID)
|
||||
// logger.Log("event", "starting task")
|
||||
// ...
|
||||
// taskHelper(task.Cmd, logger)
|
||||
// ...
|
||||
// logger.Log("event", "task complete")
|
||||
// }
|
||||
//
|
||||
// The improved version emits the same log events as the original for the
|
||||
// first and last calls to Log. Passing the contextual logger to taskHelper
|
||||
// enables each log event created by taskHelper to include the task.ID even
|
||||
// though taskHelper does not have access to that value. Using contextual
|
||||
// loggers this way simplifies producing log output that enables tracing the
|
||||
// life cycle of individual tasks. (See the Contextual example for the full
|
||||
// code of the above snippet.)
|
||||
//
|
||||
// Dynamic Contextual Values
|
||||
//
|
||||
// A Valuer function stored in a contextual logger generates a new value each
|
||||
// time an event is logged. The Valuer example demonstrates how this feature
|
||||
// works.
|
||||
//
|
||||
// Valuers provide the basis for consistently logging timestamps and source
|
||||
// code location. The log package defines several valuers for that purpose.
|
||||
// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and
|
||||
// DefaultCaller. A common logger initialization sequence that ensures all log
|
||||
// entries contain a timestamp and source location looks like this:
|
||||
//
|
||||
// logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
|
||||
// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
|
||||
//
|
||||
// Concurrent Safety
|
||||
//
|
||||
// Applications with multiple goroutines want each log event written to the
|
||||
// same logger to remain separate from other log events. Package log provides
|
||||
// two simple solutions for concurrent safe logging.
|
||||
//
|
||||
// NewSyncWriter wraps an io.Writer and serializes each call to its Write
|
||||
// method. Using a SyncWriter has the benefit that the smallest practical
|
||||
// portion of the logging logic is performed within a mutex, but it requires
|
||||
// the formatting Logger to make only one call to Write per log event.
|
||||
//
|
||||
// NewSyncLogger wraps any Logger and serializes each call to its Log method.
|
||||
// Using a SyncLogger has the benefit that it guarantees each log event is
|
||||
// handled atomically within the wrapped logger, but it typically serializes
|
||||
// both the formatting and output logic. Use a SyncLogger if the formatting
|
||||
// logger may perform multiple writes per log event.
|
||||
//
|
||||
// Error Handling
|
||||
//
|
||||
// This package relies on the practice of wrapping or decorating loggers with
|
||||
// other loggers to provide composable pieces of functionality. It also means
|
||||
// that Logger.Log must return an error because some
|
||||
// implementations—especially those that output log data to an io.Writer—may
|
||||
// encounter errors that cannot be handled locally. This in turn means that
|
||||
// Loggers that wrap other loggers should return errors from the wrapped
|
||||
// logger up the stack.
|
||||
//
|
||||
// Fortunately, the decorator pattern also provides a way to avoid the
|
||||
// necessity to check for errors every time an application calls Logger.Log.
|
||||
// An application required to panic whenever its Logger encounters
|
||||
// an error could initialize its logger as follows.
|
||||
//
|
||||
// fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
|
||||
// logger := log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
// if err := fmtlogger.Log(keyvals...); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// return nil
|
||||
// })
|
||||
package log
|
137
vendor/github.com/go-kit/kit/log/example_test.go
generated
vendored
Normal file
137
vendor/github.com/go-kit/kit/log/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
logger := log.NewLogfmtLogger(os.Stdout)
|
||||
|
||||
type Task struct {
|
||||
ID int
|
||||
}
|
||||
|
||||
RunTask := func(task Task, logger log.Logger) {
|
||||
logger.Log("taskID", task.ID, "event", "starting task")
|
||||
|
||||
logger.Log("taskID", task.ID, "event", "task complete")
|
||||
}
|
||||
|
||||
RunTask(Task{ID: 1}, logger)
|
||||
|
||||
// Output:
|
||||
// taskID=1 event="starting task"
|
||||
// taskID=1 event="task complete"
|
||||
}
|
||||
|
||||
func Example_contextual() {
|
||||
logger := log.NewLogfmtLogger(os.Stdout)
|
||||
|
||||
type Task struct {
|
||||
ID int
|
||||
Cmd string
|
||||
}
|
||||
|
||||
taskHelper := func(cmd string, logger log.Logger) {
|
||||
// execute(cmd)
|
||||
logger.Log("cmd", cmd, "dur", 42*time.Millisecond)
|
||||
}
|
||||
|
||||
RunTask := func(task Task, logger log.Logger) {
|
||||
logger = log.With(logger, "taskID", task.ID)
|
||||
logger.Log("event", "starting task")
|
||||
|
||||
taskHelper(task.Cmd, logger)
|
||||
|
||||
logger.Log("event", "task complete")
|
||||
}
|
||||
|
||||
RunTask(Task{ID: 1, Cmd: "echo Hello, world!"}, logger)
|
||||
|
||||
// Output:
|
||||
// taskID=1 event="starting task"
|
||||
// taskID=1 cmd="echo Hello, world!" dur=42ms
|
||||
// taskID=1 event="task complete"
|
||||
}
|
||||
|
||||
func Example_valuer() {
|
||||
logger := log.NewLogfmtLogger(os.Stdout)
|
||||
|
||||
count := 0
|
||||
counter := func() interface{} {
|
||||
count++
|
||||
return count
|
||||
}
|
||||
|
||||
logger = log.With(logger, "count", log.Valuer(counter))
|
||||
|
||||
logger.Log("call", "first")
|
||||
logger.Log("call", "second")
|
||||
|
||||
// Output:
|
||||
// count=1 call=first
|
||||
// count=2 call=second
|
||||
}
|
||||
|
||||
func Example_debugInfo() {
|
||||
logger := log.NewLogfmtLogger(os.Stdout)
|
||||
|
||||
// make time predictable for this test
|
||||
baseTime := time.Date(2015, time.February, 3, 10, 0, 0, 0, time.UTC)
|
||||
mockTime := func() time.Time {
|
||||
baseTime = baseTime.Add(time.Second)
|
||||
return baseTime
|
||||
}
|
||||
|
||||
logger = log.With(logger, "time", log.Timestamp(mockTime), "caller", log.DefaultCaller)
|
||||
|
||||
logger.Log("call", "first")
|
||||
logger.Log("call", "second")
|
||||
|
||||
// ...
|
||||
|
||||
logger.Log("call", "third")
|
||||
|
||||
// Output:
|
||||
// time=2015-02-03T10:00:01Z caller=example_test.go:93 call=first
|
||||
// time=2015-02-03T10:00:02Z caller=example_test.go:94 call=second
|
||||
// time=2015-02-03T10:00:03Z caller=example_test.go:98 call=third
|
||||
}
|
||||
|
||||
func Example_syncWriter() {
|
||||
w := log.NewSyncWriter(os.Stdout)
|
||||
logger := log.NewLogfmtLogger(w)
|
||||
|
||||
type Task struct {
|
||||
ID int
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
RunTask := func(task Task, logger log.Logger) {
|
||||
logger.Log("taskID", task.ID, "event", "starting task")
|
||||
|
||||
time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
|
||||
|
||||
logger.Log("taskID", task.ID, "event", "task complete")
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
go RunTask(Task{ID: 1}, logger)
|
||||
go RunTask(Task{ID: 2}, logger)
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// Unordered output:
|
||||
// taskID=1 event="starting task"
|
||||
// taskID=2 event="starting task"
|
||||
// taskID=1 event="task complete"
|
||||
// taskID=2 event="task complete"
|
||||
}
|
92
vendor/github.com/go-kit/kit/log/json_logger.go
generated
vendored
Normal file
92
vendor/github.com/go-kit/kit/log/json_logger.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type jsonLogger struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a
|
||||
// single JSON object. Each log event produces no more than one call to
|
||||
// w.Write. The passed Writer must be safe for concurrent use by multiple
|
||||
// goroutines if the returned Logger will be used concurrently.
|
||||
func NewJSONLogger(w io.Writer) Logger {
|
||||
return &jsonLogger{w}
|
||||
}
|
||||
|
||||
func (l *jsonLogger) Log(keyvals ...interface{}) error {
|
||||
n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd
|
||||
m := make(map[string]interface{}, n)
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
k := keyvals[i]
|
||||
var v interface{} = ErrMissingValue
|
||||
if i+1 < len(keyvals) {
|
||||
v = keyvals[i+1]
|
||||
}
|
||||
merge(m, k, v)
|
||||
}
|
||||
return json.NewEncoder(l.Writer).Encode(m)
|
||||
}
|
||||
|
||||
func merge(dst map[string]interface{}, k, v interface{}) {
|
||||
var key string
|
||||
switch x := k.(type) {
|
||||
case string:
|
||||
key = x
|
||||
case fmt.Stringer:
|
||||
key = safeString(x)
|
||||
default:
|
||||
key = fmt.Sprint(x)
|
||||
}
|
||||
if x, ok := v.(error); ok {
|
||||
v = safeError(x)
|
||||
}
|
||||
|
||||
// We want json.Marshaler and encoding.TextMarshaller to take priority over
|
||||
// err.Error() and v.String(). But json.Marshall (called later) does that by
|
||||
// default so we force a no-op if it's one of those 2 case.
|
||||
switch x := v.(type) {
|
||||
case json.Marshaler:
|
||||
case encoding.TextMarshaler:
|
||||
case error:
|
||||
v = safeError(x)
|
||||
case fmt.Stringer:
|
||||
v = safeString(x)
|
||||
}
|
||||
|
||||
dst[key] = v
|
||||
}
|
||||
|
||||
func safeString(str fmt.Stringer) (s string) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
s = "NULL"
|
||||
} else {
|
||||
panic(panicVal)
|
||||
}
|
||||
}
|
||||
}()
|
||||
s = str.String()
|
||||
return
|
||||
}
|
||||
|
||||
func safeError(err error) (s interface{}) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
s = nil
|
||||
} else {
|
||||
panic(panicVal)
|
||||
}
|
||||
}
|
||||
}()
|
||||
s = err.Error()
|
||||
return
|
||||
}
|
158
vendor/github.com/go-kit/kit/log/json_logger_test.go
generated
vendored
Normal file
158
vendor/github.com/go-kit/kit/log/json_logger_test.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func TestJSONLoggerCaller(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewJSONLogger(buf)
|
||||
logger = log.With(logger, "caller", log.DefaultCaller)
|
||||
|
||||
if err := logger.Log(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := `{"caller":"json_logger_test.go:18"}`+"\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONLogger(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewJSONLogger(buf)
|
||||
if err := logger.Log("err", errors.New("err"), "m", map[string]int{"0": 0}, "a", []int{1, 2, 3}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := `{"a":[1,2,3],"err":"err","m":{"0":0}}`+"\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONLoggerMissingValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewJSONLogger(buf)
|
||||
if err := logger.Log("k"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := `{"k":"(MISSING)"}`+"\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONLoggerNilStringerKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewJSONLogger(buf)
|
||||
if err := logger.Log((*stringer)(nil), "v"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := `{"NULL":"v"}`+"\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONLoggerNilErrorValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewJSONLogger(buf)
|
||||
if err := logger.Log("err", (*stringError)(nil)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := `{"err":null}`+"\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
// aller implements json.Marshaler, encoding.TextMarshaler, and fmt.Stringer.
|
||||
type aller struct{}
|
||||
|
||||
func (aller) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"json\""), nil
|
||||
}
|
||||
|
||||
func (aller) MarshalText() ([]byte, error) {
|
||||
return []byte("text"), nil
|
||||
}
|
||||
|
||||
func (aller) String() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
// textstringer implements encoding.TextMarshaler and fmt.Stringer.
|
||||
type textstringer struct{}
|
||||
|
||||
func (textstringer) MarshalText() ([]byte, error) {
|
||||
return []byte("text"), nil
|
||||
}
|
||||
|
||||
func (textstringer) String() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func TestJSONLoggerStringValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
v interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
v: aller{},
|
||||
expected: `{"v":"json"}`,
|
||||
},
|
||||
{
|
||||
v: textstringer{},
|
||||
expected: `{"v":"text"}`,
|
||||
},
|
||||
{
|
||||
v: stringer("string"),
|
||||
expected: `{"v":"string"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewJSONLogger(buf)
|
||||
if err := logger.Log("v", test.v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want, have := test.expected+"\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type stringer string
|
||||
|
||||
func (s stringer) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type stringError string
|
||||
|
||||
func (s stringError) Error() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func BenchmarkJSONLoggerSimple(b *testing.B) {
|
||||
benchmarkRunner(b, log.NewJSONLogger(ioutil.Discard), baseMessage)
|
||||
}
|
||||
|
||||
func BenchmarkJSONLoggerContextual(b *testing.B) {
|
||||
benchmarkRunner(b, log.NewJSONLogger(ioutil.Discard), withMessage)
|
||||
}
|
||||
|
||||
func TestJSONLoggerConcurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
testConcurrency(t, log.NewJSONLogger(ioutil.Discard), 10000)
|
||||
}
|
72
vendor/github.com/go-kit/kit/log/level/benchmark_test.go
generated
vendored
Normal file
72
vendor/github.com/go-kit/kit/log/level/benchmark_test.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package level_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
)
|
||||
|
||||
func Benchmark(b *testing.B) {
|
||||
contexts := []struct {
|
||||
name string
|
||||
context func(log.Logger) log.Logger
|
||||
}{
|
||||
{"NoContext", func(l log.Logger) log.Logger {
|
||||
return l
|
||||
}},
|
||||
{"TimeContext", func(l log.Logger) log.Logger {
|
||||
return log.With(l, "time", log.DefaultTimestampUTC)
|
||||
}},
|
||||
{"CallerContext", func(l log.Logger) log.Logger {
|
||||
return log.With(l, "caller", log.DefaultCaller)
|
||||
}},
|
||||
{"TimeCallerReqIDContext", func(l log.Logger) log.Logger {
|
||||
return log.With(l, "time", log.DefaultTimestampUTC, "caller", log.DefaultCaller, "reqID", 29)
|
||||
}},
|
||||
}
|
||||
|
||||
loggers := []struct {
|
||||
name string
|
||||
logger log.Logger
|
||||
}{
|
||||
{"Nop", log.NewNopLogger()},
|
||||
{"Logfmt", log.NewLogfmtLogger(ioutil.Discard)},
|
||||
{"JSON", log.NewJSONLogger(ioutil.Discard)},
|
||||
}
|
||||
|
||||
filters := []struct {
|
||||
name string
|
||||
filter func(log.Logger) log.Logger
|
||||
}{
|
||||
{"Baseline", func(l log.Logger) log.Logger {
|
||||
return l
|
||||
}},
|
||||
{"DisallowedLevel", func(l log.Logger) log.Logger {
|
||||
return level.NewFilter(l, level.AllowInfo())
|
||||
}},
|
||||
{"AllowedLevel", func(l log.Logger) log.Logger {
|
||||
return level.NewFilter(l, level.AllowAll())
|
||||
}},
|
||||
}
|
||||
|
||||
for _, c := range contexts {
|
||||
b.Run(c.name, func(b *testing.B) {
|
||||
for _, f := range filters {
|
||||
b.Run(f.name, func(b *testing.B) {
|
||||
for _, l := range loggers {
|
||||
b.Run(l.name, func(b *testing.B) {
|
||||
logger := c.context(f.filter(l.logger))
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
level.Debug(logger).Log("foo", "bar")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
22
vendor/github.com/go-kit/kit/log/level/doc.go
generated
vendored
Normal file
22
vendor/github.com/go-kit/kit/log/level/doc.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Package level implements leveled logging on top of package log. To use the
|
||||
// level package, create a logger as per normal in your func main, and wrap it
|
||||
// with level.NewFilter.
|
||||
//
|
||||
// var logger log.Logger
|
||||
// logger = log.NewLogfmtLogger(os.Stderr)
|
||||
// logger = level.NewFilter(logger, level.AllowInfoAndAbove()) // <--
|
||||
// logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
|
||||
//
|
||||
// Then, at the callsites, use one of the level.Debug, Info, Warn, or Error
|
||||
// helper methods to emit leveled log events.
|
||||
//
|
||||
// logger.Log("foo", "bar") // as normal, no level
|
||||
// level.Debug(logger).Log("request_id", reqID, "trace_data", trace.Get())
|
||||
// if value > 100 {
|
||||
// level.Error(logger).Log("value", value)
|
||||
// }
|
||||
//
|
||||
// NewFilter allows precise control over what happens when a log event is
|
||||
// emitted without a level key, or if a squelched level is used. Check the
|
||||
// Option functions for details.
|
||||
package level
|
25
vendor/github.com/go-kit/kit/log/level/example_test.go
generated
vendored
Normal file
25
vendor/github.com/go-kit/kit/log/level/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package level_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
// setup logger with level filter
|
||||
logger := log.NewLogfmtLogger(os.Stdout)
|
||||
logger = level.NewFilter(logger, level.AllowInfo())
|
||||
logger = log.With(logger, "caller", log.DefaultCaller)
|
||||
|
||||
// use level helpers to log at different levels
|
||||
level.Error(logger).Log("err", errors.New("bad data"))
|
||||
level.Info(logger).Log("event", "data saved")
|
||||
level.Debug(logger).Log("next item", 17) // filtered
|
||||
|
||||
// Output:
|
||||
// level=error caller=example_test.go:18 err="bad data"
|
||||
// level=info caller=example_test.go:19 event="data saved"
|
||||
}
|
205
vendor/github.com/go-kit/kit/log/level/level.go
generated
vendored
Normal file
205
vendor/github.com/go-kit/kit/log/level/level.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
package level
|
||||
|
||||
import "github.com/go-kit/kit/log"
|
||||
|
||||
// Error returns a logger that includes a Key/ErrorValue pair.
|
||||
func Error(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), ErrorValue())
|
||||
}
|
||||
|
||||
// Warn returns a logger that includes a Key/WarnValue pair.
|
||||
func Warn(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), WarnValue())
|
||||
}
|
||||
|
||||
// Info returns a logger that includes a Key/InfoValue pair.
|
||||
func Info(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), InfoValue())
|
||||
}
|
||||
|
||||
// Debug returns a logger that includes a Key/DebugValue pair.
|
||||
func Debug(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), DebugValue())
|
||||
}
|
||||
|
||||
// NewFilter wraps next and implements level filtering. See the commentary on
|
||||
// the Option functions for a detailed description of how to configure levels.
|
||||
// If no options are provided, all leveled log events created with Debug,
|
||||
// Info, Warn or Error helper methods are squelched and non-leveled log
|
||||
// events are passed to next unmodified.
|
||||
func NewFilter(next log.Logger, options ...Option) log.Logger {
|
||||
l := &logger{
|
||||
next: next,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(l)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
next log.Logger
|
||||
allowed level
|
||||
squelchNoLevel bool
|
||||
errNotAllowed error
|
||||
errNoLevel error
|
||||
}
|
||||
|
||||
func (l *logger) Log(keyvals ...interface{}) error {
|
||||
var hasLevel, levelAllowed bool
|
||||
for i := 1; i < len(keyvals); i += 2 {
|
||||
if v, ok := keyvals[i].(*levelValue); ok {
|
||||
hasLevel = true
|
||||
levelAllowed = l.allowed&v.level != 0
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasLevel && l.squelchNoLevel {
|
||||
return l.errNoLevel
|
||||
}
|
||||
if hasLevel && !levelAllowed {
|
||||
return l.errNotAllowed
|
||||
}
|
||||
return l.next.Log(keyvals...)
|
||||
}
|
||||
|
||||
// Option sets a parameter for the leveled logger.
|
||||
type Option func(*logger)
|
||||
|
||||
// AllowAll is an alias for AllowDebug.
|
||||
func AllowAll() Option {
|
||||
return AllowDebug()
|
||||
}
|
||||
|
||||
// AllowDebug allows error, warn, info and debug level log events to pass.
|
||||
func AllowDebug() Option {
|
||||
return allowed(levelError | levelWarn | levelInfo | levelDebug)
|
||||
}
|
||||
|
||||
// AllowInfo allows error, warn and info level log events to pass.
|
||||
func AllowInfo() Option {
|
||||
return allowed(levelError | levelWarn | levelInfo)
|
||||
}
|
||||
|
||||
// AllowWarn allows error and warn level log events to pass.
|
||||
func AllowWarn() Option {
|
||||
return allowed(levelError | levelWarn)
|
||||
}
|
||||
|
||||
// AllowError allows only error level log events to pass.
|
||||
func AllowError() Option {
|
||||
return allowed(levelError)
|
||||
}
|
||||
|
||||
// AllowNone allows no leveled log events to pass.
|
||||
func AllowNone() Option {
|
||||
return allowed(0)
|
||||
}
|
||||
|
||||
func allowed(allowed level) Option {
|
||||
return func(l *logger) { l.allowed = allowed }
|
||||
}
|
||||
|
||||
// ErrNotAllowed sets the error to return from Log when it squelches a log
|
||||
// event disallowed by the configured Allow[Level] option. By default,
|
||||
// ErrNotAllowed is nil; in this case the log event is squelched with no
|
||||
// error.
|
||||
func ErrNotAllowed(err error) Option {
|
||||
return func(l *logger) { l.errNotAllowed = err }
|
||||
}
|
||||
|
||||
// SquelchNoLevel instructs Log to squelch log events with no level, so that
|
||||
// they don't proceed through to the wrapped logger. If SquelchNoLevel is set
|
||||
// to true and a log event is squelched in this way, the error value
|
||||
// configured with ErrNoLevel is returned to the caller.
|
||||
func SquelchNoLevel(squelch bool) Option {
|
||||
return func(l *logger) { l.squelchNoLevel = squelch }
|
||||
}
|
||||
|
||||
// ErrNoLevel sets the error to return from Log when it squelches a log event
|
||||
// with no level. By default, ErrNoLevel is nil; in this case the log event is
|
||||
// squelched with no error.
|
||||
func ErrNoLevel(err error) Option {
|
||||
return func(l *logger) { l.errNoLevel = err }
|
||||
}
|
||||
|
||||
// NewInjector wraps next and returns a logger that adds a Key/level pair to
|
||||
// the beginning of log events that don't already contain a level. In effect,
|
||||
// this gives a default level to logs without a level.
|
||||
func NewInjector(next log.Logger, level Value) log.Logger {
|
||||
return &injector{
|
||||
next: next,
|
||||
level: level,
|
||||
}
|
||||
}
|
||||
|
||||
type injector struct {
|
||||
next log.Logger
|
||||
level interface{}
|
||||
}
|
||||
|
||||
func (l *injector) Log(keyvals ...interface{}) error {
|
||||
for i := 1; i < len(keyvals); i += 2 {
|
||||
if _, ok := keyvals[i].(*levelValue); ok {
|
||||
return l.next.Log(keyvals...)
|
||||
}
|
||||
}
|
||||
kvs := make([]interface{}, len(keyvals)+2)
|
||||
kvs[0], kvs[1] = key, l.level
|
||||
copy(kvs[2:], keyvals)
|
||||
return l.next.Log(kvs...)
|
||||
}
|
||||
|
||||
// Value is the interface that each of the canonical level values implement.
|
||||
// It contains unexported methods that prevent types from other packages from
|
||||
// implementing it and guaranteeing that NewFilter can distinguish the levels
|
||||
// defined in this package from all other values.
|
||||
type Value interface {
|
||||
String() string
|
||||
levelVal()
|
||||
}
|
||||
|
||||
// Key returns the unique key added to log events by the loggers in this
|
||||
// package.
|
||||
func Key() interface{} { return key }
|
||||
|
||||
// ErrorValue returns the unique value added to log events by Error.
|
||||
func ErrorValue() Value { return errorValue }
|
||||
|
||||
// WarnValue returns the unique value added to log events by Warn.
|
||||
func WarnValue() Value { return warnValue }
|
||||
|
||||
// InfoValue returns the unique value added to log events by Info.
|
||||
func InfoValue() Value { return infoValue }
|
||||
|
||||
// DebugValue returns the unique value added to log events by Warn.
|
||||
func DebugValue() Value { return debugValue }
|
||||
|
||||
var (
|
||||
// key is of type interfae{} so that it allocates once during package
|
||||
// initialization and avoids allocating every type the value is added to a
|
||||
// []interface{} later.
|
||||
key interface{} = "level"
|
||||
|
||||
errorValue = &levelValue{level: levelError, name: "error"}
|
||||
warnValue = &levelValue{level: levelWarn, name: "warn"}
|
||||
infoValue = &levelValue{level: levelInfo, name: "info"}
|
||||
debugValue = &levelValue{level: levelDebug, name: "debug"}
|
||||
)
|
||||
|
||||
type level byte
|
||||
|
||||
const (
|
||||
levelDebug level = 1 << iota
|
||||
levelInfo
|
||||
levelWarn
|
||||
levelError
|
||||
)
|
||||
|
||||
type levelValue struct {
|
||||
name string
|
||||
level
|
||||
}
|
||||
|
||||
func (v *levelValue) String() string { return v.name }
|
||||
func (v *levelValue) levelVal() {}
|
235
vendor/github.com/go-kit/kit/log/level/level_test.go
generated
vendored
Normal file
235
vendor/github.com/go-kit/kit/log/level/level_test.go
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
package level_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
)
|
||||
|
||||
func TestVariousLevels(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
allowed level.Option
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"AllowAll",
|
||||
level.AllowAll(),
|
||||
strings.Join([]string{
|
||||
`{"level":"debug","this is":"debug log"}`,
|
||||
`{"level":"info","this is":"info log"}`,
|
||||
`{"level":"warn","this is":"warn log"}`,
|
||||
`{"level":"error","this is":"error log"}`,
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"AllowDebug",
|
||||
level.AllowDebug(),
|
||||
strings.Join([]string{
|
||||
`{"level":"debug","this is":"debug log"}`,
|
||||
`{"level":"info","this is":"info log"}`,
|
||||
`{"level":"warn","this is":"warn log"}`,
|
||||
`{"level":"error","this is":"error log"}`,
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"AllowDebug",
|
||||
level.AllowInfo(),
|
||||
strings.Join([]string{
|
||||
`{"level":"info","this is":"info log"}`,
|
||||
`{"level":"warn","this is":"warn log"}`,
|
||||
`{"level":"error","this is":"error log"}`,
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"AllowWarn",
|
||||
level.AllowWarn(),
|
||||
strings.Join([]string{
|
||||
`{"level":"warn","this is":"warn log"}`,
|
||||
`{"level":"error","this is":"error log"}`,
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"AllowError",
|
||||
level.AllowError(),
|
||||
strings.Join([]string{
|
||||
`{"level":"error","this is":"error log"}`,
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"AllowNone",
|
||||
level.AllowNone(),
|
||||
``,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := level.NewFilter(log.NewJSONLogger(&buf), tc.allowed)
|
||||
|
||||
level.Debug(logger).Log("this is", "debug log")
|
||||
level.Info(logger).Log("this is", "info log")
|
||||
level.Warn(logger).Log("this is", "warn log")
|
||||
level.Error(logger).Log("this is", "error log")
|
||||
|
||||
if want, have := tc.want, strings.TrimSpace(buf.String()); want != have {
|
||||
t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrNotAllowed(t *testing.T) {
|
||||
myError := errors.New("squelched!")
|
||||
opts := []level.Option{
|
||||
level.AllowWarn(),
|
||||
level.ErrNotAllowed(myError),
|
||||
}
|
||||
logger := level.NewFilter(log.NewNopLogger(), opts...)
|
||||
|
||||
if want, have := myError, level.Info(logger).Log("foo", "bar"); want != have {
|
||||
t.Errorf("want %#+v, have %#+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := error(nil), level.Warn(logger).Log("foo", "bar"); want != have {
|
||||
t.Errorf("want %#+v, have %#+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrNoLevel(t *testing.T) {
|
||||
myError := errors.New("no level specified")
|
||||
|
||||
var buf bytes.Buffer
|
||||
opts := []level.Option{
|
||||
level.SquelchNoLevel(true),
|
||||
level.ErrNoLevel(myError),
|
||||
}
|
||||
logger := level.NewFilter(log.NewJSONLogger(&buf), opts...)
|
||||
|
||||
if want, have := myError, logger.Log("foo", "bar"); want != have {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
if want, have := ``, strings.TrimSpace(buf.String()); want != have {
|
||||
t.Errorf("\nwant '%s'\nhave '%s'", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowNoLevel(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
opts := []level.Option{
|
||||
level.SquelchNoLevel(false),
|
||||
level.ErrNoLevel(errors.New("I should never be returned!")),
|
||||
}
|
||||
logger := level.NewFilter(log.NewJSONLogger(&buf), opts...)
|
||||
|
||||
if want, have := error(nil), logger.Log("foo", "bar"); want != have {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
if want, have := `{"foo":"bar"}`, strings.TrimSpace(buf.String()); want != have {
|
||||
t.Errorf("\nwant '%s'\nhave '%s'", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLevelContext(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Wrapping the level logger with a context allows users to use
|
||||
// log.DefaultCaller as per normal.
|
||||
var logger log.Logger
|
||||
logger = log.NewLogfmtLogger(&buf)
|
||||
logger = level.NewFilter(logger, level.AllowAll())
|
||||
logger = log.With(logger, "caller", log.DefaultCaller)
|
||||
|
||||
level.Info(logger).Log("foo", "bar")
|
||||
if want, have := `level=info caller=level_test.go:149 foo=bar`, strings.TrimSpace(buf.String()); want != have {
|
||||
t.Errorf("\nwant '%s'\nhave '%s'", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextLevel(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Wrapping a context with the level logger still works, but requires users
|
||||
// to specify a higher callstack depth value.
|
||||
var logger log.Logger
|
||||
logger = log.NewLogfmtLogger(&buf)
|
||||
logger = log.With(logger, "caller", log.Caller(5))
|
||||
logger = level.NewFilter(logger, level.AllowAll())
|
||||
|
||||
level.Info(logger).Log("foo", "bar")
|
||||
if want, have := `caller=level_test.go:165 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have {
|
||||
t.Errorf("\nwant '%s'\nhave '%s'", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLevelFormatting(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
format func(io.Writer) log.Logger
|
||||
output string
|
||||
}{
|
||||
{
|
||||
name: "logfmt",
|
||||
format: log.NewLogfmtLogger,
|
||||
output: `level=info foo=bar`,
|
||||
},
|
||||
{
|
||||
name: "JSON",
|
||||
format: log.NewJSONLogger,
|
||||
output: `{"foo":"bar","level":"info"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
logger := tc.format(&buf)
|
||||
level.Info(logger).Log("foo", "bar")
|
||||
if want, have := tc.output, strings.TrimSpace(buf.String()); want != have {
|
||||
t.Errorf("\nwant: '%s'\nhave '%s'", want, have)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInjector(t *testing.T) {
|
||||
var (
|
||||
output []interface{}
|
||||
logger log.Logger
|
||||
)
|
||||
|
||||
logger = log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
output = keyvals
|
||||
return nil
|
||||
})
|
||||
logger = level.NewInjector(logger, level.InfoValue())
|
||||
|
||||
logger.Log("foo", "bar")
|
||||
if got, want := len(output), 4; got != want {
|
||||
t.Errorf("missing level not injected: got len==%d, want len==%d", got, want)
|
||||
}
|
||||
if got, want := output[0], level.Key(); got != want {
|
||||
t.Errorf("wrong level key: got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := output[1], level.InfoValue(); got != want {
|
||||
t.Errorf("wrong level value: got %#v, want %#v", got, want)
|
||||
}
|
||||
|
||||
level.Error(logger).Log("foo", "bar")
|
||||
if got, want := len(output), 4; got != want {
|
||||
t.Errorf("leveled record modified: got len==%d, want len==%d", got, want)
|
||||
}
|
||||
if got, want := output[0], level.Key(); got != want {
|
||||
t.Errorf("wrong level key: got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := output[1], level.ErrorValue(); got != want {
|
||||
t.Errorf("wrong level value: got %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
135
vendor/github.com/go-kit/kit/log/log.go
generated
vendored
Normal file
135
vendor/github.com/go-kit/kit/log/log.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package log
|
||||
|
||||
import "errors"
|
||||
|
||||
// Logger is the fundamental interface for all log operations. Log creates a
|
||||
// log event from keyvals, a variadic sequence of alternating keys and values.
|
||||
// Implementations must be safe for concurrent use by multiple goroutines. In
|
||||
// particular, any implementation of Logger that appends to keyvals or
|
||||
// modifies or retains any of its elements must make a copy first.
|
||||
type Logger interface {
|
||||
Log(keyvals ...interface{}) error
|
||||
}
|
||||
|
||||
// ErrMissingValue is appended to keyvals slices with odd length to substitute
|
||||
// the missing value.
|
||||
var ErrMissingValue = errors.New("(MISSING)")
|
||||
|
||||
// With returns a new contextual logger with keyvals prepended to those passed
|
||||
// to calls to Log. If logger is also a contextual logger created by With or
|
||||
// WithPrefix, keyvals is appended to the existing context.
|
||||
//
|
||||
// The returned Logger replaces all value elements (odd indexes) containing a
|
||||
// Valuer with their generated value for each call to its Log method.
|
||||
func With(logger Logger, keyvals ...interface{}) Logger {
|
||||
if len(keyvals) == 0 {
|
||||
return logger
|
||||
}
|
||||
l := newContext(logger)
|
||||
kvs := append(l.keyvals, keyvals...)
|
||||
if len(kvs)%2 != 0 {
|
||||
kvs = append(kvs, ErrMissingValue)
|
||||
}
|
||||
return &context{
|
||||
logger: l.logger,
|
||||
// Limiting the capacity of the stored keyvals ensures that a new
|
||||
// backing array is created if the slice must grow in Log or With.
|
||||
// Using the extra capacity without copying risks a data race that
|
||||
// would violate the Logger interface contract.
|
||||
keyvals: kvs[:len(kvs):len(kvs)],
|
||||
hasValuer: l.hasValuer || containsValuer(keyvals),
|
||||
}
|
||||
}
|
||||
|
||||
// WithPrefix returns a new contextual logger with keyvals prepended to those
|
||||
// passed to calls to Log. If logger is also a contextual logger created by
|
||||
// With or WithPrefix, keyvals is prepended to the existing context.
|
||||
//
|
||||
// The returned Logger replaces all value elements (odd indexes) containing a
|
||||
// Valuer with their generated value for each call to its Log method.
|
||||
func WithPrefix(logger Logger, keyvals ...interface{}) Logger {
|
||||
if len(keyvals) == 0 {
|
||||
return logger
|
||||
}
|
||||
l := newContext(logger)
|
||||
// Limiting the capacity of the stored keyvals ensures that a new
|
||||
// backing array is created if the slice must grow in Log or With.
|
||||
// Using the extra capacity without copying risks a data race that
|
||||
// would violate the Logger interface contract.
|
||||
n := len(l.keyvals) + len(keyvals)
|
||||
if len(keyvals)%2 != 0 {
|
||||
n++
|
||||
}
|
||||
kvs := make([]interface{}, 0, n)
|
||||
kvs = append(kvs, keyvals...)
|
||||
if len(kvs)%2 != 0 {
|
||||
kvs = append(kvs, ErrMissingValue)
|
||||
}
|
||||
kvs = append(kvs, l.keyvals...)
|
||||
return &context{
|
||||
logger: l.logger,
|
||||
keyvals: kvs,
|
||||
hasValuer: l.hasValuer || containsValuer(keyvals),
|
||||
}
|
||||
}
|
||||
|
||||
// context is the Logger implementation returned by With and WithPrefix. It
|
||||
// wraps a Logger and holds keyvals that it includes in all log events. Its
|
||||
// Log method calls bindValues to generate values for each Valuer in the
|
||||
// context keyvals.
|
||||
//
|
||||
// A context must always have the same number of stack frames between calls to
|
||||
// its Log method and the eventual binding of Valuers to their value. This
|
||||
// requirement comes from the functional requirement to allow a context to
|
||||
// resolve application call site information for a Caller stored in the
|
||||
// context. To do this we must be able to predict the number of logging
|
||||
// functions on the stack when bindValues is called.
|
||||
//
|
||||
// Two implementation details provide the needed stack depth consistency.
|
||||
//
|
||||
// 1. newContext avoids introducing an additional layer when asked to
|
||||
// wrap another context.
|
||||
// 2. With and WithPrefix avoid introducing an additional layer by
|
||||
// returning a newly constructed context with a merged keyvals rather
|
||||
// than simply wrapping the existing context.
|
||||
type context struct {
|
||||
logger Logger
|
||||
keyvals []interface{}
|
||||
hasValuer bool
|
||||
}
|
||||
|
||||
func newContext(logger Logger) *context {
|
||||
if c, ok := logger.(*context); ok {
|
||||
return c
|
||||
}
|
||||
return &context{logger: logger}
|
||||
}
|
||||
|
||||
// Log replaces all value elements (odd indexes) containing a Valuer in the
|
||||
// stored context with their generated value, appends keyvals, and passes the
|
||||
// result to the wrapped Logger.
|
||||
func (l *context) Log(keyvals ...interface{}) error {
|
||||
kvs := append(l.keyvals, keyvals...)
|
||||
if len(kvs)%2 != 0 {
|
||||
kvs = append(kvs, ErrMissingValue)
|
||||
}
|
||||
if l.hasValuer {
|
||||
// If no keyvals were appended above then we must copy l.keyvals so
|
||||
// that future log events will reevaluate the stored Valuers.
|
||||
if len(keyvals) == 0 {
|
||||
kvs = append([]interface{}{}, l.keyvals...)
|
||||
}
|
||||
bindValues(kvs[:len(l.keyvals)])
|
||||
}
|
||||
return l.logger.Log(kvs...)
|
||||
}
|
||||
|
||||
// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If
|
||||
// f is a function with the appropriate signature, LoggerFunc(f) is a Logger
|
||||
// object that calls f.
|
||||
type LoggerFunc func(...interface{}) error
|
||||
|
||||
// Log implements Logger by calling f(keyvals...).
|
||||
func (f LoggerFunc) Log(keyvals ...interface{}) error {
|
||||
return f(keyvals...)
|
||||
}
|
191
vendor/github.com/go-kit/kit/log/log_test.go
generated
vendored
Normal file
191
vendor/github.com/go-kit/kit/log/log_test.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-stack/stack"
|
||||
)
|
||||
|
||||
func TestContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewLogfmtLogger(buf)
|
||||
|
||||
kvs := []interface{}{"a", 123}
|
||||
lc := log.With(logger, kvs...)
|
||||
kvs[1] = 0 // With should copy its key values
|
||||
|
||||
lc = log.With(lc, "b", "c") // With should stack
|
||||
if err := lc.Log("msg", "message"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := "a=123 b=c msg=message\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant: %shave: %s", want, have)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
lc = log.WithPrefix(lc, "p", "first")
|
||||
if err := lc.Log("msg", "message"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := "p=first a=123 b=c msg=message\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant: %shave: %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextMissingValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
var output []interface{}
|
||||
logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
output = keyvals
|
||||
return nil
|
||||
}))
|
||||
|
||||
log.WithPrefix(log.With(logger, "k1"), "k0").Log("k2")
|
||||
if want, have := 6, len(output); want != have {
|
||||
t.Errorf("want len(output) == %v, have %v", want, have)
|
||||
}
|
||||
for i := 1; i < 6; i += 2 {
|
||||
if want, have := log.ErrMissingValue, output[i]; want != have {
|
||||
t.Errorf("want output[%d] == %#v, have %#v", i, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that context.Log has a consistent function stack depth when binding
|
||||
// Valuers, regardless of how many times With has been called.
|
||||
func TestContextStackDepth(t *testing.T) {
|
||||
t.Parallel()
|
||||
fn := fmt.Sprintf("%n", stack.Caller(0))
|
||||
|
||||
var output []interface{}
|
||||
|
||||
logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
output = keyvals
|
||||
return nil
|
||||
}))
|
||||
|
||||
stackValuer := log.Valuer(func() interface{} {
|
||||
for i, c := range stack.Trace() {
|
||||
if fmt.Sprintf("%n", c) == fn {
|
||||
return i
|
||||
}
|
||||
}
|
||||
t.Fatal("Test function not found in stack trace.")
|
||||
return nil
|
||||
})
|
||||
|
||||
logger = log.With(logger, "stack", stackValuer)
|
||||
|
||||
// Call through interface to get baseline.
|
||||
logger.Log("k", "v")
|
||||
want := output[1].(int)
|
||||
|
||||
for len(output) < 10 {
|
||||
logger.Log("k", "v")
|
||||
if have := output[1]; have != want {
|
||||
t.Errorf("%d Withs: have %v, want %v", len(output)/2-1, have, want)
|
||||
}
|
||||
|
||||
wrapped := log.With(logger)
|
||||
wrapped.Log("k", "v")
|
||||
if have := output[1]; have != want {
|
||||
t.Errorf("%d Withs: have %v, want %v", len(output)/2-1, have, want)
|
||||
}
|
||||
|
||||
logger = log.With(logger, "k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that With returns a Logger safe for concurrent use. This test
|
||||
// validates that the stored logging context does not get corrupted when
|
||||
// multiple clients concurrently log additional keyvals.
|
||||
//
|
||||
// This test must be run with go test -cpu 2 (or more) to achieve its goal.
|
||||
func TestWithConcurrent(t *testing.T) {
|
||||
// Create some buckets to count how many events each goroutine logs.
|
||||
const goroutines = 8
|
||||
counts := [goroutines]int{}
|
||||
|
||||
// This logger extracts a goroutine id from the last value field and
|
||||
// increments the referenced bucket.
|
||||
logger := log.LoggerFunc(func(kv ...interface{}) error {
|
||||
goroutine := kv[len(kv)-1].(int)
|
||||
counts[goroutine]++
|
||||
return nil
|
||||
})
|
||||
|
||||
// With must be careful about handling slices that can grow without
|
||||
// copying the underlying array, so give it a challenge.
|
||||
l := log.With(logger, make([]interface{}, 0, 2)...)
|
||||
|
||||
// Start logging concurrently. Each goroutine logs its id so the logger
|
||||
// can bucket the event counts.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(goroutines)
|
||||
const n = 10000
|
||||
for i := 0; i < goroutines; i++ {
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < n; j++ {
|
||||
l.Log("goroutineIdx", idx)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for bucket, have := range counts {
|
||||
if want := n; want != have {
|
||||
t.Errorf("bucket %d: want %d, have %d", bucket, want, have) // note Errorf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDiscard(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOneWith(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.With(logger, "k", "v")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTwoWith(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.With(logger, "k", "v")
|
||||
for i := 1; i < 2; i++ {
|
||||
lc = log.With(lc, "k", "v")
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTenWith(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.With(logger, "k", "v")
|
||||
for i := 1; i < 10; i++ {
|
||||
lc = log.With(lc, "k", "v")
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
62
vendor/github.com/go-kit/kit/log/logfmt_logger.go
generated
vendored
Normal file
62
vendor/github.com/go-kit/kit/log/logfmt_logger.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/go-logfmt/logfmt"
|
||||
)
|
||||
|
||||
type logfmtEncoder struct {
|
||||
*logfmt.Encoder
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (l *logfmtEncoder) Reset() {
|
||||
l.Encoder.Reset()
|
||||
l.buf.Reset()
|
||||
}
|
||||
|
||||
var logfmtEncoderPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
var enc logfmtEncoder
|
||||
enc.Encoder = logfmt.NewEncoder(&enc.buf)
|
||||
return &enc
|
||||
},
|
||||
}
|
||||
|
||||
type logfmtLogger struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in
|
||||
// logfmt format. Each log event produces no more than one call to w.Write.
|
||||
// The passed Writer must be safe for concurrent use by multiple goroutines if
|
||||
// the returned Logger will be used concurrently.
|
||||
func NewLogfmtLogger(w io.Writer) Logger {
|
||||
return &logfmtLogger{w}
|
||||
}
|
||||
|
||||
func (l logfmtLogger) Log(keyvals ...interface{}) error {
|
||||
enc := logfmtEncoderPool.Get().(*logfmtEncoder)
|
||||
enc.Reset()
|
||||
defer logfmtEncoderPool.Put(enc)
|
||||
|
||||
if err := enc.EncodeKeyvals(keyvals...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add newline to the end of the buffer
|
||||
if err := enc.EndRecord(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The Logger interface requires implementations to be safe for concurrent
|
||||
// use by multiple goroutines. For this implementation that means making
|
||||
// only one call to l.w.Write() for each call to Log.
|
||||
if _, err := l.w.Write(enc.buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
57
vendor/github.com/go-kit/kit/log/logfmt_logger_test.go
generated
vendored
Normal file
57
vendor/github.com/go-kit/kit/log/logfmt_logger_test.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-logfmt/logfmt"
|
||||
)
|
||||
|
||||
func TestLogfmtLogger(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.NewLogfmtLogger(buf)
|
||||
|
||||
if err := logger.Log("hello", "world"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := "hello=world\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if err := logger.Log("a", 1, "err", errors.New("error")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := "a=1 err=error\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if err := logger.Log("std_map", map[int]int{1: 2}, "my_map", mymap{0: 0}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := "std_map=\""+logfmt.ErrUnsupportedValueType.Error()+"\" my_map=special_behavior\n", buf.String(); want != have {
|
||||
t.Errorf("want %#v, have %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLogfmtLoggerSimple(b *testing.B) {
|
||||
benchmarkRunner(b, log.NewLogfmtLogger(ioutil.Discard), baseMessage)
|
||||
}
|
||||
|
||||
func BenchmarkLogfmtLoggerContextual(b *testing.B) {
|
||||
benchmarkRunner(b, log.NewLogfmtLogger(ioutil.Discard), withMessage)
|
||||
}
|
||||
|
||||
func TestLogfmtLoggerConcurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
testConcurrency(t, log.NewLogfmtLogger(ioutil.Discard), 10000)
|
||||
}
|
||||
|
||||
type mymap map[int]int
|
||||
|
||||
func (m mymap) String() string { return "special_behavior" }
|
8
vendor/github.com/go-kit/kit/log/nop_logger.go
generated
vendored
Normal file
8
vendor/github.com/go-kit/kit/log/nop_logger.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package log
|
||||
|
||||
type nopLogger struct{}
|
||||
|
||||
// NewNopLogger returns a logger that doesn't do anything.
|
||||
func NewNopLogger() Logger { return nopLogger{} }
|
||||
|
||||
func (nopLogger) Log(...interface{}) error { return nil }
|
26
vendor/github.com/go-kit/kit/log/nop_logger_test.go
generated
vendored
Normal file
26
vendor/github.com/go-kit/kit/log/nop_logger_test.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func TestNopLogger(t *testing.T) {
|
||||
t.Parallel()
|
||||
logger := log.NewNopLogger()
|
||||
if err := logger.Log("abc", 123); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := log.With(logger, "def", "ghi").Log(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNopLoggerSimple(b *testing.B) {
|
||||
benchmarkRunner(b, log.NewNopLogger(), baseMessage)
|
||||
}
|
||||
|
||||
func BenchmarkNopLoggerContextual(b *testing.B) {
|
||||
benchmarkRunner(b, log.NewNopLogger(), withMessage)
|
||||
}
|
116
vendor/github.com/go-kit/kit/log/stdlib.go
generated
vendored
Normal file
116
vendor/github.com/go-kit/kit/log/stdlib.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's
|
||||
// designed to be passed to a Go kit logger as the writer, for cases where
|
||||
// it's necessary to redirect all Go kit log output to the stdlib logger.
|
||||
//
|
||||
// If you have any choice in the matter, you shouldn't use this. Prefer to
|
||||
// redirect the stdlib log to the Go kit logger via NewStdlibAdapter.
|
||||
type StdlibWriter struct{}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (w StdlibWriter) Write(p []byte) (int, error) {
|
||||
log.Print(strings.TrimSpace(string(p)))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib
|
||||
// logger's SetOutput. It will extract date/timestamps, filenames, and
|
||||
// messages, and place them under relevant keys.
|
||||
type StdlibAdapter struct {
|
||||
Logger
|
||||
timestampKey string
|
||||
fileKey string
|
||||
messageKey string
|
||||
}
|
||||
|
||||
// StdlibAdapterOption sets a parameter for the StdlibAdapter.
|
||||
type StdlibAdapterOption func(*StdlibAdapter)
|
||||
|
||||
// TimestampKey sets the key for the timestamp field. By default, it's "ts".
|
||||
func TimestampKey(key string) StdlibAdapterOption {
|
||||
return func(a *StdlibAdapter) { a.timestampKey = key }
|
||||
}
|
||||
|
||||
// FileKey sets the key for the file and line field. By default, it's "caller".
|
||||
func FileKey(key string) StdlibAdapterOption {
|
||||
return func(a *StdlibAdapter) { a.fileKey = key }
|
||||
}
|
||||
|
||||
// MessageKey sets the key for the actual log message. By default, it's "msg".
|
||||
func MessageKey(key string) StdlibAdapterOption {
|
||||
return func(a *StdlibAdapter) { a.messageKey = key }
|
||||
}
|
||||
|
||||
// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed
|
||||
// logger. It's designed to be passed to log.SetOutput.
|
||||
func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer {
|
||||
a := StdlibAdapter{
|
||||
Logger: logger,
|
||||
timestampKey: "ts",
|
||||
fileKey: "caller",
|
||||
messageKey: "msg",
|
||||
}
|
||||
for _, option := range options {
|
||||
option(&a)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (a StdlibAdapter) Write(p []byte) (int, error) {
|
||||
result := subexps(p)
|
||||
keyvals := []interface{}{}
|
||||
var timestamp string
|
||||
if date, ok := result["date"]; ok && date != "" {
|
||||
timestamp = date
|
||||
}
|
||||
if time, ok := result["time"]; ok && time != "" {
|
||||
if timestamp != "" {
|
||||
timestamp += " "
|
||||
}
|
||||
timestamp += time
|
||||
}
|
||||
if timestamp != "" {
|
||||
keyvals = append(keyvals, a.timestampKey, timestamp)
|
||||
}
|
||||
if file, ok := result["file"]; ok && file != "" {
|
||||
keyvals = append(keyvals, a.fileKey, file)
|
||||
}
|
||||
if msg, ok := result["msg"]; ok {
|
||||
keyvals = append(keyvals, a.messageKey, msg)
|
||||
}
|
||||
if err := a.Logger.Log(keyvals...); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
const (
|
||||
logRegexpDate = `(?P<date>[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?`
|
||||
logRegexpTime = `(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?)?[ ]?`
|
||||
logRegexpFile = `(?P<file>.+?:[0-9]+)?`
|
||||
logRegexpMsg = `(: )?(?P<msg>.*)`
|
||||
)
|
||||
|
||||
var (
|
||||
logRegexp = regexp.MustCompile(logRegexpDate + logRegexpTime + logRegexpFile + logRegexpMsg)
|
||||
)
|
||||
|
||||
func subexps(line []byte) map[string]string {
|
||||
m := logRegexp.FindSubmatch(line)
|
||||
if len(m) < len(logRegexp.SubexpNames()) {
|
||||
return map[string]string{}
|
||||
}
|
||||
result := map[string]string{}
|
||||
for i, name := range logRegexp.SubexpNames() {
|
||||
result[name] = string(m[i])
|
||||
}
|
||||
return result
|
||||
}
|
205
vendor/github.com/go-kit/kit/log/stdlib_test.go
generated
vendored
Normal file
205
vendor/github.com/go-kit/kit/log/stdlib_test.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStdlibWriter(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
log.SetOutput(buf)
|
||||
log.SetFlags(log.LstdFlags)
|
||||
logger := NewLogfmtLogger(StdlibWriter{})
|
||||
logger.Log("key", "val")
|
||||
timestamp := time.Now().Format("2006/01/02 15:04:05")
|
||||
if want, have := timestamp+" key=val\n", buf.String(); want != have {
|
||||
t.Errorf("want %q, have %q", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdlibAdapterUsage(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
logger := NewLogfmtLogger(buf)
|
||||
writer := NewStdlibAdapter(logger)
|
||||
stdlog := log.New(writer, "", 0)
|
||||
|
||||
now := time.Now()
|
||||
date := now.Format("2006/01/02")
|
||||
time := now.Format("15:04:05")
|
||||
|
||||
for flag, want := range map[int]string{
|
||||
0: "msg=hello\n",
|
||||
log.Ldate: "ts=" + date + " msg=hello\n",
|
||||
log.Ltime: "ts=" + time + " msg=hello\n",
|
||||
log.Ldate | log.Ltime: "ts=\"" + date + " " + time + "\" msg=hello\n",
|
||||
log.Lshortfile: "caller=stdlib_test.go:44 msg=hello\n",
|
||||
log.Lshortfile | log.Ldate: "ts=" + date + " caller=stdlib_test.go:44 msg=hello\n",
|
||||
log.Lshortfile | log.Ldate | log.Ltime: "ts=\"" + date + " " + time + "\" caller=stdlib_test.go:44 msg=hello\n",
|
||||
} {
|
||||
buf.Reset()
|
||||
stdlog.SetFlags(flag)
|
||||
stdlog.Print("hello")
|
||||
if have := buf.String(); want != have {
|
||||
t.Errorf("flag=%d: want %#v, have %#v", flag, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdLibAdapterExtraction(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
logger := NewLogfmtLogger(buf)
|
||||
writer := NewStdlibAdapter(logger)
|
||||
for input, want := range map[string]string{
|
||||
"hello": "msg=hello\n",
|
||||
"2009/01/23: hello": "ts=2009/01/23 msg=hello\n",
|
||||
"2009/01/23 01:23:23: hello": "ts=\"2009/01/23 01:23:23\" msg=hello\n",
|
||||
"01:23:23: hello": "ts=01:23:23 msg=hello\n",
|
||||
"2009/01/23 01:23:23.123123: hello": "ts=\"2009/01/23 01:23:23.123123\" msg=hello\n",
|
||||
"2009/01/23 01:23:23.123123 /a/b/c/d.go:23: hello": "ts=\"2009/01/23 01:23:23.123123\" caller=/a/b/c/d.go:23 msg=hello\n",
|
||||
"01:23:23.123123 /a/b/c/d.go:23: hello": "ts=01:23:23.123123 caller=/a/b/c/d.go:23 msg=hello\n",
|
||||
"2009/01/23 01:23:23 /a/b/c/d.go:23: hello": "ts=\"2009/01/23 01:23:23\" caller=/a/b/c/d.go:23 msg=hello\n",
|
||||
"2009/01/23 /a/b/c/d.go:23: hello": "ts=2009/01/23 caller=/a/b/c/d.go:23 msg=hello\n",
|
||||
"/a/b/c/d.go:23: hello": "caller=/a/b/c/d.go:23 msg=hello\n",
|
||||
} {
|
||||
buf.Reset()
|
||||
fmt.Fprint(writer, input)
|
||||
if have := buf.String(); want != have {
|
||||
t.Errorf("%q: want %#v, have %#v", input, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdlibAdapterSubexps(t *testing.T) {
|
||||
for input, wantMap := range map[string]map[string]string{
|
||||
"hello world": {
|
||||
"date": "",
|
||||
"time": "",
|
||||
"file": "",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "",
|
||||
"file": "",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 01:23:23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23",
|
||||
"file": "",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"01:23:23: hello world": {
|
||||
"date": "",
|
||||
"time": "01:23:23",
|
||||
"file": "",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 01:23:23.123123: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23.123123",
|
||||
"file": "",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 01:23:23.123123 /a/b/c/d.go:23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23.123123",
|
||||
"file": "/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"01:23:23.123123 /a/b/c/d.go:23: hello world": {
|
||||
"date": "",
|
||||
"time": "01:23:23.123123",
|
||||
"file": "/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 01:23:23 /a/b/c/d.go:23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23",
|
||||
"file": "/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 /a/b/c/d.go:23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "",
|
||||
"file": "/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"/a/b/c/d.go:23: hello world": {
|
||||
"date": "",
|
||||
"time": "",
|
||||
"file": "/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 01:23:23.123123 C:/a/b/c/d.go:23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23.123123",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"01:23:23.123123 C:/a/b/c/d.go:23: hello world": {
|
||||
"date": "",
|
||||
"time": "01:23:23.123123",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 01:23:23 C:/a/b/c/d.go:23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 C:/a/b/c/d.go:23: hello world": {
|
||||
"date": "2009/01/23",
|
||||
"time": "",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"C:/a/b/c/d.go:23: hello world": {
|
||||
"date": "",
|
||||
"time": "",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": "hello world",
|
||||
},
|
||||
"2009/01/23 01:23:23.123123 C:/a/b/c/d.go:23: :.;<>_#{[]}\"\\": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23.123123",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": ":.;<>_#{[]}\"\\",
|
||||
},
|
||||
"01:23:23.123123 C:/a/b/c/d.go:23: :.;<>_#{[]}\"\\": {
|
||||
"date": "",
|
||||
"time": "01:23:23.123123",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": ":.;<>_#{[]}\"\\",
|
||||
},
|
||||
"2009/01/23 01:23:23 C:/a/b/c/d.go:23: :.;<>_#{[]}\"\\": {
|
||||
"date": "2009/01/23",
|
||||
"time": "01:23:23",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": ":.;<>_#{[]}\"\\",
|
||||
},
|
||||
"2009/01/23 C:/a/b/c/d.go:23: :.;<>_#{[]}\"\\": {
|
||||
"date": "2009/01/23",
|
||||
"time": "",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": ":.;<>_#{[]}\"\\",
|
||||
},
|
||||
"C:/a/b/c/d.go:23: :.;<>_#{[]}\"\\": {
|
||||
"date": "",
|
||||
"time": "",
|
||||
"file": "C:/a/b/c/d.go:23",
|
||||
"msg": ":.;<>_#{[]}\"\\",
|
||||
},
|
||||
} {
|
||||
haveMap := subexps([]byte(input))
|
||||
for key, want := range wantMap {
|
||||
if have := haveMap[key]; want != have {
|
||||
t.Errorf("%q: %q: want %q, have %q", input, key, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
vendor/github.com/go-kit/kit/log/sync.go
generated
vendored
Normal file
81
vendor/github.com/go-kit/kit/log/sync.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// SwapLogger wraps another logger that may be safely replaced while other
|
||||
// goroutines use the SwapLogger concurrently. The zero value for a SwapLogger
|
||||
// will discard all log events without error.
|
||||
//
|
||||
// SwapLogger serves well as a package global logger that can be changed by
|
||||
// importers.
|
||||
type SwapLogger struct {
|
||||
logger atomic.Value
|
||||
}
|
||||
|
||||
type loggerStruct struct {
|
||||
Logger
|
||||
}
|
||||
|
||||
// Log implements the Logger interface by forwarding keyvals to the currently
|
||||
// wrapped logger. It does not log anything if the wrapped logger is nil.
|
||||
func (l *SwapLogger) Log(keyvals ...interface{}) error {
|
||||
s, ok := l.logger.Load().(loggerStruct)
|
||||
if !ok || s.Logger == nil {
|
||||
return nil
|
||||
}
|
||||
return s.Log(keyvals...)
|
||||
}
|
||||
|
||||
// Swap replaces the currently wrapped logger with logger. Swap may be called
|
||||
// concurrently with calls to Log from other goroutines.
|
||||
func (l *SwapLogger) Swap(logger Logger) {
|
||||
l.logger.Store(loggerStruct{logger})
|
||||
}
|
||||
|
||||
// SyncWriter synchronizes concurrent writes to an io.Writer.
|
||||
type SyncWriter struct {
|
||||
mu sync.Mutex
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
// NewSyncWriter returns a new SyncWriter. The returned writer is safe for
|
||||
// concurrent use by multiple goroutines.
|
||||
func NewSyncWriter(w io.Writer) *SyncWriter {
|
||||
return &SyncWriter{w: w}
|
||||
}
|
||||
|
||||
// Write writes p to the underlying io.Writer. If another write is already in
|
||||
// progress, the calling goroutine blocks until the SyncWriter is available.
|
||||
func (w *SyncWriter) Write(p []byte) (n int, err error) {
|
||||
w.mu.Lock()
|
||||
n, err = w.w.Write(p)
|
||||
w.mu.Unlock()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// syncLogger provides concurrent safe logging for another Logger.
|
||||
type syncLogger struct {
|
||||
mu sync.Mutex
|
||||
logger Logger
|
||||
}
|
||||
|
||||
// NewSyncLogger returns a logger that synchronizes concurrent use of the
|
||||
// wrapped logger. When multiple goroutines use the SyncLogger concurrently
|
||||
// only one goroutine will be allowed to log to the wrapped logger at a time.
|
||||
// The other goroutines will block until the logger is available.
|
||||
func NewSyncLogger(logger Logger) Logger {
|
||||
return &syncLogger{logger: logger}
|
||||
}
|
||||
|
||||
// Log logs keyvals to the underlying Logger. If another log is already in
|
||||
// progress, the calling goroutine blocks until the syncLogger is available.
|
||||
func (l *syncLogger) Log(keyvals ...interface{}) error {
|
||||
l.mu.Lock()
|
||||
err := l.logger.Log(keyvals...)
|
||||
l.mu.Unlock()
|
||||
return err
|
||||
}
|
72
vendor/github.com/go-kit/kit/log/sync_test.go
generated
vendored
Normal file
72
vendor/github.com/go-kit/kit/log/sync_test.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func TestSwapLogger(t *testing.T) {
|
||||
t.Parallel()
|
||||
var logger log.SwapLogger
|
||||
|
||||
// Zero value does not panic or error.
|
||||
err := logger.Log("k", "v")
|
||||
if got, want := err, error(nil); got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
json := log.NewJSONLogger(buf)
|
||||
logger.Swap(json)
|
||||
|
||||
if err := logger.Log("k", "v"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := buf.String(), `{"k":"v"}`+"\n"; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
prefix := log.NewLogfmtLogger(buf)
|
||||
logger.Swap(prefix)
|
||||
|
||||
if err := logger.Log("k", "v"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := buf.String(), "k=v\n"; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
logger.Swap(nil)
|
||||
|
||||
if err := logger.Log("k", "v"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := buf.String(), ""; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwapLoggerConcurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
testConcurrency(t, &log.SwapLogger{}, 10000)
|
||||
}
|
||||
|
||||
func TestSyncLoggerConcurrency(t *testing.T) {
|
||||
var w io.Writer
|
||||
w = &bytes.Buffer{}
|
||||
logger := log.NewLogfmtLogger(w)
|
||||
logger = log.NewSyncLogger(logger)
|
||||
testConcurrency(t, logger, 10000)
|
||||
}
|
||||
|
||||
func TestSyncWriterConcurrency(t *testing.T) {
|
||||
var w io.Writer
|
||||
w = &bytes.Buffer{}
|
||||
w = log.NewSyncWriter(w)
|
||||
testConcurrency(t, log.NewLogfmtLogger(w), 10000)
|
||||
}
|
21
vendor/github.com/go-kit/kit/log/term/LICENSE
generated
vendored
Normal file
21
vendor/github.com/go-kit/kit/log/term/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Simon Eskildsen
|
||||
|
||||
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.
|
144
vendor/github.com/go-kit/kit/log/term/colorlogger.go
generated
vendored
Normal file
144
vendor/github.com/go-kit/kit/log/term/colorlogger.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
// Color represents an ANSI color. The zero value is Default.
|
||||
type Color uint8
|
||||
|
||||
// ANSI colors.
|
||||
const (
|
||||
Default = Color(iota)
|
||||
|
||||
Black
|
||||
DarkRed
|
||||
DarkGreen
|
||||
Brown
|
||||
DarkBlue
|
||||
DarkMagenta
|
||||
DarkCyan
|
||||
Gray
|
||||
|
||||
DarkGray
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
Blue
|
||||
Magenta
|
||||
Cyan
|
||||
White
|
||||
|
||||
numColors
|
||||
)
|
||||
|
||||
// For more on ANSI escape codes see
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code. See in particular
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors.
|
||||
|
||||
var (
|
||||
resetColorBytes = []byte("\x1b[39;49;22m")
|
||||
fgColorBytes [][]byte
|
||||
bgColorBytes [][]byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Default
|
||||
fgColorBytes = append(fgColorBytes, []byte("\x1b[39m"))
|
||||
bgColorBytes = append(bgColorBytes, []byte("\x1b[49m"))
|
||||
|
||||
// dark colors
|
||||
for color := Black; color < DarkGray; color++ {
|
||||
fgColorBytes = append(fgColorBytes, []byte(fmt.Sprintf("\x1b[%dm", 30+color-Black)))
|
||||
bgColorBytes = append(bgColorBytes, []byte(fmt.Sprintf("\x1b[%dm", 40+color-Black)))
|
||||
}
|
||||
|
||||
// bright colors
|
||||
for color := DarkGray; color < numColors; color++ {
|
||||
fgColorBytes = append(fgColorBytes, []byte(fmt.Sprintf("\x1b[%d;1m", 30+color-DarkGray)))
|
||||
bgColorBytes = append(bgColorBytes, []byte(fmt.Sprintf("\x1b[%d;1m", 40+color-DarkGray)))
|
||||
}
|
||||
}
|
||||
|
||||
// FgBgColor represents a foreground and background color.
|
||||
type FgBgColor struct {
|
||||
Fg, Bg Color
|
||||
}
|
||||
|
||||
func (c FgBgColor) isZero() bool {
|
||||
return c.Fg == Default && c.Bg == Default
|
||||
}
|
||||
|
||||
// NewColorLogger returns a Logger which writes colored logs to w. ANSI color
|
||||
// codes for the colors returned by color are added to the formatted output
|
||||
// from the Logger returned by newLogger and the combined result written to w.
|
||||
func NewColorLogger(w io.Writer, newLogger func(io.Writer) log.Logger, color func(keyvals ...interface{}) FgBgColor) log.Logger {
|
||||
if color == nil {
|
||||
panic("color func nil")
|
||||
}
|
||||
return &colorLogger{
|
||||
w: w,
|
||||
newLogger: newLogger,
|
||||
color: color,
|
||||
bufPool: sync.Pool{New: func() interface{} { return &loggerBuf{} }},
|
||||
noColorLogger: newLogger(w),
|
||||
}
|
||||
}
|
||||
|
||||
type colorLogger struct {
|
||||
w io.Writer
|
||||
newLogger func(io.Writer) log.Logger
|
||||
color func(keyvals ...interface{}) FgBgColor
|
||||
bufPool sync.Pool
|
||||
noColorLogger log.Logger
|
||||
}
|
||||
|
||||
func (l *colorLogger) Log(keyvals ...interface{}) error {
|
||||
color := l.color(keyvals...)
|
||||
if color.isZero() {
|
||||
return l.noColorLogger.Log(keyvals...)
|
||||
}
|
||||
|
||||
lb := l.getLoggerBuf()
|
||||
defer l.putLoggerBuf(lb)
|
||||
if color.Fg != Default {
|
||||
lb.buf.Write(fgColorBytes[color.Fg])
|
||||
}
|
||||
if color.Bg != Default {
|
||||
lb.buf.Write(bgColorBytes[color.Bg])
|
||||
}
|
||||
err := lb.logger.Log(keyvals...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if color.Fg != Default || color.Bg != Default {
|
||||
lb.buf.Write(resetColorBytes)
|
||||
}
|
||||
_, err = io.Copy(l.w, lb.buf)
|
||||
return err
|
||||
}
|
||||
|
||||
type loggerBuf struct {
|
||||
buf *bytes.Buffer
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (l *colorLogger) getLoggerBuf() *loggerBuf {
|
||||
lb := l.bufPool.Get().(*loggerBuf)
|
||||
if lb.buf == nil {
|
||||
lb.buf = &bytes.Buffer{}
|
||||
lb.logger = l.newLogger(lb.buf)
|
||||
} else {
|
||||
lb.buf.Reset()
|
||||
}
|
||||
return lb
|
||||
}
|
||||
|
||||
func (l *colorLogger) putLoggerBuf(cb *loggerBuf) {
|
||||
l.bufPool.Put(cb)
|
||||
}
|
88
vendor/github.com/go-kit/kit/log/term/colorlogger_test.go
generated
vendored
Normal file
88
vendor/github.com/go-kit/kit/log/term/colorlogger_test.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package term_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/term"
|
||||
)
|
||||
|
||||
func TestColorLogger(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := newColorLogger(&buf)
|
||||
|
||||
if err := logger.Log("hello", "world"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := "hello=world\n", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if err := logger.Log("a", 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := "\x1b[32;1m\x1b[47;1ma=1\n\x1b[39;49;22m", buf.String(); want != have {
|
||||
t.Errorf("\nwant %#v\nhave %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func newColorLogger(w io.Writer) log.Logger {
|
||||
return term.NewColorLogger(w, log.NewLogfmtLogger,
|
||||
func(keyvals ...interface{}) term.FgBgColor {
|
||||
if keyvals[0] == "a" {
|
||||
return term.FgBgColor{Fg: term.Green, Bg: term.White}
|
||||
}
|
||||
return term.FgBgColor{}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkColorLoggerSimple(b *testing.B) {
|
||||
benchmarkRunner(b, newColorLogger(ioutil.Discard), baseMessage)
|
||||
}
|
||||
|
||||
func BenchmarkColorLoggerContextual(b *testing.B) {
|
||||
benchmarkRunner(b, newColorLogger(ioutil.Discard), withMessage)
|
||||
}
|
||||
|
||||
func TestColorLoggerConcurrency(t *testing.T) {
|
||||
testConcurrency(t, newColorLogger(ioutil.Discard))
|
||||
}
|
||||
|
||||
// copied from log/benchmark_test.go
|
||||
func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) {
|
||||
lc := log.With(logger, "common_key", "common_value")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f(lc)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
baseMessage = func(logger log.Logger) { logger.Log("foo_key", "foo_value") }
|
||||
withMessage = func(logger log.Logger) { log.With(logger, "a", "b").Log("c", "d") }
|
||||
)
|
||||
|
||||
// copied from log/concurrency_test.go
|
||||
func testConcurrency(t *testing.T, logger log.Logger) {
|
||||
for _, n := range []int{10, 100, 500} {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(n)
|
||||
for i := 0; i < n; i++ {
|
||||
go func() { spam(logger); wg.Done() }()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func spam(logger log.Logger) {
|
||||
for i := 0; i < 100; i++ {
|
||||
logger.Log("a", strconv.FormatInt(int64(i), 10))
|
||||
}
|
||||
}
|
12
vendor/github.com/go-kit/kit/log/term/colorwriter_others.go
generated
vendored
Normal file
12
vendor/github.com/go-kit/kit/log/term/colorwriter_others.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !windows
|
||||
|
||||
package term
|
||||
|
||||
import "io"
|
||||
|
||||
// NewColorWriter returns an io.Writer that writes to w and provides cross
|
||||
// platform support for ANSI color codes. If w is not a terminal it is
|
||||
// returned unmodified.
|
||||
func NewColorWriter(w io.Writer) io.Writer {
|
||||
return w
|
||||
}
|
190
vendor/github.com/go-kit/kit/log/term/colorwriter_windows.go
generated
vendored
Normal file
190
vendor/github.com/go-kit/kit/log/term/colorwriter_windows.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
// The code in this file is adapted from github.com/mattn/go-colorable.
|
||||
|
||||
// +build windows
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type colorWriter struct {
|
||||
out io.Writer
|
||||
handle syscall.Handle
|
||||
lastbuf bytes.Buffer
|
||||
oldattr word
|
||||
}
|
||||
|
||||
// NewColorWriter returns an io.Writer that writes to w and provides cross
|
||||
// platform support for ANSI color codes. If w is not a terminal it is
|
||||
// returned unmodified.
|
||||
func NewColorWriter(w io.Writer) io.Writer {
|
||||
if !IsTerminal(w) {
|
||||
return w
|
||||
}
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
handle := syscall.Handle(w.(fder).Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
return &colorWriter{
|
||||
out: w,
|
||||
handle: handle,
|
||||
oldattr: csbi.attributes,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *colorWriter) Write(data []byte) (n int, err error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
er := bytes.NewBuffer(data)
|
||||
loop:
|
||||
for {
|
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
if r1 == 0 {
|
||||
break loop
|
||||
}
|
||||
|
||||
c1, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
fmt.Fprint(w.out, string(c1))
|
||||
continue
|
||||
}
|
||||
c2, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
break loop
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var m rune
|
||||
for {
|
||||
c, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
w.lastbuf.Write(buf.Bytes())
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
m = c
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
|
||||
switch m {
|
||||
case 'm':
|
||||
attr := csbi.attributes
|
||||
cs := buf.String()
|
||||
if cs == "" {
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
||||
continue
|
||||
}
|
||||
token := strings.Split(cs, ";")
|
||||
intensityMode := word(0)
|
||||
for _, ns := range token {
|
||||
if n, err = strconv.Atoi(ns); err == nil {
|
||||
switch {
|
||||
case n == 0:
|
||||
attr = w.oldattr
|
||||
case n == 1:
|
||||
attr |= intensityMode
|
||||
case 30 <= n && n <= 37:
|
||||
attr = (attr & backgroundMask)
|
||||
if (n-30)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-30)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-30)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
intensityMode = foregroundIntensity
|
||||
case n == 39: // reset foreground color
|
||||
attr &= backgroundMask
|
||||
attr |= w.oldattr & foregroundMask
|
||||
case 40 <= n && n <= 47:
|
||||
attr = (attr & foregroundMask)
|
||||
if (n-40)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-40)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-40)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
intensityMode = backgroundIntensity
|
||||
case n == 49: // reset background color
|
||||
attr &= foregroundMask
|
||||
attr |= w.oldattr & backgroundMask
|
||||
}
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(data) - w.lastbuf.Len(), nil
|
||||
}
|
||||
|
||||
var (
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||
)
|
||||
|
||||
const (
|
||||
foregroundBlue = 0x1
|
||||
foregroundGreen = 0x2
|
||||
foregroundRed = 0x4
|
||||
foregroundIntensity = 0x8
|
||||
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
|
||||
backgroundBlue = 0x10
|
||||
backgroundGreen = 0x20
|
||||
backgroundRed = 0x40
|
||||
backgroundIntensity = 0x80
|
||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||
)
|
||||
|
||||
type (
|
||||
wchar uint16
|
||||
short int16
|
||||
dword uint32
|
||||
word uint16
|
||||
)
|
||||
|
||||
type coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
|
||||
type smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
57
vendor/github.com/go-kit/kit/log/term/example_test.go
generated
vendored
Normal file
57
vendor/github.com/go-kit/kit/log/term/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package term_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/term"
|
||||
)
|
||||
|
||||
func ExampleNewLogger_redErrors() {
|
||||
// Color errors red
|
||||
colorFn := func(keyvals ...interface{}) term.FgBgColor {
|
||||
for i := 1; i < len(keyvals); i += 2 {
|
||||
if _, ok := keyvals[i].(error); ok {
|
||||
return term.FgBgColor{Fg: term.White, Bg: term.Red}
|
||||
}
|
||||
}
|
||||
return term.FgBgColor{}
|
||||
}
|
||||
|
||||
logger := term.NewLogger(os.Stdout, log.NewLogfmtLogger, colorFn)
|
||||
|
||||
logger.Log("msg", "default color", "err", nil)
|
||||
logger.Log("msg", "colored because of error", "err", errors.New("coloring error"))
|
||||
}
|
||||
|
||||
func ExampleNewLogger_levelColors() {
|
||||
// Color by level value
|
||||
colorFn := func(keyvals ...interface{}) term.FgBgColor {
|
||||
for i := 0; i < len(keyvals)-1; i += 2 {
|
||||
if keyvals[i] != "level" {
|
||||
continue
|
||||
}
|
||||
switch keyvals[i+1] {
|
||||
case "debug":
|
||||
return term.FgBgColor{Fg: term.DarkGray}
|
||||
case "info":
|
||||
return term.FgBgColor{Fg: term.Gray}
|
||||
case "warn":
|
||||
return term.FgBgColor{Fg: term.Yellow}
|
||||
case "error":
|
||||
return term.FgBgColor{Fg: term.Red}
|
||||
case "crit":
|
||||
return term.FgBgColor{Fg: term.Gray, Bg: term.DarkRed}
|
||||
default:
|
||||
return term.FgBgColor{}
|
||||
}
|
||||
}
|
||||
return term.FgBgColor{}
|
||||
}
|
||||
|
||||
logger := term.NewLogger(os.Stdout, log.NewJSONLogger, colorFn)
|
||||
|
||||
logger.Log("level", "warn", "msg", "yellow")
|
||||
logger.Log("level", "debug", "msg", "dark gray")
|
||||
}
|
22
vendor/github.com/go-kit/kit/log/term/term.go
generated
vendored
Normal file
22
vendor/github.com/go-kit/kit/log/term/term.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Package term provides tools for logging to a terminal.
|
||||
package term
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
// NewLogger returns a Logger that takes advantage of terminal features if
|
||||
// possible. Log events are formatted by the Logger returned by newLogger. If
|
||||
// w is a terminal each log event is colored according to the color function.
|
||||
func NewLogger(w io.Writer, newLogger func(io.Writer) log.Logger, color func(keyvals ...interface{}) FgBgColor) log.Logger {
|
||||
if !IsTerminal(w) {
|
||||
return newLogger(w)
|
||||
}
|
||||
return NewColorLogger(NewColorWriter(w), newLogger, color)
|
||||
}
|
||||
|
||||
type fder interface {
|
||||
Fd() uintptr
|
||||
}
|
15
vendor/github.com/go-kit/kit/log/term/terminal_appengine.go
generated
vendored
Normal file
15
vendor/github.com/go-kit/kit/log/term/terminal_appengine.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package term
|
||||
|
||||
import "io"
|
||||
|
||||
// IsTerminal always returns false on AppEngine.
|
||||
func IsTerminal(w io.Writer) bool {
|
||||
return false
|
||||
}
|
10
vendor/github.com/go-kit/kit/log/term/terminal_darwin.go
generated
vendored
Normal file
10
vendor/github.com/go-kit/kit/log/term/terminal_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
7
vendor/github.com/go-kit/kit/log/term/terminal_freebsd.go
generated
vendored
Normal file
7
vendor/github.com/go-kit/kit/log/term/terminal_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
12
vendor/github.com/go-kit/kit/log/term/terminal_linux.go
generated
vendored
Normal file
12
vendor/github.com/go-kit/kit/log/term/terminal_linux.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TCGETS
|
25
vendor/github.com/go-kit/kit/log/term/terminal_notwindows.go
generated
vendored
Normal file
25
vendor/github.com/go-kit/kit/log/term/terminal_notwindows.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux,!appengine darwin freebsd openbsd
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"io"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if w writes to a terminal.
|
||||
func IsTerminal(w io.Writer) bool {
|
||||
fw, ok := w.(fder)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
var termios syscall.Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fw.Fd(), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
5
vendor/github.com/go-kit/kit/log/term/terminal_openbsd.go
generated
vendored
Normal file
5
vendor/github.com/go-kit/kit/log/term/terminal_openbsd.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
31
vendor/github.com/go-kit/kit/log/term/terminal_windows.go
generated
vendored
Normal file
31
vendor/github.com/go-kit/kit/log/term/terminal_windows.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"io"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
)
|
||||
|
||||
// IsTerminal returns true if w writes to a terminal.
|
||||
func IsTerminal(w io.Writer) bool {
|
||||
fw, ok := w.(fder)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fw.Fd(), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
64
vendor/github.com/go-kit/kit/log/value.go
generated
vendored
Normal file
64
vendor/github.com/go-kit/kit/log/value.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-stack/stack"
|
||||
)
|
||||
|
||||
// A Valuer generates a log value. When passed to With or WithPrefix in a
|
||||
// value element (odd indexes), it represents a dynamic value which is re-
|
||||
// evaluated with each log event.
|
||||
type Valuer func() interface{}
|
||||
|
||||
// bindValues replaces all value elements (odd indexes) containing a Valuer
|
||||
// with their generated value.
|
||||
func bindValues(keyvals []interface{}) {
|
||||
for i := 1; i < len(keyvals); i += 2 {
|
||||
if v, ok := keyvals[i].(Valuer); ok {
|
||||
keyvals[i] = v()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// containsValuer returns true if any of the value elements (odd indexes)
|
||||
// contain a Valuer.
|
||||
func containsValuer(keyvals []interface{}) bool {
|
||||
for i := 1; i < len(keyvals); i += 2 {
|
||||
if _, ok := keyvals[i].(Valuer); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Timestamp returns a Valuer that invokes the underlying function when bound,
|
||||
// returning a time.Time. Users will probably want to use DefaultTimestamp or
|
||||
// DefaultTimestampUTC.
|
||||
func Timestamp(t func() time.Time) Valuer {
|
||||
return func() interface{} { return t() }
|
||||
}
|
||||
|
||||
// Caller returns a Valuer that returns a file and line from a specified depth
|
||||
// in the callstack. Users will probably want to use DefaultCaller.
|
||||
func Caller(depth int) Valuer {
|
||||
return func() interface{} { return stack.Caller(depth) }
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultTimestamp is a Valuer that returns the current wallclock time,
|
||||
// respecting time zones, when bound.
|
||||
DefaultTimestamp = Valuer(func() interface{} {
|
||||
return time.Now().Format(time.RFC3339Nano)
|
||||
})
|
||||
|
||||
// DefaultTimestampUTC is a Valuer that returns the current time in UTC
|
||||
// when bound.
|
||||
DefaultTimestampUTC = Valuer(func() interface{} {
|
||||
return time.Now().UTC().Format(time.RFC3339Nano)
|
||||
})
|
||||
|
||||
// DefaultCaller is a Valuer that returns the file and line where the Log
|
||||
// method was invoked. It can only be used with log.With.
|
||||
DefaultCaller = Caller(3)
|
||||
)
|
111
vendor/github.com/go-kit/kit/log/value_test.go
generated
vendored
Normal file
111
vendor/github.com/go-kit/kit/log/value_test.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func TestValueBinding(t *testing.T) {
|
||||
t.Parallel()
|
||||
var output []interface{}
|
||||
|
||||
logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
output = keyvals
|
||||
return nil
|
||||
}))
|
||||
|
||||
start := time.Date(2015, time.April, 25, 0, 0, 0, 0, time.UTC)
|
||||
now := start
|
||||
mocktime := func() time.Time {
|
||||
now = now.Add(time.Second)
|
||||
return now
|
||||
}
|
||||
|
||||
lc := log.With(logger, "ts", log.Timestamp(mocktime), "caller", log.DefaultCaller)
|
||||
|
||||
lc.Log("foo", "bar")
|
||||
timestamp, ok := output[1].(time.Time)
|
||||
if !ok {
|
||||
t.Fatalf("want time.Time, have %T", output[1])
|
||||
}
|
||||
if want, have := start.Add(time.Second), timestamp; want != have {
|
||||
t.Errorf("output[1]: want %v, have %v", want, have)
|
||||
}
|
||||
if want, have := "value_test.go:29", fmt.Sprint(output[3]); want != have {
|
||||
t.Errorf("output[3]: want %s, have %s", want, have)
|
||||
}
|
||||
|
||||
// A second attempt to confirm the bindings are truly dynamic.
|
||||
lc.Log("foo", "bar")
|
||||
timestamp, ok = output[1].(time.Time)
|
||||
if !ok {
|
||||
t.Fatalf("want time.Time, have %T", output[1])
|
||||
}
|
||||
if want, have := start.Add(2*time.Second), timestamp; want != have {
|
||||
t.Errorf("output[1]: want %v, have %v", want, have)
|
||||
}
|
||||
if want, have := "value_test.go:42", fmt.Sprint(output[3]); want != have {
|
||||
t.Errorf("output[3]: want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueBinding_loggingZeroKeyvals(t *testing.T) {
|
||||
t.Parallel()
|
||||
var output []interface{}
|
||||
|
||||
logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
output = keyvals
|
||||
return nil
|
||||
}))
|
||||
|
||||
start := time.Date(2015, time.April, 25, 0, 0, 0, 0, time.UTC)
|
||||
now := start
|
||||
mocktime := func() time.Time {
|
||||
now = now.Add(time.Second)
|
||||
return now
|
||||
}
|
||||
|
||||
logger = log.With(logger, "ts", log.Timestamp(mocktime))
|
||||
|
||||
logger.Log()
|
||||
timestamp, ok := output[1].(time.Time)
|
||||
if !ok {
|
||||
t.Fatalf("want time.Time, have %T", output[1])
|
||||
}
|
||||
if want, have := start.Add(time.Second), timestamp; want != have {
|
||||
t.Errorf("output[1]: want %v, have %v", want, have)
|
||||
}
|
||||
|
||||
// A second attempt to confirm the bindings are truly dynamic.
|
||||
logger.Log()
|
||||
timestamp, ok = output[1].(time.Time)
|
||||
if !ok {
|
||||
t.Fatalf("want time.Time, have %T", output[1])
|
||||
}
|
||||
if want, have := start.Add(2*time.Second), timestamp; want != have {
|
||||
t.Errorf("output[1]: want %v, have %v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValueBindingTimestamp(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.With(logger, "ts", log.DefaultTimestamp)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValueBindingCaller(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.With(logger, "caller", log.DefaultCaller)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user