Logger migration from log15 to gokit/log (#41636)

* migrate log15 to gokit/log

* fix console log

* update some unittest

* fix all unittest

* fix the build

* Update pkg/infra/log/log.go

Co-authored-by: Yuriy Tseretyan <tceretian@gmail.com>

* general type vector

* correct the level key

Co-authored-by: Yuriy Tseretyan <tceretian@gmail.com>
pull/43760/head
ying-jeanne 4 years ago committed by GitHub
parent 8898a5f0a0
commit a8eef45a44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      pkg/api/frontend_logging.go
  2. 101
      pkg/api/frontend_logging_test.go
  3. 24
      pkg/api/frontendlogging/sentry.go
  4. 67
      pkg/infra/log/file.go
  5. 8
      pkg/infra/log/file_test.go
  6. 11
      pkg/infra/log/interface.go
  7. 205
      pkg/infra/log/level/level.go
  8. 296
      pkg/infra/log/log.go
  9. 85
      pkg/infra/log/syslog.go
  10. 7
      pkg/infra/log/syslog_windows.go
  11. 5
      pkg/infra/remotecache/remotecache.go
  12. 2
      pkg/infra/usagestats/service/service.go
  13. 31
      pkg/login/social/generic_oauth_test.go
  14. 3
      pkg/middleware/recovery.go
  15. 7
      pkg/plugins/manager/loader/initializer/initializer_test.go
  16. 14
      pkg/services/login/loginservice/loginservice_test.go
  17. 31
      pkg/services/ngalert/logging/logging_test.go

@ -34,13 +34,13 @@ func NewFrontendLogMessageHandler(store *frontendlogging.SourceMapStore) fronten
switch event.Level {
case sentry.LevelError:
frontendLogger.Error(msg, ctx)
frontendLogger.Error(msg, ctx...)
case sentry.LevelWarning:
frontendLogger.Warn(msg, ctx)
frontendLogger.Warn(msg, ctx...)
case sentry.LevelDebug:
frontendLogger.Debug(msg, ctx)
frontendLogger.Debug(msg, ctx...)
default:
frontendLogger.Info(msg, ctx)
frontendLogger.Info(msg, ctx...)
}
return response.Success("ok")

@ -10,13 +10,14 @@ import (
"time"
"github.com/getsentry/sentry-go"
"github.com/go-kit/log"
"github.com/grafana/grafana/pkg/api/frontendlogging"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log/level"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
log "github.com/inconshreveable/log15"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -27,20 +28,25 @@ type SourceMapReadRecord struct {
path string
}
type logScenarioFunc func(c *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord)
type logScenarioFunc func(c *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord)
func logSentryEventScenario(t *testing.T, desc string, event frontendlogging.FrontendSentryEvent, fn logScenarioFunc) {
t.Run(desc, func(t *testing.T) {
logs := []*log.Record{}
sourceMapReads := []SourceMapReadRecord{}
origHandler := frontendLogger.GetHandler()
frontendLogger.SetHandler(log.FuncHandler(func(r *log.Record) error {
logs = append(logs, r)
var logcontent = make(map[string]interface{})
logcontent["logger"] = "frontend"
newfrontendLogger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
for i := 0; i < len(keyvals); i += 2 {
logcontent[keyvals[i].(string)] = keyvals[i+1]
}
return nil
}))
origHandler := frontendLogger.GetLogger()
frontendLogger.AddLogger(newfrontendLogger, "info", map[string]level.Option{})
sourceMapReads := []SourceMapReadRecord{}
t.Cleanup(func() {
frontendLogger.SetHandler(origHandler)
frontendLogger.SetLogger(origHandler)
})
sc := setupScenarioContext(t, "/log")
@ -91,7 +97,7 @@ func logSentryEventScenario(t *testing.T, desc string, event frontendlogging.Fro
sc.m.Post(sc.url, handler)
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
fn(sc, logs, sourceMapReads)
fn(sc, logcontent, sourceMapReads)
})
}
@ -148,18 +154,17 @@ func TestFrontendLoggingEndpoint(t *testing.T) {
}
logSentryEventScenario(t, "Should log received error event", errorEvent,
func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) {
func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) {
assert.Equal(t, 200, sc.resp.Code)
assert.Len(t, logs, 1)
assertContextContains(t, logs[0], "logger", "frontend")
assertContextContains(t, logs[0], "url", errorEvent.Request.URL)
assertContextContains(t, logs[0], "user_agent", errorEvent.Request.Headers["User-Agent"])
assertContextContains(t, logs[0], "event_id", errorEvent.EventID)
assertContextContains(t, logs[0], "original_timestamp", errorEvent.Timestamp)
assertContextContains(t, logs[0], "stacktrace", `UserError: Please replace user and try again
assertContextContains(t, logs, "logger", "frontend")
assertContextContains(t, logs, "url", errorEvent.Request.URL)
assertContextContains(t, logs, "user_agent", errorEvent.Request.Headers["User-Agent"])
assertContextContains(t, logs, "event_id", errorEvent.EventID)
assertContextContains(t, logs, "original_timestamp", errorEvent.Timestamp)
assertContextContains(t, logs, "stacktrace", `UserError: Please replace user and try again
at foofn (foo.js:123:23)
at barfn (bar.js:113:231)`)
assert.NotContains(t, logs[0].Ctx, "context")
assert.NotContains(t, logs, "context")
})
messageEvent := frontendlogging.FrontendSentryEvent{
@ -175,20 +180,21 @@ func TestFrontendLoggingEndpoint(t *testing.T) {
}
logSentryEventScenario(t, "Should log received message event", messageEvent,
func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) {
func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) {
assert.Equal(t, 200, sc.resp.Code)
assert.Len(t, logs, 1)
assert.Equal(t, "hello world", logs[0].Msg)
assert.Equal(t, log.LvlInfo, logs[0].Lvl)
assertContextContains(t, logs[0], "logger", "frontend")
assertContextContains(t, logs[0], "url", messageEvent.Request.URL)
assertContextContains(t, logs[0], "user_agent", messageEvent.Request.Headers["User-Agent"])
assertContextContains(t, logs[0], "event_id", messageEvent.EventID)
assertContextContains(t, logs[0], "original_timestamp", messageEvent.Timestamp)
assert.NotContains(t, logs[0].Ctx, "stacktrace")
assert.NotContains(t, logs[0].Ctx, "context")
assertContextContains(t, logs[0], "user_email", user.Email)
assertContextContains(t, logs[0], "user_id", user.ID)
assert.Len(t, logs, 10)
assertContextContains(t, logs, "logger", "frontend")
assertContextContains(t, logs, "msg", "hello world")
assertContextContains(t, logs, "lvl", level.InfoValue())
assertContextContains(t, logs, "logger", "frontend")
assertContextContains(t, logs, "url", messageEvent.Request.URL)
assertContextContains(t, logs, "user_agent", messageEvent.Request.Headers["User-Agent"])
assertContextContains(t, logs, "event_id", messageEvent.EventID)
assertContextContains(t, logs, "original_timestamp", messageEvent.Timestamp)
assert.NotContains(t, logs, "stacktrace")
assert.NotContains(t, logs, "context")
assertContextContains(t, logs, "user_email", user.Email)
assertContextContains(t, logs, "user_id", user.ID)
})
eventWithContext := frontendlogging.FrontendSentryEvent{
@ -211,12 +217,11 @@ func TestFrontendLoggingEndpoint(t *testing.T) {
}
logSentryEventScenario(t, "Should log event context", eventWithContext,
func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) {
func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) {
assert.Equal(t, 200, sc.resp.Code)
assert.Len(t, logs, 1)
assertContextContains(t, logs[0], "context_foo_one", "two")
assertContextContains(t, logs[0], "context_foo_three", "4")
assertContextContains(t, logs[0], "context_bar", "baz")
assertContextContains(t, logs, "context_foo_one", "two")
assertContextContains(t, logs, "context_foo_three", "4")
assertContextContains(t, logs, "context_bar", "baz")
})
errorEventForSourceMapping := frontendlogging.FrontendSentryEvent{
@ -278,10 +283,10 @@ func TestFrontendLoggingEndpoint(t *testing.T) {
}
logSentryEventScenario(t, "Should load sourcemap and transform stacktrace line when possible",
errorEventForSourceMapping, func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) {
errorEventForSourceMapping, func(sc *scenarioContext, logs map[string]interface{}, sourceMapReads []SourceMapReadRecord) {
assert.Equal(t, 200, sc.resp.Code)
assert.Len(t, logs, 1)
assertContextContains(t, logs[0], "stacktrace", `UserError: Please replace user and try again
assert.Len(t, logs, 9)
assertContextContains(t, logs, "stacktrace", `UserError: Please replace user and try again
at ? (core|webpack:///./some_source.ts:2:2)
at ? (telepathic|webpack:///./some_source.ts:3:2)
at explode (http://localhost:3000/public/build/error.js:3:10)
@ -306,17 +311,7 @@ func TestFrontendLoggingEndpoint(t *testing.T) {
})
}
func indexOf(arr []interface{}, item string) int {
for i, elem := range arr {
if elem == item {
return i
}
}
return -1
}
func assertContextContains(t *testing.T, logRecord *log.Record, label string, value interface{}) {
assert.Contains(t, logRecord.Ctx, label)
labelIdx := indexOf(logRecord.Ctx, label)
assert.Equal(t, value, logRecord.Ctx[labelIdx+1])
func assertContextContains(t *testing.T, logRecord map[string]interface{}, label string, value interface{}) {
assert.Contains(t, logRecord, label)
assert.Equal(t, value, logRecord[label])
}

@ -7,9 +7,10 @@ import (
"github.com/getsentry/sentry-go"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/inconshreveable/log15"
)
type CtxVector []interface{}
var logger = log.New("frontendlogging")
type FrontendSentryExceptionValue struct {
@ -65,31 +66,28 @@ func (exception *FrontendSentryException) FmtStacktraces(store *SourceMapStore)
return strings.Join(stacktraces, "\n\n")
}
func addEventContextToLogContext(rootPrefix string, logCtx log15.Ctx, eventCtx map[string]interface{}) {
func addEventContextToLogContext(rootPrefix string, logCtx *CtxVector, eventCtx map[string]interface{}) {
for key, element := range eventCtx {
prefix := fmt.Sprintf("%s_%s", rootPrefix, key)
switch v := element.(type) {
case map[string]interface{}:
addEventContextToLogContext(prefix, logCtx, v)
default:
logCtx[prefix] = fmt.Sprintf("%v", v)
*logCtx = append(*logCtx, prefix, fmt.Sprintf("%v", v))
}
}
}
func (event *FrontendSentryEvent) ToLogContext(store *SourceMapStore) log15.Ctx {
var ctx = make(log15.Ctx)
ctx["url"] = event.Request.URL
ctx["user_agent"] = event.Request.Headers["User-Agent"]
ctx["event_id"] = event.EventID
ctx["original_timestamp"] = event.Timestamp
func (event *FrontendSentryEvent) ToLogContext(store *SourceMapStore) []interface{} {
var ctx = CtxVector{"url", event.Request.URL, "user_agent", event.Request.Headers["User-Agent"],
"event_id", event.EventID, "original_timestamp", event.Timestamp}
if event.Exception != nil {
ctx["stacktrace"] = event.Exception.FmtStacktraces(store)
ctx = append(ctx, "stacktrace", event.Exception.FmtStacktraces(store))
}
addEventContextToLogContext("context", ctx, event.Contexts)
addEventContextToLogContext("context", &ctx, event.Contexts)
if len(event.User.Email) > 0 {
ctx["user_email"] = event.User.Email
ctx["user_id"] = event.User.ID
ctx = append(ctx, "user_email", event.User.Email, "user_id", event.User.ID)
}
return ctx

@ -15,15 +15,13 @@ import (
"sync"
"time"
"github.com/inconshreveable/log15"
"github.com/go-kit/log"
)
// FileLogWriter implements LoggerInterface.
// It writes messages by lines limit, file size limit, or time frequency.
type FileLogWriter struct {
mw *MuxWriter
Format log15.Format
Format Formatedlogger
Filename string
Maxlines int
maxlinesCurlines int
@ -39,30 +37,28 @@ type FileLogWriter struct {
Rotate bool
startLock sync.Mutex
}
// an *os.File writer with locker.
type MuxWriter struct {
logger log.Logger
sync.Mutex
fd *os.File
}
// write to os.File.
func (l *MuxWriter) Write(b []byte) (int, error) {
l.Lock()
defer l.Unlock()
return l.fd.Write(b)
func (w *FileLogWriter) Write(b []byte) (int, error) {
w.docheck(len(b))
w.Lock()
defer w.Unlock()
return w.fd.Write(b)
}
// set os.File in writer.
func (l *MuxWriter) setFD(fd *os.File) error {
if l.fd != nil {
if err := l.fd.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
func (w *FileLogWriter) setFD(fd *os.File) error {
if w.fd != nil {
if err := w.fd.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
return fmt.Errorf("closing old file in MuxWriter failed: %w", err)
}
}
l.fd = fd
w.fd = fd
return nil
}
@ -70,30 +66,31 @@ func (l *MuxWriter) setFD(fd *os.File) error {
func NewFileWriter() *FileLogWriter {
w := &FileLogWriter{
Filename: "",
Format: log15.LogfmtFormat(),
Format: func(w io.Writer) log.Logger {
return log.NewLogfmtLogger(w)
},
Maxlines: 1000000,
Maxsize: 1 << 28, // 256 MB
Daily: true,
Maxdays: 7,
Rotate: true,
}
// use MuxWriter instead direct use os.File for lock write when rotate
w.mw = new(MuxWriter)
return w
}
func (w *FileLogWriter) Log(r *log15.Record) error {
data := w.Format.Format(r)
w.docheck(len(data))
_, err := w.mw.Write(data)
return err
func (w *FileLogWriter) Log(keyvals ...interface{}) error {
return w.logger.Log(keyvals...)
}
func (w *FileLogWriter) Init() error {
if len(w.Filename) == 0 {
return errors.New("config must have filename")
}
return w.StartLogger()
if err := w.StartLogger(); err != nil {
return err
}
w.logger = log.NewLogfmtLogger(log.NewSyncWriter(w))
return nil
}
// start file logger. create log file and set to locker-inside file writer.
@ -102,7 +99,7 @@ func (w *FileLogWriter) StartLogger() error {
if err != nil {
return err
}
if err := w.mw.setFD(fd); err != nil {
if err := w.setFD(fd); err != nil {
return err
}
@ -159,7 +156,7 @@ func (w *FileLogWriter) lineCounter() (int, error) {
}
func (w *FileLogWriter) initFd() error {
fd := w.mw.fd
fd := w.fd
finfo, err := fd.Stat()
if err != nil {
return fmt.Errorf("get stat: %s", err)
@ -196,10 +193,10 @@ func (w *FileLogWriter) DoRotate() error {
}
// block Logger's io.Writer
w.mw.Lock()
defer w.mw.Unlock()
w.Lock()
defer w.Unlock()
fd := w.mw.fd
fd := w.fd
if err := fd.Close(); err != nil {
return err
}
@ -244,14 +241,14 @@ func (w *FileLogWriter) deleteOldLog() {
// destroy file logger, close file writer.
func (w *FileLogWriter) Close() error {
return w.mw.fd.Close()
return w.fd.Close()
}
// flush file logger.
// there are no buffering messages in file logger in memory.
// flush file means sync file from disk.
func (w *FileLogWriter) Flush() {
if err := w.mw.fd.Sync(); err != nil {
if err := w.fd.Sync(); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
}
}
@ -259,11 +256,11 @@ func (w *FileLogWriter) Flush() {
// Reload file logger
func (w *FileLogWriter) Reload() error {
// block Logger's io.Writer
w.mw.Lock()
defer w.mw.Unlock()
w.Lock()
defer w.Unlock()
// Close
fd := w.mw.fd
fd := w.fd
if err := fd.Close(); err != nil {
return err
}

@ -10,12 +10,8 @@ import (
)
func (w *FileLogWriter) WriteLine(line string) error {
n, err := w.mw.Write([]byte(line))
if err != nil {
return err
}
w.docheck(n)
return nil
_, err := w.Write([]byte(line))
return err
}
func TestLogFile(t *testing.T) {

@ -1,7 +1,5 @@
package log
import "github.com/inconshreveable/log15"
type Lvl int
const (
@ -14,18 +12,13 @@ const (
type Logger interface {
// New returns a new Logger that has this logger's context plus the given context
New(ctx ...interface{}) log15.Logger
// GetHandler gets the handler associated with the logger.
GetHandler() log15.Handler
New(ctx ...interface{}) MultiLoggers
// SetHandler updates the logger to write records to the specified handler.
SetHandler(h log15.Handler)
Log(keyvals ...interface{}) error
// Log a message at the given level with context key/value pairs
Debug(msg string, ctx ...interface{})
Info(msg string, ctx ...interface{})
Warn(msg string, ctx ...interface{})
Error(msg string, ctx ...interface{})
Crit(msg string, ctx ...interface{})
}

@ -0,0 +1,205 @@
package level
import "github.com/go-kit/log"
// Error returns a logger that includes a Key/ErrorValue pair.
func Error(logger log.Logger) log.Logger {
return log.WithPrefix(logger, Key(), ErrorValue())
}
// Warn returns a logger that includes a Key/WarnValue pair.
func Warn(logger log.Logger) log.Logger {
return log.WithPrefix(logger, Key(), WarnValue())
}
// Info returns a logger that includes a Key/InfoValue pair.
func Info(logger log.Logger) log.Logger {
return log.WithPrefix(logger, Key(), InfoValue())
}
// Debug returns a logger that includes a Key/DebugValue pair.
func Debug(logger log.Logger) log.Logger {
return log.WithPrefix(logger, Key(), DebugValue())
}
// NewFilter wraps next and implements level filtering. See the commentary on
// the Option functions for a detailed description of how to configure levels.
// If no options are provided, all leveled log events created with Debug,
// Info, Warn or Error helper methods are squelched and non-leveled log
// events are passed to next unmodified.
func NewFilter(next log.Logger, options ...Option) log.Logger {
l := &logger{
next: next,
}
for _, option := range options {
option(l)
}
return l
}
type logger struct {
next log.Logger
allowed level
squelchNoLevel bool
errNotAllowed error
errNoLevel error
}
func (l *logger) Log(keyvals ...interface{}) error {
var hasLevel, levelAllowed bool
for i := 1; i < len(keyvals); i += 2 {
if v, ok := keyvals[i].(*levelValue); ok {
hasLevel = true
levelAllowed = l.allowed&v.level != 0
break
}
}
if !hasLevel && l.squelchNoLevel {
return l.errNoLevel
}
if hasLevel && !levelAllowed {
return l.errNotAllowed
}
return l.next.Log(keyvals...)
}
// Option sets a parameter for the leveled logger.
type Option func(*logger)
// AllowAll is an alias for AllowDebug.
func AllowAll() Option {
return AllowDebug()
}
// AllowDebug allows error, warn, info and debug level log events to pass.
func AllowDebug() Option {
return allowed(levelError | levelWarn | levelInfo | levelDebug)
}
// AllowInfo allows error, warn and info level log events to pass.
func AllowInfo() Option {
return allowed(levelError | levelWarn | levelInfo)
}
// AllowWarn allows error and warn level log events to pass.
func AllowWarn() Option {
return allowed(levelError | levelWarn)
}
// AllowError allows only error level log events to pass.
func AllowError() Option {
return allowed(levelError)
}
// AllowNone allows no leveled log events to pass.
func AllowNone() Option {
return allowed(0)
}
func allowed(allowed level) Option {
return func(l *logger) { l.allowed = allowed }
}
// ErrNotAllowed sets the error to return from Log when it squelches a log
// event disallowed by the configured Allow[Level] option. By default,
// ErrNotAllowed is nil; in this case the log event is squelched with no
// error.
func ErrNotAllowed(err error) Option {
return func(l *logger) { l.errNotAllowed = err }
}
// SquelchNoLevel instructs Log to squelch log events with no level, so that
// they don't proceed through to the wrapped logger. If SquelchNoLevel is set
// to true and a log event is squelched in this way, the error value
// configured with ErrNoLevel is returned to the caller.
func SquelchNoLevel(squelch bool) Option {
return func(l *logger) { l.squelchNoLevel = squelch }
}
// ErrNoLevel sets the error to return from Log when it squelches a log event
// with no level. By default, ErrNoLevel is nil; in this case the log event is
// squelched with no error.
func ErrNoLevel(err error) Option {
return func(l *logger) { l.errNoLevel = err }
}
// NewInjector wraps next and returns a logger that adds a Key/level pair to
// the beginning of log events that don't already contain a level. In effect,
// this gives a default level to logs without a level.
func NewInjector(next log.Logger, level Value) log.Logger {
return &injector{
next: next,
level: level,
}
}
type injector struct {
next log.Logger
level interface{}
}
func (l *injector) Log(keyvals ...interface{}) error {
for i := 1; i < len(keyvals); i += 2 {
if _, ok := keyvals[i].(*levelValue); ok {
return l.next.Log(keyvals...)
}
}
kvs := make([]interface{}, len(keyvals)+2)
kvs[0], kvs[1] = key, l.level
copy(kvs[2:], keyvals)
return l.next.Log(kvs...)
}
// Value is the interface that each of the canonical level values implement.
// It contains unexported methods that prevent types from other packages from
// implementing it and guaranteeing that NewFilter can distinguish the levels
// defined in this package from all other values.
type Value interface {
String() string
levelVal()
}
// Key returns the unique key added to log events by the loggers in this
// package.
func Key() interface{} { return key }
// ErrorValue returns the unique value added to log events by Error.
func ErrorValue() Value { return errorValue }
// WarnValue returns the unique value added to log events by Warn.
func WarnValue() Value { return warnValue }
// InfoValue returns the unique value added to log events by Info.
func InfoValue() Value { return infoValue }
// DebugValue returns the unique value added to log events by 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{} = "lvl"
errorValue = &levelValue{level: levelError, name: "eror"}
warnValue = &levelValue{level: levelWarn, name: "warn"}
infoValue = &levelValue{level: levelInfo, name: "info"}
debugValue = &levelValue{level: levelDebug, name: "dbug"}
)
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() {}

@ -6,88 +6,166 @@ package log
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
gokitlog "github.com/go-kit/log"
"github.com/go-kit/log/term"
"github.com/go-stack/stack"
"github.com/grafana/grafana/pkg/infra/log/level"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/inconshreveable/log15"
isatty "github.com/mattn/go-isatty"
"github.com/mattn/go-isatty"
"gopkg.in/ini.v1"
)
var Root log15.Logger
var loggersToClose []DisposableHandler
var loggersToReload []ReloadableHandler
var filters map[string]log15.Lvl
var filters map[string]level.Option
var Root MultiLoggers
func init() {
loggersToClose = make([]DisposableHandler, 0)
loggersToReload = make([]ReloadableHandler, 0)
filters = map[string]log15.Lvl{}
Root = log15.Root()
Root.SetHandler(log15.DiscardHandler())
filters = map[string]level.Option{}
Root.AddLogger(gokitlog.NewLogfmtLogger(os.Stderr), "info", filters)
}
func New(logger string, ctx ...interface{}) Logger {
params := append([]interface{}{"logger", logger}, ctx...)
return Root.New(params...)
type LogWithFilters struct {
val gokitlog.Logger
filters map[string]level.Option
maxLevel level.Option
}
func Close() error {
var err error
for _, logger := range loggersToClose {
if e := logger.Close(); e != nil && err == nil {
err = e
}
type MultiLoggers struct {
loggers []LogWithFilters
}
func (ml *MultiLoggers) AddLogger(val gokitlog.Logger, levelName string, filters map[string]level.Option) {
logger := LogWithFilters{val: val, filters: filters, maxLevel: getLogLevelFromString(levelName)}
ml.loggers = append(ml.loggers, logger)
}
func (ml *MultiLoggers) SetLogger(des MultiLoggers) {
ml.loggers = des.loggers
}
func (ml *MultiLoggers) GetLogger() MultiLoggers {
return *ml
}
func (ml MultiLoggers) Warn(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.WarnValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
loggersToClose = make([]DisposableHandler, 0)
}
return err
func (ml MultiLoggers) Debug(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.DebugValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
}
// Reload reloads all loggers.
func Reload() error {
for _, logger := range loggersToReload {
if err := logger.Reload(); err != nil {
func (ml MultiLoggers) Error(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.ErrorValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
}
func (ml MultiLoggers) Info(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.InfoValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
}
func (ml MultiLoggers) Log(keyvals ...interface{}) error {
for _, multilogger := range ml.loggers {
multilogger.val = gokitlog.With(multilogger.val, "t", gokitlog.TimestampFormat(time.Now, "2006-01-02T15:04:05.99-0700"))
if err := multilogger.val.Log(keyvals...); err != nil {
return err
}
}
return nil
}
var logLevels = map[string]log15.Lvl{
"trace": log15.LvlDebug,
"debug": log15.LvlDebug,
"info": log15.LvlInfo,
"warn": log15.LvlWarn,
"error": log15.LvlError,
"critical": log15.LvlCrit,
// we need to implement new function for multiloggers
func (ml MultiLoggers) New(ctx ...interface{}) MultiLoggers {
var newloger MultiLoggers
for _, logWithFilter := range ml.loggers {
logWithFilter.val = gokitlog.With(logWithFilter.val, ctx)
if len(ctx) > 0 {
v, ok := logWithFilter.filters[ctx[0].(string)]
if ok {
logWithFilter.val = level.NewFilter(logWithFilter.val, v)
} else {
logWithFilter.val = level.NewFilter(logWithFilter.val, logWithFilter.maxLevel)
}
}
newloger.loggers = append(newloger.loggers, logWithFilter)
}
return newloger
}
func New(ctx ...interface{}) MultiLoggers {
if len(ctx) == 0 {
return Root
}
var newloger MultiLoggers
ctx = append([]interface{}{"logger"}, ctx...)
for _, logWithFilter := range Root.loggers {
logWithFilter.val = gokitlog.With(logWithFilter.val, ctx...)
v, ok := logWithFilter.filters[ctx[0].(string)]
if ok {
logWithFilter.val = level.NewFilter(logWithFilter.val, v)
} else {
logWithFilter.val = level.NewFilter(logWithFilter.val, logWithFilter.maxLevel)
}
newloger.loggers = append(newloger.loggers, logWithFilter)
}
return newloger
}
var logLevels = map[string]level.Option{
"trace": level.AllowDebug(),
"debug": level.AllowDebug(),
"info": level.AllowInfo(),
"warn": level.AllowWarn(),
"error": level.AllowError(),
"critical": level.AllowError(),
}
func getLogLevelFromConfig(key string, defaultName string, cfg *ini.File) (string, log15.Lvl) {
func getLogLevelFromConfig(key string, defaultName string, cfg *ini.File) (string, level.Option) {
levelName := cfg.Section(key).Key("level").MustString(defaultName)
levelName = strings.ToLower(levelName)
level := getLogLevelFromString(levelName)
return levelName, level
}
func getLogLevelFromString(levelName string) log15.Lvl {
level, ok := logLevels[levelName]
func getLogLevelFromString(levelName string) level.Option {
loglevel, ok := logLevels[levelName]
if !ok {
Root.Error("Unknown log level", "level", levelName)
return log15.LvlError
_ = level.Error(Root).Log("Unknown log level", "level", levelName)
return level.AllowError()
}
return level
return loglevel
}
func getFilters(filterStrArray []string) map[string]log15.Lvl {
filterMap := make(map[string]log15.Lvl)
// the filter is composed with logger name and level
func getFilters(filterStrArray []string) map[string]level.Option {
filterMap := make(map[string]level.Option)
for _, filterStr := range filterStrArray {
parts := strings.Split(filterStr, ":")
@ -99,20 +177,87 @@ func getFilters(filterStrArray []string) map[string]log15.Lvl {
return filterMap
}
func getLogFormat(format string) log15.Format {
func Stack(skip int) string {
call := stack.Caller(skip)
s := stack.Trace().TrimBelow(call).TrimRuntime()
return s.String()
}
type Formatedlogger func(w io.Writer) gokitlog.Logger
func terminalColorFn(keyvals ...interface{}) term.FgBgColor {
for i := 0; i < len(keyvals)-1; i += 2 {
if keyvals[i] != level.Key() {
continue
}
switch keyvals[i+1] {
case "trace":
return term.FgBgColor{Fg: term.Gray}
case level.DebugValue():
return term.FgBgColor{Fg: term.Gray}
case level.InfoValue():
return term.FgBgColor{Fg: term.Green}
case level.WarnValue():
return term.FgBgColor{Fg: term.Yellow}
case level.ErrorValue():
return term.FgBgColor{Fg: term.Red}
case "crit":
return term.FgBgColor{Fg: term.Gray, Bg: term.DarkRed}
default:
return term.FgBgColor{}
}
}
return term.FgBgColor{}
}
func getLogFormat(format string) Formatedlogger {
switch format {
case "console":
if isatty.IsTerminal(os.Stdout.Fd()) {
return log15.TerminalFormat()
return func(w io.Writer) gokitlog.Logger {
return term.NewColorLogger(w, gokitlog.NewLogfmtLogger, terminalColorFn)
}
}
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewLogfmtLogger(w)
}
return log15.LogfmtFormat()
case "text":
return log15.LogfmtFormat()
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewLogfmtLogger(w)
}
case "json":
return log15.JsonFormat()
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewJSONLogger(gokitlog.NewSyncWriter(w))
}
default:
return log15.LogfmtFormat()
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewLogfmtLogger(w)
}
}
}
// this is for file logger only
func Close() error {
var err error
for _, logger := range loggersToClose {
if e := logger.Close(); e != nil && err == nil {
err = e
}
}
loggersToClose = make([]DisposableHandler, 0)
return err
}
// Reload reloads all loggers.
func Reload() error {
for _, logger := range loggersToReload {
if err := logger.Reload(); err != nil {
return err
}
}
return nil
}
func ReadLoggingConfig(modes []string, logsPath string, cfg *ini.File) error {
@ -123,32 +268,31 @@ func ReadLoggingConfig(modes []string, logsPath string, cfg *ini.File) error {
defaultLevelName, _ := getLogLevelFromConfig("log", "info", cfg)
defaultFilters := getFilters(util.SplitString(cfg.Section("log").Key("filters").String()))
handlers := make([]log15.Handler, 0)
var configLoggers []LogWithFilters
for _, mode := range modes {
mode = strings.TrimSpace(mode)
sec, err := cfg.GetSection("log." + mode)
if err != nil {
Root.Error("Unknown log mode", "mode", mode)
_ = level.Error(Root).Log("Unknown log mode", "mode", mode)
return errutil.Wrapf(err, "failed to get config section log.%s", mode)
}
// Log level.
_, level := getLogLevelFromConfig("log."+mode, defaultLevelName, cfg)
_, leveloption := getLogLevelFromConfig("log."+mode, defaultLevelName, cfg)
modeFilters := getFilters(util.SplitString(sec.Key("filters").String()))
format := getLogFormat(sec.Key("format").MustString(""))
var handler log15.Handler
var handler LogWithFilters
// Generate log configuration.
switch mode {
case "console":
handler = log15.StreamHandler(os.Stdout, format)
handler.val = format(os.Stdout)
case "file":
fileName := sec.Key("file_name").MustString(filepath.Join(logsPath, "grafana.log"))
dpath := filepath.Dir(fileName)
if err := os.MkdirAll(dpath, os.ModePerm); err != nil {
Root.Error("Failed to create directory", "dpath", dpath, "err", err)
_ = level.Error(Root).Log("Failed to create directory", "dpath", dpath, "err", err)
return errutil.Wrapf(err, "failed to create log directory %q", dpath)
}
fileHandler := NewFileWriter()
@ -160,65 +304,43 @@ func ReadLoggingConfig(modes []string, logsPath string, cfg *ini.File) error {
fileHandler.Daily = sec.Key("daily_rotate").MustBool(true)
fileHandler.Maxdays = sec.Key("max_days").MustInt64(7)
if err := fileHandler.Init(); err != nil {
Root.Error("Failed to initialize file handler", "dpath", dpath, "err", err)
_ = level.Error(Root).Log("Failed to initialize file handler", "dpath", dpath, "err", err)
return errutil.Wrapf(err, "failed to initialize file handler")
}
loggersToClose = append(loggersToClose, fileHandler)
loggersToReload = append(loggersToReload, fileHandler)
handler = fileHandler
handler.val = fileHandler
case "syslog":
sysLogHandler := NewSyslog(sec, format)
loggersToClose = append(loggersToClose, sysLogHandler)
handler = sysLogHandler
handler.val = sysLogHandler.logger
}
if handler == nil {
if handler.val == nil {
panic(fmt.Sprintf("Handler is uninitialized for mode %q", mode))
}
// join default filters and mode filters together
for key, value := range defaultFilters {
if _, exist := modeFilters[key]; !exist {
modeFilters[key] = value
}
}
// copy joined default + mode filters into filters
for key, value := range modeFilters {
if _, exist := filters[key]; !exist {
filters[key] = value
}
}
handler = LogFilterHandler(level, modeFilters, handler)
handlers = append(handlers, handler)
handler.filters = modeFilters
handler.maxLevel = leveloption
// handler = LogFilterHandler(leveloption, modeFilters, handler)
configLoggers = append(configLoggers, handler)
}
if len(configLoggers) > 0 {
Root.loggers = configLoggers
}
Root.SetHandler(log15.MultiHandler(handlers...))
return nil
}
func LogFilterHandler(maxLevel log15.Lvl, filters map[string]log15.Lvl, h log15.Handler) log15.Handler {
return log15.FilterHandler(func(r *log15.Record) (pass bool) {
if len(filters) > 0 {
for i := 0; i < len(r.Ctx); i += 2 {
key, ok := r.Ctx[i].(string)
if ok && key == "logger" {
loggerName, strOk := r.Ctx[i+1].(string)
if strOk {
if filterLevel, ok := filters[loggerName]; ok {
return r.Lvl <= filterLevel
}
}
}
}
}
return r.Lvl <= maxLevel
}, h)
}
func Stack(skip int) string {
call := stack.Caller(skip)
s := stack.Trace().TrimBelow(call).TrimRuntime()
return s.String()
}

@ -4,11 +4,12 @@
package log
import (
"errors"
"log/syslog"
"os"
"github.com/inconshreveable/log15"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
gokitsyslog "github.com/go-kit/log/syslog"
"gopkg.in/ini.v1"
)
@ -18,13 +19,41 @@ type SysLogHandler struct {
Address string
Facility string
Tag string
Format log15.Format
Format Formatedlogger
logger log.Logger
}
func NewSyslog(sec *ini.Section, format log15.Format) *SysLogHandler {
handler := &SysLogHandler{
Format: log15.LogfmtFormat(),
var selector = func(keyvals ...interface{}) syslog.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 syslog.LOG_EMERG
case "alert":
return syslog.LOG_ALERT
case "critical":
return syslog.LOG_CRIT
case "error":
return syslog.LOG_ERR
case "warning":
return syslog.LOG_WARNING
case "notice":
return syslog.LOG_NOTICE
case "info":
return syslog.LOG_INFO
case "debug":
return syslog.LOG_DEBUG
}
return syslog.LOG_LOCAL0
}
}
}
return syslog.LOG_LOCAL0
}
func NewSyslog(sec *ini.Section, format Formatedlogger) *SysLogHandler {
handler := &SysLogHandler{}
handler.Format = format
handler.Network = sec.Key("network").MustString("")
@ -33,18 +62,16 @@ func NewSyslog(sec *ini.Section, format log15.Format) *SysLogHandler {
handler.Tag = sec.Key("tag").MustString("")
if err := handler.Init(); err != nil {
Root.Error("Failed to init syslog log handler", "error", err)
_ = level.Error(Root).Log("Failed to init syslog log handler", "error", err)
os.Exit(1)
}
handler.logger = gokitsyslog.NewSyslogLogger(handler.syslog, format, gokitsyslog.PrioritySelectorOption(selector))
return handler
}
func (sw *SysLogHandler) Init() error {
prio, err := parseFacility(sw.Facility)
if err != nil {
return err
}
// the facility is the origin of the syslog message
prio := parseFacility(sw.Facility)
w, err := syslog.Dial(sw.Network, sw.Address, prio, sw.Tag)
if err != nil {
@ -55,26 +82,8 @@ func (sw *SysLogHandler) Init() error {
return nil
}
func (sw *SysLogHandler) Log(r *log15.Record) error {
var err error
msg := string(sw.Format.Format(r))
switch r.Lvl {
case log15.LvlDebug:
err = sw.syslog.Debug(msg)
case log15.LvlInfo:
err = sw.syslog.Info(msg)
case log15.LvlWarn:
err = sw.syslog.Warning(msg)
case log15.LvlError:
err = sw.syslog.Err(msg)
case log15.LvlCrit:
err = sw.syslog.Crit(msg)
default:
err = errors.New("invalid syslog level")
}
func (sw *SysLogHandler) Log(keyvals ...interface{}) error {
err := sw.logger.Log(keyvals...)
return err
}
@ -95,11 +104,11 @@ var facilities = map[string]syslog.Priority{
"local7": syslog.LOG_LOCAL7,
}
func parseFacility(facility string) (syslog.Priority, error) {
prio, ok := facilities[facility]
if !ok {
return syslog.LOG_LOCAL0, errors.New("invalid syslog facility")
func parseFacility(facility string) syslog.Priority {
v, found := facilities[facility]
if !found {
// default the facility level to LOG_LOCAL7
return syslog.LOG_LOCAL7
}
return prio, nil
return v
}

@ -4,18 +4,19 @@
package log
import (
"github.com/inconshreveable/log15"
"github.com/go-kit/log"
"gopkg.in/ini.v1"
)
type SysLogHandler struct {
logger log.Logger
}
func NewSyslog(sec *ini.Section, format log15.Format) *SysLogHandler {
func NewSyslog(sec *ini.Section, format Formatedlogger) *SysLogHandler {
return &SysLogHandler{}
}
func (sw *SysLogHandler) Log(r *log15.Record) error {
func (sw *SysLogHandler) Log(keyvals ...interface{}) error {
return nil
}

@ -7,7 +7,8 @@ import (
"errors"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/go-kit/log"
glog "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
@ -35,7 +36,7 @@ func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore) (*RemoteCache
s := &RemoteCache{
SQLStore: sqlStore,
Cfg: cfg,
log: log.New("cache.remote"),
log: glog.New("cache.remote"),
client: client,
}
return s, nil

@ -23,7 +23,7 @@ type UsageStats struct {
SocialService social.Service
kvStore *kvstore.NamespacedKVStore
log log.Logger
log log.MultiLoggers
oauthProviders map[string]bool
externalMetrics []usagestats.MetricsFunc

@ -4,28 +4,19 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/inconshreveable/log15"
"github.com/mattn/go-isatty"
"github.com/grafana/grafana/pkg/infra/log/level"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
)
func getLogFormat() log15.Format {
if isatty.IsTerminal(os.Stdout.Fd()) {
return log15.TerminalFormat()
}
return log15.LogfmtFormat()
}
func newLogger(name string, level log15.Lvl) log.Logger {
func newLogger(name string, lev string) log.Logger {
logger := log.Root.New("logger", name)
logger.SetHandler(log15.LvlFilterHandler(level, log15.StreamHandler(os.Stdout, getLogFormat())))
logger.AddLogger(logger, lev, map[string]level.Option{})
return logger
}
@ -33,7 +24,7 @@ func TestSearchJSONForEmail(t *testing.T) {
t.Run("Given a generic OAuth provider", func(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
}
@ -121,7 +112,7 @@ func TestSearchJSONForGroups(t *testing.T) {
t.Run("Given a generic OAuth provider", func(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
}
@ -184,7 +175,7 @@ func TestSearchJSONForRole(t *testing.T) {
t.Run("Given a generic OAuth provider", func(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
}
@ -247,7 +238,7 @@ func TestUserInfoSearchesForEmailAndRole(t *testing.T) {
t.Run("Given a generic OAuth provider", func(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
emailAttributePath: "email",
}
@ -456,7 +447,7 @@ func TestUserInfoSearchesForLogin(t *testing.T) {
t.Run("Given a generic OAuth provider", func(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
loginAttributePath: "login",
}
@ -551,7 +542,7 @@ func TestUserInfoSearchesForName(t *testing.T) {
t.Run("Given a generic OAuth provider", func(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
nameAttributePath: "name",
}
@ -649,7 +640,7 @@ func TestUserInfoSearchesForGroup(t *testing.T) {
t.Run("Given a generic OAuth provider", func(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
}
@ -717,7 +708,7 @@ func TestUserInfoSearchesForGroup(t *testing.T) {
func TestPayloadCompression(t *testing.T) {
provider := SocialGenericOAuth{
SocialBase: &SocialBase{
log: newLogger("generic_oauth_test", log15.LvlDebug),
log: newLogger("generic_oauth_test", "debug"),
},
emailAttributePath: "email",
}

@ -106,7 +106,8 @@ func Recovery(cfg *setting.Cfg) web.Handler {
return func(c *web.Context) {
defer func() {
if r := recover(); r != nil {
panicLogger := log.Root
var panicLogger log.Logger
panicLogger = log.Root
// try to get request logger
ctx := contexthandler.FromContext(c.Req.Context())
if ctx != nil {

@ -5,7 +5,6 @@ import (
"path/filepath"
"testing"
"github.com/inconshreveable/log15"
"github.com/stretchr/testify/assert"
"github.com/grafana/grafana/pkg/infra/log"
@ -357,11 +356,11 @@ type testPlugin struct {
}
type fakeLogger struct {
log.Logger
log.MultiLoggers
}
func (f fakeLogger) New(_ ...interface{}) log15.Logger {
return fakeLogger{}
func (f fakeLogger) New(_ ...interface{}) log.MultiLoggers {
return log.MultiLoggers{}
}
func (f fakeLogger) Warn(_ string, _ ...interface{}) {

@ -1,14 +1,16 @@
package loginservice
import (
"bytes"
"context"
"errors"
"testing"
"github.com/go-kit/log"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log/level"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
log "github.com/inconshreveable/log15"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -43,11 +45,8 @@ func Test_syncOrgRoles_doesNotBreakWhenTryingToRemoveLastOrgAdmin(t *testing.T)
}
func Test_syncOrgRoles_whenTryingToRemoveLastOrgLogsError(t *testing.T) {
logs := []string{}
logger.SetHandler(log.FuncHandler(func(r *log.Record) error {
logs = append(logs, r.Msg)
return nil
}))
buf := &bytes.Buffer{}
logger.AddLogger(log.NewLogfmtLogger(buf), "info", map[string]level.Option{})
user := createSimpleUser()
externalUser := createSimpleExternalUser()
@ -57,7 +56,6 @@ func Test_syncOrgRoles_whenTryingToRemoveLastOrgLogsError(t *testing.T) {
defer bus.ClearBusHandlers()
bus.AddHandler("test", func(ctx context.Context, q *models.GetUserOrgListQuery) error {
q.Result = createUserOrgDTO()
return nil
})
@ -74,7 +72,7 @@ func Test_syncOrgRoles_whenTryingToRemoveLastOrgLogsError(t *testing.T) {
err := syncOrgRoles(context.Background(), &user, &externalUser)
require.NoError(t, err)
assert.Contains(t, logs, models.ErrLastOrgAdmin.Error())
assert.Contains(t, buf.String(), models.ErrLastOrgAdmin.Error())
}
type authInfoServiceMock struct {

@ -6,18 +6,17 @@ import (
"io"
"testing"
gokit_log "github.com/go-kit/kit/log"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/inconshreveable/log15"
glog "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/log/level"
"github.com/stretchr/testify/require"
)
func Test_GoKitWrapper(t *testing.T) {
getLogger := func(writer io.Writer) log.Logger {
log15Logger := log15.New()
log15Logger.SetHandler(log15.StreamHandler(writer, log15.LogfmtFormat()))
return NewWrapper(log15Logger)
gLogger := glog.New()
gLogger.AddLogger(log.NewLogfmtLogger(writer), "info", map[string]level.Option{})
return NewWrapper(gLogger)
}
tests := []struct {
@ -71,20 +70,21 @@ func Test_GoKitWrapper(t *testing.T) {
}
func Benchmark_Baseline(t *testing.B) {
log15Logger := log15.New()
gLogger := glog.New()
var buff bytes.Buffer
log15Logger.SetHandler(log15.StreamHandler(&buff, log15.LogfmtFormat()))
gLogger.AddLogger(log.NewLogfmtLogger(&buff), "info", map[string]level.Option{})
for i := 0; i < t.N; i++ {
log15Logger.Info("test", "some", "more", "context", "data")
gLogger.Info("test", "some", "more", "context", "data")
}
}
func Benchmark_WrapperLogger(t *testing.B) {
log15Logger := log15.New()
gLogger := glog.New()
var buff bytes.Buffer
log15Logger.SetHandler(log15.StreamHandler(&buff, log15.LogfmtFormat()))
gokit := NewWrapper(log15Logger)
gLogger.AddLogger(log.NewLogfmtLogger(&buff), "info", map[string]level.Option{})
gokit := NewWrapper(gLogger)
for i := 0; i < t.N; i++ {
_ = level.Info(gokit).Log("msg", "test", "some", "more", "context", "data")
@ -92,10 +92,11 @@ func Benchmark_WrapperLogger(t *testing.B) {
}
func Benchmark_WrapperWriter(t *testing.B) {
log15Logger := log15.New()
gLogger := glog.New()
var buff bytes.Buffer
log15Logger.SetHandler(log15.StreamHandler(&buff, log15.LogfmtFormat()))
gokit := gokit_log.NewLogfmtLogger(NewWrapper(log15Logger))
gLogger.AddLogger(log.NewLogfmtLogger(&buff), "info", map[string]level.Option{})
gokit := NewWrapper(gLogger)
for i := 0; i < t.N; i++ {
_ = level.Info(gokit).Log("msg", "test", "some", "more", "context", "data")
}

Loading…
Cancel
Save