Skip to content
Snippets Groups Projects
logger.go 5.24 KiB
Newer Older
Silas Davis's avatar
Silas Davis committed
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package logging

import (
	kitlog "github.com/go-kit/kit/log"
	"github.com/hyperledger/burrow/logging/structure"
)

// InfoTraceLogger maintains provides two logging 'channels' that are interlaced
// to provide a coarse grained filter to distinguish human-consumable 'Info'
// messages and execution level 'Trace' messages.
type Logger struct {
	// Send a log message to the Info channel, formed of a sequence of key value
	// pairs. Info messages should be operationally interesting to a human who is
	// monitoring the logs. But not necessarily a human who is trying to
	// understand or debug the system. Any handled errors or warnings should be
	// sent to the Info channel (where you may wish to tag them with a suitable
	// key-value pair to categorise them as such).
	Info kitlog.Logger
	// Send an log message to the Trace channel, formed of a sequence of key-value
	// pairs. Trace messages can be used for any state change in the system that
	// may be of interest to a machine consumer or a human who is trying to debug
	// the system or trying to understand the system in detail. If the messages
	// are very point-like and contain little structure, consider using a metric
	// instead.
	Trace  kitlog.Logger
	Output *kitlog.SwapLogger
}

// Create an InfoTraceLogger by passing the initial outputLogger.
func NewLogger(outputLogger kitlog.Logger) *Logger {
Silas Davis's avatar
Silas Davis committed
	// We will never halt the progress of a log emitter. If log output takes too
	// long will start dropping log lines by using a ring buffer.
	swapLogger := new(kitlog.SwapLogger)
	swapLogger.Swap(outputLogger)
	return &Logger{
		Output: swapLogger,
		// logging contexts
Silas Davis's avatar
Silas Davis committed
			structure.ChannelKey, structure.InfoChannelName,
		),
Silas Davis's avatar
Silas Davis committed
			structure.ChannelKey, structure.TraceChannelName,
		),
Silas Davis's avatar
Silas Davis committed
}

func NewNoopLogger() *Logger {
	return &Logger{
		Info:   kitlog.NewNopLogger(),
		Trace:  kitlog.NewNopLogger(),
		Output: new(kitlog.SwapLogger),
	}
}

// A logging context (see go-kit log's Context). Takes a sequence key values
// via With or WithPrefix and ensures future calls to log will have those
// contextual values appended to the call to an underlying logger.
// Values can be dynamic by passing an instance of the kitlog.Valuer interface
// This provides an interface version of the kitlog.Context struct to be used
// For implementations that wrap a kitlog.Context. In addition it makes no
// assumption about the name or signature of the logging method(s).
// See InfoTraceLogger
func (l *Logger) With(keyvals ...interface{}) *Logger {
	return &Logger{
		Output: l.Output,
		Info:   kitlog.With(l.Info, keyvals...),
		Trace:  kitlog.With(l.Trace, keyvals...),
	}
}

// Establish a context on the Info channel keeping Trace the same
func (l *Logger) WithInfo(keyvals ...interface{}) *Logger {
	return &Logger{
		Output: l.Output,
		Info:   kitlog.With(l.Info, keyvals...),
		Trace:  l.Trace,
	}
}

// Establish a context on the Trace channel keeping Info the same
func (l *Logger) WithTrace(keyvals ...interface{}) *Logger {
	return &Logger{
		Output: l.Output,
		Info:   l.Info,
		Trace:  kitlog.With(l.Trace, keyvals...),
	}
}

func (l *Logger) WithPrefix(keyvals ...interface{}) *Logger {
	return &Logger{
		Output: l.Output,
		Info:   kitlog.WithPrefix(l.Info, keyvals...),
		Trace:  kitlog.WithPrefix(l.Trace, keyvals...),
	}
}

// Hot swap the underlying outputLogger with another one to re-route messages
func (l *Logger) SwapOutput(infoLogger kitlog.Logger) {
	l.Output.Swap(infoLogger)
}

// Record structured Info lo`g line with a message
func (l *Logger) InfoMsg(message string, keyvals ...interface{}) error {
	return Msg(l.Info, message, keyvals...)
}

// Record structured Trace log line with a message
func (l *Logger) TraceMsg(message string, keyvals ...interface{}) error {
	return Msg(l.Trace, message, keyvals...)
}

// Establish or extend the scope of this logger by appending scopeName to the Scope vector.
// Like With the logging scope is append only but can be used to provide parenthetical scopes by hanging on to the
// parent scope and using once the scope has been exited. The scope mechanism does is agnostic to the type of scope
// so can be used to identify certain segments of the call stack, a lexical scope, or any other nested scope.
func (l *Logger) WithScope(scopeName string) *Logger {
	// InfoTraceLogger will collapse successive (ScopeKey, scopeName) pairs into a vector in the order which they appear
	return l.With(structure.ScopeKey, scopeName)
}

// Record a structured log line with a message
func Msg(logger kitlog.Logger, message string, keyvals ...interface{}) error {
	prepended := structure.CopyPrepend(keyvals, structure.MessageKey, message)
	return logger.Log(prepended...)
}