Add file logger support (#269)

This commit is contained in:
Ivan Daniluk 2017-09-01 20:44:50 +02:00 committed by Ivan Tomilov
parent fffd60d675
commit 0c4603d825
12 changed files with 107 additions and 43 deletions

View File

@ -7,7 +7,6 @@ import (
"runtime" "runtime"
"github.com/status-im/status-go/geth/api" "github.com/status-im/status-go/geth/api"
"github.com/status-im/status-go/geth/log"
"github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/geth/params"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
@ -87,7 +86,14 @@ var (
LogLevelFlag = cli.StringFlag{ LogLevelFlag = cli.StringFlag{
Name: "log", Name: "log",
Usage: `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`, Usage: `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`,
Value: "INFO", Value: "",
}
// LogFileFlag defines a log filename
LogFileFlag = cli.StringFlag{
Name: "logfile",
Usage: `Path to the log file`,
Value: "",
} }
) )
@ -107,6 +113,7 @@ func init() {
DataDirFlag, DataDirFlag,
NetworkIDFlag, NetworkIDFlag,
LogLevelFlag, LogLevelFlag,
LogFileFlag,
} }
app.Before = func(ctx *cli.Context) error { app.Before = func(ctx *cli.Context) error {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
@ -151,10 +158,11 @@ func makeNodeConfig(ctx *cli.Context) (*params.NodeConfig, error) {
nodeConfig.NodeKeyFile = ctx.GlobalString(NodeKeyFileFlag.Name) nodeConfig.NodeKeyFile = ctx.GlobalString(NodeKeyFileFlag.Name)
if logLevel := ctx.GlobalString(LogLevelFlag.Name); len(logLevel) > 0 { if logLevel := ctx.GlobalString(LogLevelFlag.Name); logLevel != "" {
nodeConfig.LogEnabled = true
nodeConfig.LogLevel = logLevel nodeConfig.LogLevel = logLevel
log.SetLevel(logLevel) }
if logFile := ctx.GlobalString(LogFileFlag.Name); logFile != "" {
nodeConfig.LogFile = logFile
} }
return nodeConfig, nil return nodeConfig, nil

View File

@ -33,7 +33,6 @@ var nodeConfigJSON = `{
"DataDir": "` + TestDataDir + `", "DataDir": "` + TestDataDir + `",
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `, "HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `, "WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
"LogEnabled": true,
"LogLevel": "INFO" "LogLevel": "INFO"
}` }`

View File

@ -43,7 +43,6 @@ func (s *APITestSuite) TestCHTUpdate() {
configJSON := `{ configJSON := `{
"NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `, "NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `,
"DataDir": "` + tmpDir + `", "DataDir": "` + tmpDir + `",
"LogEnabled": true,
"LogLevel": "INFO", "LogLevel": "INFO",
"RPCEnabled": true "RPCEnabled": true
}` }`

View File

@ -2,16 +2,28 @@ package log
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// logger is package scope instance of log.Logger // Logger is a wrapper around log.Logger.
var logger = log.New("geth", "StatusIM") type Logger struct {
log.Logger
Level log.Lvl
Handler log.Handler
}
// logger is package scope instance of Logger
var logger = Logger{
Logger: log.New("geth", "StatusIM"),
Level: log.LvlError,
Handler: log.StreamHandler(os.Stdout, log.TerminalFormat(true)),
}
func init() { func init() {
SetLevel("INFO") setHandler(logger.Level, logger.Handler)
} }
// SetLevel inits status and ethereum-go logging packages, // SetLevel inits status and ethereum-go logging packages,
@ -20,17 +32,36 @@ func init() {
// Our log levels are in form "DEBUG|ERROR|WARN|etc", while // Our log levels are in form "DEBUG|ERROR|WARN|etc", while
// ethereum-go expects names in lower case: "debug|error|warn|etc". // ethereum-go expects names in lower case: "debug|error|warn|etc".
func SetLevel(level string) { func SetLevel(level string) {
lvl, err := log.LvlFromString(strings.ToLower(level)) lvl := levelFromString(level)
if err != nil {
fmt.Printf("Incorrect log level: %s, using defaults\n", level)
lvl = log.LvlInfo
}
setHandler(lvl, log.StdoutHandler) logger.Level = lvl
setHandler(lvl, logger.Handler)
} }
// setHandler is a init helper that allows (re)initialization // SetLogFile configures logger to write output into file.
// with different handler. Useful for testing. // This call preserves current logging level.
func SetLogFile(filename string) error {
handler, err := log.FileHandler(filename, log.TerminalFormat(false))
if err != nil {
return err
}
logger.Handler = handler
setHandler(logger.Level, handler)
return nil
}
func levelFromString(level string) log.Lvl {
lvl, err := log.LvlFromString(strings.ToLower(level))
if err != nil {
fmt.Fprintf(os.Stderr, "Incorrect log level: %s, using defaults\n", level)
lvl = log.LvlInfo
}
return lvl
}
// setHandler is a helper that allows log (re)initialization
// with different level and handler. Useful for testing.
func setHandler(lvl log.Lvl, handler log.Handler) { func setHandler(lvl log.Lvl, handler log.Handler) {
h := log.LvlFilterHandler(lvl, handler) h := log.LvlFilterHandler(lvl, handler)
logger.SetHandler(h) logger.SetHandler(h)

View File

@ -2,9 +2,11 @@ package log
import ( import (
"bytes" "bytes"
"io/ioutil"
"testing" "testing"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
) )
const ( const (
@ -44,8 +46,28 @@ func TestLogLevels(t *testing.T) {
Warn(warn) Warn(warn)
Error(err) Error(err)
if buf.String() != test.out { require.Equal(t, test.out, buf.String())
t.Errorf("Expecting log output to be '%s', got '%s'", test.out, buf.String())
}
} }
} }
func TestLogFile(t *testing.T) {
file, err := ioutil.TempFile("", "statusim_log_test")
require.NoError(t, err)
defer file.Close()
// setup log
SetLevel("INFO")
SetLogFile(file.Name())
// test log output to file
Info(info)
Debug(debug)
data, err := ioutil.ReadAll(file)
require.NoError(t, err)
got := string(data)
require.Contains(t, got, info)
require.NotContains(t, got, debug)
}

View File

@ -70,6 +70,8 @@ func (m *NodeManager) startNode(config *params.NodeConfig) (<-chan struct{}, err
return nil, ErrNodeExists return nil, ErrNodeExists
} }
m.initLog(config)
ethNode, err := MakeNode(config) ethNode, err := MakeNode(config)
if err != nil { if err != nil {
return nil, err return nil, err
@ -580,3 +582,16 @@ func (m *NodeManager) RPCServer() (*rpc.Server, error) {
return m.rpcServer, nil return m.rpcServer, nil
} }
// initLog initializes global logger parameters based on
// provided node configurations.
func (m *NodeManager) initLog(config *params.NodeConfig) {
log.SetLevel(config.LogLevel)
if config.LogFile != "" {
err := log.SetLogFile(config.LogFile)
if err != nil {
fmt.Println("Failed to open log file, using stdout")
}
}
}

View File

@ -287,9 +287,6 @@ type NodeConfig struct {
// handshake phase, counted separately for inbound and outbound connections. // handshake phase, counted separately for inbound and outbound connections.
MaxPendingPeers int MaxPendingPeers int
// LogToFile specified whether logs should be saved into file
LogEnabled bool
// LogFile is filename where exposed logs get written to // LogFile is filename where exposed logs get written to
LogFile string LogFile string

View File

@ -52,10 +52,10 @@ const (
DatabaseCache = 16 DatabaseCache = 16
// LogFile defines where to write logs to // LogFile defines where to write logs to
LogFile = "geth.log" LogFile = ""
// LogLevel defines the minimum log level to report // LogLevel defines the minimum log level to report
LogLevel = "INFO" LogLevel = "ERROR"
// LogLevelSuccinct defines the log level when only errors are reported. // LogLevelSuccinct defines the log level when only errors are reported.
// Useful when the default INFO level becomes too verbose. // Useful when the default INFO level becomes too verbose.

View File

@ -18,9 +18,8 @@
"TLSEnabled": false, "TLSEnabled": false,
"MaxPeers": 25, "MaxPeers": 25,
"MaxPendingPeers": 0, "MaxPendingPeers": 0,
"LogEnabled": false, "LogFile": "",
"LogFile": "geth.log", "LogLevel": "ERROR",
"LogLevel": "INFO",
"LogToStderr": true, "LogToStderr": true,
"UpstreamConfig": { "UpstreamConfig": {
"Enabled": false, "Enabled": false,
@ -62,4 +61,4 @@
"SwarmConfig": { "SwarmConfig": {
"Enabled": false "Enabled": false
} }
} }

View File

@ -18,9 +18,8 @@
"TLSEnabled": false, "TLSEnabled": false,
"MaxPeers": 25, "MaxPeers": 25,
"MaxPendingPeers": 0, "MaxPendingPeers": 0,
"LogEnabled": false, "LogFile": "",
"LogFile": "geth.log", "LogLevel": "ERROR",
"LogLevel": "INFO",
"LogToStderr": true, "LogToStderr": true,
"UpstreamConfig": { "UpstreamConfig": {
"Enabled": false, "Enabled": false,
@ -62,4 +61,4 @@
"SwarmConfig": { "SwarmConfig": {
"Enabled": false "Enabled": false
} }
} }

View File

@ -18,9 +18,8 @@
"TLSEnabled": false, "TLSEnabled": false,
"MaxPeers": 25, "MaxPeers": 25,
"MaxPendingPeers": 0, "MaxPendingPeers": 0,
"LogEnabled": false, "LogFile": "",
"LogFile": "geth.log", "LogLevel": "ERROR",
"LogLevel": "INFO",
"LogToStderr": true, "LogToStderr": true,
"UpstreamConfig": { "UpstreamConfig": {
"Enabled": false, "Enabled": false,
@ -74,4 +73,4 @@
"SwarmConfig": { "SwarmConfig": {
"Enabled": false "Enabled": false
} }
} }

View File

@ -11,7 +11,6 @@ import (
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/geth/common" "github.com/status-im/status-go/geth/common"
"github.com/status-im/status-go/geth/log"
"github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/geth/params"
assertions "github.com/stretchr/testify/require" assertions "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -54,8 +53,6 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
log.SetLevel("ERROR")
} }
// BaseTestSuite defines a base tests suit which others suites can embedded to // BaseTestSuite defines a base tests suit which others suites can embedded to
@ -131,8 +128,7 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
"DataDir": "` + filepath.Join(TestDataDir, TestNetworkNames[networkID]) + `", "DataDir": "` + filepath.Join(TestDataDir, TestNetworkNames[networkID]) + `",
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `, "HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `, "WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
"LogEnabled": true, "LogLevel": "INFO"
"LogLevel": "ERROR"
}` }`
nodeConfig, err := params.LoadNodeConfig(configJSON) nodeConfig, err := params.LoadNodeConfig(configJSON)
if err != nil { if err != nil {