146 lines
3.2 KiB
Go
146 lines
3.2 KiB
Go
|
package params
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/ethereum/go-ethereum/logger"
|
||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||
|
)
|
||
|
|
||
|
// Logger is wrapper for custom logging
|
||
|
type Logger struct {
|
||
|
sync.Mutex
|
||
|
logFile *os.File
|
||
|
observer chan string
|
||
|
started chan struct{}
|
||
|
stopped chan struct{}
|
||
|
stopFlag bool
|
||
|
}
|
||
|
|
||
|
var onceStartLogger sync.Once
|
||
|
|
||
|
// SetupLogger configs logger using parameters in config
|
||
|
func SetupLogger(config *NodeConfig) (nodeLogger *Logger, err error) {
|
||
|
if !config.LogEnabled {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
nodeLogger = &Logger{
|
||
|
started: make(chan struct{}, 1),
|
||
|
stopped: make(chan struct{}, 1),
|
||
|
}
|
||
|
|
||
|
onceStartLogger.Do(func() {
|
||
|
err = nodeLogger.createAndStartLogger(config)
|
||
|
})
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// SetV allows to dynamically change log level of messages being written
|
||
|
func (l *Logger) SetV(logLevel string) {
|
||
|
glog.SetV(parseLogLevel(logLevel))
|
||
|
}
|
||
|
|
||
|
// Stop marks logger as stopped, forcing to relinquish hold
|
||
|
// on os.Stderr and restore it back to the original
|
||
|
func (l *Logger) Stop() (stopped chan struct{}) {
|
||
|
l.Lock()
|
||
|
defer l.Unlock()
|
||
|
|
||
|
l.stopFlag = true
|
||
|
stopped = l.stopped
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Observe registers extra writer where logs should be written to.
|
||
|
// This method is used in unit tests, and should NOT be relied upon otherwise.
|
||
|
func (l *Logger) Observe(observer chan string) (started chan struct{}) {
|
||
|
l.observer = observer
|
||
|
started = l.started
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// createAndStartLogger initializes and starts logger by replacing os.Stderr with custom writer.
|
||
|
// Custom writer intercepts all requests to os.Stderr, then forwards to multiple readers, which
|
||
|
// include log file and the original os.Stderr (so that logs output on screen as well)
|
||
|
func (l *Logger) createAndStartLogger(config *NodeConfig) error {
|
||
|
// customize glog
|
||
|
glog.CopyStandardLogTo("INFO")
|
||
|
glog.SetToStderr(true)
|
||
|
glog.SetV(parseLogLevel(config.LogLevel))
|
||
|
|
||
|
// create log file
|
||
|
logFile, err := os.OpenFile(filepath.Join(config.DataDir, config.LogFile), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// inject reader to pipe all writes to Stderr
|
||
|
r, w, err := os.Pipe()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// replace Stderr
|
||
|
origStderr := os.Stderr
|
||
|
os.Stderr = w
|
||
|
scanner := bufio.NewScanner(r)
|
||
|
|
||
|
// configure writer, send to the original os.Stderr and log file
|
||
|
logWriter := io.MultiWriter(origStderr, logFile)
|
||
|
|
||
|
go func() {
|
||
|
defer func() { // restore original Stderr
|
||
|
os.Stderr = origStderr
|
||
|
logFile.Close()
|
||
|
close(l.stopped)
|
||
|
}()
|
||
|
|
||
|
// notify observer that it can start polling (unit test, normally)
|
||
|
close(l.started)
|
||
|
|
||
|
for scanner.Scan() {
|
||
|
fmt.Fprintln(logWriter, scanner.Text())
|
||
|
|
||
|
if l.observer != nil {
|
||
|
l.observer <- scanner.Text()
|
||
|
}
|
||
|
|
||
|
// allow to restore original os.Stderr if logger is stopped
|
||
|
if l.stopFlag {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := scanner.Err(); err != nil {
|
||
|
fmt.Fprintf(origStderr, "error reading logs: %v\n", err)
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// parseLogLevel parses string and returns logger.* constant
|
||
|
func parseLogLevel(logLevel string) int {
|
||
|
switch logLevel {
|
||
|
case "ERROR":
|
||
|
return logger.Error
|
||
|
case "WARNING":
|
||
|
return logger.Warn
|
||
|
case "INFO":
|
||
|
return logger.Info
|
||
|
case "DEBUG":
|
||
|
return logger.Debug
|
||
|
case "DETAIL":
|
||
|
return logger.Detail
|
||
|
}
|
||
|
|
||
|
return logger.Info
|
||
|
}
|