mirror of
https://github.com/go-kit/kit.git
synced 2025-07-15 01:04:44 +02:00
Replace kit/log with log (#1173)
* Implement log/... packages with github.com/go-kit/log * Use github.com/go-kit/log/... in all the other packages
This commit is contained in:
@ -95,7 +95,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/go-kit/kit/auth/jwt"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
grpctransport "github.com/go-kit/kit/transport/grpc"
|
||||
)
|
||||
|
||||
|
3
go.mod
3
go.mod
@ -16,8 +16,7 @@ require (
|
||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db // indirect
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.0
|
||||
github.com/go-stack/stack v1.8.0
|
||||
github.com/go-kit/log v0.1.0
|
||||
github.com/go-zookeeper/zk v1.0.2
|
||||
github.com/hashicorp/consul/api v1.8.1
|
||||
github.com/hudl/fargo v1.3.0
|
||||
|
1
go.sum
1
go.sum
@ -79,6 +79,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
|
@ -1,5 +1,14 @@
|
||||
# package log
|
||||
|
||||
**Deprecation notice:** The core Go kit log packages (log, log/level, log/term, and
|
||||
log/syslog) have been moved to their own repository at github.com/go-kit/log.
|
||||
The corresponding packages in this directory remain for backwards compatibility.
|
||||
Their types alias the types and their functions call the functions provided by
|
||||
the new repository. Using either import path should be equivalent. Prefer the
|
||||
new import path when practical.
|
||||
|
||||
______
|
||||
|
||||
`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,
|
||||
|
@ -1,21 +0,0 @@
|
||||
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") }
|
||||
)
|
@ -1,40 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
// Package levels implements leveled logging on top of Go kit's log package.
|
||||
//
|
||||
// Deprecated: Use github.com/go-kit/log/level instead.
|
||||
package levels
|
||||
|
||||
import "github.com/go-kit/kit/log"
|
||||
import "github.com/go-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
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
levels "github.com/go-kit/kit/log/deprecated_levels"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestDefaultLevels(t *testing.T) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
// Package log provides a structured logger.
|
||||
//
|
||||
// Deprecated: Use github.com/go-kit/log instead.
|
||||
//
|
||||
// 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
|
||||
|
@ -1,91 +1,15 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type jsonLogger struct {
|
||||
io.Writer
|
||||
}
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
enc := json.NewEncoder(l.Writer)
|
||||
enc.SetEscapeHTML(false)
|
||||
return enc.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)
|
||||
}
|
||||
|
||||
// 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
|
||||
return log.NewJSONLogger(w)
|
||||
}
|
||||
|
@ -1,174 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONLoggerNoHTMLEscape(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":"<&>"}`+"\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"
|
||||
}
|
||||
|
||||
func (aller) Error() string {
|
||||
return "error"
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
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")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
// Package level implements leveled logging on top of Go kit's log package. To
|
||||
// use the level package, create a logger as per normal in your func main, and
|
||||
// wrap it with level.NewFilter.
|
||||
// Package level implements leveled logging on top of Go kit's log package.
|
||||
//
|
||||
// Deprecated: Use github.com/go-kit/log/level instead.
|
||||
//
|
||||
// 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)
|
||||
|
@ -1,25 +1,28 @@
|
||||
package level
|
||||
|
||||
import "github.com/go-kit/kit/log"
|
||||
import (
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
)
|
||||
|
||||
// Error returns a logger that includes a Key/ErrorValue pair.
|
||||
func Error(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), ErrorValue())
|
||||
return level.Error(logger)
|
||||
}
|
||||
|
||||
// Warn returns a logger that includes a Key/WarnValue pair.
|
||||
func Warn(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), WarnValue())
|
||||
return level.Warn(logger)
|
||||
}
|
||||
|
||||
// Info returns a logger that includes a Key/InfoValue pair.
|
||||
func Info(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), InfoValue())
|
||||
return level.Info(logger)
|
||||
}
|
||||
|
||||
// Debug returns a logger that includes a Key/DebugValue pair.
|
||||
func Debug(logger log.Logger) log.Logger {
|
||||
return log.WithPrefix(logger, Key(), DebugValue())
|
||||
return level.Debug(logger)
|
||||
}
|
||||
|
||||
// NewFilter wraps next and implements level filtering. See the commentary on
|
||||
@ -28,76 +31,40 @@ func Debug(logger log.Logger) log.Logger {
|
||||
// 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...)
|
||||
return level.NewFilter(next, options...)
|
||||
}
|
||||
|
||||
// Option sets a parameter for the leveled logger.
|
||||
type Option func(*logger)
|
||||
type Option = level.Option
|
||||
|
||||
// AllowAll is an alias for AllowDebug.
|
||||
func AllowAll() Option {
|
||||
return AllowDebug()
|
||||
return level.AllowAll()
|
||||
}
|
||||
|
||||
// AllowDebug allows error, warn, info and debug level log events to pass.
|
||||
func AllowDebug() Option {
|
||||
return allowed(levelError | levelWarn | levelInfo | levelDebug)
|
||||
return level.AllowDebug()
|
||||
}
|
||||
|
||||
// AllowInfo allows error, warn and info level log events to pass.
|
||||
func AllowInfo() Option {
|
||||
return allowed(levelError | levelWarn | levelInfo)
|
||||
return level.AllowInfo()
|
||||
}
|
||||
|
||||
// AllowWarn allows error and warn level log events to pass.
|
||||
func AllowWarn() Option {
|
||||
return allowed(levelError | levelWarn)
|
||||
return level.AllowWarn()
|
||||
}
|
||||
|
||||
// AllowError allows only error level log events to pass.
|
||||
func AllowError() Option {
|
||||
return allowed(levelError)
|
||||
return level.AllowError()
|
||||
}
|
||||
|
||||
// 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 }
|
||||
return level.AllowNone()
|
||||
}
|
||||
|
||||
// ErrNotAllowed sets the error to return from Log when it squelches a log
|
||||
@ -105,7 +72,7 @@ func allowed(allowed level) Option {
|
||||
// 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 }
|
||||
return level.ErrNotAllowed(err)
|
||||
}
|
||||
|
||||
// SquelchNoLevel instructs Log to squelch log events with no level, so that
|
||||
@ -113,93 +80,41 @@ func ErrNotAllowed(err error) Option {
|
||||
// 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 }
|
||||
return level.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 }
|
||||
return level.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...)
|
||||
func NewInjector(next log.Logger, lvl Value) log.Logger {
|
||||
return level.NewInjector(next, lvl)
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
type Value = level.Value
|
||||
|
||||
// Key returns the unique key added to log events by the loggers in this
|
||||
// package.
|
||||
func Key() interface{} { return key }
|
||||
func Key() interface{} { return level.Key() }
|
||||
|
||||
// ErrorValue returns the unique value added to log events by Error.
|
||||
func ErrorValue() Value { return errorValue }
|
||||
func ErrorValue() Value { return level.ErrorValue() }
|
||||
|
||||
// WarnValue returns the unique value added to log events by Warn.
|
||||
func WarnValue() Value { return warnValue }
|
||||
func WarnValue() Value { return level.WarnValue() }
|
||||
|
||||
// InfoValue returns the unique value added to log events by Info.
|
||||
func InfoValue() Value { return infoValue }
|
||||
func InfoValue() Value { return level.InfoValue() }
|
||||
|
||||
// DebugValue returns the unique value added to log events by Debug.
|
||||
func DebugValue() Value { return debugValue }
|
||||
|
||||
var (
|
||||
// key is of type interface{} so that it allocates once during package
|
||||
// initialization and avoids allocating every time 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() {}
|
||||
func DebugValue() Value { return level.DebugValue() }
|
||||
|
@ -1,235 +0,0 @@
|
||||
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"),
|
||||
},
|
||||
{
|
||||
"AllowInfo",
|
||||
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)
|
||||
}
|
||||
}
|
146
log/log.go
146
log/log.go
@ -1,19 +1,19 @@
|
||||
package log
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
type Logger = log.Logger
|
||||
|
||||
// ErrMissingValue is appended to keyvals slices with odd length to substitute
|
||||
// the missing value.
|
||||
var ErrMissingValue = errors.New("(MISSING)")
|
||||
var ErrMissingValue = log.ErrMissingValue
|
||||
|
||||
// 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,
|
||||
@ -22,25 +22,7 @@ var ErrMissingValue = errors.New("(MISSING)")
|
||||
// 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),
|
||||
sKeyvals: l.sKeyvals,
|
||||
sHasValuer: l.sHasValuer,
|
||||
}
|
||||
return log.With(logger, keyvals...)
|
||||
}
|
||||
|
||||
// WithPrefix returns a new contextual logger with keyvals prepended to those
|
||||
@ -50,31 +32,7 @@ func With(logger Logger, keyvals ...interface{}) Logger {
|
||||
// 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),
|
||||
sKeyvals: l.sKeyvals,
|
||||
sHasValuer: l.sHasValuer,
|
||||
}
|
||||
return log.WithPrefix(logger, keyvals...)
|
||||
}
|
||||
|
||||
// WithSuffix returns a new contextual logger with keyvals appended to those
|
||||
@ -84,96 +42,10 @@ func WithPrefix(logger Logger, keyvals ...interface{}) Logger {
|
||||
// The returned Logger replaces all value elements (odd indexes) containing a
|
||||
// Valuer with their generated value for each call to its Log method.
|
||||
func WithSuffix(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.sKeyvals) + 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(l.sKeyvals, kvs...)
|
||||
return &context{
|
||||
logger: l.logger,
|
||||
keyvals: l.keyvals,
|
||||
hasValuer: l.hasValuer,
|
||||
sKeyvals: kvs,
|
||||
sHasValuer: l.sHasValuer || containsValuer(keyvals),
|
||||
}
|
||||
}
|
||||
|
||||
// context is the Logger implementation returned by With, WithPrefix, and
|
||||
// WithSuffix. 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, WithPrefix, and WithSuffix 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{}
|
||||
sKeyvals []interface{} // suffixes
|
||||
hasValuer bool
|
||||
sHasValuer 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))])
|
||||
}
|
||||
kvs = append(kvs, l.sKeyvals...)
|
||||
if l.sHasValuer {
|
||||
bindValues(kvs[len(kvs) - len(l.sKeyvals):])
|
||||
}
|
||||
return l.logger.Log(kvs...)
|
||||
return log.WithSuffix(logger, keyvals...)
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
||||
type LoggerFunc = log.LoggerFunc
|
||||
|
349
log/log_test.go
349
log/log_test.go
@ -1,349 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPrefixAndSuffix(t *testing.T) {
|
||||
t.Parallel()
|
||||
var output []interface{}
|
||||
logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
output = keyvals
|
||||
return nil
|
||||
}))
|
||||
|
||||
lc := log.WithPrefix(logger, "a", "first")
|
||||
lc = log.WithSuffix(lc, "z", "last")
|
||||
if err := lc.Log("msg", "message"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := 6, len(output); want != have {
|
||||
t.Errorf("want len(output) == %v, have %v", want, have)
|
||||
}
|
||||
want := []string{"a", "first", "msg", "message", "z", "last"}
|
||||
for i := 0; i < 6; i++ {
|
||||
if want, have := want[i], output[i]; want != have {
|
||||
t.Errorf("want output[%d] == %#v, have %#v", i, want, have)
|
||||
}
|
||||
}
|
||||
|
||||
lc = log.With(logger, "b", "second")
|
||||
lc = log.WithPrefix(lc, "a", "first")
|
||||
lc = log.With(lc, "c", "third")
|
||||
lc = log.WithSuffix(lc, "z", "last")
|
||||
lc = log.WithSuffix(lc, "aa", "sequel")
|
||||
if err := lc.Log("msg", "message"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := 12, len(output); want != have {
|
||||
t.Errorf("want len(output) == %v, have %v", want, have)
|
||||
}
|
||||
want = []string{
|
||||
"a", "first",
|
||||
"b", "second",
|
||||
"c", "third",
|
||||
"msg", "message",
|
||||
"z", "last",
|
||||
"aa", "sequel",
|
||||
}
|
||||
for i := 0; i < 12; i++ {
|
||||
if want, have := want[i], 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 TestLogCopiesValuers(t *testing.T) {
|
||||
t.Parallel()
|
||||
var output []interface{}
|
||||
logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
output = keyvals
|
||||
return nil
|
||||
}))
|
||||
|
||||
valuerCallCount := 0
|
||||
counterValuer := log.Valuer(func() interface{} {
|
||||
valuerCallCount++
|
||||
return valuerCallCount
|
||||
})
|
||||
lc := log.WithPrefix(logger, "a", counterValuer)
|
||||
lc = log.WithSuffix(lc, "z", counterValuer)
|
||||
|
||||
if err := lc.Log(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []interface{}{"a", 1, "z", 2}
|
||||
for i := 0; i < 4; i++ {
|
||||
if want, have := want[i], output[i]; want != have {
|
||||
t.Errorf("want output[%d] == %#v, have %#v", i, want, have)
|
||||
}
|
||||
}
|
||||
|
||||
if err := lc.Log(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want = []interface{}{"a", 3, "z", 4}
|
||||
for i := 0; i < 4; i++ {
|
||||
if want, have := want[i], output[i]; want != have {
|
||||
t.Errorf("want output[%d] == %#v, have %#v", i, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOneWithPrefix(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.WithPrefix(logger, "a", "first")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTenWithPrefix(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.WithPrefix(logger, "a", "first")
|
||||
for i := 1; i < 10; i++ {
|
||||
lc = log.WithPrefix(lc, "a", "first")
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOneWithSuffix(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.WithSuffix(logger, "z", "last")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTenWithSuffix(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.WithSuffix(logger, "z", "last")
|
||||
for i := 1; i < 10; i++ {
|
||||
lc = log.WithSuffix(lc, "z", "last")
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOneWithPrefixSuffix(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.WithSuffix(logger, "a", "first")
|
||||
lc = log.WithSuffix(lc, "z", "last")
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTenWithPrefixSuffix(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
lc := log.WithPrefix(logger, "a", "first")
|
||||
lc = log.WithSuffix(lc, "z", "last")
|
||||
for i := 1; i < 10; i++ {
|
||||
lc = log.WithPrefix(lc, "a", "first")
|
||||
lc = log.WithSuffix(lc, "z", "last")
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lc.Log("k", "v")
|
||||
}
|
||||
}
|
@ -1,62 +1,15 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/go-logfmt/logfmt"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
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
|
||||
return log.NewLogfmtLogger(w)
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
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" }
|
@ -6,7 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package log
|
||||
|
||||
type nopLogger struct{}
|
||||
import "github.com/go-kit/log"
|
||||
|
||||
// NewNopLogger returns a logger that doesn't do anything.
|
||||
func NewNopLogger() Logger { return nopLogger{} }
|
||||
|
||||
func (nopLogger) Log(...interface{}) error { return nil }
|
||||
func NewNopLogger() Logger {
|
||||
return log.NewNopLogger()
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
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)
|
||||
}
|
117
log/stdlib.go
117
log/stdlib.go
@ -1,11 +1,9 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's
|
||||
@ -14,42 +12,29 @@ import (
|
||||
//
|
||||
// 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
|
||||
}
|
||||
type StdlibWriter = log.StdlibWriter
|
||||
|
||||
// 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
|
||||
prefix string
|
||||
joinPrefixToMsg bool
|
||||
}
|
||||
type StdlibAdapter = log.StdlibAdapter
|
||||
|
||||
// StdlibAdapterOption sets a parameter for the StdlibAdapter.
|
||||
type StdlibAdapterOption func(*StdlibAdapter)
|
||||
type StdlibAdapterOption = log.StdlibAdapterOption
|
||||
|
||||
// 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 }
|
||||
return log.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 }
|
||||
return log.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 }
|
||||
return log.MessageKey(key)
|
||||
}
|
||||
|
||||
// Prefix configures the adapter to parse a prefix from stdlib log events. If
|
||||
@ -59,93 +44,11 @@ func MessageKey(key string) StdlibAdapterOption {
|
||||
// By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to
|
||||
// true if you want to include the parsed prefix in the msg.
|
||||
func Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption {
|
||||
return func(a *StdlibAdapter) { a.prefix = prefix; a.joinPrefixToMsg = joinPrefixToMsg }
|
||||
return log.Prefix(prefix, joinPrefixToMsg)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
p = a.handlePrefix(p)
|
||||
|
||||
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 {
|
||||
msg = a.handleMessagePrefix(msg)
|
||||
keyvals = append(keyvals, a.messageKey, msg)
|
||||
}
|
||||
if err := a.Logger.Log(keyvals...); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (a StdlibAdapter) handlePrefix(p []byte) []byte {
|
||||
if a.prefix != "" {
|
||||
p = bytes.TrimPrefix(p, []byte(a.prefix))
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (a StdlibAdapter) handleMessagePrefix(msg string) string {
|
||||
if a.prefix == "" {
|
||||
return msg
|
||||
}
|
||||
|
||||
msg = strings.TrimPrefix(msg, a.prefix)
|
||||
if a.joinPrefixToMsg {
|
||||
msg = a.prefix + msg
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
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>(?s:.*))`
|
||||
)
|
||||
|
||||
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] = strings.TrimRight(string(m[i]), "\n")
|
||||
}
|
||||
return result
|
||||
return log.NewStdlibAdapter(logger, options...)
|
||||
}
|
||||
|
@ -1,261 +0,0 @@
|
||||
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 TestStdLibAdapterPrefixedExtraction(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
logger := NewLogfmtLogger(buf)
|
||||
writer := NewStdlibAdapter(logger, Prefix("some prefix ", false))
|
||||
for input, want := range map[string]string{
|
||||
"some prefix hello": "msg=hello\n",
|
||||
"some prefix 2009/01/23: hello": "ts=2009/01/23 msg=hello\n",
|
||||
"some prefix 2009/01/23 01:23:23: hello": "ts=\"2009/01/23 01:23:23\" msg=hello\n",
|
||||
"some prefix 01:23:23: hello": "ts=01:23:23 msg=hello\n",
|
||||
"some prefix 2009/01/23 01:23:23.123123: hello": "ts=\"2009/01/23 01:23:23.123123\" msg=hello\n",
|
||||
"some prefix 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",
|
||||
"some prefix 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",
|
||||
"some prefix 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",
|
||||
"some prefix 2009/01/23 /a/b/c/d.go:23: hello": "ts=2009/01/23 caller=/a/b/c/d.go:23 msg=hello\n",
|
||||
"some prefix /a/b/c/d.go:23: hello": "caller=/a/b/c/d.go:23 msg=hello\n",
|
||||
"/a/b/c/d.go:23: some prefix 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 TestStdLibAdapterPrefixedExtractionWithJoinToMessage(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
logger := NewLogfmtLogger(buf)
|
||||
writer := NewStdlibAdapter(logger, Prefix("some prefix ", true))
|
||||
for input, want := range map[string]string{
|
||||
"some prefix hello": "msg=\"some prefix hello\"\n",
|
||||
"some prefix 2009/01/23: hello": "ts=2009/01/23 msg=\"some prefix hello\"\n",
|
||||
"some prefix 2009/01/23 01:23:23: hello": "ts=\"2009/01/23 01:23:23\" msg=\"some prefix hello\"\n",
|
||||
"some prefix 01:23:23: hello": "ts=01:23:23 msg=\"some prefix hello\"\n",
|
||||
"some prefix 2009/01/23 01:23:23.123123: hello": "ts=\"2009/01/23 01:23:23.123123\" msg=\"some prefix hello\"\n",
|
||||
"some prefix 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=\"some prefix hello\"\n",
|
||||
"some prefix 01:23:23.123123 /a/b/c/d.go:23: hello": "ts=01:23:23.123123 caller=/a/b/c/d.go:23 msg=\"some prefix hello\"\n",
|
||||
"some prefix 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=\"some prefix hello\"\n",
|
||||
"some prefix 2009/01/23 /a/b/c/d.go:23: hello": "ts=2009/01/23 caller=/a/b/c/d.go:23 msg=\"some prefix hello\"\n",
|
||||
"some prefix /a/b/c/d.go:23: hello": "caller=/a/b/c/d.go:23 msg=\"some prefix hello\"\n",
|
||||
"/a/b/c/d.go:23: some prefix hello": "caller=/a/b/c/d.go:23 msg=\"some prefix 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",
|
||||
},
|
||||
"hello\nworld": {
|
||||
"date": "",
|
||||
"time": "",
|
||||
"file": "",
|
||||
"msg": "hello\nworld",
|
||||
},
|
||||
"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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
log/sync.go
86
log/sync.go
@ -2,8 +2,8 @@ package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// SwapLogger wraps another logger that may be safely replaced while other
|
||||
@ -12,29 +12,7 @@ import (
|
||||
//
|
||||
// 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})
|
||||
}
|
||||
type SwapLogger = log.SwapLogger
|
||||
|
||||
// NewSyncWriter returns a new writer that is safe for concurrent use by
|
||||
// multiple goroutines. Writes to the returned writer are passed on to w. If
|
||||
@ -47,53 +25,7 @@ func (l *SwapLogger) Swap(logger Logger) {
|
||||
// Fd() uintptr
|
||||
// }
|
||||
func NewSyncWriter(w io.Writer) io.Writer {
|
||||
switch w := w.(type) {
|
||||
case fdWriter:
|
||||
return &fdSyncWriter{fdWriter: w}
|
||||
default:
|
||||
return &syncWriter{Writer: w}
|
||||
}
|
||||
}
|
||||
|
||||
// syncWriter synchronizes concurrent writes to an io.Writer.
|
||||
type syncWriter struct {
|
||||
sync.Mutex
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// 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.Lock()
|
||||
defer w.Unlock()
|
||||
return w.Writer.Write(p)
|
||||
}
|
||||
|
||||
// fdWriter is an io.Writer that also has an Fd method. The most common
|
||||
// example of an fdWriter is an *os.File.
|
||||
type fdWriter interface {
|
||||
io.Writer
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
// fdSyncWriter synchronizes concurrent writes to an fdWriter.
|
||||
type fdSyncWriter struct {
|
||||
sync.Mutex
|
||||
fdWriter
|
||||
}
|
||||
|
||||
// Write writes p to the underlying io.Writer. If another write is already in
|
||||
// progress, the calling goroutine blocks until the fdSyncWriter is available.
|
||||
func (w *fdSyncWriter) Write(p []byte) (n int, err error) {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
return w.fdWriter.Write(p)
|
||||
}
|
||||
|
||||
// syncLogger provides concurrent safe logging for another Logger.
|
||||
type syncLogger struct {
|
||||
mu sync.Mutex
|
||||
logger Logger
|
||||
return log.NewSyncWriter(w)
|
||||
}
|
||||
|
||||
// NewSyncLogger returns a logger that synchronizes concurrent use of the
|
||||
@ -101,13 +33,5 @@ type syncLogger struct {
|
||||
// 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()
|
||||
defer l.mu.Unlock()
|
||||
return l.logger.Log(keyvals...)
|
||||
return log.NewSyncLogger(logger)
|
||||
}
|
||||
|
101
log/sync_test.go
101
log/sync_test.go
@ -1,101 +0,0 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"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)
|
||||
}
|
||||
|
||||
func TestSyncWriterFd(t *testing.T) {
|
||||
_, ok := log.NewSyncWriter(os.Stdout).(interface {
|
||||
Fd() uintptr
|
||||
})
|
||||
|
||||
if !ok {
|
||||
t.Error("NewSyncWriter does not pass through Fd method")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncLoggerPanic(t *testing.T) {
|
||||
var logger log.Logger
|
||||
logger = log.LoggerFunc(func(...interface{}) error { panic("!") })
|
||||
logger = log.NewSyncLogger(logger)
|
||||
|
||||
f := func() {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
t.Log(x)
|
||||
}
|
||||
}()
|
||||
logger.Log("hello", "world")
|
||||
}
|
||||
|
||||
f()
|
||||
f() // without defer Unlock, this one can deadlock
|
||||
}
|
@ -1,147 +1,34 @@
|
||||
// +build !windows
|
||||
// +build !plan9
|
||||
// +build !nacl
|
||||
//go:build !windows && !plan9 && !nacl
|
||||
// +build !windows,!plan9,!nacl
|
||||
|
||||
// Deprecated: Use github.com/go-kit/log/syslog instead.
|
||||
package syslog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
gosyslog "log/syslog"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/syslog"
|
||||
)
|
||||
|
||||
// SyslogWriter is an interface wrapping stdlib syslog Writer.
|
||||
type SyslogWriter interface {
|
||||
Write([]byte) (int, error)
|
||||
Close() error
|
||||
Emerg(string) error
|
||||
Alert(string) error
|
||||
Crit(string) error
|
||||
Err(string) error
|
||||
Warning(string) error
|
||||
Notice(string) error
|
||||
Info(string) error
|
||||
Debug(string) error
|
||||
}
|
||||
type SyslogWriter = syslog.SyslogWriter
|
||||
|
||||
// NewSyslogLogger returns a new Logger which writes to syslog in syslog format.
|
||||
// The body of the log message is the formatted output from the Logger returned
|
||||
// by newLogger.
|
||||
func NewSyslogLogger(w SyslogWriter, newLogger func(io.Writer) log.Logger, options ...Option) log.Logger {
|
||||
l := &syslogLogger{
|
||||
w: w,
|
||||
newLogger: newLogger,
|
||||
prioritySelector: defaultPrioritySelector,
|
||||
bufPool: sync.Pool{New: func() interface{} {
|
||||
return &loggerBuf{}
|
||||
}},
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(l)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
type syslogLogger struct {
|
||||
w SyslogWriter
|
||||
newLogger func(io.Writer) log.Logger
|
||||
prioritySelector PrioritySelector
|
||||
bufPool sync.Pool
|
||||
}
|
||||
|
||||
func (l *syslogLogger) Log(keyvals ...interface{}) error {
|
||||
level := l.prioritySelector(keyvals...)
|
||||
|
||||
lb := l.getLoggerBuf()
|
||||
defer l.putLoggerBuf(lb)
|
||||
if err := lb.logger.Log(keyvals...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch level {
|
||||
case gosyslog.LOG_EMERG:
|
||||
return l.w.Emerg(lb.buf.String())
|
||||
case gosyslog.LOG_ALERT:
|
||||
return l.w.Alert(lb.buf.String())
|
||||
case gosyslog.LOG_CRIT:
|
||||
return l.w.Crit(lb.buf.String())
|
||||
case gosyslog.LOG_ERR:
|
||||
return l.w.Err(lb.buf.String())
|
||||
case gosyslog.LOG_WARNING:
|
||||
return l.w.Warning(lb.buf.String())
|
||||
case gosyslog.LOG_NOTICE:
|
||||
return l.w.Notice(lb.buf.String())
|
||||
case gosyslog.LOG_INFO:
|
||||
return l.w.Info(lb.buf.String())
|
||||
case gosyslog.LOG_DEBUG:
|
||||
return l.w.Debug(lb.buf.String())
|
||||
default:
|
||||
_, err := l.w.Write(lb.buf.Bytes())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
type loggerBuf struct {
|
||||
buf *bytes.Buffer
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (l *syslogLogger) 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 *syslogLogger) putLoggerBuf(lb *loggerBuf) {
|
||||
l.bufPool.Put(lb)
|
||||
return syslog.NewSyslogLogger(w, newLogger, options...)
|
||||
}
|
||||
|
||||
// Option sets a parameter for syslog loggers.
|
||||
type Option func(*syslogLogger)
|
||||
type Option = syslog.Option
|
||||
|
||||
// PrioritySelector inspects the list of keyvals and selects a syslog priority.
|
||||
type PrioritySelector func(keyvals ...interface{}) gosyslog.Priority
|
||||
type PrioritySelector = syslog.PrioritySelector
|
||||
|
||||
// PrioritySelectorOption sets priority selector function to choose syslog
|
||||
// priority.
|
||||
func PrioritySelectorOption(selector PrioritySelector) Option {
|
||||
return func(l *syslogLogger) { l.prioritySelector = selector }
|
||||
}
|
||||
|
||||
func defaultPrioritySelector(keyvals ...interface{}) gosyslog.Priority {
|
||||
l := len(keyvals)
|
||||
for i := 0; i < l; i += 2 {
|
||||
if keyvals[i] == level.Key() {
|
||||
var val interface{}
|
||||
if i+1 < l {
|
||||
val = keyvals[i+1]
|
||||
}
|
||||
if v, ok := val.(level.Value); ok {
|
||||
switch v {
|
||||
case level.DebugValue():
|
||||
return gosyslog.LOG_DEBUG
|
||||
case level.InfoValue():
|
||||
return gosyslog.LOG_INFO
|
||||
case level.WarnValue():
|
||||
return gosyslog.LOG_WARNING
|
||||
case level.ErrorValue():
|
||||
return gosyslog.LOG_ERR
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return gosyslog.LOG_INFO
|
||||
return syslog.PrioritySelectorOption(selector)
|
||||
}
|
||||
|
@ -1,170 +0,0 @@
|
||||
// +build !windows
|
||||
// +build !plan9
|
||||
// +build !nacl
|
||||
|
||||
package syslog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
gosyslog "log/syslog"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
)
|
||||
|
||||
func TestSyslogLoggerDefaultPrioritySelector(t *testing.T) {
|
||||
w := &testSyslogWriter{}
|
||||
l := NewSyslogLogger(w, log.NewLogfmtLogger)
|
||||
|
||||
l.Log("level", level.WarnValue(), "msg", "one")
|
||||
l.Log("level", "undefined", "msg", "two")
|
||||
l.Log("level", level.InfoValue(), "msg", "three")
|
||||
l.Log("level", level.ErrorValue(), "msg", "four")
|
||||
l.Log("level", level.DebugValue(), "msg", "five")
|
||||
|
||||
l.Log("msg", "six", "level", level.ErrorValue())
|
||||
l.Log("msg", "seven", "level", level.DebugValue())
|
||||
l.Log("msg", "eight", "level", level.InfoValue())
|
||||
l.Log("msg", "nine", "level", "undefined")
|
||||
l.Log("msg", "ten", "level", level.WarnValue())
|
||||
|
||||
l.Log("level", level.ErrorValue(), "msg")
|
||||
l.Log("msg", "eleven", "level")
|
||||
|
||||
want := []string{
|
||||
"warning: level=warn msg=one\n",
|
||||
"info: level=undefined msg=two\n",
|
||||
"info: level=info msg=three\n",
|
||||
"err: level=error msg=four\n",
|
||||
"debug: level=debug msg=five\n",
|
||||
|
||||
"err: msg=six level=error\n",
|
||||
"debug: msg=seven level=debug\n",
|
||||
"info: msg=eight level=info\n",
|
||||
"info: msg=nine level=undefined\n",
|
||||
"warning: msg=ten level=warn\n",
|
||||
|
||||
"err: level=error msg=null\n",
|
||||
"info: msg=eleven level=null\n",
|
||||
}
|
||||
have := w.writes
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("wrong writes: want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyslogLoggerExhaustivePrioritySelector(t *testing.T) {
|
||||
w := &testSyslogWriter{}
|
||||
selector := func(keyvals ...interface{}) gosyslog.Priority {
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
if keyvals[i] == level.Key() {
|
||||
if v, ok := keyvals[i+1].(string); ok {
|
||||
switch v {
|
||||
case "emergency":
|
||||
return gosyslog.LOG_EMERG
|
||||
case "alert":
|
||||
return gosyslog.LOG_ALERT
|
||||
case "critical":
|
||||
return gosyslog.LOG_CRIT
|
||||
case "error":
|
||||
return gosyslog.LOG_ERR
|
||||
case "warning":
|
||||
return gosyslog.LOG_WARNING
|
||||
case "notice":
|
||||
return gosyslog.LOG_NOTICE
|
||||
case "info":
|
||||
return gosyslog.LOG_INFO
|
||||
case "debug":
|
||||
return gosyslog.LOG_DEBUG
|
||||
}
|
||||
return gosyslog.LOG_LOCAL0
|
||||
}
|
||||
}
|
||||
}
|
||||
return gosyslog.LOG_LOCAL0
|
||||
}
|
||||
l := NewSyslogLogger(w, log.NewLogfmtLogger, PrioritySelectorOption(selector))
|
||||
|
||||
l.Log("level", "warning", "msg", "one")
|
||||
l.Log("level", "error", "msg", "two")
|
||||
l.Log("level", "critical", "msg", "three")
|
||||
l.Log("level", "debug", "msg", "four")
|
||||
l.Log("level", "info", "msg", "five")
|
||||
l.Log("level", "alert", "msg", "six")
|
||||
l.Log("level", "emergency", "msg", "seven")
|
||||
l.Log("level", "notice", "msg", "eight")
|
||||
l.Log("level", "unknown", "msg", "nine")
|
||||
|
||||
want := []string{
|
||||
"warning: level=warning msg=one\n",
|
||||
"err: level=error msg=two\n",
|
||||
"crit: level=critical msg=three\n",
|
||||
"debug: level=debug msg=four\n",
|
||||
"info: level=info msg=five\n",
|
||||
"alert: level=alert msg=six\n",
|
||||
"emerg: level=emergency msg=seven\n",
|
||||
"notice: level=notice msg=eight\n",
|
||||
"write: level=unknown msg=nine\n",
|
||||
}
|
||||
have := w.writes
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("wrong writes: want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
type testSyslogWriter struct {
|
||||
writes []string
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Write(b []byte) (int, error) {
|
||||
msg := string(b)
|
||||
w.writes = append(w.writes, fmt.Sprintf("write: %s", msg))
|
||||
return len(msg), nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Emerg(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("emerg: %s", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Alert(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("alert: %s", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Crit(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("crit: %s", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Err(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("err: %s", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Warning(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("warning: %s", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Notice(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("notice: %s", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Info(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("info: %s", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *testSyslogWriter) Debug(msg string) error {
|
||||
w.writes = append(w.writes, fmt.Sprintf("debug: %s", msg))
|
||||
return nil
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
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.
|
@ -1,144 +1,44 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/term"
|
||||
)
|
||||
|
||||
// Color represents an ANSI color. The zero value is Default.
|
||||
type Color uint8
|
||||
type Color = term.Color
|
||||
|
||||
// ANSI colors.
|
||||
const (
|
||||
Default = Color(iota)
|
||||
Default = term.Default
|
||||
|
||||
Black
|
||||
DarkRed
|
||||
DarkGreen
|
||||
Brown
|
||||
DarkBlue
|
||||
DarkMagenta
|
||||
DarkCyan
|
||||
Gray
|
||||
Black = term.Black
|
||||
DarkRed = term.DarkRed
|
||||
DarkGreen = term.DarkGreen
|
||||
Brown = term.Brown
|
||||
DarkBlue = term.DarkBlue
|
||||
DarkMagenta = term.DarkMagenta
|
||||
DarkCyan = term.DarkCyan
|
||||
Gray = term.Gray
|
||||
|
||||
DarkGray
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
Blue
|
||||
Magenta
|
||||
Cyan
|
||||
White
|
||||
|
||||
numColors
|
||||
DarkGray = term.DarkGray
|
||||
Red = term.Red
|
||||
Green = term.Green
|
||||
Yellow = term.Yellow
|
||||
Blue = term.Blue
|
||||
Magenta = term.Magenta
|
||||
Cyan = term.Cyan
|
||||
White = term.White
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
type FgBgColor = term.FgBgColor
|
||||
|
||||
// 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)
|
||||
return term.NewColorLogger(w, newLogger, color)
|
||||
}
|
||||
|
@ -1,88 +0,0 @@
|
||||
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))
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
// +build !windows
|
||||
|
||||
package term
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-kit/log/term"
|
||||
)
|
||||
|
||||
// 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
|
||||
return term.NewColorWriter(w)
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
// The code in this file is adapted from github.com/mattn/go-colorable.
|
||||
|
||||
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 !IsConsole(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
|
||||
}
|
@ -1,22 +1,23 @@
|
||||
// Package term provides tools for logging to a terminal.
|
||||
//
|
||||
// Deprecated: Use github.com/go-kit/log/term instead.
|
||||
package term
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/term"
|
||||
)
|
||||
|
||||
// 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)
|
||||
return term.NewLogger(w, newLogger, color)
|
||||
}
|
||||
|
||||
type fder interface {
|
||||
Fd() uintptr
|
||||
// IsTerminal returns true if w writes to a terminal.
|
||||
func IsTerminal(w io.Writer) bool {
|
||||
return term.IsTerminal(w)
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
// 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
|
@ -1,7 +0,0 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
@ -1,12 +0,0 @@
|
||||
// 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
|
@ -1,25 +0,0 @@
|
||||
// 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
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
@ -1,15 +0,0 @@
|
||||
// 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 js
|
||||
|
||||
package term
|
||||
|
||||
import "io"
|
||||
|
||||
// IsTerminal always returns false on AppEngine.
|
||||
func IsTerminal(w io.Writer) bool {
|
||||
return false
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
// 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 (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
|
||||
msysPipeNameRegex = regexp.MustCompile(`\\(cygwin|msys)-\w+-pty\d?-(to|from)-master`)
|
||||
)
|
||||
|
||||
const (
|
||||
fileNameInfo = 0x02
|
||||
)
|
||||
|
||||
// IsTerminal returns true if w writes to a terminal.
|
||||
func IsTerminal(w io.Writer) bool {
|
||||
return IsConsole(w) || IsMSYSTerminal(w)
|
||||
}
|
||||
|
||||
// IsConsole returns true if w writes to a Windows console.
|
||||
func IsConsole(w io.Writer) bool {
|
||||
var handle syscall.Handle
|
||||
|
||||
if fw, ok := w.(fder); ok {
|
||||
handle = syscall.Handle(fw.Fd())
|
||||
} else {
|
||||
// The writer has no file-descriptor and so can't be a terminal.
|
||||
return false
|
||||
}
|
||||
|
||||
var st uint32
|
||||
err := syscall.GetConsoleMode(handle, &st)
|
||||
|
||||
// If the handle is attached to a terminal, GetConsoleMode returns a
|
||||
// non-zero value containing the console mode flags. We don't care about
|
||||
// the specifics of flags, just that it is not zero.
|
||||
return (err == nil && st != 0)
|
||||
}
|
||||
|
||||
// IsMSYSTerminal returns true if w writes to a MSYS/MSYS2 terminal.
|
||||
func IsMSYSTerminal(w io.Writer) bool {
|
||||
var handle syscall.Handle
|
||||
|
||||
if fw, ok := w.(fder); ok {
|
||||
handle = syscall.Handle(fw.Fd())
|
||||
} else {
|
||||
// The writer has no file-descriptor and so can't be a terminal.
|
||||
return false
|
||||
}
|
||||
|
||||
// MSYS(2) terminal reports as a pipe for STDIN/STDOUT/STDERR. If it isn't
|
||||
// a pipe, it can't be a MSYS(2) terminal.
|
||||
filetype, err := syscall.GetFileType(handle)
|
||||
|
||||
if filetype != syscall.FILE_TYPE_PIPE || err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// MSYS2/Cygwin terminal's name looks like: \msys-dd50a72ab4668b33-pty2-to-master
|
||||
data := make([]byte, 256, 256)
|
||||
|
||||
r, _, e := syscall.Syscall6(
|
||||
procGetFileInformationByHandleEx.Addr(),
|
||||
4,
|
||||
uintptr(handle),
|
||||
uintptr(fileNameInfo),
|
||||
uintptr(unsafe.Pointer(&data[0])),
|
||||
uintptr(len(data)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if r != 0 && e == 0 {
|
||||
// The first 4 bytes of the buffer are the size of the UTF16 name, in bytes.
|
||||
unameLen := binary.LittleEndian.Uint32(data[:4]) / 2
|
||||
uname := make([]uint16, unameLen, unameLen)
|
||||
|
||||
for i := uint32(0); i < unameLen; i++ {
|
||||
uname[i] = binary.LittleEndian.Uint16(data[i*2+4 : i*2+2+4])
|
||||
}
|
||||
|
||||
name := syscall.UTF16ToString(uname)
|
||||
|
||||
return msysPipeNameRegex.MatchString(name)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type myWriter struct {
|
||||
fd uintptr
|
||||
}
|
||||
|
||||
func (w *myWriter) Write(p []byte) (int, error) {
|
||||
return 0, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (w *myWriter) Fd() uintptr {
|
||||
return w.fd
|
||||
}
|
||||
|
||||
var procGetStdHandle = kernel32.NewProc("GetStdHandle")
|
||||
|
||||
const stdOutputHandle = ^uintptr(0) - 11 + 1
|
||||
|
||||
func getConsoleHandle() syscall.Handle {
|
||||
ptr, err := syscall.UTF16PtrFromString("CONOUT$")
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
handle, err := syscall.CreateFile(ptr, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, 0, 0)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return handle
|
||||
}
|
||||
|
||||
func TestIsTerminal(t *testing.T) {
|
||||
// This is necessary because depending on whether `go test` is called with
|
||||
// the `-v` option, stdout will or will not be bound, changing the behavior
|
||||
// of the test. So we refer to it directly to avoid flakyness.
|
||||
handle := getConsoleHandle()
|
||||
|
||||
writer := &myWriter{
|
||||
fd: uintptr(handle),
|
||||
}
|
||||
|
||||
if !IsTerminal(writer) {
|
||||
t.Errorf("output is supposed to be a terminal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsConsole(t *testing.T) {
|
||||
// This is necessary because depending on whether `go test` is called with
|
||||
// the `-v` option, stdout will or will not be bound, changing the behavior
|
||||
// of the test. So we refer to it directly to avoid flakyness.
|
||||
handle := getConsoleHandle()
|
||||
|
||||
writer := &myWriter{
|
||||
fd: uintptr(handle),
|
||||
}
|
||||
|
||||
if !IsConsole(writer) {
|
||||
t.Errorf("output is supposed to be a console")
|
||||
}
|
||||
}
|
76
log/value.go
76
log/value.go
@ -1,37 +1,15 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// A Valuer generates a log value. When passed to With, WithPrefix, or
|
||||
// WithSuffix 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
|
||||
}
|
||||
type Valuer = log.Valuer
|
||||
|
||||
// Timestamp returns a timestamp Valuer. It invokes the t function to get the
|
||||
// time; unless you are doing something tricky, pass time.Now.
|
||||
@ -39,7 +17,7 @@ func containsValuer(keyvals []interface{}) bool {
|
||||
// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which
|
||||
// are TimestampFormats that use the RFC3339Nano format.
|
||||
func Timestamp(t func() time.Time) Valuer {
|
||||
return func() interface{} { return t() }
|
||||
return log.Timestamp(t)
|
||||
}
|
||||
|
||||
// TimestampFormat returns a timestamp Valuer with a custom time format. It
|
||||
@ -50,61 +28,25 @@ func Timestamp(t func() time.Time) Valuer {
|
||||
// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which
|
||||
// are TimestampFormats that use the RFC3339Nano format.
|
||||
func TimestampFormat(t func() time.Time, layout string) Valuer {
|
||||
return func() interface{} {
|
||||
return timeFormat{
|
||||
time: t(),
|
||||
layout: layout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A timeFormat represents an instant in time and a layout used when
|
||||
// marshaling to a text format.
|
||||
type timeFormat struct {
|
||||
time time.Time
|
||||
layout string
|
||||
}
|
||||
|
||||
func (tf timeFormat) String() string {
|
||||
return tf.time.Format(tf.layout)
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaller.
|
||||
func (tf timeFormat) MarshalText() (text []byte, err error) {
|
||||
// The following code adapted from the standard library time.Time.Format
|
||||
// method. Using the same undocumented magic constant to extend the size
|
||||
// of the buffer as seen there.
|
||||
b := make([]byte, 0, len(tf.layout)+10)
|
||||
b = tf.time.AppendFormat(b, tf.layout)
|
||||
return b, nil
|
||||
return log.TimestampFormat(t, layout)
|
||||
}
|
||||
|
||||
// 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{} {
|
||||
_, file, line, _ := runtime.Caller(depth)
|
||||
idx := strings.LastIndexByte(file, '/')
|
||||
// using idx+1 below handles both of following cases:
|
||||
// idx == -1 because no "/" was found, or
|
||||
// idx >= 0 and we want to start at the character after the found "/".
|
||||
return file[idx+1:] + ":" + strconv.Itoa(line)
|
||||
}
|
||||
return log.Caller(depth)
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultTimestamp is a Valuer that returns the current wallclock time,
|
||||
// respecting time zones, when bound.
|
||||
DefaultTimestamp = TimestampFormat(time.Now, time.RFC3339Nano)
|
||||
DefaultTimestamp = log.DefaultTimestamp
|
||||
|
||||
// DefaultTimestampUTC is a Valuer that returns the current time in UTC
|
||||
// when bound.
|
||||
DefaultTimestampUTC = TimestampFormat(
|
||||
func() time.Time { return time.Now().UTC() },
|
||||
time.RFC3339Nano,
|
||||
)
|
||||
DefaultTimestampUTC = log.DefaultTimestampUTC
|
||||
|
||||
// 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)
|
||||
DefaultCaller = log.DefaultCaller
|
||||
)
|
||||
|
@ -1,150 +0,0 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"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:31", 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:44", 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 TestTimestampFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
tv := log.TimestampFormat(mocktime, time.RFC822)
|
||||
|
||||
if want, have := now.Add(time.Second).Format(time.RFC822), fmt.Sprint(tv()); want != have {
|
||||
t.Errorf("wrong time format: want %v, have %v", want, have)
|
||||
}
|
||||
|
||||
if want, have := now.Add(2*time.Second).Format(time.RFC822), fmt.Sprint(tv()); want != have {
|
||||
t.Errorf("wrong time format: want %v, have %v", want, have)
|
||||
}
|
||||
|
||||
mustMarshal := func(v interface{}) []byte {
|
||||
b, err := v.(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
t.Fatal("error marshaling text:", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
if want, have := now.Add(3*time.Second).AppendFormat(nil, time.RFC822), mustMarshal(tv()); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("wrong time format: want %s, have %s", want, have)
|
||||
}
|
||||
|
||||
if want, have := now.Add(4*time.Second).AppendFormat(nil, time.RFC822), mustMarshal(tv()); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("wrong time format: want %s, have %s", 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")
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package zap
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
@ -12,10 +12,10 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/generic"
|
||||
"github.com/go-kit/kit/metrics/internal/lv"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -10,9 +10,9 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/teststat"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
const metricNameToGenerateError = "metric_name_used_to_throw_an_error"
|
||||
|
@ -13,10 +13,10 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/internal/convert"
|
||||
"github.com/go-kit/kit/metrics/internal/lv"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -20,12 +20,12 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/generic"
|
||||
"github.com/go-kit/kit/metrics/internal/lv"
|
||||
"github.com/go-kit/kit/metrics/internal/ratemap"
|
||||
"github.com/go-kit/kit/util/conn"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Dogstatsd receives metrics observations and forwards them to a DogStatsD
|
||||
|
@ -3,8 +3,8 @@ package dogstatsd
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics/teststat"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestCounter(t *testing.T) {
|
||||
|
@ -14,10 +14,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/generic"
|
||||
"github.com/go-kit/kit/util/conn"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Graphite receives metrics observations and forwards them to a Graphite server.
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics/teststat"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestCounter(t *testing.T) {
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
|
||||
influxdb "github.com/influxdata/influxdb1-client/v2"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func ExampleCounter() {
|
||||
|
@ -9,10 +9,10 @@ import (
|
||||
|
||||
influxdb "github.com/influxdata/influxdb1-client/v2"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/generic"
|
||||
"github.com/go-kit/kit/metrics/internal/lv"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Influx is a store for metrics that will be emitted to an Influx database.
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
|
||||
influxdb "github.com/influxdata/influxdb1-client/v2"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics/teststat"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestCounter(t *testing.T) {
|
||||
|
@ -19,12 +19,12 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/generic"
|
||||
"github.com/go-kit/kit/metrics/internal/lv"
|
||||
"github.com/go-kit/kit/metrics/internal/ratemap"
|
||||
"github.com/go-kit/kit/util/conn"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Influxstatsd receives metrics observations and forwards them to a server.
|
||||
|
@ -3,8 +3,8 @@ package influxstatsd
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics/teststat"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestCounter(t *testing.T) {
|
||||
|
@ -14,11 +14,11 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/internal/lv"
|
||||
"github.com/go-kit/kit/metrics/internal/ratemap"
|
||||
"github.com/go-kit/kit/util/conn"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Statsd receives metrics observations and forwards them to a StatsD server.
|
||||
|
@ -3,8 +3,8 @@ package statsd
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/metrics/teststat"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestCounter(t *testing.T) {
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func BenchmarkEndpoints(b *testing.B) {
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/internal/instance"
|
||||
"github.com/go-kit/kit/util/conn"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
const defaultIndex = 0
|
||||
|
@ -2,13 +2,14 @@ package consul
|
||||
|
||||
import (
|
||||
"context"
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
var _ sd.Instancer = (*Instancer)(nil) // API check
|
||||
|
@ -1,3 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package consul
|
||||
@ -9,8 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
stdconsul "github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
|
||||
stdconsul "github.com/hashicorp/consul/api"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Registrar registers service instance liveness information to Consul.
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
|
||||
stdconsul "github.com/hashicorp/consul/api"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestRegistrar(t *testing.T) {
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/internal/instance"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// ErrPortZero is returned by the resolve machinery
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
var _ sd.Instancer = (*Instancer)(nil) // API check
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// endpointCache collects the most recent set of instances from a service discovery
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestEndpointCache(t *testing.T) {
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Endpointer listens to a service discovery system and yields a set of
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/internal/instance"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestDefaultEndpointer(t *testing.T) {
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/lb"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
|
@ -1,9 +1,9 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/internal/instance"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Instancer yields instances stored in a certain etcd keyspace. Any kind of
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
|
||||
stdetcd "go.etcd.io/etcd/client/v2"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
var _ sd.Instancer = (*Instancer)(nil) // API check
|
||||
|
@ -1,3 +1,4 @@
|
||||
//go:build flaky_integration
|
||||
// +build flaky_integration
|
||||
|
||||
package etcd
|
||||
@ -10,8 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Package sd/etcd provides a wrapper around the etcd key/value store. This
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
|
||||
etcd "go.etcd.io/etcd/client/v2"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
const minHeartBeatTime = 500 * time.Millisecond
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// testClient is a basic implementation of Client
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/lb"
|
||||
"github.com/go-kit/log"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package etcdv3
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/internal/instance"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Instancer yields instances stored in a certain etcd keyspace. Any kind of
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
var _ sd.Instancer = (*Instancer)(nil) // API check
|
||||
|
@ -1,3 +1,4 @@
|
||||
//go:build flaky_integration
|
||||
// +build flaky_integration
|
||||
|
||||
package etcdv3
|
||||
@ -10,8 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func runIntegration(settings integrationSettings, client Client, service Service, t *testing.T) {
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
const minHeartBeatTime = 500 * time.Millisecond
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// testClient is a basic implementation of Client
|
||||
|
@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/hudl/fargo"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/internal/instance"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Instancer yields instances stored in the Eureka registry for the given app.
|
||||
|
@ -1,3 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package eureka
|
||||
@ -9,7 +10,7 @@ import (
|
||||
|
||||
"github.com/hudl/fargo"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Package sd/eureka provides a wrapper around the Netflix Eureka service
|
||||
|
@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/hudl/fargo"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Matches official Netflix Java client default.
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/hudl/fargo"
|
||||
)
|
||||
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
var _ sd.Instancer = (*Cache)(nil) // API check
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/go-zookeeper/zk"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// DefaultACL is the default ACL to use for creating znodes.
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
|
||||
stdzk "github.com/go-zookeeper/zk"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
|
@ -3,9 +3,9 @@ package zk
|
||||
import (
|
||||
"github.com/go-zookeeper/zk"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/internal/instance"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Instancer yield instances stored in a certain ZooKeeper path. Any kind of
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/go-zookeeper/zk"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// wrapLogger wraps a Go kit logger so we can use it as the logging service for
|
||||
|
@ -1,6 +1,6 @@
|
||||
package zk
|
||||
|
||||
import "github.com/go-kit/kit/log"
|
||||
import "github.com/go-kit/log"
|
||||
|
||||
// Registrar registers service instance liveness information to ZooKeeper.
|
||||
type Registrar struct {
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
"github.com/go-zookeeper/zk"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// ContextToGRPC returns a grpc RequestFunc that injects an OpenTracing Span
|
||||
|
@ -8,8 +8,8 @@ import (
|
||||
"github.com/opentracing/opentracing-go/mocktracer"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
kitot "github.com/go-kit/kit/tracing/opentracing"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestTraceGRPCRequestRoundtrip(t *testing.T) {
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// ContextToHTTP returns an http RequestFunc that injects an OpenTracing Span
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/opentracing/opentracing-go/mocktracer"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
kitot "github.com/go-kit/kit/tracing/opentracing"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestTraceHTTPRequestRoundtrip(t *testing.T) {
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
kitgrpc "github.com/go-kit/kit/transport/grpc"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// GRPCClientTrace enables native Zipkin tracing of a Go kit gRPC transport
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation/b3"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// HTTPClientTrace enables native Zipkin tracing of a Go kit HTTP transport
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user