Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions log/alt_experimental_level/benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package level_test

import (
"fmt"
"io/ioutil"
"testing"

"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/alt_experimental_level"
)

func BenchmarkNopBaseline(b *testing.B) {
singleRecordBenchmarkRunner(b, log.NewNopLogger())
}

func BenchmarkNopDisallowedLevel(b *testing.B) {
singleRecordBenchmarkRunner(b,
level.AllowingInfoAndAbove(log.NewNopLogger()))
}

func BenchmarkNopAllowedLevel(b *testing.B) {
singleRecordBenchmarkRunner(b,
level.AllowingAll(log.NewNopLogger()))
}

func BenchmarkJSONBaseline(b *testing.B) {
singleRecordBenchmarkRunner(b, log.NewJSONLogger(ioutil.Discard))
}

func BenchmarkJSONDisallowedLevel(b *testing.B) {
singleRecordBenchmarkRunner(b,
level.AllowingInfoAndAbove(log.NewJSONLogger(ioutil.Discard)))
}

func BenchmarkJSONAllowedLevel(b *testing.B) {
singleRecordBenchmarkRunner(b,
level.AllowingAll(log.NewJSONLogger(ioutil.Discard)))
}

func BenchmarkLogfmtBaseline(b *testing.B) {
singleRecordBenchmarkRunner(b, log.NewLogfmtLogger(ioutil.Discard))
}

func BenchmarkLogfmtDisallowedLevel(b *testing.B) {
singleRecordBenchmarkRunner(b,
level.AllowingInfoAndAbove(log.NewLogfmtLogger(ioutil.Discard)))
}

func BenchmarkLogfmtAllowedLevel(b *testing.B) {
singleRecordBenchmarkRunner(b,
level.AllowingAll(log.NewLogfmtLogger(ioutil.Discard)))
}

func singleRecordBenchmarkRunner(b *testing.B, logger log.Logger) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
level.Debug(logger).Log("foo", "bar")
}
}

func BenchmarkDroppedRecords(b *testing.B) {
logger := log.NewNopLogger()
logger = log.NewContext(logger).With("ts", log.DefaultTimestamp, "caller", log.DefaultCaller)
for _, dropped := range []uint{1, 3, 9, 99, 999} {
b.Run(fmt.Sprintf("%d-of-%d", dropped, dropped+1), func(b *testing.B) {
manyRecordBenchmarkRunner(b, logger, dropped)
})
}
}

func manyRecordBenchmarkRunner(b *testing.B, logger log.Logger, droppedRecords uint) {
logger = level.AllowingInfoAndAbove(logger)
debug := level.Debug(logger)
info := level.Info(logger)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
// Only this one will be retained.
info.Log("foo", "bar")
for dropped := droppedRecords; dropped != 0; dropped-- {
debug.Log("baz", "quux")
}
}
}
144 changes: 144 additions & 0 deletions log/alt_experimental_level/level.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package level

import "github.com/go-kit/kit/log"

type leveler interface {
Debug(log.Logger) log.Logger
Info(log.Logger) log.Logger
Warn(log.Logger) log.Logger
Error(log.Logger) log.Logger
}

type level byte

const (
levelDebug level = 1 << iota
levelInfo
levelWarn
levelError
)

type levelValue struct {
level
name string
}

func (v *levelValue) String() string {
return v.name
}

var (
valDebug = &levelValue{levelDebug, "debug"}
valInfo = &levelValue{levelInfo, "info"}
valWarn = &levelValue{levelWarn, "warn"}
valError = &levelValue{levelError, "error"}
)

func withLevel(v *levelValue, logger log.Logger) *log.Context {
return log.NewContext(logger).WithPrefix("level", v)
}

// Debug returns a logger ready to emit log records at the "debug"
// level, intended for fine-level detailed tracing information. If the
// supplied logger disallows records at that level, it instead returns
// an inert logger that drops the record.
func Debug(logger log.Logger) log.Logger {
return withLevel(valDebug, logger)
}

// Info returns a logger ready to emit log records at the "info"
// level, intended for informational messages. If the supplied logger
// disallows records at that level, it instead returns an inert logger
// that drops the record.
func Info(logger log.Logger) log.Logger {
return withLevel(valInfo, logger)
}

// Warn returns a logger ready to emit log records at the "warn"
// level, intended for indicating potential problems. If the supplied
// logger disallows records at that level, it instead returns an inert
// logger that drops the record.
func Warn(logger log.Logger) log.Logger {
return withLevel(valWarn, logger)
}

// Error returns a logger ready to emit log records at the "error"
// level, intended for indicating serious failures. If the supplied
// logger disallows records at that level, it instead returns an inert
// logger that drops the record.
func Error(logger log.Logger) log.Logger {
return withLevel(valError, logger)
}

func rejectLevelsOtherThan(mask level) log.Projection {
return func(keyvals []interface{}) ([]interface{}, bool) {
for i, end := 1, len(keyvals); i < end; i += 2 {
if l, ok := keyvals[i].(*levelValue); ok {
if l.level&mask == 0 {
return nil, false
}
break
}
}
return keyvals, true
}
}

var (
preserveInfoAndAbove = rejectLevelsOtherThan(levelInfo | levelWarn | levelError)
preserveWarnAndAbove = rejectLevelsOtherThan(levelWarn | levelError)
preserveErrorOnly = rejectLevelsOtherThan(levelError)
)

// AllowingAll returns a logger allowed to emit log records at all
// levels, unless the supplied logger is already restricted to some
// narrower set of levels, in which case it retains that restriction.
//
// The behavior is equivalent to AllowingDebugAndAbove.
func AllowingAll(logger log.Logger) log.Logger {
return AllowingDebugAndAbove(logger)
}

// AllowingDebugAndAbove returns a logger allowed to emit log records
// at all levels, unless the supplied logger is already restricted to
// some narrower set of levels, in which case it retains that
// restriction.
func AllowingDebugAndAbove(logger log.Logger) log.Logger {
return logger
}

// AllowingInfoAndAbove returns a logger allowed to emit log records
// at levels "info" and above, dropping "debug"-level records, unless
// the supplied logger is already restricted to some narrower set of
// levels, in which case it retains that restriction.
func AllowingInfoAndAbove(logger log.Logger) log.Logger {
return log.NewContext(logger).WithProjection(preserveInfoAndAbove)
}

// AllowingWarnAndAbove returns a logger allowed to emit log records
// at levels "warn" and above, dropping "debug"- and "info"-level
// records, unless the supplied logger is already restricted to some
// narrower set of levels, in which case it retains that restriction.
func AllowingWarnAndAbove(logger log.Logger) log.Logger {
return log.NewContext(logger).WithProjection(preserveWarnAndAbove)
}

// AllowingErrorOnly returns a logger allowed to emit log records only
// at level "error", dropping "debug"-, "info"-, and "warn"-level
// records, unless the supplied logger is already restricted to some
// narrower set of levels, in which case it retains that restriction.
func AllowingErrorOnly(logger log.Logger) log.Logger {
return log.NewContext(logger).WithProjection(preserveErrorOnly)
}

// AllowingNone returns a logger that drops log records at all levels.
func AllowingNone(logger log.Logger) log.Logger {
return log.NewContext(logger).WithProjection(func(keyvals []interface{}) ([]interface{}, bool) {
for i, end := 1, len(keyvals); i < end; i += 2 {
if _, ok := keyvals[i].(*levelValue); ok {
return nil, false
}
}
return keyvals, true
})
}
Loading