loggers.go 5.67 KB
Newer Older
1
/*
obscuren's avatar
obscuren committed
2
Package logger implements a multi-output leveled logger.
3

4 5 6 7
Other packages use tagged logger to send log messages to shared
(process-wide) logging engine. The shared logging engine dispatches to
multiple log systems. The log level can be set separately per log
system.
8

9
Logging is asynchronous and does not block the caller. Message
10 11 12
formatting is performed by the caller goroutine to avoid incorrect
logging of mutable state.
*/
obscuren's avatar
obscuren committed
13
package logger
zelig's avatar
zelig committed
14 15

import (
zelig's avatar
zelig committed
16 17 18 19 20
	"fmt"
	"io"
	"log"
	"os"
	"sync"
21
	"sync/atomic"
zelig's avatar
zelig committed
22 23
)

24 25
// LogSystem is implemented by log output devices.
// All methods can be called concurrently from multiple goroutines.
zelig's avatar
zelig committed
26
type LogSystem interface {
zelig's avatar
zelig committed
27 28
	GetLogLevel() LogLevel
	SetLogLevel(i LogLevel)
29
	LogPrint(LogLevel, string)
zelig's avatar
zelig committed
30 31
}

32 33 34
type message struct {
	level LogLevel
	msg   string
zelig's avatar
zelig committed
35 36 37 38 39
}

type LogLevel uint8

const (
40
	// Standard log levels
zelig's avatar
zelig committed
41 42 43 44 45 46
	Silence LogLevel = iota
	ErrorLevel
	WarnLevel
	InfoLevel
	DebugLevel
	DebugDetailLevel
zelig's avatar
zelig committed
47 48
)

49
var (
Felix Lange's avatar
Felix Lange committed
50 51 52 53
	logMessageC = make(chan message)
	addSystemC  = make(chan LogSystem)
	flushC      = make(chan chan struct{})
	resetC      = make(chan chan struct{})
54 55 56 57
)

func init() {
	go dispatchLoop()
zelig's avatar
zelig committed
58 59
}

Felix Lange's avatar
Felix Lange committed
60 61 62 63
// each system can buffer this many messages before
// blocking incoming log messages.
const sysBufferSize = 500

64
func dispatchLoop() {
Felix Lange's avatar
Felix Lange committed
65 66 67 68 69 70 71 72 73 74 75 76
	var (
		systems  []LogSystem
		systemIn []chan message
		systemWG sync.WaitGroup
	)
	bootSystem := func(sys LogSystem) {
		in := make(chan message, sysBufferSize)
		systemIn = append(systemIn, in)
		systemWG.Add(1)
		go sysLoop(sys, in, &systemWG)
	}

zelig's avatar
zelig committed
77 78
	for {
		select {
Felix Lange's avatar
Felix Lange committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
		case msg := <-logMessageC:
			for _, c := range systemIn {
				c <- msg
			}

		case sys := <-addSystemC:
			systems = append(systems, sys)
			bootSystem(sys)

		case waiter := <-resetC:
			// reset means terminate all systems
			for _, c := range systemIn {
				close(c)
			}
			systems = nil
			systemIn = nil
			systemWG.Wait()
			close(waiter)

		case waiter := <-flushC:
			// flush means reboot all systems
			for _, c := range systemIn {
				close(c)
102
			}
Felix Lange's avatar
Felix Lange committed
103 104 105 106
			systemIn = nil
			systemWG.Wait()
			for _, sys := range systems {
				bootSystem(sys)
107
			}
Felix Lange's avatar
Felix Lange committed
108
			close(waiter)
zelig's avatar
zelig committed
109 110
		}
	}
zelig's avatar
zelig committed
111 112
}

Felix Lange's avatar
Felix Lange committed
113 114
func sysLoop(sys LogSystem, in <-chan message, wg *sync.WaitGroup) {
	for msg := range in {
115 116
		if sys.GetLogLevel() >= msg.level {
			sys.LogPrint(msg.level, msg.msg)
117 118
		}
	}
Felix Lange's avatar
Felix Lange committed
119
	wg.Done()
120 121
}

122
// Reset removes all active log systems.
Felix Lange's avatar
Felix Lange committed
123
// It blocks until all current messages have been delivered.
zelig's avatar
zelig committed
124
func Reset() {
Felix Lange's avatar
Felix Lange committed
125 126 127
	waiter := make(chan struct{})
	resetC <- waiter
	<-waiter
zelig's avatar
zelig committed
128 129
}

130 131
// Flush waits until all current log messages have been dispatched to
// the active log systems.
zelig's avatar
zelig committed
132
func Flush() {
133
	waiter := make(chan struct{})
Felix Lange's avatar
Felix Lange committed
134
	flushC <- waiter
135
	<-waiter
zelig's avatar
zelig committed
136 137
}

138
// AddLogSystem starts printing messages to the given LogSystem.
Felix Lange's avatar
Felix Lange committed
139 140
func AddLogSystem(sys LogSystem) {
	addSystemC <- sys
141 142
}

143 144 145
// A Logger prints messages prefixed by a given tag. It provides named
// Printf and Println style methods for all loglevels. Each ethereum
// component should have its own logger with a unique prefix.
zelig's avatar
zelig committed
146
type Logger struct {
zelig's avatar
zelig committed
147
	tag string
zelig's avatar
zelig committed
148 149 150
}

