1
0
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:
Chris Hines
2021-08-18 11:15:32 -04:00
committed by GitHub
parent 3220134a9b
commit 801da84a68
112 changed files with 215 additions and 3168 deletions

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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,

View File

@ -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") }
)

View File

@ -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
}

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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")
}
})
}
})
}
})
}
}

View File

@ -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)

View File

@ -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() }

View File

@ -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)
}
}

View File

@ -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

View File

@ -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")
}
}

View File

@ -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)
}

View File

@ -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" }

View File

@ -6,7 +6,7 @@ import (
"errors"
"fmt"
"github.com/go-kit/kit/log"
"github.com/go-kit/log"
"github.com/sirupsen/logrus"
)

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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...)
}

View File

@ -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)
}
}
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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.

View File

@ -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)
}

View File

@ -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))
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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

View File

@ -1,7 +0,0 @@
package term
import (
"syscall"
)
const ioctlReadTermios = syscall.TIOCGETA

View File

@ -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

View File

@ -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
}

View File

@ -1,5 +0,0 @@
package term
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
)

View File

@ -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")
}
}

View File

@ -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"
)

View File

@ -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 (

View File

@ -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"

View File

@ -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 (

View File

@ -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

View File

@ -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) {

View File

@ -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.

View File

@ -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) {

View File

@ -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() {

View File

@ -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.

View File

@ -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) {

View File

@ -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.

View File

@ -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) {

View File

@ -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.

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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"
)

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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() {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"
)

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -4,7 +4,7 @@ import (
"sync"
"time"
"github.com/go-kit/kit/log"
"github.com/go-kit/log"
)
const minHeartBeatTime = 500 * time.Millisecond

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -7,7 +7,7 @@ import (
"sync"
"time"
"github.com/go-kit/kit/log"
"github.com/go-kit/log"
"github.com/hudl/fargo"
)

View File

@ -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

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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 (

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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