func NewLogger(tag string) *Logger {
151
	return &Logger{"[" + tag + "] "}
zelig's avatar
zelig committed
152 153
}

zelig's avatar
zelig committed
154
func (logger *Logger) sendln(level LogLevel, v ...interface{}) {
Felix Lange's avatar
Felix Lange committed
155
	logMessageC <- message{level, logger.tag + fmt.Sprintln(v...)}
zelig's avatar
zelig committed
156 157
}

zelig's avatar
zelig committed
158
func (logger *Logger) sendf(level LogLevel, format string, v ...interface{}) {
Felix Lange's avatar
Felix Lange committed
159
	logMessageC <- message{level, logger.tag + fmt.Sprintf(format, v...)}
zelig's avatar
zelig committed
160 161
}

162
// Errorln writes a message with ErrorLevel.
zelig's avatar
zelig committed
163 164
func (logger *Logger) Errorln(v ...interface{}) {
	logger.sendln(ErrorLevel, v...)
zelig's avatar
zelig committed
165 166
}

167
// Warnln writes a message with WarnLevel.
zelig's avatar
zelig committed
168 169
func (logger *Logger) Warnln(v ...interface{}) {
	logger.sendln(WarnLevel, v...)
zelig's avatar
zelig committed
170 171
}

172
// Infoln writes a message with InfoLevel.
zelig's avatar
zelig committed
173 174
func (logger *Logger) Infoln(v ...interface{}) {
	logger.sendln(InfoLevel, v...)
zelig's avatar
zelig committed
175 176
}

177
// Debugln writes a message with DebugLevel.
zelig's avatar
zelig committed
178 179
func (logger *Logger) Debugln(v ...interface{}) {
	logger.sendln(DebugLevel, v...)
zelig's avatar
zelig committed
180 181
}

182
// DebugDetailln writes a message with DebugDetailLevel.
zelig's avatar
zelig committed
183 184
func (logger *Logger) DebugDetailln(v ...interface{}) {
	logger.sendln(DebugDetailLevel, v...)
185 186
}

187
// Errorf writes a message with ErrorLevel.
zelig's avatar
zelig committed
188 189
func (logger *Logger) Errorf(format string, v ...interface{}) {
	logger.sendf(ErrorLevel, format, v...)
zelig's avatar
zelig committed
190 191
}

192
// Warnf writes a message with WarnLevel.
zelig's avatar
zelig committed
193 194
func (logger *Logger) Warnf(format string, v ...interface{}) {
	logger.sendf(WarnLevel, format, v...)
zelig's avatar
zelig committed
195 196
}

197
// Infof writes a message with InfoLevel.
zelig's avatar
zelig committed
198 199
func (logger *Logger) Infof(format string, v ...interface{}) {
	logger.sendf(InfoLevel, format, v...)
zelig's avatar
zelig committed
200 201
}

202
// Debugf writes a message with DebugLevel.
zelig's avatar
zelig committed
203 204
func (logger *Logger) Debugf(format string, v ...interface{}) {
	logger.sendf(DebugLevel, format, v...)
zelig's avatar
zelig committed
205 206
}

207
// DebugDetailf writes a message with DebugDetailLevel.
zelig's avatar
zelig committed
208 209
func (logger *Logger) DebugDetailf(format string, v ...interface{}) {
	logger.sendf(DebugDetailLevel, format, v...)
210 211
}

212
// Fatalln writes a message with ErrorLevel and exits the program.
zelig's avatar
zelig committed
213 214 215 216
func (logger *Logger) Fatalln(v ...interface{}) {
	logger.sendln(ErrorLevel, v...)
	Flush()
	os.Exit(0)
zelig's avatar
zelig committed
217 218
}

219
// Fatalf writes a message with ErrorLevel and exits the program.
zelig's avatar
zelig committed
220 221 222 223
func (logger *Logger) Fatalf(format string, v ...interface{}) {
	logger.sendf(ErrorLevel, format, v...)
	Flush()
	os.Exit(0)
zelig's avatar
zelig committed
224 225
}

226 227 228 229 230 231 232 233
// NewStdLogSystem creates a LogSystem that prints to the given writer.
// The flag values are defined package log.
func NewStdLogSystem(writer io.Writer, flags int, level LogLevel) LogSystem {
	logger := log.New(writer, "", flags)
	return &stdLogSystem{logger, uint32(level)}
}

type stdLogSystem struct {
zelig's avatar
zelig committed
234
	logger *log.Logger
235
	level  uint32
zelig's avatar
zelig committed
236 237
}

238 239
func (t *stdLogSystem) LogPrint(level LogLevel, msg string) {
	t.logger.Print(msg)
zelig's avatar
zelig committed
240 241
}

242
func (t *stdLogSystem) SetLogLevel(i LogLevel) {
243
	atomic.StoreUint32(&t.level, uint32(i))
zelig's avatar
zelig committed
244 245
}

246
func (t *stdLogSystem) GetLogLevel() LogLevel {
247
	return LogLevel(atomic.LoadUint32(&t.level))
zelig's avatar
zelig committed
248
}