From 94537d6738c575e9cf9b36be699bf86070613c30 Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 08:45:18 -0600 Subject: [PATCH 01/60] problem: glog to be able to handle display vs. debug logs solution: create alternate loggingT instance, using glog.D (vs. glog.V) update tests colorize output respectively ensure debug (glog.V) logs are still rendered in stderr with traces for severity=.Error implement convenience colorizing methods --- logger/glog/glog.go | 319 +++++++++++++++++++++++++++++++++------ logger/glog/glog_file.go | 10 +- logger/glog/glog_test.go | 39 ++++- logger/log.go | 25 +++ 4 files changed, 344 insertions(+), 49 deletions(-) diff --git a/logger/glog/glog.go b/logger/glog/glog.go index 94c4fd354..082f656be 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -97,6 +97,9 @@ import ( // the flag.Value interface. The -stderrthreshold flag is of type severity and // should be modified only through the flag.Value interface. The values match // the corresponding constants in C++. +// Severity is determined by the method called upon receiver Verbose, +// eg. glog.V(logger.Debug).Warnf("this log's severity is %v", warningLog) +// eg. glog.V(logger.Error).Infof("This log's severity is %v", infoLog) type severity int32 // sync/atomic int32 // These constants identify the log levels in order of increasing severity. @@ -112,6 +115,9 @@ const ( const severityChar = "IWEF" +const severityColorReset = "\x1b[0m" // reset both foreground and background +var severityColor = []string{"\x1b[2m", "\x1b[33m", "\x1b[31m", "\x1b[35m"} // info:dim warn:yellow, error:red, fatal:magenta + var severityName = []string{ infoLog: "INFO", warningLog: "WARNING", @@ -138,6 +144,10 @@ func SetV(v int) { logging.verbosity.set(Level(v)) } +func SetD(v int) { + display.verbosity.set(Level(v)) +} + // SetToStderr sets the global output style func SetToStderr(toStderr bool) { logging.mu.Lock() @@ -168,6 +178,10 @@ func GetVerbosity() *Level { return &logging.verbosity } +func GetDisplayable() *Level { + return &display.verbosity +} + // get returns the value of the severity. func (s *severity) get() severity { return severity(atomic.LoadInt32((*int32)(s))) @@ -454,27 +468,16 @@ type flushSyncWriter interface { io.Writer } -func init() { - //flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") - //flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") - //flag.Var(&logging.verbosity, "v", "log level for V logs") - //flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") - //flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") - //flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") - - // Default stderrThreshold is ERROR. - logging.stderrThreshold = errorLog - logging.setVState(3, nil, false) - go logging.flushDaemon() -} +type logTName string -// Flush flushes all pending log I/O. -func Flush() { - logging.lockAndFlushAll() -} +const ( + fileLog logTName = "file" + displayLog logTName = "display" +) // loggingT collects all the global state of the logging setup. type loggingT struct { + logTName // Boolean flags. Not handled atomically because the flag.Value interface // does not let us avoid the =true, and that shorthand is necessary for // compatibility. TODO: does this matter enough to fix? Seems unlikely. @@ -511,6 +514,20 @@ type loggingT struct { // safely using atomic.LoadInt32. vmodule moduleSpec // The state of the -vmodule flag. verbosity Level // V logging level, the value of the -v flag/ + + // severityTraceThreshold determines the minimum severity at which + // file traces will be logged in the header. See severity const iota above. + // Only severities at or above this number will be logged with a trace, + // eg. at severityTraceThreshold = 1, then only severities errorLog and fatalLog + // will log with traces. + severityTraceThreshold severity + + // verbosityTraceThreshold determines the minimum verbosity at which + // file traces will be logged in the header. + // Only levels at or above this number will be logged with a trace, + // eg. at verbosityTraceThreshold = 5, then only verbosities Debug, Detail, and Ridiculousness + // will log with traces. + verbosityTraceThreshold Level } // buffer holds a byte Buffer for reuse. The zero value is ready for use. @@ -521,25 +538,97 @@ type buffer struct { } var logging loggingT +var display loggingT + +func init() { + //flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") + //flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") + //flag.Var(&logging.verbosity, "v", "log level for V logs") + //flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") + //flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") + //flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") + + logging.logTName = fileLog + // Default stderrThreshold is ERROR. + // This makes V(logger.Error) logs print ALSO to stderr. + logging.stderrThreshold = errorLog + + // Establish defaults for trace thresholds. + logging.verbosityTraceThreshold.set(0) + logging.severityTraceThreshold.set(2) + + // Default for verbosity. + logging.setVState(5, nil, false) + go logging.flushDaemon() + + display.logTName = displayLog + // Renders anything at or below (Warn...) level Info to stderr, which + // is set by default anyway. + display.stderrThreshold = infoLog + + // toStderr makes it ONLY print to stderr, not to file + display.toStderr = true + + // Should never reach... unless we get real fancy with D(levels) + display.verbosityTraceThreshold.set(5) + // Only includes traces for severity=fatal logs for dispaly. + // This should never be reached; fatal logs should ALWAYS be logged to file, + // and they will also be written to stderr (anything Error and above is). + display.severityTraceThreshold.set(3) + // Set display verbosity default Info. So it will render + // all Fatal, Error, Warn, and Info log levels. + // Please don't use Fatal for display; again, Fatal logs should only go through file logging + // (they will be printed to stderr anyway). + display.setVState(2, nil, false) + go display.flushDaemon() +} + +// Flush flushes all pending log I/O. +func Flush() { + logging.lockAndFlushAll() + display.lockAndFlushAll() +} + +// traceThreshold determines the arbitrary level for log lines to be printed +// with caller trace information in the header. +func (l *loggingT) traceThreshold(s severity) bool { + return s >= l.severityTraceThreshold || l.verbosity >= l.verbosityTraceThreshold +} + +// GetVTraceThreshold gets the current verbosity trace threshold for logging. +func GetVTraceThreshold() *Level { + return &logging.verbosityTraceThreshold +} + +// SetVTraceThreshold sets the current verbosity trace threshold for logging. +func SetVTraceThreshold(v int) { + logging.mu.Lock() + defer logging.mu.Unlock() + + l := logging.verbosity.get() + logging.verbosity.set(0) + logging.verbosityTraceThreshold.set(Level(v)) + logging.verbosity.set(l) +} // setVState sets a consistent state for V logging. // l.mu is held. func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { // Turn verbosity off so V will not fire while we are in transition. - logging.verbosity.set(0) + l.verbosity.set(0) // Ditto for filter length. - atomic.StoreInt32(&logging.filterLength, 0) + atomic.StoreInt32(&l.filterLength, 0) // Set the new filters and wipe the pc->Level map if the filter has changed. if setFilter { - logging.vmodule.filter = filter - logging.vmap = make(map[uintptr]Level) + l.vmodule.filter = filter + l.vmap = make(map[uintptr]Level) } // Things are consistent now, so enable filtering and verbosity. // They are enabled in order opposite to that in V. - atomic.StoreInt32(&logging.filterLength, int32(len(filter))) - logging.verbosity.set(verbosity) + atomic.StoreInt32(&l.filterLength, int32(len(filter))) + l.verbosity.set(verbosity) } // getBuffer returns a new, ready-to-use buffer. @@ -624,25 +713,49 @@ func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { _, month, day := now.Date() hour, minute, second := now.Clock() // Lmmdd hh:mm:ss.uuuuuu threadid file:line] + + //buf.nDigits(8, 0, severityColor[s],'') + buf.WriteString(severityColor[s]) buf.tmp[0] = severityChar[s] - buf.twoDigits(1, int(month)) - buf.twoDigits(3, day) - buf.tmp[5] = ' ' - buf.twoDigits(6, hour) - buf.tmp[8] = ':' - buf.twoDigits(9, minute) - buf.tmp[11] = ':' - buf.twoDigits(12, second) - buf.tmp[14] = '.' - buf.nDigits(6, 15, now.Nanosecond()/1000, '0') - buf.tmp[21] = ' ' - buf.Write(buf.tmp[:22]) - buf.WriteString(file) - buf.tmp[0] = ':' - n := buf.someDigits(1, line) - buf.tmp[n+1] = ']' - buf.tmp[n+2] = ' ' - buf.Write(buf.tmp[:n+3]) + buf.Write(buf.tmp[:1]) + if l.logTName == displayLog { + buf.WriteString(severityColorReset) + // Write dim for rest of line, eg. date and time + buf.WriteString(severityColor[infoLog]) + } else { + buf.WriteString(severityColorReset) + } + buf.twoDigits(0, int(month)) + buf.twoDigits(2, day) + buf.tmp[4] = ' ' + buf.twoDigits(5, hour) + buf.tmp[7] = ':' + buf.twoDigits(8, minute) + buf.tmp[10] = ':' + buf.twoDigits(11, second) + // Only keep nanoseconds for file logs + if l.logTName == fileLog { + buf.tmp[13] = '.' + buf.nDigits(6, 14, now.Nanosecond()/1000, '0') + buf.Write(buf.tmp[:20]) + } else { + buf.Write(buf.tmp[:13]) + buf.WriteString(severityColorReset) + } + buf.WriteString(" ") + if l.traceThreshold(s) { + buf.WriteString(file) + buf.tmp[0] = ':' + n := buf.someDigits(1, line) + buf.tmp[n+1] = ']' + buf.tmp[n+2] = ' ' + buf.Write(buf.tmp[:n+3]) + } + //else { + // buf.tmp[0] = ' ' + // buf.Write(buf.tmp[:1]) + //} + return buf } @@ -955,6 +1068,12 @@ func (l *loggingT) flushAll() { for s := fatalLog; s >= infoLog; s-- { file := l.file[s] if file != nil { + // if e := file.Flush(); e != nil { + // stdLog.Fatalln(e) + // } + // if e := file.Sync(); e != nil { + // stdLog.Fatalln(e) + // } file.Flush() // ignore error file.Sync() // ignore error } @@ -1005,6 +1124,7 @@ func (lb logBridge) Write(b []byte) (n int, err error) { } // printWithFileLine with alsoToStderr=true, so standard log messages // always appear on standard error. + logging.printWithFileLine(severity(lb), file, line, true, text) return len(b), nil } @@ -1032,6 +1152,7 @@ func (l *loggingT) setV(pc uintptr) Level { // Verbose is a boolean type that implements Infof (like Printf) etc. // See the documentation of V for more information. type Verbose bool +type Displayable bool // V reports whether verbosity at the call site is at least the requested level. // The returned value is a boolean of type Verbose, which implements Info, Infoln @@ -1055,7 +1176,6 @@ func V(level Level) Verbose { if logging.verbosity.get() >= level { return Verbose(true) } - // It's off globally but it vmodule may still be set. // Here is another cheap but safe test to see if vmodule is enabled. if atomic.LoadInt32(&logging.filterLength) > 0 { @@ -1076,6 +1196,71 @@ func V(level Level) Verbose { return Verbose(false) } +func D(level Level) Displayable { + // This function tries hard to be cheap unless there's work to do. + // The fast path is two atomic loads and compares. + + // Here is a cheap but safe test to see if V logging is enabled globally. + if display.verbosity.get() >= level { + return Displayable(true) + } + // It's off globally but it vmodule may still be set. + // Here is another cheap but safe test to see if vmodule is enabled. + if atomic.LoadInt32(&display.filterLength) > 0 { + // Now we need a proper lock to use the logging structure. The pcs field + // is shared so we must lock before accessing it. This is fairly expensive, + // but if V logging is enabled we're slow anyway. + display.mu.Lock() + defer display.mu.Unlock() + if runtime.Callers(2, display.pcs[:]) == 0 { + return Displayable(false) + } + v, ok := display.vmap[logging.pcs[0]] + if !ok { + v = display.setV(logging.pcs[0]) + } + return Displayable(v >= level) + } + return Displayable(false) +} + +func (d Displayable) Infoln(args ...interface{}) { + if d { + display.println(infoLog, args...) + } +} + +func (d Displayable) Infof(format string, args ...interface{}) { + if d { + display.printfmt(infoLog, format, args...) + } +} + +func (d Displayable) Warnln(args ...interface{}) { + if d { + display.println(warningLog, args...) + } +} + +func (d Displayable) Warnf(format string, args ...interface{}) { + if d { + display.printfmt(warningLog, format, args...) + } +} + +func (d Displayable) Errorln(args ...interface{}) { + if d { + display.println(errorLog, args...) + } +} + +func (d Displayable) Errorf(format string, args ...interface{}) { + if d { + display.printfmt(errorLog, format, args...) + } +} + +// INFO // Info is equivalent to the global Info function, guarded by the value of v. // See the documentation of V for usage. func (v Verbose) Info(args ...interface{}) { @@ -1100,6 +1285,56 @@ func (v Verbose) Infof(format string, args ...interface{}) { } } +// WARN +// Warn is equivalent to the global Warn function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Warn(args ...interface{}) { + if v { + logging.print(warningLog, args...) + } +} + +// Warnln is equivalent to the global Warnln function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Warnln(args ...interface{}) { + if v { + logging.println(warningLog, args...) + } +} + +// Warnf is equivalent to the global Warnf function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Warnf(format string, args ...interface{}) { + if v { + logging.printfmt(warningLog, format, args...) + } +} + +// ERROR +// Error is equivalent to the global Error function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Error(args ...interface{}) { + if v { + logging.print(errorLog, args...) + } +} + +// Errorln is equivalent to the global Errorln function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Errorln(args ...interface{}) { + if v { + logging.println(errorLog, args...) + } +} + +// Errorf is equivalent to the global Errorf function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Errorf(format string, args ...interface{}) { + if v { + logging.printfmt(errorLog, format, args...) + } +} + // Separator creates a line, ie --------------------------------- func Separator(iterable string) string { return strings.Repeat(iterable, 110) diff --git a/logger/glog/glog_file.go b/logger/glog/glog_file.go index 99944d9f3..1a9b228e0 100644 --- a/logger/glog/glog_file.go +++ b/logger/glog/glog_file.go @@ -37,7 +37,7 @@ var logDirs []string // If non-empty, overrides the choice of directory in which to write logs. // See createLogDirs for the full list of possible destinations. -//var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") +// var logDir = flag.String("log-dir", "", "If non-empty, write log files in this directory") var logDir *string = new(string) @@ -45,6 +45,14 @@ func SetLogDir(str string) { *logDir = str } +func GetLogDir() string { + return *logDir +} + +func GetLogDirs() []string { + return logDirs +} + func createLogDirs() { if *logDir != "" { logDirs = append(logDirs, *logDir) diff --git a/logger/glog/glog_test.go b/logger/glog/glog_test.go index b58f3d642..e4b7334ca 100644 --- a/logger/glog/glog_test.go +++ b/logger/glog/glog_test.go @@ -1,6 +1,7 @@ // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ // // Copyright 2013 Google Inc. All Rights Reserved. +// Modifications copyright 2017 ETC Dev Team. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -115,7 +116,7 @@ func TestInfoDepth(t *testing.T) { } for i, m := range msgs { - if !strings.HasPrefix(m, "I") { + if !strings.HasPrefix(m, severityColor[infoLog]+"I") { t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) } w := fmt.Sprintf("depth-test%d", i) @@ -170,7 +171,7 @@ func TestStandardLog(t *testing.T) { } // Test that the header has the correct format. -func TestHeader(t *testing.T) { +func TestHeader1ErrorLog(t *testing.T) { setFlags() defer logging.swap(logging.newBuffers()) defer func(previous func() time.Time) { timeNow = previous }(timeNow) @@ -178,16 +179,42 @@ func TestHeader(t *testing.T) { return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) } pid = 1234 - Info("test") + Error("test") var line int - format := "I0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" - n, err := fmt.Sscanf(contents(infoLog), format, &line) + format := severityColor[errorLog] + "E" + severityColorReset + "0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" + n, err := fmt.Sscanf(contents(errorLog), format, &line) if n != 1 || err != nil { - t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) + t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(errorLog)) } // Scanf treats multiple spaces as equivalent to a single space, // so check for correct space-padding also. want := fmt.Sprintf(format, line) + if contents(errorLog) != want { + t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(errorLog), want) + } +} + +// Test that the header has the correct format. +func TestHeader2InfoLog(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + defer func(previous func() time.Time) { timeNow = previous }(timeNow) + timeNow = func() time.Time { + return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) + } + s := logging.verbosityTraceThreshold.get() + logging.verbosityTraceThreshold.set(5) // Use app flag defaults + defer logging.verbosityTraceThreshold.set(s) + pid = 1234 + Info("test") + format := severityColor[infoLog] + "I" + severityColorReset + "0102 15:04:05.067890 logger/glog/glog_test.go:209] test\n" + n, err := fmt.Sscanf(contents(infoLog), format) + if err != nil { + t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) + } + // Scanf treats multiple spaces as equivalent to a single space, + // so check for correct space-padding also. + want := fmt.Sprintf(format) if contents(infoLog) != want { t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) } diff --git a/logger/log.go b/logger/log.go index 8ed96fa68..ac7e17163 100644 --- a/logger/log.go +++ b/logger/log.go @@ -77,3 +77,28 @@ func NewJSONsystem(datadir string, logFile string) LogSystem { return sys } + +const ( + reset = "\x1b[39m" + green = "\x1b[32m" + blue = "\x1b[36m" + yellow = "\x1b[33m" + red = "\x1b[31m" + magenta = "\x1b[35m" +) + +func ColorGreen(s string) (coloredString string) { + return green + s + reset +} +func ColorRed(s string) (coloredString string) { + return red + s + reset +} +func ColorBlue(s string) (coloredString string) { + return blue + s + reset +} +func ColorYellow(s string) (coloredString string) { + return yellow + s + reset +} +func ColorMagenta(s string) (coloredString string) { + return magenta + s + reset +} From 9ac4f6a3ad4c6f48876e82876a572439872df796 Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 08:51:26 -0600 Subject: [PATCH 02/60] problem: configure flags and default log settings solution: use display logs as default for stderr, sending glog.V logs to file set global verbosity for glog.V -> logger.Debug (Level 5), noting that --verbosity flag can still override use --neckbeard flag to enable sending debug logs to stderr INSTEAD of display logs expose VerbosityTraceFloor as debug API <-- note: this to be REMOVED --- cmd/geth/flags.go | 23 ++++++++++--- cmd/geth/main.go | 65 ++++++++++++++++++++++++++++++++----- cmd/geth/usage.go | 2 ++ eth/api.go | 12 +++++++ internal/web3ext/web3ext.go | 6 ++++ 5 files changed, 94 insertions(+), 14 deletions(-) diff --git a/cmd/geth/flags.go b/cmd/geth/flags.go index 815832d0d..f741af945 100644 --- a/cmd/geth/flags.go +++ b/cmd/geth/flags.go @@ -140,25 +140,39 @@ var ( } // logging and debug settings + NeckbeardFlag = cli.BoolFlag{ + Name: "neckbeard", + Usage: "Use verbose->stderr defaults for logging (verbosity=5,log-status='sync=60')", + } VerbosityFlag = cli.GenericFlag{ Name: "verbosity", Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=detail", Value: glog.GetVerbosity(), } + DisplayFlag = cli.IntFlag{ + Name: "display", + Usage: "Display verbosity: 0=silent, 1=basics, 2=status, 3=events", + Value: 2, + } VModuleFlag = cli.GenericFlag{ Name: "vmodule", Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=6,p2p=5)", Value: glog.GetVModule(), } + VerbosityTraceFloorFlag = cli.IntFlag{ + Name: "verbosity-trace-floor", + Usage: "Floor verbosity level at which to include file traces on log lines.", + Value: 0, + } LogDirFlag = DirectoryFlag{ Name: "log-dir,logdir", Usage: "Directory in which to write log files.", - Value: DirectoryString{filepath.Join(common.DefaultDataDir(), "logs")}, + Value: DirectoryString{filepath.Join(common.DefaultDataDir(), "mainnet", "log")}, } LogStatusFlag = cli.StringFlag{ Name: "log-status", - Usage: `Toggle interval-based STATUS logs: comma-separated list of =`, - Value: "sync=60", + Usage: `Toggle interval-based STATUS logs: comma-separated list of =. Use 'off' to disable entirely.'`, + Value: "sync=30", } MLogFlag = cli.StringFlag{ Name: "mlog", @@ -168,8 +182,7 @@ var ( MLogDirFlag = DirectoryFlag{ Name: "mlog-dir", Usage: "Directory in which to write machine log files", - // TODO: move to chain-subdir? - Value: DirectoryString{filepath.Join(common.DefaultDataDir(), "mlogs")}, + Value: DirectoryString{filepath.Join(common.DefaultDataDir(), "mainnet", "mlogs")}, } MLogComponentsFlag = cli.StringFlag{ Name: "mlog-components", diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 64ac76232..24bb1692e 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -171,8 +171,11 @@ func makeCLIApp() (app *cli.App) { TestNetFlag, NetworkIdFlag, RPCCORSDomainFlag, + NeckbeardFlag, VerbosityFlag, + DisplayFlag, VModuleFlag, + VerbosityTraceFloorFlag, LogDirFlag, LogStatusFlag, MLogFlag, @@ -225,16 +228,60 @@ func makeCLIApp() (app *cli.App) { glog.CopyStandardLogTo("INFO") + // log.Println("Writing logs to ", logDir) + // Turn on only file logging, disabling logging(T).toStderr and logging(T).alsoToStdErr + glog.SetToStderr(false) + glog.SetAlsoToStderr(false) + + // Set up file logging. + logDir := filepath.Join(MustMakeChainDataDir(ctx), "log") if ctx.GlobalIsSet(aliasableName(LogDirFlag.Name, ctx)) { - if p := ctx.GlobalString(aliasableName(LogDirFlag.Name, ctx)); p != "" { - if e := os.MkdirAll(p, os.ModePerm); e != nil { - return e - } - glog.SetLogDir(p) - glog.SetAlsoToStderr(true) + ld := ctx.GlobalString(aliasableName(LogDirFlag.Name, ctx)) + ldAbs, err := filepath.Abs(ld) + if err != nil { + glog.Fatalln(err) + } + logDir = ldAbs + } + // Ensure mkdir -p + if e := os.MkdirAll(logDir, os.ModePerm); e != nil { + return e + } + // Set log dir. + // GOTCHA: There may be NO glog.V logs called before this is set. + // Otherwise everything will get all fucked and there will be no logs. + glog.SetLogDir(logDir) + + if ctx.GlobalIsSet(DisplayFlag.Name) { + i := ctx.GlobalInt(DisplayFlag.Name) + if i > 3 { + return fmt.Errorf("Error: --%s level must be 0 <= i <= 3, got: %d", DisplayFlag.Name, i) } - } else { - glog.SetToStderr(true) + glog.SetD(i) + } + + if ctx.GlobalBool(NeckbeardFlag.Name) { + glog.SetD(0) + // Allow manual overrides + if !ctx.GlobalIsSet(LogStatusFlag.Name) { + ctx.Set(LogStatusFlag.Name, "sync=60") // set log-status interval + } + if !ctx.GlobalIsSet(VerbosityTraceFloorFlag.Name) { + glog.SetVTraceThreshold(0) + } + if !ctx.GlobalIsSet(VerbosityFlag.Name) { + glog.SetV(5) + } + glog.SetAlsoToStderr(true) + } + // If --log-status not set, set default 60s interval + if !ctx.GlobalIsSet(LogStatusFlag.Name) { + ctx.Set(LogStatusFlag.Name, "sync=30") + } + if ctx.GlobalIsSet(VerbosityTraceFloorFlag.Name) { + val := ctx.GlobalInt(VerbosityTraceFloorFlag.Name) + log.Println("--verbosity-trace-floor", "val", val) + glog.SetVTraceThreshold(val) } if s := ctx.String("metrics"); s != "" { @@ -295,7 +342,7 @@ func geth(ctx *cli.Context) error { n := MakeSystemNode(Version, ctx) ethe := startNode(ctx, n) - if ctx.GlobalIsSet(LogStatusFlag.Name) { + if ctx.GlobalString(LogStatusFlag.Name) != "off" { dispatchStatusLogs(ctx, ethe) } diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 2d3fc01a0..49b9fb4e5 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -136,8 +136,10 @@ var AppHelpFlagGroups = []flagGroup{ { Name: "LOGGING AND DEBUGGING", Flags: []cli.Flag{ + NeckbeardFlag, VerbosityFlag, VModuleFlag, + VerbosityTraceFloorFlag, LogDirFlag, LogStatusFlag, MLogFlag, diff --git a/eth/api.go b/eth/api.go index 7b5119bfa..d9654dbd8 100644 --- a/eth/api.go +++ b/eth/api.go @@ -1772,6 +1772,18 @@ func (api *PublicDebugAPI) Vmodule(s string) (string, error) { return glog.GetVModule().String(), err } +func (api *PublicDebugAPI) VerbosityTraceFloor(n uint64) (int, error) { + nint := int(n) + if nint == 0 { + return int(*glog.GetVTraceThreshold()), nil + } + if nint <= logger.Detail || nint == logger.Ridiculousness { + glog.SetVTraceThreshold(nint) + return int(*glog.GetVTraceThreshold()), nil + } + return -1, errors.New("invalid logging level") +} + // ExecutionResult groups all structured logs emitted by the EVM // while replaying a transaction in debug mode as well as the amount of // gas used and the return value diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index e1aa668c5..55f4cb6ce 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -191,6 +191,12 @@ web3._extend({ params: 1, inputFormatter: [web3._extend.formatters.inputOptionalNumberFormatter] }), + new web3._extend.Method({ + name: 'verbosityTraceFloor', + call: 'debug_verbosityTraceFloor', + params: 1, + inputFormatter: [web3._extend.formatters.inputOptionalNumberFormatter] + }), new web3._extend.Method({ name: 'vmodule', call: 'debug_vmodule', From 02db14644034a8e1e33bd31e587cd5426b6805bc Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 09:02:33 -0600 Subject: [PATCH 03/60] problem: implement display logging cmd to log status, events solution: able to provide rolling status logs at glog.D(logger.Warn) verbosity, and event logs for blockchain, headerchain, mining, and downloading --- cmd/geth/cmd.go | 409 +++++++++++++++++++++++++---------- core/blockchain.go | 22 +- core/blockchain_test.go | 8 +- core/events.go | 25 ++- core/headerchain.go | 28 ++- eth/downloader/api.go | 19 +- eth/downloader/downloader.go | 7 + 7 files changed, 398 insertions(+), 120 deletions(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index 613ec7f04..48fb28607 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -20,6 +20,18 @@ import ( "bufio" "errors" "fmt" + "io" + "io/ioutil" + "math/big" + "os" + "os/signal" + "path/filepath" + "runtime" + "strconv" + "strings" + "syscall" + "time" + "github.com/ethereumproject/ethash" "github.com/ethereumproject/go-ethereum/core" "github.com/ethereumproject/go-ethereum/core/state" @@ -33,17 +45,6 @@ import ( "github.com/ethereumproject/go-ethereum/pow" "github.com/ethereumproject/go-ethereum/rlp" "gopkg.in/urfave/cli.v1" - "io" - "io/ioutil" - "math/big" - "os" - "os/signal" - "path/filepath" - "runtime" - "strconv" - "strings" - "syscall" - "time" ) const ( @@ -115,6 +116,7 @@ func StartNode(stack *node.Node) { }() } +// ImportChain imports a blockchain. func ImportChain(chain *core.BlockChain, fn string) error { // Watch for Ctrl-C while the import is running. // If a signal is received, the import will stop at the next batch. @@ -756,7 +758,6 @@ type LogStatusFeatAvailability int const ( StatusFeatAvailable LogStatusFeatAvailability = iota StatusFeatRegistered - StatusFeatNonexistent ) // availableLogStatusFeatures stores state of implemented log STATUS features. @@ -765,6 +766,29 @@ var availableLogStatusFeatures = map[string]LogStatusFeatAvailability{ "sync": StatusFeatAvailable, } +type lsMode int + +const ( + lsModeDiscover lsMode = iota + lsModeFullSync + lsModeFastSync + lsModeImport +) + +var lsModeName = []string{ + "Discover", + "FullSync", + "FastSync", + "Import", +} + +var lsModeIcon = []string{ + "", + "︎◉", + "◎", + "▶︎", +} + // dispatchStatusLogs handle parsing --log-status=argument and toggling appropriate goroutine status feature logging. func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { flagName := aliasableName(LogStatusFlag.Name, ctx) @@ -781,13 +805,18 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { // If possible, split sync=60 into ["sync", "60"], otherwise yields ["sync"], ["60"], or ["someothernonsense"] eqs := strings.Split(p, "=") + if len(eqs) < 2 { + glog.Errorf("Invalid log status value: %v. Must be comma-separated pairs of module=interval.", eqs) + os.Exit(1) + } // Catch unavailable and duplicate status feature logs - if availableLogStatusFeatures[eqs[0]] == StatusFeatNonexistent { - glog.Fatalf("%v: %v: unavailable status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) - } - if availableLogStatusFeatures[eqs[0]] == StatusFeatRegistered { - glog.Fatalf("%v: %v: duplicate status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) + if status, ok := availableLogStatusFeatures[eqs[0]]; !ok { + glog.Errorf("%v: %v: unavailable status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) + os.Exit(1) + } else if status == StatusFeatRegistered { + glog.Errorf("%v: %v: duplicate status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) + os.Exit(1) } // If user just uses "sync" instead of "sync=42", append empty string and delegate to each status log function how to handle it @@ -797,7 +826,7 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { switch eqs[0] { case "sync": availableLogStatusFeatures["sync"] = StatusFeatRegistered - go runStatusSyncLogs(ethe, eqs[1], ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + go runStatusSyncLogs(ctx, ethe, eqs[1], ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) } } } @@ -805,133 +834,293 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { // runStatusSyncLogs starts STATUS SYNC logging at a given interval. // It should be run as a goroutine. // eg. --log-status="sync=42" logs SYNC information every 42 seconds -func runStatusSyncLogs(e *eth.Ethereum, interval string, maxPeers int) { - - // Establish default interval. +func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, interval string, maxPeers int) { + // Establish default interval and parse desired interval from context. + // Includes convenience notifications for UI/UX. intervalI := 60 - if interval != "" { i, e := strconv.Atoi(interval) if e != nil { - glog.Fatalf("STATUS SYNC %v: could not parse argument: %v", e, interval) + glog.Fatalf("SYNC %v: could not parse argument: %v", e, interval) } if i < 1 { - glog.Fatalf("STATUS SYNC interval value must be a positive integer, got: %d", i) + glog.Fatalf("SYNC interval value must be a positive integer, got: %d", i) } intervalI = i } + glog.V(logger.Info).Infof("Rolling SYNC log interval set: %d seconds", intervalI) + + // Only use severity=warn if --log-status not in use (ie using defaults) + statIntervalNotice := fmt.Sprintf("Rolling SYNC status logs set to every %d seconds. ", intervalI) + if !ctx.GlobalIsSet(LogStatusFlag.Name) { + statIntervalNotice += fmt.Sprintf("You can adjust this with the --%s flag.", LogStatusFlag.Name) + glog.D(logger.Error).Warnln(statIntervalNotice) + // statIntervalNoticeFn = glog.D(logger.Error).Warnf + } else { + glog.D(logger.Error).Infoln(statIntervalNotice) + } - glog.V(logger.Info).Infof("STATUS SYNC Log interval set: %d seconds", intervalI) - + // Set up ticker based on established interval. tickerInterval := time.Second * time.Duration(int32(intervalI)) ticker := time.NewTicker(tickerInterval) + var chainEventLastSent time.Time + // Bookmark vars. var lastLoggedBlockNumber uint64 + + var lsMode = lsModeDiscover // init + var lsModeN int + var lsModeDiscoverSpinners = []string{"➫", "➬", "➭"} + + var dominoes = []string{"🁣", "🁤", "🁥", "🁦", "🁭", "🁴", "🁻", "🁼", "🂃", "🂄", "🂋", "🂌", "🂓"} // 🁣🁤🁥🁦🁭🁴🁻🁼🂃🂄🂋🂌🂓 + chainIcon := "◼⋯⋯" + logger.ColorGreen("◼") + forkIcon := "◼⋯⦦" + logger.ColorGreen("◼") + headerIcon := "◼⋯⋯" + logger.ColorGreen("❐") + downloaderIcon := "◼⋯⋯" + logger.ColorGreen("⬇") + minedIcon := "◼⋯⋯" + logger.ColorGreen("⟠") + var sigc = make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigc) - for { - select { - case <-ticker.C: - lenPeers := e.Downloader().GetPeers().Len() - - _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - mode := e.Downloader().GetMode() - - // Get our head block - blockchain := e.BlockChain() - currentBlockHex := blockchain.CurrentBlock().Hash().Hex() - - // Discover -> not synchronising (searching for peers) - // FullSync/FastSync -> synchronising - // Import -> synchronising, at full height - fMode := "Discover" - fOfHeight := fmt.Sprintf(" of #%7d", height) - - // Calculate and format percent sync of known height - heightRatio := float64(current) / float64(height) - heightRatio = heightRatio * 100 - fHeightRatio := fmt.Sprintf("(%4.2f%%)", heightRatio) - - // Wait until syncing because real dl mode will not be engaged until then - if e.Downloader().Synchronising() { - switch mode { - case downloader.FullSync: - fMode = "FullSync" - case downloader.FastSync: - fMode = "FastSync" - currentBlockHex = blockchain.CurrentFastBlock().Hash().Hex() + // Should listen for events. + // Proof of concept create event subscription + ethEvents := e.EventMux().Subscribe( + // ChainEvent is called when a single block is inserted into the local blockchain. + // core.ChainEvent{}, + // ChainSideEvent is called when a forked block is inserted into the local blockchain. + core.ChainSideEvent{}, + // NewMinedBlockEvent is called when a new block is mined locally. + core.NewMinedBlockEvent{}, + // ChainInsertEvent is called when a batch of block is finished processing through the bc.InsertChain fn. + // It includes statistics. Processed, queued, ignored, txcount, etc. + core.ChainInsertEvent{}, + // HeaderChainInsertEvent is called when headers are inserted into the headerchain, ie. fastsync. + core.HeaderChainInsertEvent{}, + // StartEvent is called when a peer is selected for synchronisation and sync begins. + downloader.StartEvent{}, + // DoneEvent is called when synchronisation with a peer finishes without error. + downloader.DoneEvent{}, + // FailedEvent is called when synchronisation with a peer finishes with an error. + downloader.FailedEvent{}, + ) + greenParenify := func(s string) string { + return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫") + } + redParenify := func(s string) string { + return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") + } + handleDownloaderEvent := func(e interface{}) { + s := downloaderIcon + " " + switch d := e.(type) { + case downloader.StartEvent: + s += "Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + glog.D(logger.Info).Infoln(s) + case downloader.DoneEvent: + s += "Done " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + glog.D(logger.Info).Infoln(s) + case downloader.FailedEvent: + s += "Fail " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error()) + glog.D(logger.Info).Warnln(s) + } + } + + go func() { + for e := range ethEvents.Chan() { + switch d := e.Data.(type) { + // case core.ChainEvent: + // glog.D(logger.Info).Infof("chainevent time=%v block=%v", e.Time, d.Block.NumberU64()) + case core.ChainInsertEvent: + glog.D(logger.Info).Infof(chainIcon+" Insert "+logger.ColorGreen("blocks")+"=%s "+logger.ColorGreen("◼")+"=%s "+logger.ColorGreen("took")+"=%s", + greenParenify(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)), + greenParenify(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))), + greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))), + ) + if bool(glog.D(logger.Info)) { + chainEventLastSent = time.Now() } + case core.ChainSideEvent: + glog.D(logger.Info).Infof(forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9]))) + case core.HeaderChainInsertEvent: + glog.D(logger.Info).Infof(headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s", + greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)), + greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])), + greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))), + ) + if bool(glog.D(logger.Info)) { + chainEventLastSent = time.Now() + } + case core.NewMinedBlockEvent: + glog.D(logger.Info).Infof(minedIcon+" Mined "+logger.ColorGreen("◼")+"="+greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d", + d.Block.NumberU64(), + d.Block.Hash().Hex()[:9], + d.Block.Coinbase().Hex()[:9], + len(d.Block.Transactions()), + len(d.Block.Uncles()), + ))) + default: + handleDownloaderEvent(d) } - if current >= height && !(current == 0 && height == 0) { - fMode = "Import " // with spaces to make same length as Discover, FastSync, FullSync - fOfHeight = strings.Repeat(" ", 12) - fHeightRatio = strings.Repeat(" ", 7) - } - if height == 0 { - fOfHeight = strings.Repeat(" ", 12) - fHeightRatio = strings.Repeat(" ", 7) + } + }() + + printIntervalStatusLog := func() { + lenPeers := e.Downloader().GetPeers().Len() + + rtt, ttl, conf := e.Downloader().Qos() + confS := fmt.Sprintf("%01.2f", conf) + qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS) + + _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + mode := e.Downloader().GetMode() + if mode == downloader.FastSync { + current = e.BlockChain().CurrentFastBlock().NumberU64() + } + + // Get our head block + blockchain := e.BlockChain() + currentBlockHex := blockchain.CurrentBlock().Hash().Hex() + + // Discover -> not synchronising (searching for peers) + // FullSync/FastSync -> synchronising + // Import -> synchronising, at full height + fOfHeight := fmt.Sprintf("%7d", height) + + // Calculate and format percent sync of known height + heightRatio := float64(current) / float64(height) + heightRatio = heightRatio * 100 + fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) + + // Wait until syncing because real dl mode will not be engaged until then + lsMode = lsModeDiscover + if e.Downloader().Synchronising() { + switch mode { + case downloader.FullSync: + lsMode = lsModeFullSync + case downloader.FastSync: + lsMode = lsModeFastSync + currentBlockHex = blockchain.CurrentFastBlock().Hash().Hex() } + } + importMode := lenPeers > 0 && lsMode == lsModeDiscover && current >= height && !(current == 0 && height == 0) + if importMode { + lsMode = lsModeImport + fOfHeight = "" // strings.Repeat(" ", 12) + fHeightRatio = "" // strings.Repeat(" ", 7) + } + if height == 0 { + fOfHeight = "" // strings.Repeat(" ", 12) + fHeightRatio = "" // strings.Repeat(" ", 7) + } - // Calculate block stats for interval - numBlocksDiff := current - lastLoggedBlockNumber - numTxsDiff := 0 - mGas := new(big.Int) - - var numBlocksDiffPerSecond uint64 - var numTxsDiffPerSecond int - var mGasPerSecond = new(big.Int) - - if numBlocksDiff > 0 && numBlocksDiff != current { - for i := lastLoggedBlockNumber; i <= current; i++ { - b := blockchain.GetBlockByNumber(i) - if b != nil { - numTxsDiff += b.Transactions().Len() - mGas = new(big.Int).Add(mGas, b.GasUsed()) + // Calculate block stats for interval + numBlocksDiff := current - lastLoggedBlockNumber + numTxsDiff := 0 + mGas := new(big.Int) + + var numBlocksDiffPerSecond uint64 + var numTxsDiffPerSecond int + var mGasPerSecond = new(big.Int) + + var dominoGraph string + var nDom int + if numBlocksDiff > 0 && numBlocksDiff != current { + for i := lastLoggedBlockNumber + 1; i <= current; i++ { + b := blockchain.GetBlockByNumber(i) + if b != nil { + txLen := b.Transactions().Len() + // Add to tallies + numTxsDiff += txLen + mGas = new(big.Int).Add(mGas, b.GasUsed()) + // Domino effect + if lsMode == lsModeImport { + if txLen > len(dominoes)-1 { + // prevent slice out of bounds + txLen = len(dominoes) - 1 + } + if nDom <= 20 { + dominoGraph += dominoes[txLen] + } + nDom++ } } } - - // Convert to per-second stats - // FIXME(?): Some degree of rounding will happen. - // For example, if interval is 10s and we get 6 blocks imported in that span, - // stats will show '0' blocks/second. Looks a little strange; but on the other hand, - // precision costs visual space, and normally just looks weird when starting up sync or - // syncing slowly. - numBlocksDiffPerSecond = numBlocksDiff / uint64(intervalI) - - // Don't show initial current / per second val - if lastLoggedBlockNumber == 0 { - numBlocksDiffPerSecond = 0 + if nDom > 20 { + dominoGraph += "…" } + } + dominoGraph = logger.ColorGreen(dominoGraph) + + // Convert to per-second stats + // FIXME(?): Some degree of rounding will happen. + // For example, if interval is 10s and we get 6 blocks imported in that span, + // stats will show '0' blocks/second. Looks a little strange; but on the other hand, + // precision costs visual space, and normally just looks weird when starting up sync or + // syncing slowly. + numBlocksDiffPerSecond = numBlocksDiff / uint64(intervalI) + + // Don't show initial current / per second val + if lastLoggedBlockNumber == 0 { + numBlocksDiffPerSecond = 0 + numBlocksDiff = 0 + } - // Divide by interval to yield per-second stats - numTxsDiffPerSecond = numTxsDiff / intervalI - mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(intervalI))) - mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) - mGasPerSecondI := mGasPerSecond.Int64() + // Divide by interval to yield per-second stats + numTxsDiffPerSecond = numTxsDiff / intervalI + mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(intervalI))) + mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) + mGasPerSecondI := mGasPerSecond.Int64() - // Update last logged current block number - lastLoggedBlockNumber = current + // Update last logged current block number + lastLoggedBlockNumber = current - // Format head block hex for printing (eg. d4e…fa3) - cbhexstart := currentBlockHex[2:5] // trim off '0x' prefix - cbhexend := currentBlockHex[(len(currentBlockHex) - 3):] + // Format head block hex for printing (eg. d4e…fa3) + cbhexstart := currentBlockHex[:9] // trim off '0x' prefix - blockprogress := fmt.Sprintf("#%7d%s", current, fOfHeight) - cbhexdisplay := fmt.Sprintf("%s…%s", cbhexstart, cbhexend) - peersdisplay := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) - blocksprocesseddisplay := fmt.Sprintf("%4d/%4d/%2d blks/txs/mgas sec", numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI) + localHeadHeight := fmt.Sprintf("#%7d", current) + localHeadHex := fmt.Sprintf("%s…", cbhexstart) + peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) + domOrHeight := fOfHeight + " " + fHeightRatio + if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 { + domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight) + } else { + domOrHeight = "" + } + var blocksprocesseddisplay string + qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay) + if lsMode != lsModeImport { + blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI)) + } else { + blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64())) + domOrHeight = dominoGraph + qosDisplayable = "" + } + + // Log to ERROR. + headDisplay := greenParenify(localHeadHeight + " " + localHeadHex) + peerDisplay := greenParenify(peersOfMax) - // Log to ERROR. - // This allows maximum user optionality for desired integration with rest of event-based logging. - glog.V(logger.Error).Infof("STATUS SYNC %s %s %s %s %s %s", fMode, blockprogress, fHeightRatio, cbhexdisplay, blocksprocesseddisplay, peersdisplay) + modeIcon := logger.ColorGreen(lsModeIcon[lsMode]) + if lsMode == lsModeDiscover { + modeIcon = lsModeDiscoverSpinners[lsModeN%3] + } + modeIcon = logger.ColorGreen(modeIcon) + lsModeN++ + + // This allows maximum user optionality for desired integration with rest of event-based logging. + glog.D(logger.Warn).Infof("SYNC %s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", lsModeName[lsMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) + } + for { + select { + case <-ticker.C: + if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(intervalI/2))) { + printIntervalStatusLog() + } case <-sigc: // Listen for interrupt ticker.Stop() - glog.V(logger.Debug).Infoln("STATUS SYNC Stopping logging.") + glog.D(logger.Warn).Warnln("SYNC Stopping.") return } } diff --git a/core/blockchain.go b/core/blockchain.go index 252d7835f..8220fa822 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -134,7 +134,7 @@ func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux gv := func() HeaderValidator { return bc.Validator() } var err error - bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt) + bc.hc, err = NewHeaderChain(chainDb, config, mux, gv, bc.getProcInterrupt) if err != nil { return nil, err } @@ -182,7 +182,7 @@ func NewBlockChainDryrun(chainDb ethdb.Database, config *ChainConfig, pow pow.Po gv := func() HeaderValidator { return bc.Validator() } var err error - bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt) + bc.hc, err = NewHeaderChain(chainDb, config, mux, gv, bc.getProcInterrupt) if err != nil { return nil, err } @@ -208,6 +208,10 @@ func NewBlockChainDryrun(chainDb ethdb.Database, config *ChainConfig, pow pow.Po return bc, nil } +func (self *BlockChain) GetEventMux() *event.TypeMux { + return self.eventMux +} + func (self *BlockChain) getProcInterrupt() bool { return atomic.LoadInt32(&self.procInterrupt) == 1 } @@ -1386,6 +1390,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (chainIndex int, err err defer close(nonceAbort) txcount := 0 + var latestBlockTime time.Time for i, block := range chain { if atomic.LoadInt32(&self.procInterrupt) == 1 { glog.V(logger.Debug).Infoln("Premature abort during block chain processing") @@ -1482,6 +1487,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (chainIndex int, err err if err != nil { return i, err } + latestBlockTime = time.Unix(block.Time().Int64(), 0) switch status { case CanonStatTy: @@ -1511,9 +1517,19 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (chainIndex int, err err stats.processed++ } - if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) { + if stats.queued > 0 || stats.processed > 0 || stats.ignored > 0 { tend := time.Since(tstart) start, end := chain[0], chain[len(chain)-1] + events = append(events, ChainInsertEvent{ + stats.processed, + stats.queued, + stats.ignored, + txcount, + end.NumberU64(), + end.Hash(), + tend, + latestBlockTime, + }) if logger.MlogEnabled() { mlogBlockchain.Send(mlogBlockchainInsertBlocks.SetDetailValues( stats.processed, diff --git a/core/blockchain_test.go b/core/blockchain_test.go index fa9f187f2..23da1fd61 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -34,10 +34,16 @@ import ( "github.com/ethereumproject/go-ethereum/crypto" "github.com/ethereumproject/go-ethereum/ethdb" "github.com/ethereumproject/go-ethereum/event" + "github.com/ethereumproject/go-ethereum/logger/glog" "github.com/ethereumproject/go-ethereum/rlp" "github.com/hashicorp/golang-lru" ) +func init() { + // Disable any display logs for tests. + glog.SetD(0) +} + // GenesisBlockForTesting creates a block in which addr has the given wei balance. // The state trie of the block is written to db. the passed db needs to contain a state root func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { @@ -496,7 +502,7 @@ func chm(t testing.TB, genesis *types.Block, db ethdb.Database) *BlockChain { } valFn := func() HeaderValidator { return bc.Validator() } var err error - bc.hc, err = NewHeaderChain(db, config, valFn, bc.getProcInterrupt) + bc.hc, err = NewHeaderChain(db, config, bc.eventMux, valFn, bc.getProcInterrupt) if err != nil { t.Fatal(err) } diff --git a/core/events.go b/core/events.go index 7277e637d..0170ae14e 100644 --- a/core/events.go +++ b/core/events.go @@ -19,6 +19,8 @@ package core import ( "math/big" + "time" + "github.com/ethereumproject/go-ethereum/common" "github.com/ethereumproject/go-ethereum/core/types" "github.com/ethereumproject/go-ethereum/core/vm" @@ -67,11 +69,32 @@ type ChainSideEvent struct { Logs vm.Logs } +// TODO: no usages found in project files type PendingBlockEvent struct { Block *types.Block Logs vm.Logs } +type ChainInsertEvent struct { + Processed int + Queued int + Ignored int + TxCount int + LastNumber uint64 + LastHash common.Hash + Elasped time.Duration + LatestBlockTime time.Time +} + +type HeaderChainInsertEvent struct { + Processed int + Ignored int + LastNumber uint64 + LastHash common.Hash + Elasped time.Duration +} + +// TODO: no usages found in project files type ChainUncleEvent struct { Block *types.Block } @@ -82,4 +105,4 @@ type GasPriceChanged struct{ Price *big.Int } // Mining operation events type StartMining struct{} -type TopMining struct{} +type StopMining struct{} diff --git a/core/headerchain.go b/core/headerchain.go index 477793d99..1af35f4e2 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -29,6 +29,7 @@ import ( "github.com/ethereumproject/go-ethereum/common" "github.com/ethereumproject/go-ethereum/core/types" "github.com/ethereumproject/go-ethereum/ethdb" + "github.com/ethereumproject/go-ethereum/event" "github.com/ethereumproject/go-ethereum/logger" "github.com/ethereumproject/go-ethereum/logger/glog" "github.com/ethereumproject/go-ethereum/pow" @@ -56,6 +57,7 @@ type HeaderChain struct { rand *mrand.Rand getValidator getHeaderValidatorFn + eventMux *event.TypeMux } // getHeaderValidatorFn returns a HeaderValidator interface @@ -65,7 +67,7 @@ type getHeaderValidatorFn func() HeaderValidator // getValidator should return the parent's validator // procInterrupt points to the parent's interrupt semaphore // wg points to the parent's shutdown wait group -func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { +func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, mux *event.TypeMux, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { headerCache, _ := lru.New(headerCacheLimit) tdCache, _ := lru.New(tdCacheLimit) @@ -77,6 +79,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator ge hc := &HeaderChain{ config: config, + eventMux: mux, chainDb: chainDb, headerCache: headerCache, tdCache: tdCache, @@ -207,6 +210,7 @@ type WhCallback func(*types.Header) error // because nonces can be verified sparsely, not needing to check each. func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, writeHeader WhCallback) (int, error) { // Collect some import statistics to report on + var events []interface{} stats := struct{ processed, ignored int }{} start := time.Now() @@ -307,8 +311,18 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, w } // Report some public statistics so the user has a clue what's going on first, last := chain[0], chain[len(chain)-1] + elapsed := time.Since(start) glog.V(logger.Info).Infof("imported %d header(s) (%d ignored) in %v. #%v [%x… / %x…]", stats.processed, stats.ignored, - time.Since(start), last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) + elapsed, last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) + + events = append(events, HeaderChainInsertEvent{ + Processed: stats.processed, + Ignored: stats.ignored, + LastNumber: last.Number.Uint64(), + LastHash: last.Hash(), + Elasped: elapsed, + }) + go hc.postChainEvents(events) return 0, nil } @@ -489,6 +503,16 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) { hc.genesisHeader = head } +// postChainEvents iterates over the events generated by a chain insertion and +// posts them into the event mux. +func (hc *HeaderChain) postChainEvents(events []interface{}) { + // post event logs for further processing + for _, event := range events { + // Fire the insertion events individually + hc.eventMux.Post(event) + } +} + // headerValidator is responsible for validating block headers // // headerValidator implements HeaderValidator. diff --git a/eth/downloader/api.go b/eth/downloader/api.go index 85e8c42b9..22adc91d7 100644 --- a/eth/downloader/api.go +++ b/eth/downloader/api.go @@ -22,11 +22,24 @@ import ( "github.com/ethereumproject/go-ethereum/event" "github.com/ethereumproject/go-ethereum/rpc" + "github.com/ethereumproject/go-ethereum/common" + "math/big" ) -type DoneEvent struct{} -type StartEvent struct{} -type FailedEvent struct{ Err error } +type DoneEvent struct{ + Peer *peer + Hash common.Hash + TD *big.Int +} +type StartEvent struct{ + Peer *peer + Hash common.Hash + TD *big.Int +} +type FailedEvent struct{ + Peer *peer + Err error +} // PublicDownloaderAPI provides an API which gives information about the current synchronisation status. // It offers only methods that operates on data that can be available to anyone without security risks. diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 2106798ad..8b1b210ba 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -246,6 +246,13 @@ func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) { return d.syncStatsChainOrigin, current, d.syncStatsChainHeight, d.syncStatsStateDone, d.syncStatsStateDone + pendingStates } +func (d *Downloader) Qos() (rtt time.Duration, ttl time.Duration, conf float64) { + rtt = d.requestRTT() + ttl = d.requestTTL() + conf = float64(d.rttConfidence) / 1000000.0 + return +} + // Synchronising returns whether the downloader is currently retrieving blocks. func (d *Downloader) Synchronising() bool { return atomic.LoadInt32(&d.synchronising) > 0 From 0b16bccea026d3c68929149f0837afd59435fac1 Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 09:20:50 -0600 Subject: [PATCH 04/60] problem: update logging to better manage severity, verbosity, and use display solution: makes quite a few log lines better aware of verbosity and severity, so errors use .Error, more important logs have verbosities Warn, etc. add glog.D log lines for configuration notices, cmd progress reports, and other important HUD events --- accounts/watch.go | 2 +- cmd/geth/accountcmd.go | 4 +- cmd/geth/cmd.go | 99 +++++++++++++------------- cmd/geth/flag.go | 63 +++++++++++------ cmd/geth/migrate_datadir.go | 14 ++-- core/blockchain.go | 55 +++++++++------ core/database_util.go | 10 +-- eth/backend.go | 14 ++-- eth/downloader/downloader.go | 111 ++++++++++++++++++------------ eth/downloader/downloader_test.go | 7 +- eth/downloader/peer.go | 6 +- eth/handler.go | 16 +++-- eth/peer.go | 6 +- ethdb/database.go | 3 + node/node.go | 6 ++ p2p/dial.go | 2 +- p2p/discover/udp.go | 5 +- p2p/nat/nat.go | 6 +- p2p/peer.go | 2 +- p2p/rlpx.go | 2 +- p2p/server.go | 14 ++-- 21 files changed, 262 insertions(+), 185 deletions(-) diff --git a/accounts/watch.go b/accounts/watch.go index be039868f..e380f22b7 100644 --- a/accounts/watch.go +++ b/accounts/watch.go @@ -68,7 +68,7 @@ func (w *watcher) loop() { err := notify.Watch(w.ac.getKeydir(), w.ev, notify.All) if err != nil { - glog.V(logger.Detail).Infof("can't watch %s: %v", w.ac.getKeydir(), err) + glog.V(logger.Warn).Errorf("can't watch %s: %v", w.ac.getKeydir(), err) return } defer notify.Stop(w.ev) diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go index 63fce463f..6c3b73744 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -204,7 +204,7 @@ func accountIndex(ctx *cli.Context) error { if len(errs) > 0 { for _, e := range errs { if e != nil { - glog.V(logger.Error).Infof("init cache db err: %v", e) + glog.V(logger.Error).Errorf("init cache db err: %v", e) } } } @@ -236,10 +236,12 @@ func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i err = accman.Unlock(account, password) if err == nil { glog.V(logger.Info).Infof("Unlocked account %x", account.Address) + glog.D(logger.Error).Infof("Unlocked account %x", account.Address) return account, password } if err, ok := err.(*accounts.AmbiguousAddrError); ok { glog.V(logger.Info).Infof("Unlocked account %x", account.Address) + glog.D(logger.Error).Infof("Unlocked account %x", account.Address) return ambiguousAddrRecovery(accman, err, password), password } if err != accounts.ErrDecrypt { diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index 48fb28607..e10e60637 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -82,7 +82,7 @@ func StartNode(stack *node.Node) { signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigc) sig := <-sigc - glog.Infof("Got %v, shutting down...", sig) + glog.V(logger.Warn).Warnf("Got %v, shutting down...", sig) fails := make(chan error, 1) go func(fs chan error) { @@ -90,7 +90,7 @@ func StartNode(stack *node.Node) { select { case e := <-fs: if e != nil { - glog.V(logger.Error).Infof("node stop failure: %v", e) + glog.V(logger.Error).Errorf("node stop failure: %v", e) } } } @@ -127,7 +127,7 @@ func ImportChain(chain *core.BlockChain, fn string) error { defer close(interrupt) go func() { if _, ok := <-interrupt; ok { - glog.Info("caught interrupt during import, will stop at next batch") + glog.D(logger.Warn).Warnln("caught interrupt during import, will stop at next batch") } close(stop) }() @@ -140,7 +140,7 @@ func ImportChain(chain *core.BlockChain, fn string) error { } } - glog.Infoln("Importing blockchain ", fn) + glog.D(logger.Error).Infoln("Importing blockchain ", fn) fh, err := os.Open(fn) if err != nil { return err @@ -180,7 +180,7 @@ func ImportChain(chain *core.BlockChain, fn string) error { return fmt.Errorf("interrupted") } if hasAllBlocks(chain, blocks[:i]) { - glog.Infof("skipping batch %d, all blocks present [%x / %x]", + glog.D(logger.Warn).Warnf("skipping batch %d, all blocks present [%x / %x]", batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4]) continue } @@ -202,7 +202,7 @@ func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { } func ExportChain(blockchain *core.BlockChain, fn string) error { - glog.Infoln("Exporting blockchain to ", fn) + glog.D(logger.Warn).Infoln("Exporting blockchain to ", fn) fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) if err != nil { return err @@ -211,12 +211,12 @@ func ExportChain(blockchain *core.BlockChain, fn string) error { if err := blockchain.Export(fh); err != nil { return err } - glog.Infoln("Exported blockchain to ", fn) + glog.D(logger.Error).Infoln("Exported blockchain to ", fn) return nil } func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error { - glog.Infoln("Exporting blockchain to ", fn) + glog.D(logger.Warn).Infoln("Exporting blockchain to ", fn) // TODO verify mode perms fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) if err != nil { @@ -226,7 +226,7 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las if err := blockchain.ExportN(fh, first, last); err != nil { return err } - glog.Infoln("Exported blockchain to ", fn) + glog.D(logger.Error).Infoln("Exported blockchain to ", fn) return nil } @@ -234,32 +234,24 @@ func withLineBreak(s string) string { return s + "\n" } -func colorGreen(s interface{}) string { - return fmt.Sprintf("\x1b[32m%v\x1b[39m", s) -} - -func colorBlue(s interface{}) string { - return fmt.Sprintf("\x1b[36m%v\x1b[39m", s) -} - func formatStatusKeyValue(prefix string, ss ...interface{}) (s string) { s = "" // Single arg; category? ie Forks? if len(ss) == 1 { - s += colorBlue(ss[0]) + s += logger.ColorBlue(fmt.Sprintf("%v", ss[0])) } if len(ss) == 2 { if ss[0] == "" { - s += fmt.Sprintf("%v", colorGreen(ss[1])) + s += fmt.Sprintf("%v", logger.ColorGreen(fmt.Sprintf("%v", ss[1]))) } else { - s += fmt.Sprintf("%v: %v", ss[0], colorGreen(ss[1])) + s += fmt.Sprintf("%v: %v", ss[0], logger.ColorGreen(fmt.Sprintf("%v", ss[1]))) } } if len(ss) > 2 { s += fmt.Sprintf("%v:", ss[0]) for i := 2; i < len(ss); i++ { - s += withLineBreak(fmt.Sprintf(" %v", colorGreen(ss[i]))) + s += withLineBreak(fmt.Sprintf(" %v", logger.ColorGreen(fmt.Sprintf("%v", ss[i])))) } } @@ -529,16 +521,16 @@ func status(ctx *cli.Context) error { for _, p := range printme { s += withLineBreak(sep) // right align category title - s += withLineBreak(strings.Repeat(" ", len(sep)-len(p.title)) + colorBlue(p.title)) + s += withLineBreak(strings.Repeat(" ", len(sep)-len(p.title)) + logger.ColorBlue(p.title)) for _, v := range p.keyVals { s += v } } - glog.V(logger.Info).Info(s) + glog.D(logger.Info).Infoln(s) // Return here if database has not been initialized. if !shouldUseExisting { - glog.V(logger.Info).Info("Geth has not been initialized; no database information available yet.") + glog.D(logger.Info).Infoln("Geth has not been initialized; no database information available yet.") return nil } @@ -547,11 +539,11 @@ func status(ctx *cli.Context) error { s = "\n" s += withLineBreak(sep) title := "Chain database status" - s += withLineBreak(strings.Repeat(" ", len(sep)-len(title)) + colorBlue(title)) + s += withLineBreak(strings.Repeat(" ", len(sep)-len(title)) + logger.ColorBlue(title)) for _, v := range formatChainDataPretty(datadir, chaindata) { s += v } - glog.V(logger.Info).Info(s) + glog.D(logger.Info).Infoln(s) return nil } @@ -572,10 +564,10 @@ func rollback(ctx *cli.Context) error { bc, chainDB := MakeChain(ctx) defer chainDB.Close() - glog.Warning("Rolling back blockchain...") + glog.D(logger.Warn).Infoln("Rolling back blockchain...") if err := bc.SetHead(blockIndex); err != nil { - glog.V(logger.Warn).Infof("error setting head: %v", err) + glog.D(logger.Error).Errorf("error setting head: %v", err) } // Check if *neither* block nor fastblock numbers match desired head number @@ -584,7 +576,7 @@ func rollback(ctx *cli.Context) error { if nowCurrentHead != blockIndex && nowCurrentFastHead != blockIndex { glog.Fatalf("ERROR: Wanted rollback to set head to: %v, instead current head is: %v", blockIndex, nowCurrentHead) } - glog.Infof("SUCCESS: Head block set to: %v", nowCurrentHead) + glog.D(logger.Error).Infof("Success. Head block set to: %v", nowCurrentHead) return nil } @@ -598,7 +590,7 @@ func dumpChainConfig(ctx *cli.Context) error { glog.Fatal("Dump config should only be used with default chain configurations (mainnet or morden).") } - glog.V(logger.Info).Infof("Dumping configuration for: %v", chainIdentity) + glog.D(logger.Warn).Infof("Dumping configuration for: %v", chainIdentity) chainConfigFilePath := ctx.Args().First() chainConfigFilePath = filepath.Clean(chainConfigFilePath) @@ -618,7 +610,7 @@ func dumpChainConfig(ctx *cli.Context) error { } di, _ = os.Stat(fb) // update var with new dir info } else { - glog.V(logger.Error).Infof("err: %v (at '%v')", de, fb) + glog.V(logger.Error).Errorf("err: %v (at '%v')", de, fb) } } if !di.IsDir() { @@ -658,7 +650,7 @@ func dumpChainConfig(ctx *cli.Context) error { return writeError } - glog.V(logger.Info).Info(fmt.Sprintf("Wrote chain config file to \x1b[32m%s\x1b[39m.", chainConfigFilePath)) + glog.D(logger.Error).Infoln(fmt.Sprintf("Wrote chain config file to \x1b[32m%s\x1b[39m.", chainConfigFilePath)) return nil } @@ -706,6 +698,7 @@ func makedag(ctx *cli.Context) error { glog.Fatal("Can't find dir") } glog.V(logger.Info).Infoln("making DAG, this could take awhile...") + glog.D(logger.Warn).Infoln("making DAG, this could take awhile...") ethash.MakeDAG(blockNum, dir) } default: @@ -1207,7 +1200,7 @@ func recoverChaindata(ctx *cli.Context) error { if !ctx.GlobalBool(aliasableName(FakePoWFlag.Name, ctx)) { pow = ethash.New() } else { - glog.V(logger.Info).Info("Consensus: fake") + glog.V(logger.Warn).Info("Consensus: fake") } bc, err := core.NewBlockChainDryrun(bcdb, sconf.ChainConfig, pow, new(event.TypeMux)) @@ -1216,7 +1209,7 @@ func recoverChaindata(ctx *cli.Context) error { } if blockchainLoadError := bc.LoadLastState(true); blockchainLoadError != nil { - glog.V(logger.Error).Infof("! Found error while loading blockchain: %v", blockchainLoadError) + glog.V(logger.Error).Errorf("Error while loading blockchain: %v", blockchainLoadError) // but do not return } @@ -1224,34 +1217,34 @@ func recoverChaindata(ctx *cli.Context) error { currentBlock := bc.CurrentBlock() currentFastBlock := bc.CurrentFastBlock() - glog.V(logger.Error).Infoln("Current status (before recovery attempt):") + glog.D(logger.Error).Infoln("Current status (before recovery attempt):") if header != nil { - glog.V(logger.Error).Infof("Last header: #%d\n", header.Number.Uint64()) + glog.D(logger.Error).Infof("Last header: #%d\n", header.Number.Uint64()) if currentBlock != nil { - glog.V(logger.Error).Infof("Last block: #%d\n", currentBlock.Number()) + glog.D(logger.Error).Infof("Last block: #%d\n", currentBlock.Number()) } else { - glog.V(logger.Error).Infoln("! Last block: nil") + glog.D(logger.Error).Errorf("! Last block: nil") } if currentFastBlock != nil { - glog.V(logger.Error).Infof("Last fast block: #%d\n", currentFastBlock.Number()) + glog.D(logger.Error).Infof("Last fast block: #%d\n", currentFastBlock.Number()) } else { - glog.V(logger.Error).Infoln("! Last fast block: nil") + glog.D(logger.Error).Errorln("! Last fast block: nil") } } else { - glog.V(logger.Error).Infoln("! Last header: nil") + glog.D(logger.Error).Errorln("! Last header: nil") } - glog.V(logger.Error).Infoln(glog.Separator("-")) + glog.D(logger.Error).Infoln(glog.Separator("-")) - glog.V(logger.Error).Infoln("Checking db validity and recoverable data...") + glog.D(logger.Error).Infoln("Checking db validity and recoverable data...") checkpoint := bc.Recovery(1, 2048) - glog.V(logger.Error).Infof("Found last recoverable checkpoint=#%d\n", checkpoint) + glog.D(logger.Error).Infof("Found last recoverable checkpoint=#%d\n", checkpoint) - glog.V(logger.Error).Infoln(glog.Separator("-")) + glog.D(logger.Error).Infoln(glog.Separator("-")) - glog.V(logger.Error).Infoln("Setting blockchain db head to last safe checkpoint...") + glog.D(logger.Error).Infoln("Setting blockchain db head to last safe checkpoint...") if setHeadErr := bc.SetHead(checkpoint); setHeadErr != nil { - glog.V(logger.Error).Infof("Error setting head: %v\n", setHeadErr) + glog.D(logger.Error).Errorf("Error setting head: %v\n", setHeadErr) return setHeadErr } return nil @@ -1268,7 +1261,7 @@ func askForConfirmation(s string) bool { reader := bufio.NewReader(os.Stdin) for { - glog.V(logger.Error).Infof("%s [y/n]: ", s) + glog.D(logger.Error).Warnf("%s [y/n]: ", s) response, err := reader.ReadString('\n') if err != nil { @@ -1282,9 +1275,9 @@ func askForConfirmation(s string) bool { } else if response == "n" || response == "no" { return false } else { - glog.V(logger.Error).Infoln(glog.Separator("*")) - glog.V(logger.Error).Infoln("* INVALID RESPONSE: Please respond with [y|yes] or [n|no], or use CTRL-C to abort.") - glog.V(logger.Error).Infoln(glog.Separator("*")) + glog.D(logger.Error).Warnln(glog.Separator("*")) + glog.D(logger.Error).Errorln("* INVALID RESPONSE: Please respond with [y|yes] or [n|no], or use CTRL-C to abort.") + glog.D(logger.Error).Warnln(glog.Separator("*")) } } } @@ -1300,9 +1293,9 @@ func resetChaindata(ctx *cli.Context) error { if err := os.RemoveAll(dir); err != nil { return err } - glog.V(logger.Error).Infof("Successfully removed chaindata directory: '%s'\n", dir) + glog.D(logger.Error).Infof("Successfully removed chaindata directory: '%s'\n", dir) } else { - glog.V(logger.Error).Infoln("Leaving chaindata untouched. As you were.") + glog.D(logger.Error).Infoln("Leaving chaindata untouched. As you were.") } return nil } diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index 056742a2f..434c60454 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -131,7 +131,7 @@ func copyChainConfigFileToChainDataDir(ctx *cli.Context, identity, configFilePat return e } - glog.V(logger.Debug).Infof("Copying %v to %v/chain.json", configFilePath, identityDirPath) + // glog.V(logger.Debug).Infof("Copying %v to %v/chain.json", configFilePath, identityDirPath) b, e := ioutil.ReadFile(configFilePath) if e != nil { return e @@ -184,7 +184,7 @@ func mustMakeChainIdentity(ctx *cli.Context) (identity string) { glog.V(logger.Debug).Infof("Found existing file at --%v: %v", aliasableName(ChainIdentityFlag.Name, ctx), chainFlagVal) c, configurationError := core.ReadExternalChainConfigFromFile(filepath.Clean(chainFlagVal)) if configurationError == nil { - glog.V(logger.Debug).Infof("OK: Valid chain configuration. Chain identity: %v", c.Identity) + // glog.V(logger.Debug).Infof("OK: Valid chain configuration. Chain identity: %v", c.Identity) if e := copyChainConfigFileToChainDataDir(ctx, c.Identity, filepath.Clean(chainFlagVal)); e != nil { glog.Fatalf("Could not copy chain configuration: %v", e) } @@ -201,7 +201,7 @@ func mustMakeChainIdentity(ctx *cli.Context) (identity string) { glog.Fatalf("Invalid chain config file at --%v: '%v': %v \nAssuming literal identity argument.", aliasableName(ChainIdentityFlag.Name, ctx), chainFlagVal, configurationError) } - glog.V(logger.Debug).Infof("No existing file at --%v: '%v'. Using literal chain identity.", aliasableName(ChainIdentityFlag.Name, ctx), chainFlagVal) + // glog.V(logger.Debug).Infof("No existing file at --%v: '%v'. Using literal chain identity.", aliasableName(ChainIdentityFlag.Name, ctx), chainFlagVal) identity = chainFlagVal return identity } else if ctx.GlobalIsSet(aliasableName(ChainIdentityFlag.Name, ctx)) { @@ -357,11 +357,11 @@ func MakeWSRpcHost(ctx *cli.Context) string { // for Geth and returns half of the allowance to assign to the database. func MakeDatabaseHandles() int { if err := raiseFdLimit(2048); err != nil { - glog.V(logger.Warn).Info("Failed to raise file descriptor allowance: ", err) + glog.V(logger.Warn).Errorf("Failed to raise file descriptor allowance: ", err) } limit, err := getFdLimit() if err != nil { - glog.V(logger.Warn).Info("Failed to retrieve file descriptor allowance: ", err) + glog.V(logger.Warn).Errorf("Failed to retrieve file descriptor allowance: ", err) } if limit > 2048 { // cap database file descriptors even if more is available limit = 2048 @@ -385,7 +385,7 @@ func MakeAccountManager(ctx *cli.Context) *accounts.Manager { if path := ctx.GlobalString(aliasableName(KeyStoreDirFlag.Name, ctx)); path != "" { af, e := filepath.Abs(path) if e != nil { - glog.V(logger.Error).Infof("keydir path could not be made absolute: %v: %v", path, e) + glog.V(logger.Error).Errorf("keydir path could not be made absolute: %v: %v", path, e) } else { keydir = af } @@ -418,7 +418,7 @@ func MakeAddress(accman *accounts.Manager, account string) (accounts.Account, er func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address { accounts := accman.Accounts() if !ctx.GlobalIsSet(aliasableName(EtherbaseFlag.Name, ctx)) && len(accounts) == 0 { - glog.V(logger.Warn).Infoln("WARNING: No etherbase set and no accounts found as default") + glog.V(logger.Warn).Warnf("WARNING: No etherbase set and no accounts found as default") return common.Address{} } etherbase := ctx.GlobalString(aliasableName(EtherbaseFlag.Name, ctx)) @@ -507,8 +507,8 @@ func mustRegisterMLogsFromContext(ctx *cli.Context) { for k := range logger.MLogRegistryAvailable { components = append(components, string(k)) } - glog.V(logger.Error).Infof("Error: %s", e) - glog.V(logger.Error).Infof("Available machine log components: %v", components) + glog.V(logger.Error).Errorf("Error: %s", e) + glog.V(logger.Error).Errorf("Available machine log components: %v", components) os.Exit(1) } // Set the global logger mlog format from context @@ -520,6 +520,7 @@ func mustRegisterMLogsFromContext(ctx *cli.Context) { glog.Fatalf("Failed to start machine log: %v", e) } glog.V(logger.Info).Infof("Machine logs file: %v", fname) + glog.D(logger.Info).Infof("Machine logs (mlog) file: %v", logger.ColorGreen(fname)) } // MakeSystemNode sets up a local node, configures the services to launch and @@ -588,12 +589,13 @@ func MakeSystemNode(version string, ctx *cli.Context) *node.Node { } else { // Just demonstrative code. if b := logger.SetMlogEnabled(false); b == false && logger.MlogEnabled() == false { - glog.V(logger.Warn).Infof("Machine logs: disabled") + glog.V(logger.Warn).Warnf("Machine logs: disabled") + glog.D(logger.Info).Warnf("Machine logs disabled.") } } if ctx.GlobalBool(Unused1.Name) { - glog.V(logger.Info).Infoln(fmt.Sprintf("Geth started with --%s flag, which is unused by Geth Classic and can be omitted", Unused1.Name)) + glog.V(logger.Warn).Warnln(fmt.Sprintf("Geth started with --%s flag, which is unused by Geth Classic and can be omitted", Unused1.Name)) } return stack @@ -737,11 +739,13 @@ func mustMakeSufficientChainConfig(ctx *cli.Context) *core.SufficientChainConfig } if ctx.GlobalIsSet(aliasableName(BootnodesFlag.Name, ctx)) { config.ParsedBootstrap = MakeBootstrapNodesFromContext(ctx) - glog.V(logger.Warn).Infof(`WARNING: overwriting external bootnodes configuration with those from --%s flag. Value set from flag: %v`, aliasableName(BootnodesFlag.Name, ctx), config.ParsedBootstrap) + glog.V(logger.Warn).Warnf(`Overwriting external bootnodes configuration with those from --%s flag. Value set from flag: %v`, aliasableName(BootnodesFlag.Name, ctx), config.ParsedBootstrap) + glog.D(logger.Warn).Warnf(`Overwriting external bootnodes configuration with those from --%s flag. Value set from flag: %v`, aliasableName(BootnodesFlag.Name, ctx), config.ParsedBootstrap) } if ctx.GlobalIsSet(aliasableName(NetworkIdFlag.Name, ctx)) { i := ctx.GlobalInt(aliasableName(NetworkIdFlag.Name, ctx)) - glog.V(logger.Warn).Infof(`WARNING: overwriting external network id configuration with that from --%s flag. Value set from flag: %d`, aliasableName(NetworkIdFlag.Name, ctx), i) + glog.V(logger.Warn).Warnf(`Overwriting external network id configuration with that from --%s flag. Value set from flag: %d`, aliasableName(NetworkIdFlag.Name, ctx), i) + glog.D(logger.Warn).Warnf(`Overwriting external network id configuration with that from --%s flag. Value set from flag: %d`, aliasableName(NetworkIdFlag.Name, ctx), i) if i < 1 { glog.Fatalf("Network ID cannot be less than 1. Got: %d", i) } @@ -806,26 +810,38 @@ func mustMakeSufficientChainConfig(ctx *cli.Context) *core.SufficientChainConfig } func logChainConfiguration(ctx *cli.Context, config *core.SufficientChainConfig) { - chainIdentity := mustMakeChainIdentity(ctx) chainIsCustom := !(chainIdentitiesMain[chainIdentity] || chainIdentitiesMorden[chainIdentity]) + + // File logs. + // TODO: uglify V logs? provide more detail for debugging? normal users won't see this. if chainIsCustom { - glog.V(logger.Info).Infof("Using custom chain configuration: \x1b[32m%s\x1b[39m", chainIdentity) + glog.V(logger.Info).Infof("Using custom chain configuration: %s", chainIdentity) + glog.D(logger.Warn).Infof("Custom chain config: %s", logger.ColorGreen(chainIdentity)) } glog.V(logger.Info).Info(glog.Separator("-")) - glog.V(logger.Info).Infof("Starting Geth Classic \x1b[32m%s\x1b[39m", ctx.App.Version) - glog.V(logger.Info).Infof("Geth is configured to use ETC blockchain: \x1b[32m%v\x1b[39m", config.Name) - glog.V(logger.Info).Infof("Using chain database at: \x1b[32m%s\x1b[39m", MustMakeChainDataDir(ctx)+"/chaindata") + glog.V(logger.Info).Infof("Starting Geth Classic %s", ctx.App.Version) + glog.D(logger.Warn).Infof("Geth Classic version: %s", logger.ColorGreen(ctx.App.Version)) + + glog.V(logger.Info).Infof("Geth is configured to use ETC blockchain: %v", config.Name) + glog.D(logger.Warn).Infof("ETC blockchain: %s", logger.ColorGreen(config.Name)) + + chaindataDirName := MustMakeChainDataDir(ctx) + "/chaindata" + glog.V(logger.Info).Infof("Using chain database at: %s", chaindataDirName) + glog.D(logger.Warn).Infof("Chain database: %s", logger.ColorGreen(chaindataDirName)) glog.V(logger.Info).Infof("%v blockchain upgrades associated with this configuration:", len(config.ChainConfig.Forks)) + glog.D(logger.Warn).Infof("Blockchain upgrades configured: %s", logger.ColorGreen(strconv.Itoa(len(config.ChainConfig.Forks)))) for i := range config.ChainConfig.Forks { - glog.V(logger.Info).Infof(" %7v %v", config.ChainConfig.Forks[i].Block, config.ChainConfig.Forks[i].Name) + f := fmt.Sprintf(" %7v %v", config.ChainConfig.Forks[i].Block, config.ChainConfig.Forks[i].Name) if !config.ChainConfig.Forks[i].RequiredHash.IsEmpty() { - glog.V(logger.Info).Infof(" with block %v", config.ChainConfig.Forks[i].RequiredHash.Hex()) + f += fmt.Sprintf(" (%v)", config.ChainConfig.Forks[i].RequiredHash.Hex()) } + glog.V(logger.Info).Infoln(f) + glog.D(logger.Warn).Infoln(f) for _, feat := range config.ChainConfig.Forks[i].Features { glog.V(logger.Debug).Infof(" id: %v", feat.ID) for k, v := range feat.Options { @@ -835,7 +851,9 @@ func logChainConfiguration(ctx *cli.Context, config *core.SufficientChainConfig) } if chainIsCustom { - glog.V(logger.Info).Infof("State starting nonce: %v", colorGreen(state.StartingNonce)) + sn := strconv.Itoa(int(state.StartingNonce)) + glog.V(logger.Info).Infof("State starting nonce: %s", logger.ColorGreen(sn)) + glog.D(logger.Warn).Infof("State starting nonce: %s", logger.ColorGreen(sn)) } glog.V(logger.Info).Info(glog.Separator("-")) @@ -875,7 +893,8 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database if !ctx.GlobalBool(aliasableName(FakePoWFlag.Name, ctx)) { pow = ethash.New() } else { - glog.V(logger.Info).Info("Consensus: fake") + glog.V(logger.Info).Infoln("Consensus: fake") + glog.D(logger.Warn).Warnln("Consensus: fake") } chain, err = core.NewBlockChain(chainDb, sconf.ChainConfig, pow, new(event.TypeMux)) diff --git a/cmd/geth/migrate_datadir.go b/cmd/geth/migrate_datadir.go index fe6aa07d6..1b57d6277 100644 --- a/cmd/geth/migrate_datadir.go +++ b/cmd/geth/migrate_datadir.go @@ -60,7 +60,7 @@ func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { // NOTE: Since ETH stores chaindata by default in Ethereum/geth/..., this path // will not exist if the existing data belongs to ETH, so it works as a valid check for us as well. if _, err := os.Stat(ethChainDBPath); os.IsNotExist(err) { - glog.V(logger.Debug).Infof(`No existing default chaindata dir found at: %v + glog.V(logger.Debug).Warnf(`No existing default chaindata dir found at: %v Using default data directory at: %v`, ethChainDBPath, etcDataDirPath) return nil @@ -71,7 +71,7 @@ func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { for _, f := range requiredFiles { p := filepath.Join(ethChainDBPath, f) if _, err := os.Stat(p); os.IsNotExist(err) { - glog.V(logger.Debug).Infof(`No existing default file found at: %v + glog.V(logger.Debug).Warnf(`No existing default file found at: %v Using default data directory at: %v`, p, etcDataDirPath) } else { @@ -86,7 +86,7 @@ func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { // check if there is existing etf blockchain data in unclassic default dir (ie //Ethereum) chainDB, err := ethdb.NewLDBDatabase(ethChainDBPath, 0, 0) if err != nil { - glog.V(logger.Error).Info(`Failed to check blockchain compatibility for existing Ethereum chaindata database at: %v + glog.V(logger.Error).Errorf(`Failed to check blockchain compatibility for existing Ethereum chaindata database at: %v Using default data directory at: %v`, err, etcDataDirPath) return nil @@ -101,7 +101,7 @@ func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { hh = core.GetHeadFastBlockHash(chainDB) } if hh.IsEmpty() { - glog.V(logger.Debug).Info("There was no head block for the old database. It could be very young.") + glog.V(logger.Debug).Warnln("There was no head block for the old database. It could be very young.") } hasRequiredForkIfSufficientHeight := true @@ -120,13 +120,13 @@ func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { hf := conf.ForkByName("The DAO Hard Fork") if hf == nil || hf.Block == nil || new(big.Int).Cmp(hf.Block) == 0 || hf.RequiredHash.IsEmpty() { - glog.V(logger.Debug).Info("DAO Hard Fork required hash not configured for database chain. Not migrating.") + glog.V(logger.Debug).Warnln("DAO Hard Fork required hash not configured for database chain. Not migrating.") return nil } b := core.GetBlock(chainDB, hh) if b == nil { - glog.V(logger.Debug).Info("There was a problem checking the head block of old-namespaced database. The head hash was: %v", hh.Hex()) + glog.V(logger.Debug).Warnf("There was a problem checking the head block of old-namespaced database. The head hash was: %v", hh.Hex()) return nil } @@ -152,7 +152,7 @@ func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { if hasRequiredForkIfSufficientHeight { // if any of the LOG, LOCK, or CURRENT files are missing from old chaindata/, don't migrate - glog.V(logger.Info).Infof(`Found existing data directory named 'Ethereum' with default ETC chaindata. + glog.V(logger.Warn).Warnf(`Found existing data directory named 'Ethereum' with default ETC chaindata. Moving it from: %v, to: %v To specify a different data directory use the '--datadir' flag.`, ethDataDirPath, etcDataDirPath) diff --git a/core/blockchain.go b/core/blockchain.go index 8220fa822..ee63ba616 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -29,6 +29,9 @@ import ( "sync/atomic" "time" + "reflect" + "strconv" + "github.com/ethereumproject/go-ethereum/common" "github.com/ethereumproject/go-ethereum/core/state" "github.com/ethereumproject/go-ethereum/core/types" @@ -42,7 +45,6 @@ import ( "github.com/ethereumproject/go-ethereum/rlp" "github.com/ethereumproject/go-ethereum/trie" "github.com/hashicorp/golang-lru" - "reflect" ) var ( @@ -449,11 +451,12 @@ func (self *BlockChain) Recovery(from int, increment int) (checkpoint uint64) { } // Set up logging for block recovery progress. - ticker := time.NewTicker(time.Second * 2) + ticker := time.NewTicker(time.Second * 3) defer ticker.Stop() go func() { for range ticker.C { - glog.V(logger.Info).Infof("Recovered checkpoints through block #%d", checkpoint) + glog.V(logger.Info).Warnf("Recovered checkpoints through block #%d", checkpoint) + glog.D(logger.Warn).Warnf("Recovered checkpoints through block #%d", checkpoint) } }() @@ -472,10 +475,10 @@ func (self *BlockChain) Recovery(from int, increment int) (checkpoint uint64) { if checkpointBlockNext == nil { // Traverse in small steps (increment =1) from last known big step (increment >1) checkpoint. if increment > 1 && i-increment > 1 { - glog.V(logger.Debug).Infof("Reached nil block #%d, retrying recovery beginning from #%d, incrementing +%d", i, i-increment, 1) + glog.V(logger.Debug).Warnf("Reached nil block #%d, retrying recovery beginning from #%d, incrementing +%d", i, i-increment, 1) return self.Recovery(i-increment, 1) // hone in } - glog.V(logger.Debug).Infof("No block data available for block #%d", uint64(i)) + glog.V(logger.Debug).Warnf("No block data available for block #%d", uint64(i)) break } @@ -502,14 +505,14 @@ func (self *BlockChain) Recovery(from int, increment int) (checkpoint uint64) { } continue } - glog.V(logger.Error).Infof("WARNING: Found unhealthy block #%d (%v): \n\n%v", i, ee, checkpointBlockNext) + glog.V(logger.Error).Errorf("Found unhealthy block #%d (%v): \n\n%v", i, ee, checkpointBlockNext) if increment == 1 { break } return self.Recovery(i-increment, 1) } if checkpoint > 0 { - glog.V(logger.Warn).Infof("WARNING: Found recoverable blockchain data through block #%d", checkpoint) + glog.V(logger.Warn).Warnf("Found recoverable blockchain data through block #%d", checkpoint) } return checkpoint } @@ -534,7 +537,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { defer self.mu.Lock() if recoveredHeight == 0 { - glog.V(logger.Warn).Infoln("WARNING: No recoverable data found, resetting to genesis.") + glog.V(logger.Error).Errorln("WARNING: No recoverable data found, resetting to genesis.") return self.Reset() } // Remove all block header and canonical data above recoveredHeight @@ -547,7 +550,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { if head == (common.Hash{}) { // Corrupt or empty database, init from scratch if !dryrun { - glog.V(logger.Warn).Infoln("WARNING: Empty database, attempting chain reset with recovery.") + glog.V(logger.Warn).Errorln("WARNING: Empty database, attempting chain reset with recovery.") return recoverOrReset() } return errors.New("empty HeadBlockHash") @@ -560,7 +563,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { if currentBlock == nil { // Corrupt or empty database, init from scratch if !dryrun { - glog.V(logger.Warn).Infof("WARNING: Head block missing, hash: %x\nAttempting chain reset with recovery.", head) + glog.V(logger.Warn).Errorf("WARNING: Head block missing, hash: %x\nAttempting chain reset with recovery.", head) return recoverOrReset() } return errors.New("nil currentBlock") @@ -572,7 +575,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { glog.V(logger.Info).Infof("Validating currentBlock: %v", currentBlock.Number()) if e := self.blockIsInvalid(currentBlock); e != nil { if !dryrun { - glog.V(logger.Warn).Infof("WARNING: Found unhealthy head full block #%d (%x): %v \nAttempting chain reset with recovery.", currentBlock.Number(), currentBlock.Hash(), e) + glog.V(logger.Warn).Errorf("Found unhealthy head full block #%d (%x): %v \nAttempting chain reset with recovery.", currentBlock.Number(), currentBlock.Hash(), e) return recoverOrReset() } return fmt.Errorf("invalid currentBlock: %v", e) @@ -594,7 +597,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { // Ensure total difficulty exists and is valid for current header. if td := currentHeader.Difficulty; td == nil || td.Sign() < 1 { if !dryrun { - glog.V(logger.Warn).Infof("WARNING: Found current header #%d with invalid TD=%v\nAttempting chain reset with recovery...", currentHeader.Number, td) + glog.V(logger.Warn).Errorf("Found current header #%d with invalid TD=%v\nAttempting chain reset with recovery...", currentHeader.Number, td) return recoverOrReset() } return fmt.Errorf("invalid TD=%v for currentHeader=#%d", td, currentHeader.Number) @@ -616,7 +619,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { glog.V(logger.Info).Infof("Validating currentFastBlock: %v", self.currentFastBlock.Number()) if e := self.blockIsInvalid(self.currentFastBlock); e != nil { if !dryrun { - glog.V(logger.Warn).Infof("WARNING: Found unhealthy head fast block #%d [%x]: %v \nAttempting chain reset with recovery.", self.currentFastBlock.Number(), self.currentFastBlock.Hash(), e) + glog.V(logger.Warn).Errorf("Found unhealthy head fast block #%d (%x): %v \nAttempting chain reset with recovery.", self.currentFastBlock.Number(), self.currentFastBlock.Hash(), e) return recoverOrReset() } return fmt.Errorf("invalid currentFastBlock: %v", e) @@ -648,19 +651,19 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { aboveHighestApparentHead := highestApparentHead + 2048 if b := self.GetBlockByNumber(aboveHighestApparentHead); b != nil { - glog.V(logger.Warn).Infof("WARNING: Found block data beyond apparent head (head=%d, found=%d)", highestApparentHead, aboveHighestApparentHead) + glog.V(logger.Warn).Errorf("Found block data beyond apparent head (head=%d, found=%d)", highestApparentHead, aboveHighestApparentHead) return recoverOrReset() } // Check head block number congruent to hash. if b := self.GetBlockByNumber(self.currentBlock.NumberU64()); b != nil && b.Header() != nil && b.Header().Hash() != self.currentBlock.Hash() { - glog.V(logger.Error).Infof("WARNING: Found head block number and hash mismatch: number=%d, hash=%x", self.currentBlock.NumberU64(), self.currentBlock.Hash()) + glog.V(logger.Error).Errorf("Found head block number and hash mismatch: number=%d, hash=%x", self.currentBlock.NumberU64(), self.currentBlock.Hash()) return recoverOrReset() } // Check head header number congruent to hash. if h := self.hc.GetHeaderByNumber(self.hc.CurrentHeader().Number.Uint64()); h != nil && self.hc.GetHeader(h.Hash()) != h { - glog.V(logger.Error).Infof("WARNING: Found head header number and hash mismatch: number=%d, hash=%x", self.hc.CurrentHeader().Number.Uint64(), self.hc.CurrentHeader().Hash()) + glog.V(logger.Error).Errorf("Found head header number and hash mismatch: number=%d, hash=%x", self.hc.CurrentHeader().Number.Uint64(), self.hc.CurrentHeader().Hash()) return recoverOrReset() } @@ -672,7 +675,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { } // If the current header is behind head full block OR fast block, we should reset to the height of last OK header. if self.hc.CurrentHeader().Number.Cmp(highestCurrentBlockFastOrFull) < 0 { - glog.V(logger.Warn).Infof("WARNING: Found header height below block height, attempting reset with recovery...") + glog.V(logger.Error).Errorf("Found header height below block height, attempting reset with recovery...") return recoverOrReset() } @@ -688,9 +691,21 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { headerTd := self.GetTd(self.hc.CurrentHeader().Hash()) blockTd := self.GetTd(self.currentBlock.Hash()) fastTd := self.GetTd(self.currentFastBlock.Hash()) - glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.hc.CurrentHeader().Number, self.hc.CurrentHeader().Hash().Bytes()[:4], headerTd) - glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd) - glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd) + glog.V(logger.Warn).Infof("Last header: #%d [%x…] TD=%v", self.hc.CurrentHeader().Number, self.hc.CurrentHeader().Hash().Bytes()[:4], headerTd) + glog.V(logger.Warn).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd) + glog.V(logger.Warn).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd) + glog.D(logger.Warn).Infof("Local head header: #%s [%s…] TD=%s", + logger.ColorGreen(strconv.Itoa(int(self.hc.CurrentHeader().Number.Uint64()))), + logger.ColorGreen(self.hc.CurrentHeader().Hash().Hex()[:8]), + logger.ColorGreen(strconv.Itoa(int(headerTd.Uint64())))) + glog.D(logger.Warn).Infof("Local head full block: #%s [%s…] TD=%s", + logger.ColorGreen(strconv.Itoa(int(self.currentBlock.Number().Uint64()))), + logger.ColorGreen(self.currentBlock.Hash().Hex()[:8]), + logger.ColorGreen(strconv.Itoa(int(blockTd.Uint64())))) + glog.D(logger.Warn).Infof("Local head fast block: #%s [%s…] TD=%s", + logger.ColorGreen(strconv.Itoa(int(self.currentFastBlock.Number().Uint64()))), + logger.ColorGreen(self.currentFastBlock.Hash().Hex()[:8]), + logger.ColorGreen(strconv.Itoa(int(fastTd.Uint64())))) return nil } diff --git a/core/database_util.go b/core/database_util.go index 930ad3078..ce06606ed 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -229,7 +229,7 @@ func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { var receipt types.ReceiptForStorage err := rlp.DecodeBytes(data, &receipt) if err != nil { - glog.V(logger.Core).Infoln("GetReceipt err:", err) + glog.V(logger.Core).Errorln("GetReceipt err:", err) } return (*types.Receipt)(&receipt) } @@ -282,7 +282,7 @@ func WriteHeader(db ethdb.Database, header *types.Header) error { glog.Fatalf("failed to store header into database: %v", err) return err } - glog.V(logger.Debug).Infof("stored header #%v [%x…]", header.Number, header.Hash().Bytes()[:4]) + glog.V(logger.Detail).Infof("stored header #%v [%x…]", header.Number, header.Hash().Bytes()[:4]) return nil } @@ -297,7 +297,7 @@ func WriteBody(db ethdb.Database, hash common.Hash, body *types.Body) error { glog.Fatalf("failed to store block body into database: %v", err) return err } - glog.V(logger.Debug).Infof("stored block body [%x…]", hash.Bytes()[:4]) + glog.V(logger.Detail).Infof("stored block body [%x…]", hash.Bytes()[:4]) return nil } @@ -312,7 +312,7 @@ func WriteTd(db ethdb.Database, hash common.Hash, td *big.Int) error { glog.Fatalf("failed to store block total difficulty into database: %v", err) return err } - glog.V(logger.Debug).Infof("stored block total difficulty [%x…]: %v", hash.Bytes()[:4], td) + glog.V(logger.Detail).Infof("stored block total difficulty [%x…]: %v", hash.Bytes()[:4], td) return nil } @@ -348,7 +348,7 @@ func WriteBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Rece glog.Fatalf("failed to store block receipts into database: %v", err) return err } - glog.V(logger.Debug).Infof("stored block receipts [%x…]", hash.Bytes()[:4]) + glog.V(logger.Detail).Infof("stored block receipts [%x…]", hash.Bytes()[:4]) return nil } diff --git a/eth/backend.go b/eth/backend.go index 5ab4d6100..00eeec8c8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -24,6 +24,7 @@ import ( "math/big" "os" "path/filepath" + "strconv" "sync" "time" @@ -153,6 +154,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v, Chain Id: %v", ProtocolVersions, config.NetworkId, config.ChainConfig.GetChainID()) + glog.D(logger.Warn).Infof("Protocol Versions: %v, Network Id: %v, Chain Id: %v", logger.ColorGreen(fmt.Sprintf("%v", ProtocolVersions)), logger.ColorGreen(strconv.Itoa(config.NetworkId)), logger.ColorGreen(config.ChainConfig.GetChainID().String())) // Load up any custom genesis block if requested if config.Genesis != nil { @@ -226,17 +228,21 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if err != nil { return nil, err } - glog.V(logger.Info).Infof("Successfully wrote default ethereum mainnet genesis block: %s", genesis.Hash().Hex()) + glog.V(logger.Info).Infof("Successfully wrote default ethereum mainnet genesis block: %s", logger.ColorGreen(genesis.Hash().Hex())) + glog.D(logger.Warn).Infof("Wrote mainnet genesis block: %s", logger.ColorGreen(genesis.Hash().Hex())) } // Log genesis block information. + var genName string if fmt.Sprintf("%x", genesis.Hash()) == "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" { - glog.V(logger.Info).Infof("Successfully established morden testnet genesis block: \x1b[36m%s\x1b[39m", genesis.Hash().Hex()) + genName = "morden testnet" } else if fmt.Sprintf("%x", genesis.Hash()) == "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" { - glog.V(logger.Info).Infof("Successfully established mainnet genesis block: \x1b[36m%s\x1b[39m", genesis.Hash().Hex()) + genName = "mainnet" } else { - glog.V(logger.Info).Infof("Successfully established custom genesis block: \x1b[36m%s\x1b[39m", genesis.Hash().Hex()) + genName = "custom" } + glog.V(logger.Info).Infof("Successfully established %s genesis block: %s", genName, genesis.Hash().Hex()) + glog.D(logger.Warn).Infof("Genesis block: %s (%s)", logger.ColorGreen(genesis.Hash().Hex()), genName) if config.ChainConfig == nil { return nil, errors.New("missing chain config") diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 8b1b210ba..c59624f3c 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -21,7 +21,6 @@ import ( "crypto/rand" "errors" "fmt" - "log" "math" "math/big" "strings" @@ -269,7 +268,7 @@ func (d *Downloader) GetPeers() *peerSet { // RegisterPeer injects a new download peer into the set of block source to be // used for fetching hashes and blocks from. -func (d *Downloader) RegisterPeer(id string, version int, currentHead currentHeadRetrievalFn, +func (d *Downloader) RegisterPeer(id string, version int, name string, currentHead currentHeadRetrievalFn, getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn, getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error { @@ -285,9 +284,9 @@ func (d *Downloader) RegisterPeer(id string, version int, currentHead currentHea }() glog.V(logger.Detail).Infoln("Registering peer", id) - err = d.peers.Register(newPeer(id, version, currentHead, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)) + err = d.peers.Register(newPeer(id, version, name, currentHead, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)) if err != nil { - glog.V(logger.Error).Infoln("Register failed:", err) + glog.V(logger.Error).Errorf("Register failed:", err) return err } d.qosReduceConfidence() @@ -314,7 +313,7 @@ func (d *Downloader) UnregisterPeer(id string) error { glog.V(logger.Detail).Infoln("Unregistering peer", id) err = d.peers.Unregister(id) if err != nil { - glog.V(logger.Error).Infoln("Unregister failed:", err) + glog.V(logger.Error).Errorln("Unregister failed:", err) return err } d.queue.Revoke(id) @@ -336,22 +335,22 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode err := d.synchronise(id, head, td, mode) switch err { case nil: - log.Printf("peer %q sync complete", id) + glog.V(logger.Core).Infof("Peer %s: sync complete", id) return true case errBusy: - glog.V(logger.Debug).Info("sync busy") + glog.V(logger.Debug).Warnln("sync busy") case errTimeout, errBadPeer, errStallingPeer, errEmptyHashSet, errEmptyHeaderSet, errPeersUnavailable, errTooOld, errInvalidAncestor, errInvalidChain: - log.Printf("peer %q drop: %s", id, err) + glog.V(logger.Core).Warnf("Peer %s: drop: %s", id, err) d.dropPeer(id) case errCancelBlockFetch, errCancelHeaderFetch, errCancelBodyFetch, errCancelReceiptFetch, errCancelStateFetch, errCancelHeaderProcessing, errCancelContentProcessing: default: - log.Printf("peer %q sync: %s", id, err) + glog.V(logger.Core).Errorf("Peer %s: sync: %s", id, err) } return false @@ -425,23 +424,23 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode // syncWithPeer starts a block synchronization based on the hash chain from the // specified peer and head hash. func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err error) { - d.mux.Post(StartEvent{}) + d.mux.Post(StartEvent{p, hash, td}) defer func() { // reset on error if err != nil { - d.mux.Post(FailedEvent{err}) + d.mux.Post(FailedEvent{p, err}) } else { - d.mux.Post(DoneEvent{}) + d.mux.Post(DoneEvent{p, hash, td}) } }() glog.V(logger.Debug).Infof("Synchronising with the network using: %s [eth/%d]", p.id, p.version) defer func(start time.Time) { - glog.V(logger.Debug).Infof("Synchronisation terminated after %v", time.Since(start)) + glog.V(logger.Debug).Warnf("Synchronisation with [%v][eth/%d] terminated after %v", p, p.version, time.Since(start)) }(time.Now()) if p.version < 62 { - glog.Infof("download: peer %q protocol %d too old", p.id, p.version) + glog.V(logger.Debug).Warnf("download: peer %q protocol %d too old", p.id, p.version) return errBadPeer } @@ -588,6 +587,10 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) { head, _ := p.currentHead() go p.getRelHeaders(head, 1, 0, false) + // After waits for the duration to elapse and then sends the current time on the returned channel. + // It is equivalent to NewTimer(d).C. + // The underlying Timer is not recovered by the garbage collector until the timer fires. + // If efficiency is a concern, use NewTimer instead and call Timer.Stop if the timer is no longer needed. timeout := time.After(d.requestTTL()) for { select { @@ -667,19 +670,19 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { case packet := <-d.headerCh: // Discard anything not from the origin peer if packet.PeerId() != p.id { - glog.V(logger.Debug).Infof("Received headers from incorrect peer(%s)", packet.PeerId()) + glog.V(logger.Debug).Warnf("Received headers from incorrect peer(%s)", packet.PeerId()) break } // Make sure the peer actually gave something valid headers := packet.(*headerPack).headers if len(headers) == 0 { - glog.V(logger.Warn).Infof("%v: empty head header set", p) + glog.V(logger.Debug).Warnf("%v: empty head header set", p) return 0, errEmptyHeaderSet } // Make sure the peer's reply conforms to the request for i := 0; i < len(headers); i++ { if number := headers[i].Number.Int64(); number != from+int64(i)*16 { - glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ordering: requested %d, got %d", p, i, from+int64(i)*16, number) + glog.V(logger.Core).Warnf("%v: head header set (item %d) broke chain ordering: requested %d, got %d", p, i, from+int64(i)*16, number) return 0, errInvalidChain } } @@ -696,7 +699,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { // If every header is known, even future ones, the peer straight out lied about its head if number > height && i == limit-1 { - glog.V(logger.Warn).Infof("%v: lied about chain head: reported %d, found above %d", p, height, number) + glog.V(logger.Warn).Errorf("%v: lied about chain head: reported %d, found above %d", p, height, number) return 0, errStallingPeer } break @@ -704,7 +707,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { } case <-timeout: - glog.V(logger.Debug).Infof("%v: head header timeout", p) + glog.V(logger.Core).Warnf("%v: head header timeout", p) return 0, errTimeout case <-d.bodyCh: @@ -716,7 +719,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { // If the head fetch already found an ancestor, return if !common.EmptyHash(hash) { if int64(number) <= floor { - glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, number, hash, floor) + glog.V(logger.Warn).Errorf("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, number, hash[:4], floor) return 0, errInvalidAncestor } glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, number, hash) @@ -743,13 +746,13 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { case packer := <-d.headerCh: // Discard anything not from the origin peer if packer.PeerId() != p.id { - glog.V(logger.Debug).Infof("Received headers from incorrect peer(%s)", packer.PeerId()) + glog.V(logger.Debug).Warnf("Received headers from incorrect peer(%s)", packer.PeerId()) break } // Make sure the peer actually gave something valid headers := packer.(*headerPack).headers if len(headers) != 1 { - glog.V(logger.Debug).Infof("%v: invalid search header set (%d)", p, len(headers)) + glog.V(logger.Core).Warnf("%v: invalid search header set (%d)", p, len(headers)) return 0, errBadPeer } arrived = true @@ -761,13 +764,13 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { } header := d.getHeader(headers[0].Hash()) // Independent of sync mode, header surely exists if header.Number.Uint64() != check { - glog.V(logger.Debug).Infof("%v: non requested header #%d [%x…], instead of #%d", p, header.Number, header.Hash().Bytes()[:4], check) + glog.V(logger.Core).Warnf("%v: non requested header #%d [%x…], instead of #%d", p, header.Number, header.Hash().Bytes()[:4], check) return 0, errBadPeer } start = check case <-timeout: - glog.V(logger.Debug).Infof("%v: search header timeout", p) + glog.V(logger.Core).Warnf("%v: search header timeout", p) return 0, errTimeout case <-d.bodyCh: @@ -779,7 +782,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { } // Ensure valid ancestry and return if int64(start) <= floor { - glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, start, hash[:4], floor) + glog.V(logger.Warn).Warnf("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, start, hash[:4], floor) return 0, errInvalidAncestor } glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, start, hash[:4]) @@ -828,7 +831,7 @@ func (d *Downloader) fetchHeaders(p *peer, from uint64) error { case packet := <-d.headerCh: // Make sure the active peer is giving us the skeleton headers if packet.PeerId() != p.id { - glog.V(logger.Debug).Infof("Received skeleton headers from incorrect peer (%s)", packet.PeerId()) + glog.V(logger.Debug).Warnf("Received skeleton headers from incorrect peer (%s)", packet.PeerId()) break } metrics.DLHeaderTimer.UpdateSince(request) @@ -856,7 +859,7 @@ func (d *Downloader) fetchHeaders(p *peer, from uint64) error { if skeleton { filled, proced, err := d.fillHeaderSkeleton(from, headers) if err != nil { - glog.V(logger.Debug).Infof("%v: skeleton chain invalid: %v", p, err) + glog.V(logger.Core).Warnf("%v: skeleton chain invalid: %v", p, err) return errInvalidChain } headers = filled[proced:] @@ -876,7 +879,7 @@ func (d *Downloader) fetchHeaders(p *peer, from uint64) error { case <-timeout.C: // Header retrieval timed out, consider the peer bad and drop - glog.V(logger.Debug).Infof("%v: header request timed out", p) + glog.V(logger.Core).Warnf("%v: header request timed out", p) metrics.DLHeaderTimeouts.Mark(1) d.dropPeer(p.id) @@ -927,7 +930,11 @@ func (d *Downloader) fillHeaderSkeleton(from uint64, skeleton []*types.Header) ( d.queue.PendingHeaders, d.queue.InFlightHeaders, throttle, reserve, nil, fetch, capacity, d.peers.HeaderIdlePeers, setIdle, "Header") - glog.V(logger.Debug).Infof("Skeleton fill terminated: %v", err) + if err != nil { + glog.V(logger.Core).Warnf("Skeleton fill terminated. err=%v", err) + } else { + glog.V(logger.Core).Infof("Skeleton fill terminated. err=%v", err) + } filled, proced := d.queue.RetrieveHeaders() return filled, proced, err @@ -953,7 +960,12 @@ func (d *Downloader) fetchBodies(from uint64) error { d.queue.PendingBlocks, d.queue.InFlightBlocks, d.queue.ShouldThrottleBlocks, d.queue.ReserveBodies, d.bodyFetchHook, fetch, capacity, d.peers.BodyIdlePeers, setIdle, "Body") - glog.V(logger.Debug).Infof("Block body download terminated: %v", err) + if err != nil { + glog.V(logger.Core).Warnf("Block body download terminated. err=%v", err) + } else { + glog.V(logger.Core).Infof("Block body download terminated. err=%v", err) + } + return err } @@ -977,7 +989,12 @@ func (d *Downloader) fetchReceipts(from uint64) error { d.queue.PendingReceipts, d.queue.InFlightReceipts, d.queue.ShouldThrottleReceipts, d.queue.ReserveReceipts, d.receiptFetchHook, fetch, capacity, d.peers.ReceiptIdlePeers, setIdle, "Receipt") - glog.V(logger.Debug).Infof("Receipt download terminated: %v", err) + if err != nil { + glog.V(logger.Core).Warnf("Receipt download terminated. err=%v", err) + } else { + glog.V(logger.Core).Infof("Receipt download terminated. err=%v", err) + } + return err } @@ -993,12 +1010,12 @@ func (d *Downloader) fetchNodeData() error { return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(err error, delivered int) { // If the peer returned old-requested data, forgive if err == trie.ErrNotRequested { - glog.V(logger.Info).Infof("peer %s: replied to stale state request, forgiving", packet.PeerId()) + glog.V(logger.Info).Warnf("peer %s: replied to stale state request, forgiving", packet.PeerId()) return } if err != nil { // If the node data processing failed, the root hash is very wrong, abort - glog.V(logger.Error).Infof("peer %d: state processing failed: %v", packet.PeerId(), err) + glog.V(logger.Error).Errorf("peer %d: state processing failed: %v", packet.PeerId(), err) d.cancel() return } @@ -1033,7 +1050,12 @@ func (d *Downloader) fetchNodeData() error { d.queue.PendingNodeData, d.queue.InFlightNodeData, throttle, reserve, nil, fetch, capacity, d.peers.NodeDataIdlePeers, setIdle, "State") - glog.V(logger.Debug).Infof("Node state data download terminated: %v", err) + if err != nil { + glog.V(logger.Core).Warnf("Node state data download terminated. err=%v", err) + } else { + glog.V(logger.Core).Infof("Node state data download terminated. err=%v", err) + } + return err } @@ -1086,6 +1108,7 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv // Deliver the received chunk of data and check chain validity accepted, err := deliver(packet) if err == errInvalidChain { + glog.V(logger.Core).Warnf("%s: %s delivery failed: err=%v", peer, strings.ToLower(kind), err) return err } // Unless a peer delivered something completely else than requested (usually @@ -1143,11 +1166,12 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv // The reason the minimum threshold is 2 is because the downloader tries to estimate the bandwidth // and latency of a peer separately, which requires pushing the measures capacity a bit and seeing // how response times reacts, to it always requests one more than the minimum (i.e. min 2). - if fails > 2 { - glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind)) + // FIXME?: Is this backwards? + if fails <= 2 { + glog.V(logger.Detail).Warnf("%s: %s delivery timeout", peer, strings.ToLower(kind)) setIdle(peer, 0) } else { - glog.V(logger.Debug).Infof("%s: stalling %s delivery, dropping", peer, strings.ToLower(kind)) + glog.V(logger.Debug).Warnf("%s: stalling %s delivery, dropping", peer, strings.ToLower(kind)) d.dropPeer(pid) } } @@ -1233,7 +1257,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { } lastHeader, lastFastBlock, lastBlock := d.headHeader().Number, d.headFastBlock().Number(), d.headBlock().Number() d.rollback(hashes) - glog.V(logger.Warn).Infof("Rolled back %d headers (LH: %d->%d, FB: %d->%d, LB: %d->%d)", + glog.V(logger.Warn).Warnf("Rolled back %d headers (LH: %d->%d, FB: %d->%d, LB: %d->%d)", len(hashes), lastHeader, d.headHeader().Number, lastFastBlock, d.headFastBlock().Number(), lastBlock, d.headBlock().Number()) // If we're already past the pivot point, this could be an attack, thread carefully @@ -1242,7 +1266,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { if d.fsPivotFails == 0 { for _, header := range rollback { if header.Number.Uint64() == pivot { - glog.V(logger.Warn).Infof("Fast-sync critical section failure, locked pivot to header #%d [%x…]", pivot, header.Hash().Bytes()[:4]) + glog.V(logger.Warn).Warnf("Fast-sync critical section failure, locked pivot to header #%d [%x…]", pivot, header.Hash().Bytes()[:4]) d.fsPivotLock = header } } @@ -1337,7 +1361,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { if n > 0 { rollback = append(rollback, chunk[:n]...) } - glog.V(logger.Debug).Infof("invalid header #%d [%x…]: %v", chunk[n].Number, chunk[n].Hash().Bytes()[:4], err) + glog.V(logger.Debug).Warnf("invalid header #%d [%x…]: %v", chunk[n].Number, chunk[n].Hash().Bytes()[:4], err) return errInvalidChain } // All verifications passed, store newly found uncertain headers @@ -1349,7 +1373,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { // If we're fast syncing and just pulled in the pivot, make sure it's the one locked in if d.mode == FastSync && d.fsPivotLock != nil && chunk[0].Number.Uint64() <= pivot && chunk[len(chunk)-1].Number.Uint64() >= pivot { if pivot := chunk[int(pivot-chunk[0].Number.Uint64())]; pivot.Hash() != d.fsPivotLock.Hash() { - glog.V(logger.Warn).Infof("Pivot doesn't match locked in version: have #%v [%x…], want #%v [%x…]", pivot.Number, pivot.Hash().Bytes()[:4], d.fsPivotLock.Number, d.fsPivotLock.Hash().Bytes()[:4]) + glog.V(logger.Warn).Warnf("Pivot doesn't match locked in version: have #%v [%x…], want #%v [%x…]", pivot.Number, pivot.Hash().Bytes()[:4], d.fsPivotLock.Number, d.fsPivotLock.Hash().Bytes()[:4]) return errInvalidChain } } @@ -1366,7 +1390,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { // Otherwise insert the headers for content retrieval inserts := d.queue.Schedule(chunk, origin) if len(inserts) != len(chunk) { - glog.V(logger.Debug).Infof("stale headers") + glog.V(logger.Debug).Warnf("stale headers") return errBadPeer } } @@ -1441,7 +1465,7 @@ func (d *Downloader) processContent() error { index, err = d.insertBlocks(blocks) } if err != nil { - glog.V(logger.Debug).Infof("Result #%d [%x…] processing failed: %v", results[index].Header.Number, results[index].Header.Hash().Bytes()[:4], err) + glog.V(logger.Debug).Errorf("Result #%d [%x…] processing failed: %v", results[index].Header.Number, results[index].Header.Hash().Bytes()[:4], err) return errInvalidChain } // Shift the results to the next batch @@ -1501,6 +1525,7 @@ func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, m func (d *Downloader) qosTuner() { for { // Retrieve the current median RTT and integrate into the previoust target RTT + // https://en.wikipedia.org/wiki/Round-trip_delay_time rtt := time.Duration(float64(1-qosTuningImpact)*float64(atomic.LoadUint64(&d.rttEstimate)) + qosTuningImpact*float64(d.peers.medianRTT())) atomic.StoreUint64(&d.rttEstimate, uint64(rtt)) diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 7b5b1fd09..7b8467e1b 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -423,15 +423,16 @@ func (dl *downloadTester) newPeer(id string, version int, hashes []common.Hash, func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Hash, headers map[common.Hash]*types.Header, blocks map[common.Hash]*types.Block, receipts map[common.Hash]types.Receipts, delay time.Duration) error { dl.lock.Lock() defer dl.lock.Unlock() + name := "slow peer" var err error switch version { case 62: - err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil) + err = dl.downloader.RegisterPeer(id, version, name, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil) case 63: - err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay)) + err = dl.downloader.RegisterPeer(id, version, name, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay)) case 64: - err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay)) + err = dl.downloader.RegisterPeer(id, version, name, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay)) } if err == nil { // Assign the owned hashes, headers and blocks to the peer (deep copy) diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go index b1feb3d07..386ff9e3c 100644 --- a/eth/downloader/peer.go +++ b/eth/downloader/peer.go @@ -87,12 +87,13 @@ type peer struct { getNodeData stateFetcherFn // [eth/63] Method to retrieve a batch of state trie data version int // Eth protocol version number to switch strategies + name string lock sync.RWMutex } // newPeer create a new downloader peer, with specific hash and block retrieval // mechanisms. -func newPeer(id string, version int, currentHead currentHeadRetrievalFn, +func newPeer(id string, version int, name string, currentHead currentHeadRetrievalFn, getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn, getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer { return &peer{ @@ -108,6 +109,7 @@ func newPeer(id string, version int, currentHead currentHeadRetrievalFn, getNodeData: getNodeData, version: version, + name: name, } } @@ -337,7 +339,7 @@ func (p *peer) String() string { p.lock.RLock() defer p.lock.RUnlock() - return fmt.Sprintf("Peer %s [%s]", p.id, strings.Join([]string{ + return fmt.Sprintf("Peer id=%s eth/%d [%s] [%s]", p.id, p.version, p.name, strings.Join([]string{ fmt.Sprintf("hs %3.2f/s", p.headerThroughput), fmt.Sprintf("bs %3.2f/s", p.blockThroughput), fmt.Sprintf("rs %3.2f/s", p.receiptThroughput), diff --git a/eth/handler.go b/eth/handler.go index 50e010d6c..129bf17f1 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -106,11 +106,13 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, } // Figure out whether to allow fast sync or not if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { - glog.V(logger.Warn).Infof("WARNING: Blockchain not empty, fast sync disabled") + glog.V(logger.Warn).Infoln("WARNING: Blockchain not empty, fast sync disabled") + glog.D(logger.Warn).Warnln("Blockchain not empty. Fast sync disabled.") fastSync = false } if fastSync { manager.fastSync = uint32(1) + glog.D(logger.Warn).Infoln("Fast sync mode enabled.") } // Initiate a sub-protocol for every implemented version we can handle manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions)) @@ -261,14 +263,14 @@ func (pm *ProtocolManager) handle(p *peer) error { // Register the peer locally glog.V(logger.Detail).Infof("%v: adding peer", p) if err := pm.peers.Register(p); err != nil { - glog.V(logger.Error).Infof("%v: addition failed: %v", p, err) + glog.V(logger.Error).Errorf("%v: addition failed: %v", p, err) return err } defer pm.removePeer(p.id) // Register the peer in the downloader. If the downloader considers it banned, we disconnect // TODO Causing error in tests - if err := pm.downloader.RegisterPeer(p.id, p.version, p.Head, + if err := pm.downloader.RegisterPeer(p.id, p.version, p.Name(), p.Head, p.RequestHeadersByHash, p.RequestHeadersByNumber, p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil { return err @@ -432,7 +434,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if len(headers) > 0 || !filter { err := pm.downloader.DeliverHeaders(p.id, headers) if err != nil { - glog.V(logger.Debug).Infoln(err) + glog.V(logger.Debug).Infoln("peer", p.id, err) } } @@ -524,7 +526,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } // Deliver all to the downloader if err := pm.downloader.DeliverNodeData(p.id, data); err != nil { - glog.V(logger.Debug).Infof("failed to deliver node state data: %v", err) + glog.V(logger.Core).Warnf("failed to deliver node state data: %v", err) } case p.version >= eth63 && msg.Code == GetReceiptsMsg: @@ -571,7 +573,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } // Deliver all to the downloader if err := pm.downloader.DeliverReceipts(p.id, receipts); err != nil { - glog.V(logger.Debug).Infof("failed to deliver receipts: %v", err) + glog.V(logger.Core).Warnf("failed to deliver receipts: %v", err) } case msg.Code == NewBlockHashesMsg: @@ -644,7 +646,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if _, td := p.Head(); trueTD.Cmp(td) > 0 { glog.V(logger.Debug).Infof("Peer %s: setting head: tdWas=%v trueTD=%v", p.id, td, trueTD) p.SetHead(trueHead, trueTD) - + // Schedule a sync if above ours. Note, this will not fire a sync for a gap of // a singe block (as the true TD is below the propagated block), however this // scenario should easily be covered by the fetcher. diff --git a/eth/peer.go b/eth/peer.go index 83b6b7e84..0dfa258f2 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -213,20 +213,20 @@ func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, rever // RequestBodies fetches a batch of blocks' bodies corresponding to the hashes // specified. func (p *peer) RequestBodies(hashes []common.Hash) error { - glog.V(logger.Debug).Infof("%v fetching %d block bodies", p, len(hashes)) + glog.V(logger.Debug).Infof("%v fetching %d block bodies first=%s", p, len(hashes), hashes[0].Hex()) return p2p.Send(p.rw, GetBlockBodiesMsg, hashes) } // RequestNodeData fetches a batch of arbitrary data from a node's known state // data, corresponding to the specified hashes. func (p *peer) RequestNodeData(hashes []common.Hash) error { - glog.V(logger.Debug).Infof("%v fetching %v state data", p, len(hashes)) + glog.V(logger.Debug).Infof("%v fetching %v state data first=%s", p, len(hashes), hashes[0].Hex()) return p2p.Send(p.rw, GetNodeDataMsg, hashes) } // RequestReceipts fetches a batch of transaction receipts from a remote node. func (p *peer) RequestReceipts(hashes []common.Hash) error { - glog.V(logger.Debug).Infof("%v fetching %v receipts", p, len(hashes)) + glog.V(logger.Debug).Infof("%v fetching %v receipts first=%s", p, len(hashes), hashes[0].Hex()) return p2p.Send(p.rw, GetReceiptsMsg, hashes) } diff --git a/ethdb/database.go b/ethdb/database.go index 61122eb0e..4363e7b8a 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -19,6 +19,8 @@ package ethdb import ( "path/filepath" + "strconv" + "github.com/ethereumproject/go-ethereum/logger" "github.com/ethereumproject/go-ethereum/logger/glog" "github.com/syndtr/goleveldb/leveldb" @@ -61,6 +63,7 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { handles = 16 } glog.V(logger.Info).Infof("Allotted %dMB cache and %d file handles to %s", cache, handles, file) + glog.D(logger.Warn).Infof("Allotted %s cache and %s file handles to %s", logger.ColorGreen(strconv.Itoa(cache)+"MB"), logger.ColorGreen(strconv.Itoa(handles)), logger.ColorGreen(file)) // Open the db and recover any potential corruptions db, err := leveldb.OpenFile(file, &opt.Options{ diff --git a/node/node.go b/node/node.go index bda15f0fe..7a1afc5ba 100644 --- a/node/node.go +++ b/node/node.go @@ -293,6 +293,7 @@ func (n *Node) startIPC(apis []rpc.API) error { } go func() { glog.V(logger.Info).Infof("IPC endpoint opened: %s", n.ipcEndpoint) + glog.D(logger.Warn).Infof("IPC endpoint opened: %s", logger.ColorGreen(n.ipcEndpoint)) for { conn, err := listener.Accept() @@ -325,6 +326,7 @@ func (n *Node) stopIPC() { n.ipcListener = nil glog.V(logger.Info).Infof("IPC endpoint closed: %s", n.ipcEndpoint) + glog.D(logger.Warn).Warnf("IPC endpoint closed: %s", n.ipcEndpoint) } if n.ipcHandler != nil { n.ipcHandler.Stop() @@ -363,6 +365,7 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors } go rpc.NewHTTPServer(cors, handler).Serve(listener) glog.V(logger.Info).Infof("HTTP endpoint opened: http://%s", endpoint) + glog.D(logger.Warn).Infof("HTTP endpoint: http://%s", logger.ColorGreen(endpoint)) // All listeners booted successfully n.httpEndpoint = endpoint @@ -380,6 +383,7 @@ func (n *Node) stopHTTP() { n.httpListener = nil glog.V(logger.Info).Infof("HTTP endpoint closed: http://%s", n.httpEndpoint) + glog.D(logger.Warn).Warnf("HTTP endpoint closed: http://%s", n.httpEndpoint) } if n.httpHandler != nil { n.httpHandler.Stop() @@ -418,6 +422,7 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig } go rpc.NewWSServer(wsOrigins, handler).Serve(listener) glog.V(logger.Info).Infof("WebSocket endpoint opened: ws://%s", endpoint) + glog.D(logger.Warn).Infof("WebSocket endpoint opened: ws://%s", logger.ColorGreen(endpoint)) // All listeners booted successfully n.wsEndpoint = endpoint @@ -435,6 +440,7 @@ func (n *Node) stopWS() { n.wsListener = nil glog.V(logger.Info).Infof("WebSocket endpoint closed: ws://%s", n.wsEndpoint) + glog.V(logger.Warn).Warnf("WebSocket endpoint closed: ws://%s", n.wsEndpoint) } if n.wsHandler != nil { n.wsHandler.Stop() diff --git a/p2p/dial.go b/p2p/dial.go index 8553442b8..e4fa7861e 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -260,7 +260,7 @@ func (t *dialTask) resolve(srv *Server) bool { // dial performs the actual connection attempt. func (t *dialTask) dial(srv *Server, dest *discover.Node) bool { addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)} - glog.V(logger.Debug).Infof("dial tcp %v (%x)\n", addr, dest.ID[:6]) + glog.V(logger.Detail).Infof("dial tcp %v (%x)\n", addr, dest.ID[:6]) fd, err := srv.Dialer.Dial("tcp", addr.String()) if err != nil { glog.V(logger.Detail).Infof("%v", err) diff --git a/p2p/discover/udp.go b/p2p/discover/udp.go index 44ad8384a..516f37365 100644 --- a/p2p/discover/udp.go +++ b/p2p/discover/udp.go @@ -247,6 +247,7 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP return nil, err } glog.V(logger.Info).Infoln("Listening,", tab.self) + glog.D(logger.Warn).Infoln("UDP listening. Client enode:", logger.ColorGreen(tab.self.String())) return tab, nil } @@ -338,9 +339,7 @@ func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node var okNodes []*Node for _, n := range nodes { if isReserved(n.IP) { - if glog.V(logger.Debug) { - glog.Infof("%v: removing from neighbors: toaddr: %v, id: %v, ip: %v", errReservedAddress, toaddr, n.ID, n.IP) - } + glog.V(logger.Detail).Warnf("%v: removing from neighbors: toaddr: %v, id: %v, ip: %v", errReservedAddress, toaddr, n.ID, n.IP) continue } okNodes = append(okNodes, n) diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index 7b3a58ea3..3fd4f0cf6 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "net" + "strconv" "strings" "sync" "time" @@ -106,9 +107,10 @@ func Map(m Interface, c chan struct{}, protocol string, extport, intport int, na m.DeleteMapping(protocol, extport, intport) }() if err := m.AddMapping(protocol, intport, extport, name, mapTimeout); err != nil { - glog.V(logger.Debug).Infof("Network port %s:%d could not be mapped: %v\n", protocol, intport, err) + glog.V(logger.Warn).Errorf("Network port %s:%d could not be mapped: %v\n", protocol, intport, err) } else { glog.V(logger.Info).Infof("Mapped network port %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) + glog.D(logger.Warn).Infof("Mapped network port %s:%s -> %s (%s) using %s\n", logger.ColorGreen(protocol), logger.ColorGreen(strconv.Itoa(extport)), logger.ColorGreen(strconv.Itoa(intport)), name, m) } for { select { @@ -119,7 +121,7 @@ func Map(m Interface, c chan struct{}, protocol string, extport, intport int, na case <-refresh.C: glog.V(logger.Detail).Infof("Refresh port mapping %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) if err := m.AddMapping(protocol, intport, extport, name, mapTimeout); err != nil { - glog.V(logger.Debug).Infof("Network port %s:%d could not be mapped: %v\n", protocol, intport, err) + glog.V(logger.Warn).Errorf("Network port %s:%d could not be mapped: %v\n", protocol, intport, err) } refresh.Reset(mapUpdateInterval) } diff --git a/p2p/peer.go b/p2p/peer.go index dd657814e..a10db8c05 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -118,7 +118,7 @@ func (p *Peer) Disconnect(reason DiscReason) { // String implements fmt.Stringer. func (p *Peer) String() string { - return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr()) + return fmt.Sprintf("Peer id=%x addr=%v name=%s", p.rw.id[:8], p.RemoteAddr(), p.Name()) } func newPeer(conn *conn, protocols []Protocol) *Peer { diff --git a/p2p/rlpx.go b/p2p/rlpx.go index 695b385c5..04d124a57 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -140,7 +140,7 @@ func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake, } if msg.Code == discMsg { // Disconnect before protocol handshake is valid according to the - // spec and we send it ourself if the posthanshake checks fail. + // spec and we send it ourself if the posthandshake checks fail. // We can't return the reason directly, though, because it is echoed // back otherwise. Wrap it in a string instead. var reason [1]DiscReason diff --git a/p2p/server.go b/p2p/server.go index a200bd807..7966555e6 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -308,6 +308,7 @@ func (srv *Server) Start() (err error) { } srv.running = true glog.V(logger.Info).Infoln("Starting Server") + glog.D(logger.Warn).Infoln("Starting server...") // static fields if srv.PrivateKey == nil { @@ -358,6 +359,7 @@ func (srv *Server) Start() (err error) { } if srv.NoDial && srv.ListenAddr == "" { glog.V(logger.Warn).Infoln("I will be kind-of useless, neither dialing nor listening.") + glog.V(logger.Warn).Warnln("Server will be kind of useless, neither dialing nor listening.") } srv.loopWG.Add(1) @@ -611,36 +613,36 @@ func (srv *Server) setupConn(fd net.Conn, flags connFlag, dialDest *discover.Nod // Run the encryption handshake. var err error if c.id, err = c.doEncHandshake(srv.PrivateKey, dialDest); err != nil { - glog.V(logger.Debug).Infof("%v faild enc handshake: %v", c, err) + glog.V(logger.Debug).Warnf("%v faild enc handshake: %v", c, err) c.close(err) return } // For dialed connections, check that the remote public key matches. if dialDest != nil && c.id != dialDest.ID { c.close(DiscUnexpectedIdentity) - glog.V(logger.Debug).Infof("%v dialed identity mismatch, want %x", c, dialDest.ID[:8]) + glog.V(logger.Debug).Warnf("%v dialed identity mismatch, want %x", c, dialDest.ID[:8]) return } if err := srv.checkpoint(c, srv.posthandshake); err != nil { - glog.V(logger.Debug).Infof("%v failed checkpoint posthandshake: %v", c, err) + glog.V(logger.Debug).Warnf("%v failed checkpoint posthandshake: %v", c, err) c.close(err) return } // Run the protocol handshake phs, err := c.doProtoHandshake(srv.ourHandshake) if err != nil { - glog.V(logger.Debug).Infof("%v failed proto handshake: %v", c, err) + glog.V(logger.Debug).Warnf("%v failed proto handshake: %v", c, err) c.close(err) return } if phs.ID != c.id { - glog.V(logger.Debug).Infof("%v wrong proto handshake identity: %x", c, phs.ID[:8]) + glog.V(logger.Debug).Warnf("%v wrong proto handshake identity: %x", c, phs.ID[:8]) c.close(DiscUnexpectedIdentity) return } c.caps, c.name = phs.Caps, phs.Name if err := srv.checkpoint(c, srv.addpeer); err != nil { - glog.V(logger.Debug).Infof("%v failed checkpoint addpeer: %v", c, err) + glog.V(logger.Debug).Warnf("%v failed checkpoint addpeer: %v", c, err) c.close(err) return } From c667f25bc60e7b984f1d9da6c031a8f4693a1f2d Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 12:19:09 -0600 Subject: [PATCH 05/60] problem: chain identity parsed duplicitously making chainIsMorden fail when --testnet used solution: refactor --- cmd/geth/cmd.go | 2 +- cmd/geth/flag.go | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index e10e60637..5046877cd 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -581,7 +581,7 @@ func rollback(ctx *cli.Context) error { } // dumpChainConfig exports chain configuration based on context to JSON file. -// It is not compatible with --chain-config flag; it is intended to move from flags -> file, +// It is not compatible with --chain flag; it is intended to move from default configs -> file, // and not the other way around. func dumpChainConfig(ctx *cli.Context) error { diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index 434c60454..13cef8758 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -113,11 +113,6 @@ var ( // '--testnet' (legacy), or // '--chain=morden|testnet' func chainIsMorden(ctx *cli.Context) bool { - if ctx.GlobalIsSet(aliasableName(TestNetFlag.Name, ctx)) && ctx.GlobalIsSet(aliasableName(ChainIdentityFlag.Name, ctx)) { - glog.Fatalf(`%v: used redundant/conflicting flags: %v, %v - Please use one or the other, but not both.`, ErrInvalidFlag, aliasableName(TestNetFlag.Name, ctx), aliasableName(ChainIdentityFlag.Name, ctx)) - return false - } return ctx.GlobalBool(aliasableName(TestNetFlag.Name, ctx)) || chainIdentitiesMorden[ctx.GlobalString(aliasableName(ChainIdentityFlag.Name, ctx))] } @@ -151,6 +146,13 @@ func mustMakeChainIdentity(ctx *cli.Context) (identity string) { if cacheChainIdentity != "" { return cacheChainIdentity } + + if ctx.GlobalIsSet(aliasableName(TestNetFlag.Name, ctx)) && ctx.GlobalIsSet(aliasableName(ChainIdentityFlag.Name, ctx)) { + glog.Fatalf(`%v: used redundant/conflicting flags: --%v, --%v + Please use one or the other, but not both.`, ErrInvalidFlag, aliasableName(TestNetFlag.Name, ctx), aliasableName(ChainIdentityFlag.Name, ctx)) + return "" + } + defer func() { cacheChainIdentity = identity }() From ea18863cda9930e900dee389f31d434ab957423c Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 13:54:00 -0600 Subject: [PATCH 06/60] problem: bats tests failing solution: fix tests (adjust bats tests to new syntax/+logging cases - move datadir schema migration logic to cli.Before because it requires that the default log directory (in datadir) have not been established when checking for non/existing datadirs. --- cmd/geth/chainconfig.bats | 2 +- cmd/geth/cli.bats | 66 +++++++++++++++++++++++---------------- cmd/geth/flag.go | 18 ----------- cmd/geth/main.go | 24 ++++++++++++++ cmd/geth/rollback.bats | 9 +++--- 5 files changed, 69 insertions(+), 50 deletions(-) diff --git a/cmd/geth/chainconfig.bats b/cmd/geth/chainconfig.bats index 0249987d0..3cd8c4535 100644 --- a/cmd/geth/chainconfig.bats +++ b/cmd/geth/chainconfig.bats @@ -281,7 +281,7 @@ teardown() { run $GETH_CMD --data-dir $DATA_DIR --chain kitty --bootnodes=enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303 console echo "$output" [ "$status" -eq 0 ] - [[ "$output" == *"WARNING"* ]] + [[ "$output" == *"Overwriting external bootnodes"* ]] } # Test fails to load invalid chain configuration from testdata/ JSON file. diff --git a/cmd/geth/cli.bats b/cmd/geth/cli.bats index 5c8a3a3e3..95489f99c 100644 --- a/cmd/geth/cli.bats +++ b/cmd/geth/cli.bats @@ -41,9 +41,9 @@ teardown() { echo "$output" [ "$status" -eq 0 ] - [[ "$output" == *"Starting"* ]] - [[ "$output" == *"Blockchain DB Version: "* ]] - [[ "$output" == *"Starting Server"* ]] + [[ "$output" == *"Blockchain"* ]] + [[ "$output" == *"Local head"* ]] + [[ "$output" == *"Starting server"* ]] } @test "displays help with invalid flag and valid command" { @@ -75,7 +75,7 @@ teardown() { @test "custom testnet subdir --testnet --chain=morden2 | exit !=0" { run $GETH_CMD --data-dir $DATA_DIR --testnet --chain=morden2 --maxpeers 0 --nodiscover --nat none --ipcdisable --exec 'exit' console echo "$output" - [ "$status" -ne 0 ] + [ "$status" -ne 0 ] [[ "$output" == *"invalid flag "* ]] } @@ -109,57 +109,67 @@ teardown() { # # ipc-path/ipcpath run $GETH_CMD --data-dir $DATA_DIR --ipc-path abc.ipc console [ "$status" -eq 0 ] - [[ "$output" == *"IPC endpoint opened: $DATA_DIR/mainnet/abc.ipc"* ]] + echo "$output" + [[ "$output" == *"IPC endpoint opened"* ]] + [[ "$output" == *"$DATA_DIR/mainnet/abc.ipc"* ]] run $GETH_CMD --data-dir $DATA_DIR --ipcpath $DATA_DIR/mainnet/abc.ipc console [ "$status" -eq 0 ] - [[ "$output" == *"IPC endpoint opened: $DATA_DIR/mainnet/abc.ipc"* ]] + echo "$output" + [[ "$output" == *"IPC endpoint opened"* ]] + [[ "$output" == *"$DATA_DIR/mainnet/abc.ipc"* ]] run $GETH_CMD --data-dir $DATA_DIR --ipc-path $DATA_DIR/mainnet/abc console [ "$status" -eq 0 ] - [[ "$output" == *"IPC endpoint opened: $DATA_DIR/mainnet/abc"* ]] + echo "$output" + [[ "$output" == *"IPC endpoint opened"* ]] + [[ "$output" == *"$DATA_DIR/mainnet/abc"* ]] run $GETH_CMD --data-dir $DATA_DIR --ipcpath $DATA_DIR/mainnet/abc console [ "$status" -eq 0 ] - [[ "$output" == *"IPC endpoint opened: $DATA_DIR/mainnet/abc"* ]] - + echo "$output" + [[ "$output" == *"IPC endpoint opened"* ]] + [[ "$output" == *"$DATA_DIR/mainnet/abc"* ]] + } # ... assuming that if two work, the rest will work. @test "aliasing hyphenated flags: --no-discover==--nodiscover, --ipc-disable==--ipcdisable | exit 0" { - old_command_names=(nodiscover ipcdisable) + old_command_names=(nodiscover ipcdisable) new_command_names=(no-discover ipc-disable) - + for var in "${old_command_names[@]}" do # hardcode --datadir/--data-dir run $GETH_CMD --datadir $DATA_DIR --$var --exec 'exit' console [ "$status" -eq 0 ] - [[ "$output" == *"Starting"* ]] - [[ "$output" == *"Blockchain DB Version: "* ]] - [[ "$output" == *"Starting Server"* ]] + [[ "$output" == *"Blockchain"* ]] + [[ "$output" == *"Local head"* ]] + [[ "$output" == *"Starting server"* ]] done for var in "${new_command_names[@]}" do run $GETH_CMD --data-dir $DATA_DIR --$var --exec 'exit' console [ "$status" -eq 0 ] - [[ "$output" == *"Starting"* ]] - [[ "$output" == *"Blockchain DB Version: "* ]] - [[ "$output" == *"Starting Server"* ]] + [[ "$output" == *"Blockchain"* ]] + [[ "$output" == *"Local head"* ]] + [[ "$output" == *"Starting server"* ]] done } @test "--cache 16 | exit 0" { run $GETH_CMD --data-dir $DATA_DIR --cache 17 console [ "$status" -eq 0 ] - [[ "$output" == *"Allotted 17MB cache"* ]] + [[ "$output" == *"Allotted"* ]] + [[ "$output" == *"17MB"* ]] + [[ "$output" == *"cache"* ]] } # Test `dump` command. # All tests copy testdata/testdatadir to $DATA_DIR to ensure we're consistently testing against a not-only-init'ed chaindb. @test "dump [noargs] | exit >0" { - cp -a $BATS_TEST_DIRNAME/../../cmd/geth/testdata/testdatadir/. $DATA_DIR/ + cp -a $BATS_TEST_DIRNAME/../../cmd/geth/testdata/testdatadir/. $DATA_DIR/ run $GETH_CMD --data-dir $DATA_DIR dump echo "$output" [ "$status" -gt 0 ] @@ -174,8 +184,8 @@ teardown() { [[ "$output" == *"balance"* ]] [[ "$output" == *"accounts"* ]] [[ "$output" == *"d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"* ]] # block state root - [[ "$output" == *"ffec0913c635baca2f5e57a37aa9fb7b6c9b6e26"* ]] # random hex address existing in genesis - [[ "$output" == *"253319000000000000000"* ]] # random address balance existing in genesis + [[ "$output" == *"ffec0913c635baca2f5e57a37aa9fb7b6c9b6e26"* ]] # random hex address existing in genesis + [[ "$output" == *"253319000000000000000"* ]] # random address balance existing in genesis } @test "dump 0 fff7ac99c8e4feb60c9750054bdc14ce1857f181 | exit 0" { cp -a $BATS_TEST_DIRNAME/../../cmd/geth/testdata/testdatadir/. $DATA_DIR/ @@ -198,7 +208,7 @@ teardown() { [[ "$output" == *"balance"* ]] [[ "$output" == *"accounts"* ]] [[ "$output" == *"d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"* ]] # block 0 state root - + [[ "$output" == *"fff7ac99c8e4feb60c9750054bdc14ce1857f181"* ]] # hex address [[ "$output" == *"1000000000000000000000"* ]] # address balance @@ -214,10 +224,10 @@ teardown() { [[ "$output" == *"root"* ]] # block state root [[ "$output" == *"balance"* ]] [[ "$output" == *"accounts"* ]] - + [[ "$output" == *"d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"* ]] # block 0 state root [[ "$output" == *"d67e4d450343046425ae4271474353857ab860dbc0a1dde64b41b5cd3a532bf3"* ]] # block 1 state root - + [[ "$output" == *"fff7ac99c8e4feb60c9750054bdc14ce1857f181"* ]] # hex address [[ "$output" == *"1000000000000000000000"* ]] # address balance @@ -232,9 +242,9 @@ teardown() { [[ "$output" == *"root"* ]] # block state root [[ "$output" == *"balance"* ]] [[ "$output" == *"accounts"* ]] - + [[ "$output" == *"d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"* ]] # block 0 state root - + [[ "$output" == *"fff7ac99c8e4feb60c9750054bdc14ce1857f181"* ]] # hex address [[ "$output" == *"1000000000000000000000"* ]] # address balance @@ -277,6 +287,7 @@ teardown() { # When I tried using heredoc and other more elegant solutions than line-by-line checking. # Chain Configuration Genesis + echo "$output" [[ "$output" == *"mainnet"* ]] [[ "$output" == *"Ethereum Classic Mainnet"* ]] [[ "$output" == *"Genesis"* ]] @@ -293,6 +304,7 @@ teardown() { [ "$status" -eq 0 ] # Chain database Genesis + echo "$output" [[ "$output" == *"Genesis"* ]] [[ "$output" == *"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"* ]] [[ "$output" == *"0x0000000000000000000000000000000000000000000000000000000000000000"* ]] @@ -337,4 +349,4 @@ teardown() { [[ "$output" == *"3141592"* ]] [[ "$output" == *"0"* ]] [[ "$output" == *"[]"* ]] -} \ No newline at end of file +} diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index 13cef8758..7a64eac5d 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -539,24 +539,6 @@ func MakeSystemNode(version string, ctx *cli.Context) *node.Node { miner.HeaderExtra = []byte(s) } - // Data migrations if should. - if shouldAttemptDirMigration(ctx) { - // Rename existing default datadir // to /. - // Only do this if --datadir flag is not specified AND / does NOT already exist (only migrate once and only for defaulty). - // If it finds an 'Ethereum' directory, it will check if it contains default ETC or ETHF chain data. - // If it contains ETC data, it will rename the dir. If ETHF data, if will do nothing. - if migrationError := migrateExistingDirToClassicNamingScheme(ctx); migrationError != nil { - glog.Fatalf("%v: failed to migrate existing Classic database: %v", ErrDirectoryStructure, migrationError) - } - - // Move existing mainnet data to pertinent chain-named subdir scheme (ie ethereum-classic/mainnet). - // This should only happen if the given (newly defined in this protocol) subdir doesn't exist, - // and the dirs&files (nodekey, dapp, keystore, chaindata, nodes) do exist, - if subdirMigrateErr := migrateToChainSubdirIfNecessary(ctx); subdirMigrateErr != nil { - glog.Fatalf("%v: failed to migrate existing data to chain-specific subdir: %v", ErrDirectoryStructure, subdirMigrateErr) - } - } - // Makes sufficient configuration from JSON file or DB pending flags. // Delegates flag usage. config := mustMakeSufficientChainConfig(ctx) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 24bb1692e..147fb6423 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -228,6 +228,30 @@ func makeCLIApp() (app *cli.App) { glog.CopyStandardLogTo("INFO") + // Data migrations if should. + glog.SetToStderr(true) + // Turn verbosity down for migration check. If migration happens, it will print to Warn. + // Otherwise logs are just debuggers. + glog.SetV(3) + if shouldAttemptDirMigration(ctx) { + // Rename existing default datadir // to /. + // Only do this if --datadir flag is not specified AND / does NOT already exist (only migrate once and only for defaulty). + // If it finds an 'Ethereum' directory, it will check if it contains default ETC or ETHF chain data. + // If it contains ETC data, it will rename the dir. If ETHF data, if will do nothing. + if migrationError := migrateExistingDirToClassicNamingScheme(ctx); migrationError != nil { + glog.Fatalf("%v: failed to migrate existing Classic database: %v", ErrDirectoryStructure, migrationError) + } + + // Move existing mainnet data to pertinent chain-named subdir scheme (ie ethereum-classic/mainnet). + // This should only happen if the given (newly defined in this protocol) subdir doesn't exist, + // and the dirs&files (nodekey, dapp, keystore, chaindata, nodes) do exist, + if subdirMigrateErr := migrateToChainSubdirIfNecessary(ctx); subdirMigrateErr != nil { + glog.Fatalf("%v: failed to migrate existing data to chain-specific subdir: %v", ErrDirectoryStructure, subdirMigrateErr) + } + } + // Set default debug verbosity level. + glog.SetV(5) + // log.Println("Writing logs to ", logDir) // Turn on only file logging, disabling logging(T).toStderr and logging(T).alsoToStdErr glog.SetToStderr(false) diff --git a/cmd/geth/rollback.bats b/cmd/geth/rollback.bats index 8d85bb3df..59d03a3d2 100644 --- a/cmd/geth/rollback.bats +++ b/cmd/geth/rollback.bats @@ -15,8 +15,9 @@ teardown() { run $GETH_CMD --datadir $DATA_DIR rollback 42 echo "$output" [ "$status" -eq 0 ] - [[ "$output" == *"Last block: #384"* ]] # original head - [[ "$output" == *"SUCCESS: Head block set to: 42"* ]] + [[ "$output" == *"Local head full block"* ]] # original head + [[ "$output" == *"384"* ]] # original head + [[ "$output" == *"Success. Head block set to: 42"* ]] # Check that 'latest' block is 42. @@ -37,5 +38,5 @@ teardown() { run $GETH_CMD --datadir $DATA_DIR rollback 420 echo "$output" [ "$status" -gt 0 ] - [[ "$output" == *'ERROR: Wanted rollback to set head to: 420, instead current head is: 384'* ]] # original head -} \ No newline at end of file + [[ "$output" == *'ERROR: Wanted rollback to set head to: 420, instead current head is: 384'* ]] # original head +} From f2cf2f2113fd9f712c8a39e9a1386d75ec5b661a Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 13:56:29 -0600 Subject: [PATCH 07/60] fix: refactor chainIsMorden logic to check more readably and to be able to handle possibly unexpected edge cases better --- cmd/geth/flag.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index 7a64eac5d..b5fbe26b9 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -113,7 +113,21 @@ var ( // '--testnet' (legacy), or // '--chain=morden|testnet' func chainIsMorden(ctx *cli.Context) bool { - return ctx.GlobalBool(aliasableName(TestNetFlag.Name, ctx)) || chainIdentitiesMorden[ctx.GlobalString(aliasableName(ChainIdentityFlag.Name, ctx))] + if ctx.GlobalBool(aliasableName(TestNetFlag.Name, ctx)) { + return true + } + if _, ok := chainIdentitiesMorden[cacheChainIdentity]; ok { + return ok + } + if _, ok := chainIdentitiesMorden[ctx.GlobalString(aliasableName(ChainIdentityFlag.Name, ctx))]; ok { + return ok + } + if cacheChainConfig != nil { + if _, ok := chainIdentitiesMorden[cacheChainConfig.Identity]; ok { + return ok + } + } + return false } // if argument to --chain is a path and is a valid configuration file, copy it to From 2d5fe5707433f22084576f9da0acfb8626916b81 Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 13:57:05 -0600 Subject: [PATCH 08/60] fix: edit log lines ref severity,verbosity --- cmd/geth/cmd.go | 6 +++--- cmd/geth/flag.go | 3 ++- cmd/geth/migrate_datadir.go | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index 5046877cd..aa11ac665 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -526,11 +526,11 @@ func status(ctx *cli.Context) error { s += v } } - glog.D(logger.Info).Infoln(s) + glog.D(logger.Warn).Infoln(s) // Return here if database has not been initialized. if !shouldUseExisting { - glog.D(logger.Info).Infoln("Geth has not been initialized; no database information available yet.") + glog.D(logger.Warn).Warnln("Geth has not been initialized; no database information available yet.") return nil } @@ -543,7 +543,7 @@ func status(ctx *cli.Context) error { for _, v := range formatChainDataPretty(datadir, chaindata) { s += v } - glog.D(logger.Info).Infoln(s) + glog.D(logger.Warn).Infoln(s) return nil } diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index b5fbe26b9..0c1da4d2f 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -435,6 +435,7 @@ func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address { accounts := accman.Accounts() if !ctx.GlobalIsSet(aliasableName(EtherbaseFlag.Name, ctx)) && len(accounts) == 0 { glog.V(logger.Warn).Warnf("WARNING: No etherbase set and no accounts found as default") + glog.D(logger.Warn).Warnf("No etherbase set and no accounts found as default") return common.Address{} } etherbase := ctx.GlobalString(aliasableName(EtherbaseFlag.Name, ctx)) @@ -824,7 +825,7 @@ func logChainConfiguration(ctx *cli.Context, config *core.SufficientChainConfig) glog.D(logger.Warn).Infof("Geth Classic version: %s", logger.ColorGreen(ctx.App.Version)) glog.V(logger.Info).Infof("Geth is configured to use ETC blockchain: %v", config.Name) - glog.D(logger.Warn).Infof("ETC blockchain: %s", logger.ColorGreen(config.Name)) + glog.D(logger.Warn).Infof("Blockchain: %s", logger.ColorGreen(config.Name)) chaindataDirName := MustMakeChainDataDir(ctx) + "/chaindata" glog.V(logger.Info).Infof("Using chain database at: %s", chaindataDirName) diff --git a/cmd/geth/migrate_datadir.go b/cmd/geth/migrate_datadir.go index 1b57d6277..cd9e6c852 100644 --- a/cmd/geth/migrate_datadir.go +++ b/cmd/geth/migrate_datadir.go @@ -86,7 +86,7 @@ func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { // check if there is existing etf blockchain data in unclassic default dir (ie //Ethereum) chainDB, err := ethdb.NewLDBDatabase(ethChainDBPath, 0, 0) if err != nil { - glog.V(logger.Error).Errorf(`Failed to check blockchain compatibility for existing Ethereum chaindata database at: %v + glog.V(logger.Debug).Warnf(`Failed to check blockchain compatibility for existing Ethereum chaindata database at: %v Using default data directory at: %v`, err, etcDataDirPath) return nil From b43f9b99a25213e4e4895575bc7ae59e123fc90a Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 6 Dec 2017 13:58:00 -0600 Subject: [PATCH 09/60] config: make default display verbosity=3 this makes the default stderr logging show chain events like insert, download, and mine in addition to rolling status logs by default --- logger/glog/glog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger/glog/glog.go b/logger/glog/glog.go index 082f656be..3d0b3dfe7 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -579,7 +579,7 @@ func init() { // all Fatal, Error, Warn, and Info log levels. // Please don't use Fatal for display; again, Fatal logs should only go through file logging // (they will be printed to stderr anyway). - display.setVState(2, nil, false) + display.setVState(3, nil, false) go display.flushDaemon() } From 5eccf169348968dd247511a45b2645700f9b54e6 Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 04:12:56 -0600 Subject: [PATCH 10/60] problem: verbosityTraceFloor flag and API unnecessary solution: remove flag and RPC method This flag and method were used to be able to configure the trace level for standard logging. With display/debug logs, there is little to no use for adjusting the trace level for debug logs --- cmd/geth/flags.go | 5 ----- cmd/geth/main.go | 9 --------- cmd/geth/usage.go | 3 +-- eth/api.go | 12 ------------ internal/web3ext/web3ext.go | 6 ------ 5 files changed, 1 insertion(+), 34 deletions(-) diff --git a/cmd/geth/flags.go b/cmd/geth/flags.go index f741af945..1a5b9937e 100644 --- a/cmd/geth/flags.go +++ b/cmd/geth/flags.go @@ -159,11 +159,6 @@ var ( Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=6,p2p=5)", Value: glog.GetVModule(), } - VerbosityTraceFloorFlag = cli.IntFlag{ - Name: "verbosity-trace-floor", - Usage: "Floor verbosity level at which to include file traces on log lines.", - Value: 0, - } LogDirFlag = DirectoryFlag{ Name: "log-dir,logdir", Usage: "Directory in which to write log files.", diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 147fb6423..6fbddf158 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -175,7 +175,6 @@ func makeCLIApp() (app *cli.App) { VerbosityFlag, DisplayFlag, VModuleFlag, - VerbosityTraceFloorFlag, LogDirFlag, LogStatusFlag, MLogFlag, @@ -290,9 +289,6 @@ func makeCLIApp() (app *cli.App) { if !ctx.GlobalIsSet(LogStatusFlag.Name) { ctx.Set(LogStatusFlag.Name, "sync=60") // set log-status interval } - if !ctx.GlobalIsSet(VerbosityTraceFloorFlag.Name) { - glog.SetVTraceThreshold(0) - } if !ctx.GlobalIsSet(VerbosityFlag.Name) { glog.SetV(5) } @@ -302,11 +298,6 @@ func makeCLIApp() (app *cli.App) { if !ctx.GlobalIsSet(LogStatusFlag.Name) { ctx.Set(LogStatusFlag.Name, "sync=30") } - if ctx.GlobalIsSet(VerbosityTraceFloorFlag.Name) { - val := ctx.GlobalInt(VerbosityTraceFloorFlag.Name) - log.Println("--verbosity-trace-floor", "val", val) - glog.SetVTraceThreshold(val) - } if s := ctx.String("metrics"); s != "" { go metrics.CollectToFile(s) diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 49b9fb4e5..8f676e9aa 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -136,12 +136,11 @@ var AppHelpFlagGroups = []flagGroup{ { Name: "LOGGING AND DEBUGGING", Flags: []cli.Flag{ - NeckbeardFlag, VerbosityFlag, VModuleFlag, - VerbosityTraceFloorFlag, LogDirFlag, LogStatusFlag, + NeckbeardFlag, MLogFlag, MLogDirFlag, MLogComponentsFlag, diff --git a/eth/api.go b/eth/api.go index d9654dbd8..7b5119bfa 100644 --- a/eth/api.go +++ b/eth/api.go @@ -1772,18 +1772,6 @@ func (api *PublicDebugAPI) Vmodule(s string) (string, error) { return glog.GetVModule().String(), err } -func (api *PublicDebugAPI) VerbosityTraceFloor(n uint64) (int, error) { - nint := int(n) - if nint == 0 { - return int(*glog.GetVTraceThreshold()), nil - } - if nint <= logger.Detail || nint == logger.Ridiculousness { - glog.SetVTraceThreshold(nint) - return int(*glog.GetVTraceThreshold()), nil - } - return -1, errors.New("invalid logging level") -} - // ExecutionResult groups all structured logs emitted by the EVM // while replaying a transaction in debug mode as well as the amount of // gas used and the return value diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 55f4cb6ce..e1aa668c5 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -191,12 +191,6 @@ web3._extend({ params: 1, inputFormatter: [web3._extend.formatters.inputOptionalNumberFormatter] }), - new web3._extend.Method({ - name: 'verbosityTraceFloor', - call: 'debug_verbosityTraceFloor', - params: 1, - inputFormatter: [web3._extend.formatters.inputOptionalNumberFormatter] - }), new web3._extend.Method({ name: 'vmodule', call: 'debug_vmodule', From a4d3ec21127627836041a0ca0fdc92f6715ada57 Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 04:43:03 -0600 Subject: [PATCH 11/60] problem: test logs expected states too verbosely solution: turn off testing.Log for visual checking of rewards --- core/state_processor_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index c1282b4a6..1dd918644 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -475,7 +475,7 @@ func TestAccumulateRewards1(t *testing.T) { AccumulateRewards(config, stateDB, header, uncles) // Check balances. - t.Logf("config=%d block=%d era=%d w:%d u1:%d u2:%d", i, bn, new(big.Int).Add(era, big.NewInt(1)), winnerB, unclesB[0], unclesB[1]) + //t.Logf("config=%d block=%d era=%d w:%d u1:%d u2:%d", i, bn, new(big.Int).Add(era, big.NewInt(1)), winnerB, unclesB[0], unclesB[1]) if wb := stateDB.GetBalance(header.Coinbase); wb.Cmp(winnerB) != 0 { t.Errorf("winner balance @ %v, want: %v, got: %v (config: %v)", bn, winnerB, wb, i) } From 9f31b40ec13ff61423e326d1c9261b478bce9668 Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 05:32:57 -0600 Subject: [PATCH 12/60] problem: tests print logs to stderr which is loud solution: toggle display logging for tests to 0 at init fn --- accounts/manager_test.go | 6 ++++++ console/console_test.go | 6 ++++++ eth/sync_test.go | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/accounts/manager_test.go b/accounts/manager_test.go index 55fe408ed..d3dfece60 100644 --- a/accounts/manager_test.go +++ b/accounts/manager_test.go @@ -19,6 +19,7 @@ package accounts import ( "fmt" "github.com/davecgh/go-spew/spew" + "github.com/ethereumproject/go-ethereum/logger/glog" "io/ioutil" "math/rand" "os" @@ -29,6 +30,11 @@ import ( "time" ) +func init() { + glog.SetD(0) + glog.SetV(0) +} + var testSigData = make([]byte, 32) func tmpManager(t *testing.T) (string, *Manager) { diff --git a/console/console_test.go b/console/console_test.go index 242aa4449..ef8e4dd68 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -32,6 +32,7 @@ import ( "github.com/ethereumproject/go-ethereum/core" "github.com/ethereumproject/go-ethereum/eth" "github.com/ethereumproject/go-ethereum/node" + "github.com/ethereumproject/go-ethereum/logger/glog" ) const ( @@ -39,6 +40,11 @@ const ( testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" ) +func init() { + glog.SetD(0) + glog.SetV(0) +} + // hookedPrompter implements UserPrompter to simulate use input via channels. type hookedPrompter struct { scheduler chan string diff --git a/eth/sync_test.go b/eth/sync_test.go index f32236b40..7cd6994fe 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -23,8 +23,14 @@ import ( "github.com/ethereumproject/go-ethereum/p2p" "github.com/ethereumproject/go-ethereum/p2p/discover" + "github.com/ethereumproject/go-ethereum/logger/glog" ) +func init() { + glog.SetD(0) + glog.SetV(0) +} + // Tests that fast sync gets disabled as soon as a real block is successfully // imported into the blockchain. func TestFastSyncDisabling(t *testing.T) { From c5e0f50e5630991053e96ca4ad92f1b2233f3591 Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 05:35:50 -0600 Subject: [PATCH 13/60] problem: glog_test lacks display tests solution: implement congruent test fns for display tests and write a few display tests --- logger/glog/glog_test.go | 196 +++++++++++++++++++++++++++------------ 1 file changed, 135 insertions(+), 61 deletions(-) diff --git a/logger/glog/glog_test.go b/logger/glog/glog_test.go index e4b7334ca..33bae948b 100644 --- a/logger/glog/glog_test.go +++ b/logger/glog/glog_test.go @@ -55,8 +55,8 @@ func (f *flushBuffer) Sync() error { return nil } -// swap sets the log writers and returns the old array. -func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { +// swapLogging sets the log writers and returns the old array. +func (l *loggingT) swapLogging(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { l.mu.Lock() defer l.mu.Unlock() old = l.file @@ -66,42 +66,70 @@ func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity] return } -// newBuffers sets the log writers to all new byte buffers and returns the old array. -func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter { - return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) +// swapLogging sets the log writers and returns the old array. +func (l *loggingT) swapDisplay(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { + l.mu.Lock() + defer l.mu.Unlock() + old = l.file + for i, w := range writers { + display.file[i] = w + } + return +} + +// newLoggingBuffers sets the log writers to all new byte buffers and returns the old array. +func (l *loggingT) newLoggingBuffers() [numSeverity]flushSyncWriter { + return l.swapLogging([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) } -// contents returns the specified log value as a string. -func contents(s severity) string { +// newDisplayBuffers sets the log writers to all new byte buffers and returns the old array. +func (l *loggingT) newDisplayBuffers() [numSeverity]flushSyncWriter { + return l.swapDisplay([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) +} + +// loggingContents returns the specified log value as a string. +func loggingContents(s severity) string { return logging.file[s].(*flushBuffer).String() } -// contains reports whether the string is contained in the log. -func contains(s severity, str string, t *testing.T) bool { - return strings.Contains(contents(s), str) +// displayContents returns the specified log value as a string. +func displayContents(s severity) string { + return display.file[s].(*flushBuffer).String() +} + +// loggingContains reports whether the string is contained in the log. +func loggingContains(s severity, str string, t *testing.T) bool { + return strings.Contains(loggingContents(s), str) +} + +// displayContains reports whether the string is contained in the log. +func displayContains(s severity, str string, t *testing.T) bool { + return strings.Contains(displayContents(s), str) } + // setFlags configures the logging flags how the test expects them. func setFlags() { logging.toStderr = false + display.toStderr = false } // Test that Info works as advertised. func TestInfo(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) + if !loggingContains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", loggingContents(infoLog)) } - if !contains(infoLog, "test", t) { + if !loggingContains(infoLog, "test", t) { t.Error("Info failed") } } func TestInfoDepth(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) f := func() { InfoDepth(1, "depth-test1") } @@ -110,7 +138,7 @@ func TestInfoDepth(t *testing.T) { InfoDepth(0, "depth-test0") f() - msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n") + msgs := strings.Split(strings.TrimSuffix(loggingContents(infoLog), "\n"), "\n") if len(msgs) != 2 { t.Fatalf("Got %d lines, expected 2", len(msgs)) } @@ -160,44 +188,49 @@ func TestCopyStandardLogToPanic(t *testing.T) { // Test that using the standard log package logs to INFO. func TestStandardLog(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) stdLog.Print("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) + if !loggingContains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", loggingContents(infoLog)) } - if !contains(infoLog, "test", t) { + if !loggingContains(infoLog, "test", t) { t.Error("Info failed") } } +func lineNumber() int { + _, _, line, _ := runtime.Caller(1) + return line +} + // Test that the header has the correct format. func TestHeader1ErrorLog(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) defer func(previous func() time.Time) { timeNow = previous }(timeNow) timeNow = func() time.Time { return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) } pid = 1234 Error("test") - var line int + line := lineNumber() - 1 // the line directly above format := severityColor[errorLog] + "E" + severityColorReset + "0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" - n, err := fmt.Sscanf(contents(errorLog), format, &line) + n, err := fmt.Sscanf(loggingContents(errorLog), format, &line) if n != 1 || err != nil { - t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(errorLog)) + t.Errorf("log format error: %d elements, error %s:\n%s", n, err, loggingContents(errorLog)) } // Scanf treats multiple spaces as equivalent to a single space, // so check for correct space-padding also. want := fmt.Sprintf(format, line) - if contents(errorLog) != want { - t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(errorLog), want) + if loggingContents(errorLog) != want { + t.Errorf("log format error: got:\n\t%q\nwant:\t%q", loggingContents(errorLog), want) } } // Test that the header has the correct format. func TestHeader2InfoLog(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) defer func(previous func() time.Time) { timeNow = previous }(timeNow) timeNow = func() time.Time { return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) @@ -207,16 +240,16 @@ func TestHeader2InfoLog(t *testing.T) { defer logging.verbosityTraceThreshold.set(s) pid = 1234 Info("test") - format := severityColor[infoLog] + "I" + severityColorReset + "0102 15:04:05.067890 logger/glog/glog_test.go:209] test\n" - n, err := fmt.Sscanf(contents(infoLog), format) + format := severityColor[infoLog] + "I" + severityColorReset + "0102 15:04:05.067890 logger/glog/glog_test.go:"+strconv.Itoa(lineNumber()-1)+"] test\n" + n, err := fmt.Sscanf(loggingContents(infoLog), format) if err != nil { - t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) + t.Errorf("log format error: %d elements, error %s:\n%s", n, err, loggingContents(infoLog)) } // Scanf treats multiple spaces as equivalent to a single space, // so check for correct space-padding also. want := fmt.Sprintf(format) - if contents(infoLog) != want { - t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) + if loggingContents(infoLog) != want { + t.Errorf("log format error: got:\n\t%q\nwant:\n\t%q", loggingContents(infoLog), want) } } @@ -225,19 +258,20 @@ func TestHeader2InfoLog(t *testing.T) { // all be identical. func TestError(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) + defer display.swapDisplay(display.newDisplayBuffers()) Error("test") - if !contains(errorLog, "E", t) { - t.Errorf("Error has wrong character: %q", contents(errorLog)) + if !loggingContains(errorLog, "E", t) { + t.Errorf("Error has wrong character: %q", loggingContents(errorLog)) } - if !contains(errorLog, "test", t) { + if !loggingContains(errorLog, "test", t) { t.Error("Error failed") } - str := contents(errorLog) - if !contains(warningLog, str, t) { + str := loggingContents(errorLog) + if !loggingContains(warningLog, str, t) { t.Error("Warning failed") } - if !contains(infoLog, str, t) { + if !loggingContains(infoLog, str, t) { t.Error("Info failed") } } @@ -245,18 +279,36 @@ func TestError(t *testing.T) { // Test that a Warning log goes to Info. // Even in the Info log, the source character will be W, so the data should // all be identical. -func TestWarning(t *testing.T) { +func TestWarningLogging(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) Warning("test") - if !contains(warningLog, "W", t) { - t.Errorf("Warning has wrong character: %q", contents(warningLog)) + if !loggingContains(warningLog, "W", t) { + t.Errorf("Warning has wrong character: %q", loggingContents(warningLog)) } - if !contains(warningLog, "test", t) { + if !loggingContains(warningLog, "test", t) { t.Error("Warning failed") } - str := contents(warningLog) - if !contains(infoLog, str, t) { + str := loggingContents(warningLog) + if !loggingContains(infoLog, str, t) { + t.Error("Info failed") + } +} + +func TestWarningDisplay(t *testing.T) { + setFlags() + defer display.swapDisplay(display.newDisplayBuffers()) + display.verbosity.Set("3") + defer display.verbosity.Set("0") + D(2).Warnln("test") + if !displayContains(warningLog, "W", t) { + t.Errorf("Warning has wrong character: %q", displayContents(warningLog)) + } + if !displayContains(warningLog, "test", t) { + t.Error("Warning failed") + } + str := displayContents(warningLog) + if !displayContains(infoLog, str, t) { t.Error("Info failed") } } @@ -264,14 +316,29 @@ func TestWarning(t *testing.T) { // Test that a V log goes to Info. func TestV(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) logging.verbosity.Set("2") defer logging.verbosity.Set("0") V(2).Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) + if !loggingContains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", loggingContents(infoLog)) } - if !contains(infoLog, "test", t) { + if !loggingContains(infoLog, "test", t) { + t.Error("Info failed") + } +} + +// Test that a V log goes to Info. +func TestD(t *testing.T) { + setFlags() + defer display.swapDisplay(display.newDisplayBuffers()) + display.verbosity.Set("2") + defer display.verbosity.Set("0") + D(2).Infoln("test") + if !displayContains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", displayContents(infoLog)) + } + if !displayContains(infoLog, "test", t) { t.Error("Info failed") } } @@ -279,7 +346,7 @@ func TestV(t *testing.T) { // Test that a vmodule enables a log in this file. func TestVmoduleOn(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) logging.vmodule.Set("glog_test.go=2") defer logging.vmodule.Set("") if !V(1) { @@ -292,10 +359,10 @@ func TestVmoduleOn(t *testing.T) { t.Error("V enabled for 3") } V(2).Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) + if !loggingContains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", loggingContents(infoLog)) } - if !contains(infoLog, "test", t) { + if !loggingContains(infoLog, "test", t) { t.Error("Info failed") } } @@ -303,7 +370,7 @@ func TestVmoduleOn(t *testing.T) { // Test that a vmodule of another file does not enable a log in this file. func TestVmoduleOff(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) logging.vmodule.Set("notthisfile=2") defer logging.vmodule.Set("") for i := 1; i <= 3; i++ { @@ -312,7 +379,7 @@ func TestVmoduleOff(t *testing.T) { } } V(2).Info("test") - if contents(infoLog) != "" { + if loggingContents(infoLog) != "" { t.Error("V logged incorrectly") } } @@ -359,7 +426,7 @@ var vGlobs = map[string]bool{ // Test that vmodule globbing works as advertised. func testVmoduleGlob(pat string, match bool, t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) defer logging.vmodule.Set("") logging.vmodule.Set(pat) if V(2) != Verbose(match) { @@ -421,7 +488,7 @@ func TestRollover(t *testing.T) { func TestLogBacktraceAt(t *testing.T) { setFlags() - defer logging.swap(logging.newBuffers()) + defer logging.swapLogging(logging.newLoggingBuffers()) // The peculiar style of this code simplifies line counting and maintenance of the // tracing block below. var infoLine string @@ -442,7 +509,7 @@ func TestLogBacktraceAt(t *testing.T) { setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls. Info("we want a stack trace here") } - numAppearances := strings.Count(contents(infoLog), infoLine) + numAppearances := strings.Count(loggingContents(infoLog), infoLine) if numAppearances < 2 { // Need 2 appearances, one in the log header and one in the trace: // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here @@ -451,13 +518,20 @@ func TestLogBacktraceAt(t *testing.T) { // ... // We could be more precise but that would require knowing the details // of the traceback format, which may not be dependable. - t.Fatal("got no trace back; log is ", contents(infoLog)) + t.Fatal("got no trace back; log is ", loggingContents(infoLog)) } } -func BenchmarkHeader(b *testing.B) { +func BenchmarkHeaderLogging(b *testing.B) { for i := 0; i < b.N; i++ { buf, _, _ := logging.header(infoLog, 0) logging.putBuffer(buf) } } + +func BenchmarkHeaderDisplay(b *testing.B) { + for i := 0; i < b.N; i++ { + buf, _, _ := display.header(infoLog, 0) + display.putBuffer(buf) + } +} From bfd9ccc03cd3c94a614180a6b7045aa1d9ff1900 Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 07:03:40 -0600 Subject: [PATCH 14/60] fix: set glog quietness for tests (finish) (finishes 9f31b40) --- cmd/geth/flag_test.go | 6 ++++++ eth/downloader/downloader_test.go | 4 ++++ eth/fetcher/fetcher_test.go | 4 ++++ eth/filters/filter_test.go | 6 ++++++ node/node_test.go | 6 ++++++ p2p/discover/table_test.go | 6 +++--- rpc/server_test.go | 6 ++++++ tests/block_test.go | 6 ++++++ 8 files changed, 41 insertions(+), 3 deletions(-) diff --git a/cmd/geth/flag_test.go b/cmd/geth/flag_test.go index dbc0d9949..cce23f44a 100644 --- a/cmd/geth/flag_test.go +++ b/cmd/geth/flag_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereumproject/go-ethereum/common" "github.com/ethereumproject/go-ethereum/core" "gopkg.in/urfave/cli.v1" + "github.com/ethereumproject/go-ethereum/logger/glog" ) var ogHome string // placeholder @@ -33,6 +34,11 @@ type flags []struct { var gFlags flags +func init() { + glog.SetD(0) + glog.SetV(0) +} + func makeTmpDataDir(t *testing.T) { ogHome = common.HomeDir() var e error diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 7b8467e1b..09fee2b3f 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -34,6 +34,7 @@ import ( "github.com/ethereumproject/go-ethereum/ethdb" "github.com/ethereumproject/go-ethereum/event" "github.com/ethereumproject/go-ethereum/trie" + "github.com/ethereumproject/go-ethereum/logger/glog" ) var ( @@ -45,6 +46,9 @@ var ( // Reduce some of the parameters to make the tester faster. func init() { + glog.SetV(0) + glog.SetD(0) + blockCacheLimit = 1024 var err error diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go index 7c3edcc86..7f4c8e086 100644 --- a/eth/fetcher/fetcher_test.go +++ b/eth/fetcher/fetcher_test.go @@ -32,6 +32,7 @@ import ( "github.com/ethereumproject/go-ethereum/core/types" "github.com/ethereumproject/go-ethereum/crypto" "github.com/ethereumproject/go-ethereum/ethdb" + "github.com/ethereumproject/go-ethereum/logger/glog" ) var ( @@ -43,6 +44,9 @@ var ( ) func init() { + glog.SetV(0) + glog.SetD(0) + var err error testKey, err = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") if err != nil { diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index c684d8df5..43d70b2c0 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -28,8 +28,14 @@ import ( "github.com/ethereumproject/go-ethereum/core/vm" "github.com/ethereumproject/go-ethereum/crypto" "github.com/ethereumproject/go-ethereum/ethdb" + "github.com/ethereumproject/go-ethereum/logger/glog" ) +func init() { + glog.SetD(0) + glog.SetV(0) +} + func makeReceipt(addr common.Address) *types.Receipt { receipt := types.NewReceipt(nil, new(big.Int)) receipt.Logs = vm.Logs{ diff --git a/node/node_test.go b/node/node_test.go index 5451897d5..636e1566a 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -27,12 +27,18 @@ import ( "github.com/ethereumproject/go-ethereum/crypto" "github.com/ethereumproject/go-ethereum/p2p" "github.com/ethereumproject/go-ethereum/rpc" + "github.com/ethereumproject/go-ethereum/logger/glog" ) var ( testNodeKey, _ = crypto.GenerateKey() ) +func init() { + glog.SetD(0) + glog.SetV(0) +} + func testNodeConfig() *Config { return &Config{ PrivateKey: testNodeKey, diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go index 55c0712b5..66aa2b6ba 100644 --- a/p2p/discover/table_test.go +++ b/p2p/discover/table_test.go @@ -256,9 +256,9 @@ func TestTable_Lookup(t *testing.T) { results := tab.Lookup(lookupTestnet.target) t.Logf("results:") - for _, e := range results { - t.Logf(" ld=%d, %x", logdist(lookupTestnet.targetSha, e.sha), e.sha[:]) - } + //for _, e := range results { + // t.Logf(" ld=%d, %x", logdist(lookupTestnet.targetSha, e.sha), e.sha[:]) + //} if len(results) != bucketSize { t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize) } diff --git a/rpc/server_test.go b/rpc/server_test.go index a8517356b..a90e96468 100644 --- a/rpc/server_test.go +++ b/rpc/server_test.go @@ -22,6 +22,7 @@ import ( "net" "reflect" "testing" + "github.com/ethereumproject/go-ethereum/logger/glog" ) type Service struct{} @@ -30,6 +31,11 @@ type Args struct { S string } +func init() { + glog.SetD(0) + glog.SetV(0) +} + func (s *Service) NoArgsRets() { } diff --git a/tests/block_test.go b/tests/block_test.go index d81458521..b2260e7a1 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -21,8 +21,14 @@ import ( "os" "path/filepath" "testing" + "github.com/ethereumproject/go-ethereum/logger/glog" ) +func init() { + glog.SetD(0) + glog.SetV(0) +} + func TestBcValidBlockTests(t *testing.T) { err := RunBlockTest(big.NewInt(1000000), big.NewInt(100000), filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests) if err != nil { From cc8599f58f8691ac783f14e6d863978d350a92ff Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 07:40:16 -0600 Subject: [PATCH 15/60] problem: tests where os.Exit(1) called are skipped solution: implement pattern to run tests which call glog.Fatal (ie os.Exit(1)) in separate process --- cmd/geth/flag_test.go | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/cmd/geth/flag_test.go b/cmd/geth/flag_test.go index cce23f44a..e4f863c7b 100644 --- a/cmd/geth/flag_test.go +++ b/cmd/geth/flag_test.go @@ -14,6 +14,7 @@ import ( "github.com/ethereumproject/go-ethereum/core" "gopkg.in/urfave/cli.v1" "github.com/ethereumproject/go-ethereum/logger/glog" + "os/exec" ) var ogHome string // placeholder @@ -136,7 +137,7 @@ func TestMustMakeChainDataDir(t *testing.T) { {[]string{"--chain", "kitty"}, filepath.Join(dd, "kitty"), nil}, // Passed when run individually, but fails when run as go test. This is not a code problem. - {[]string{"--chain", "kitty/cat"}, filepath.Join(dd, funkyName), ErrInvalidChainID}, + {[]string{"--chain", "kitty/cat"}, filepath.Join(dd, "kitty", "cat"), nil}, {[]string{"--chain", funkyName}, filepath.Join(dd, funkyName), nil}, } @@ -144,11 +145,6 @@ func TestMustMakeChainDataDir(t *testing.T) { // Unset cache. cacheChainIdentity = "" - if c.err != nil { - t.Log("skipping test for erroring use case (will pass, but go test doesn't like glog)") - continue - } - setupFlags(t) if e := set.Parse(c.flags); e != nil { @@ -156,6 +152,7 @@ func TestMustMakeChainDataDir(t *testing.T) { t.Fatal(e) } else { // don't compare the errors for now, this is enough + t.Log("got expected error/+usage info: ok\n(checks cli context flag parsing requires an argument)") continue } } @@ -189,25 +186,16 @@ func TestGetChainIdentityValue(t *testing.T) { // Custom. {[]string{"--chain", "kitty"}, "kitty"}, + {[]string{"--chain", "kitty/cat"}, "kitty/cat"}, // Blacklisted. {[]string{"--chain", "chaindata"}, ""}, - {[]string{"--chain", "dapp"}, ""}, - - // Invalid. - // These pass when test is run individually, but go test doesn't like error out. - {[]string{"--chain", "kitty/cat"}, ""}, } for _, c := range cases { // Unset cache. cacheChainIdentity = "" - if c.want == "" { - t.Log("skipping test for erroring use case (will pass, but go test doesn't like glog)") - continue - } - setupFlags(t) if e := set.Parse(c.flags); e != nil { @@ -215,10 +203,28 @@ func TestGetChainIdentityValue(t *testing.T) { } context = cli.NewContext(app, set, nil) - if got := mustMakeChainIdentity(context); c.want != got { - t.Fatalf("[%v] want: %v, got: %v", c.flags, c.want, got) + if c.want != "" { + got := mustMakeChainIdentity(context) + if c.want != got { + t.Fatalf("[%v] want: %v, got: %v", c.flags, c.want, got) + } + } else { + // https://stackoverflow.com/questions/26225513/how-to-test-os-exit-scenarios-in-go + if os.Getenv("DOES_GLOG_FATAL") == "1" { + mustMakeChainIdentity(context) + return + } + cmd := exec.Command(os.Args[0], "-test.run=TestGetChainIdentityValue") + cmd.Env = append(os.Environ(), "DOES_GLOG_FATAL=1") + err := cmd.Run() + if e, ok := err.(*exec.ExitError); ok && !e.Success() { + t.Log("expected osexit=1: ok", c.flags) + return + } + t.Fatalf("process ran with err %v, want exit status 1", err) } + } } From 685a0576f03727ee2fe3db5103a16d1aa88dd8df Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 07:53:08 -0600 Subject: [PATCH 16/60] problem: windows path separator solution: use filepath.Join for expected out --- cmd/geth/flag_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/geth/flag_test.go b/cmd/geth/flag_test.go index e4f863c7b..bc97b675c 100644 --- a/cmd/geth/flag_test.go +++ b/cmd/geth/flag_test.go @@ -136,7 +136,6 @@ func TestMustMakeChainDataDir(t *testing.T) { {[]string{"--chain", "testnet"}, filepath.Join(dd, "morden"), nil}, {[]string{"--chain", "kitty"}, filepath.Join(dd, "kitty"), nil}, - // Passed when run individually, but fails when run as go test. This is not a code problem. {[]string{"--chain", "kitty/cat"}, filepath.Join(dd, "kitty", "cat"), nil}, {[]string{"--chain", funkyName}, filepath.Join(dd, funkyName), nil}, } @@ -186,7 +185,7 @@ func TestGetChainIdentityValue(t *testing.T) { // Custom. {[]string{"--chain", "kitty"}, "kitty"}, - {[]string{"--chain", "kitty/cat"}, "kitty/cat"}, + {[]string{"--chain", "kitty/cat"}, filepath.Join("kitty", "cat")}, // Blacklisted. {[]string{"--chain", "chaindata"}, ""}, From 6d5588f6ca21d6e37e8503c77a605134e3e85708 Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 7 Dec 2017 14:21:20 -0600 Subject: [PATCH 17/60] Fix TD display on blockchain.LoadLastState --- core/blockchain.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index ee63ba616..2da8673ba 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -697,15 +697,15 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { glog.D(logger.Warn).Infof("Local head header: #%s [%s…] TD=%s", logger.ColorGreen(strconv.Itoa(int(self.hc.CurrentHeader().Number.Uint64()))), logger.ColorGreen(self.hc.CurrentHeader().Hash().Hex()[:8]), - logger.ColorGreen(strconv.Itoa(int(headerTd.Uint64())))) + logger.ColorGreen(fmt.Sprintf("%v", headerTd))) glog.D(logger.Warn).Infof("Local head full block: #%s [%s…] TD=%s", logger.ColorGreen(strconv.Itoa(int(self.currentBlock.Number().Uint64()))), logger.ColorGreen(self.currentBlock.Hash().Hex()[:8]), - logger.ColorGreen(strconv.Itoa(int(blockTd.Uint64())))) + logger.ColorGreen(fmt.Sprintf("%v", blockTd))) glog.D(logger.Warn).Infof("Local head fast block: #%s [%s…] TD=%s", logger.ColorGreen(strconv.Itoa(int(self.currentFastBlock.Number().Uint64()))), logger.ColorGreen(self.currentFastBlock.Hash().Hex()[:8]), - logger.ColorGreen(strconv.Itoa(int(fastTd.Uint64())))) + logger.ColorGreen(fmt.Sprintf("%v", fastTd))) return nil } From 413fa9b353f1a2cdb90bdbd36e70bb235d77889c Mon Sep 17 00:00:00 2001 From: ia Date: Fri, 8 Dec 2017 10:44:41 -0600 Subject: [PATCH 18/60] problem: makedag command appears silent solution: add display log --- cmd/geth/cmd.bats | 2 +- vendor/github.com/ethereumproject/ethash/ethash.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/geth/cmd.bats b/cmd/geth/cmd.bats index 81e72ad6a..cf28bfe36 100644 --- a/cmd/geth/cmd.bats +++ b/cmd/geth/cmd.bats @@ -25,4 +25,4 @@ teardown() { run $GETH_CMD --data-dir $DATA_DIR reset <<< $'y' [ "$status" -eq 0 ] ! [ -d $DATA_DIR/mainnet/chaindata ] -} \ No newline at end of file +} diff --git a/vendor/github.com/ethereumproject/ethash/ethash.go b/vendor/github.com/ethereumproject/ethash/ethash.go index 38a91142f..c1763cefa 100644 --- a/vendor/github.com/ethereumproject/ethash/ethash.go +++ b/vendor/github.com/ethereumproject/ethash/ethash.go @@ -257,6 +257,7 @@ func (d *dag) generate() { d.dir = DefaultDir } glog.V(logger.Info).Infof("Generating DAG for epoch %d (size %d) (%x)", d.epoch, dagSize, seedHash) + glog.D(logger.Error).Infof("Generating DAG for epoch %d [size %d] (%x)", d.epoch, dagSize, seedHash) // Generate a temporary cache. // TODO: this could share the cache with Light cache := C.ethash_light_new_internal(cacheSize, (*C.ethash_h256_t)(unsafe.Pointer(&seedHash[0]))) @@ -289,6 +290,7 @@ func (d *dag) Ptr() unsafe.Pointer { //export ethashGoCallback func ethashGoCallback(percent C.unsigned) C.int { glog.V(logger.Info).Infof("Generating DAG: %d%%", percent) + glog.D(logger.Error).Infof("Generating DAG: %d%%", percent) return 0 } From bb85085fb6ee7477d08ae414eabab74cd51c98cd Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 11:20:15 -0600 Subject: [PATCH 19/60] add notice that it may take a while to export bc --- cmd/geth/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index aa11ac665..f461f5a1a 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -202,7 +202,7 @@ func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { } func ExportChain(blockchain *core.BlockChain, fn string) error { - glog.D(logger.Warn).Infoln("Exporting blockchain to ", fn) + glog.D(logger.Warn).Infoln("Exporting blockchain to", fn, "(this may take a while)...") fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) if err != nil { return err From d1d37df104fe467b69a08b9f2b1a7cab92308c5a Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 11:37:14 -0600 Subject: [PATCH 20/60] the one where I install the progress bar for exporting --- core/blockchain.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/blockchain.go b/core/blockchain.go index 2da8673ba..51d328fc1 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -45,6 +45,7 @@ import ( "github.com/ethereumproject/go-ethereum/rlp" "github.com/ethereumproject/go-ethereum/trie" "github.com/hashicorp/golang-lru" + "gopkg.in/cheggaaa/pb.v1" ) var ( @@ -931,6 +932,10 @@ func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1) + + bar := pb.New(int(last)) + //bar.Format("[=>-]") + bar.Start() for nr := first; nr <= last; nr++ { block := self.GetBlockByNumber(nr) if block == nil { @@ -940,7 +945,9 @@ func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { if err := block.EncodeRLP(w); err != nil { return err } + bar.Increment() } + bar.Finish() return nil } From a227ccad9afc3c81308a3da55895728692524e3b Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 12:46:54 -0600 Subject: [PATCH 21/60] problem: default glog vars and refactor setup logs from main solution: create Default_ glog verbosity vars, move log setup and fns to cmd/geth/log_context.go file and move migration handler fn to own file --- cmd/geth/cmd.go | 1 + cmd/geth/flags.go | 6 +-- cmd/geth/log_context.go | 60 ++++++++++++++++++++++++++ cmd/geth/main.go | 85 +++++-------------------------------- cmd/geth/migrate_datadir.go | 35 +++++++++++++++ logger/glog/glog.go | 19 +++++++++ 6 files changed, 128 insertions(+), 78 deletions(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index c8ed779a5..2c6ce5af9 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -49,6 +49,7 @@ import ( const ( importBatchSize = 2500 + defaultStatusLog = "sync=30" ) // Fatalf formats a message to standard error and exits the program. diff --git a/cmd/geth/flags.go b/cmd/geth/flags.go index 7a70a4908..47e4d1417 100644 --- a/cmd/geth/flags.go +++ b/cmd/geth/flags.go @@ -155,8 +155,8 @@ var ( } DisplayFlag = cli.IntFlag{ Name: "display", - Usage: "Display verbosity: 0=silent, 1=basics, 2=status, 3=events", - Value: 2, + Usage: "Display verbosity: 0=silent, 1=basics, 2=status, 3=status+events", + Value: 3, } VModuleFlag = cli.GenericFlag{ Name: "vmodule", @@ -200,7 +200,7 @@ var ( LogStatusFlag = cli.StringFlag{ Name: "log-status", Usage: `Toggle interval-based STATUS logs: comma-separated list of =. Use 'off' to disable entirely.'`, - Value: "sync=30", + Value: defaultStatusLog, } MLogFlag = cli.StringFlag{ Name: "mlog", diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index ea5a3dd28..cf95dc7d4 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -10,8 +10,68 @@ import ( "gopkg.in/urfave/cli.v1" "github.com/ethereumproject/go-ethereum/logger/glog" + "path/filepath" + "os" ) +// setupLogging sets default +func setupLogging(ctx *cli.Context) error { + glog.CopyStandardLogTo("INFO") + + // Turn on only file logging, disabling logging(T).toStderr and logging(T).alsoToStdErr + glog.SetToStderr(glog.DefaultToStdErr) + glog.SetAlsoToStderr(glog.DefaultAlsoToStdErr) + + glog.SetV(glog.DefaultVerbosity) + + // Set up file logging. + logDir := filepath.Join(MustMakeChainDataDir(ctx), glog.DefaultLogDirName) + + // If '--log-dir' flag is in use, override the default. + if ctx.GlobalIsSet(aliasableName(LogDirFlag.Name, ctx)) { + ld := ctx.GlobalString(aliasableName(LogDirFlag.Name, ctx)) + ldAbs, err := filepath.Abs(ld) + if err != nil { + return err + } + logDir = ldAbs + } + // Ensure log dir exists; mkdir -p + if e := os.MkdirAll(logDir, os.ModePerm); e != nil { + return e + } + + // Before glog.SetLogDir is called, logs are saved to system-default temporary directory. + // If logging is started before this call, the new logDir will be used after file rotation + // (by default after 1800MB of data per file). + glog.SetLogDir(logDir) + + // Handle display level configuration. + if ctx.GlobalIsSet(DisplayFlag.Name) { + i := ctx.GlobalInt(DisplayFlag.Name) + if i > 3 { + return fmt.Errorf("--%s level must be 0 <= i <= 3, got: %d", DisplayFlag.Name, i) + } + glog.SetD(i) + } + + // Handle --neckbeard config overrides if set. + if ctx.GlobalBool(NeckbeardFlag.Name) { + glog.SetD(0) + // Allow manual overrides + if !ctx.GlobalIsSet(VerbosityFlag.Name) { + glog.SetV(5) + } + glog.SetAlsoToStderr(true) + } + + // If --log-status not set, set default 60s interval + if !ctx.GlobalIsSet(LogStatusFlag.Name) { + ctx.Set(LogStatusFlag.Name, defaultStatusLog) + } + return nil +} + func setupLogRotation(ctx *cli.Context) error { var err error glog.MaxSize, err = getSizeFlagValue(&LogMaxSizeFlag, ctx) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 719ab5480..c2abfd814 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -30,7 +30,6 @@ import ( "github.com/ethereumproject/go-ethereum/core" "github.com/ethereumproject/go-ethereum/eth" "github.com/ethereumproject/go-ethereum/logger" - "github.com/ethereumproject/go-ethereum/logger/glog" "github.com/ethereumproject/go-ethereum/metrics" ) @@ -241,83 +240,19 @@ func makeCLIApp() (app *cli.App) { runtime.GOMAXPROCS(runtime.NumCPU()) - err := setupLogRotation(ctx) - if err != nil { + // Check for migrations and handle if conditionals are met. + if err := handleIfDataDirSchemaMigrations(ctx); err != nil { return err } - glog.CopyStandardLogTo("INFO") - - // Data migrations if should. - glog.SetToStderr(true) - // Turn verbosity down for migration check. If migration happens, it will print to Warn. - // Otherwise logs are just debuggers. - glog.SetV(3) - if shouldAttemptDirMigration(ctx) { - // Rename existing default datadir // to /. - // Only do this if --datadir flag is not specified AND / does NOT already exist (only migrate once and only for defaulty). - // If it finds an 'Ethereum' directory, it will check if it contains default ETC or ETHF chain data. - // If it contains ETC data, it will rename the dir. If ETHF data, if will do nothing. - if migrationError := migrateExistingDirToClassicNamingScheme(ctx); migrationError != nil { - glog.Fatalf("%v: failed to migrate existing Classic database: %v", ErrDirectoryStructure, migrationError) - } - - // Move existing mainnet data to pertinent chain-named subdir scheme (ie ethereum-classic/mainnet). - // This should only happen if the given (newly defined in this protocol) subdir doesn't exist, - // and the dirs&files (nodekey, dapp, keystore, chaindata, nodes) do exist, - if subdirMigrateErr := migrateToChainSubdirIfNecessary(ctx); subdirMigrateErr != nil { - glog.Fatalf("%v: failed to migrate existing data to chain-specific subdir: %v", ErrDirectoryStructure, subdirMigrateErr) - } - } - // Set default debug verbosity level. - glog.SetV(5) - - // log.Println("Writing logs to ", logDir) - // Turn on only file logging, disabling logging(T).toStderr and logging(T).alsoToStdErr - glog.SetToStderr(false) - glog.SetAlsoToStderr(false) - - // Set up file logging. - logDir := filepath.Join(MustMakeChainDataDir(ctx), "log") - if ctx.GlobalIsSet(aliasableName(LogDirFlag.Name, ctx)) { - ld := ctx.GlobalString(aliasableName(LogDirFlag.Name, ctx)) - ldAbs, err := filepath.Abs(ld) - if err != nil { - glog.Fatalln(err) - } - logDir = ldAbs - } - // Ensure mkdir -p - if e := os.MkdirAll(logDir, os.ModePerm); e != nil { - return e - } - // Set log dir. - // GOTCHA: There may be NO glog.V logs called before this is set. - // Otherwise everything will get all fucked and there will be no logs. - glog.SetLogDir(logDir) - - if ctx.GlobalIsSet(DisplayFlag.Name) { - i := ctx.GlobalInt(DisplayFlag.Name) - if i > 3 { - return fmt.Errorf("Error: --%s level must be 0 <= i <= 3, got: %d", DisplayFlag.Name, i) - } - glog.SetD(i) + // Handle parsing and applying log verbosity, severities, and default configurations from context. + if err := setupLogging(ctx); err != nil { + return err } - if ctx.GlobalBool(NeckbeardFlag.Name) { - glog.SetD(0) - // Allow manual overrides - if !ctx.GlobalIsSet(LogStatusFlag.Name) { - ctx.Set(LogStatusFlag.Name, "sync=60") // set log-status interval - } - if !ctx.GlobalIsSet(VerbosityFlag.Name) { - glog.SetV(5) - } - glog.SetAlsoToStderr(true) - } - // If --log-status not set, set default 60s interval - if !ctx.GlobalIsSet(LogStatusFlag.Name) { - ctx.Set(LogStatusFlag.Name, "sync=30") + // Handle parsing and applying log rotation configs from context. + if err := setupLogRotation(ctx); err != nil { + return err } if s := ctx.String("metrics"); s != "" { @@ -334,14 +269,14 @@ func makeCLIApp() (app *cli.App) { // > The output of this command is supposed to be machine-readable. gasLimit := ctx.GlobalString(aliasableName(TargetGasLimitFlag.Name, ctx)) if _, ok := core.TargetGasLimit.SetString(gasLimit, 0); !ok { - log.Fatalf("malformed %s flag value %q", aliasableName(TargetGasLimitFlag.Name, ctx), gasLimit) + return fmt.Errorf("malformed %s flag value %q", aliasableName(TargetGasLimitFlag.Name, ctx), gasLimit) } // Set morden chain by default for dev mode. if ctx.GlobalBool(aliasableName(DevModeFlag.Name, ctx)) { if !ctx.GlobalIsSet(aliasableName(ChainIdentityFlag.Name, ctx)) { if e := ctx.Set(aliasableName(ChainIdentityFlag.Name, ctx), "morden"); e != nil { - log.Fatalf("failed to set chain value: %v", e) + return fmt.Errorf("failed to set chain value: %v", e) } } } diff --git a/cmd/geth/migrate_datadir.go b/cmd/geth/migrate_datadir.go index cd9e6c852..08d513b71 100644 --- a/cmd/geth/migrate_datadir.go +++ b/cmd/geth/migrate_datadir.go @@ -29,6 +29,41 @@ import ( "path/filepath" ) +// handleIfDataDirSchemaMigrations is a handler for the conditional logic around +// data/chain dir migrations from geth versions < 3.4 and in consideration of EF geth schemas used for ETC. +func handleIfDataDirSchemaMigrations(ctx *cli.Context) error { + + origV := int(*glog.GetVerbosity()) + if origV == 0 { + origV = glog.DefaultVerbosity + } + + // Turn verbosity down for migration check. If migration happens, it will print to Warn. + // Otherwise logs are just debuggers. + glog.SetToStderr(true) + glog.SetV(3) + + if shouldAttemptDirMigration(ctx) { + // Rename existing default datadir // to /. + // Only do this if --datadir flag is not specified AND / does NOT already exist (only migrate once and only for defaulty). + // If it finds an 'Ethereum' directory, it will check if it contains default ETC or ETHF chain data. + // If it contains ETC data, it will rename the dir. If ETHF data, if will do nothing. + if err := migrateExistingDirToClassicNamingScheme(ctx); err != nil { + return err + } + + // Move existing mainnet data to pertinent chain-named subdir scheme (ie ethereum-classic/mainnet). + // This should only happen if the given (newly defined in this protocol) subdir doesn't exist, + // and the dirs&files (nodekey, dapp, keystore, chaindata, nodes) do exist, + if err := migrateToChainSubdirIfNecessary(ctx); err != nil { + return err + } + } + // (Re)set default debug verbosity level. + glog.SetV(origV) + return nil +} + // migrateExistingDirToClassicNamingScheme renames default base data directory ".../Ethereum" to ".../EthereumClassic", pending os customs, etc... ;-) /// // Check for preexisting **Un-classic** data directory, ie "/home/path/to/Ethereum". diff --git a/logger/glog/glog.go b/logger/glog/glog.go index a2bc03102..b76ce6a7d 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -122,6 +122,25 @@ import ( "time" ) + +// DefaultVerbosity establishes the default verbosity Level for +// to-file (debug) logging. +var DefaultVerbosity = 5 + +// DefaultToStdErr establishes the default bool toggling whether logging +// should be directed ONLY to stderr. +var DefaultToStdErr = false + +// DefaultAlsoToStdErr establishes the default bool toggling whether logging +// should be written to BOTH file and stderr. +var DefaultAlsoToStdErr = false + +// DefaultLogDirName establishes the default directory name for debug (V) logs. +// Log files will be written inside this dir. +// By default, this directory will be created if it does not exist within the context's chain directory, eg. +// //log/. +var DefaultLogDirName = "log" + // MinSize is a minimum file size qualifying for rotation. This variable can be used // to avoid rotation of empty or almost emtpy files. var MinSize uint64 From ec3ee6781f0bbe9e912d1ea91c4d88fab467c49b Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 12:51:38 -0600 Subject: [PATCH 22/60] fix: remove 'WARNING' prefix from severity=warn log lines --- cmd/geth/flag.go | 2 +- core/blockchain.go | 6 +++--- core/headerchain.go | 3 ++- eth/handler.go | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index 0c1da4d2f..d405f77ac 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -434,7 +434,7 @@ func MakeAddress(accman *accounts.Manager, account string) (accounts.Account, er func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address { accounts := accman.Accounts() if !ctx.GlobalIsSet(aliasableName(EtherbaseFlag.Name, ctx)) && len(accounts) == 0 { - glog.V(logger.Warn).Warnf("WARNING: No etherbase set and no accounts found as default") + glog.V(logger.Warn).Warnf("No etherbase set and no accounts found as default") glog.D(logger.Warn).Warnf("No etherbase set and no accounts found as default") return common.Address{} } diff --git a/core/blockchain.go b/core/blockchain.go index 51d328fc1..89fe060c0 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -538,7 +538,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { defer self.mu.Lock() if recoveredHeight == 0 { - glog.V(logger.Error).Errorln("WARNING: No recoverable data found, resetting to genesis.") + glog.V(logger.Error).Errorln("No recoverable data found, resetting to genesis.") return self.Reset() } // Remove all block header and canonical data above recoveredHeight @@ -551,7 +551,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { if head == (common.Hash{}) { // Corrupt or empty database, init from scratch if !dryrun { - glog.V(logger.Warn).Errorln("WARNING: Empty database, attempting chain reset with recovery.") + glog.V(logger.Warn).Errorln("Empty database, attempting chain reset with recovery.") return recoverOrReset() } return errors.New("empty HeadBlockHash") @@ -564,7 +564,7 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { if currentBlock == nil { // Corrupt or empty database, init from scratch if !dryrun { - glog.V(logger.Warn).Errorf("WARNING: Head block missing, hash: %x\nAttempting chain reset with recovery.", head) + glog.V(logger.Warn).Errorf("Head block missing, hash: %x\nAttempting chain reset with recovery.", head) return recoverOrReset() } return errors.New("nil currentBlock") diff --git a/core/headerchain.go b/core/headerchain.go index 1af35f4e2..1fb64c65b 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -104,7 +104,8 @@ func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, mux *event.Type if err != nil { return nil, err } - glog.V(logger.Info).Infof("WARNING: Wrote default ethereum %v genesis block", genname) + glog.V(logger.Info).Infof("Wrote default ethereum %v genesis block", genname) + glog.D(logger.Info).Infof("Wrote default ethereum %v genesis block", logger.ColorGreen(genname)) hc.genesisHeader = genesisBlock.Header() } diff --git a/eth/handler.go b/eth/handler.go index 129bf17f1..f4d658f9a 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -106,7 +106,7 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, } // Figure out whether to allow fast sync or not if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { - glog.V(logger.Warn).Infoln("WARNING: Blockchain not empty, fast sync disabled") + glog.V(logger.Warn).Infoln("Blockchain not empty, fast sync disabled") glog.D(logger.Warn).Warnln("Blockchain not empty. Fast sync disabled.") fastSync = false } From a639c65766bd8b5e719496d70145a1b142727b59 Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 15:16:04 -0600 Subject: [PATCH 23/60] fix: refactor status log cmd enables reuse of parse fn and delegation of error handling/logging --- cmd/geth/cmd.go | 70 +++++++++++++++-------------------------- cmd/geth/log_context.go | 5 +++ 2 files changed, 31 insertions(+), 44 deletions(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index 2c6ce5af9..22548b777 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -49,7 +49,6 @@ import ( const ( importBatchSize = 2500 - defaultStatusLog = "sync=30" ) // Fatalf formats a message to standard error and exits the program. @@ -746,18 +745,10 @@ func version(ctx *cli.Context) error { return nil } -// LogStatusFeatAvailability is used to register and track use of available status logging features, eg. "STATUS SYNC" -type LogStatusFeatAvailability int - -const ( - StatusFeatAvailable LogStatusFeatAvailability = iota - StatusFeatRegistered -) - // availableLogStatusFeatures stores state of implemented log STATUS features. // New features should be registered here, and their status updates by dispatchStatusLogs if in use (to avoid dupe goroutine logging). -var availableLogStatusFeatures = map[string]LogStatusFeatAvailability{ - "sync": StatusFeatAvailable, +var availableLogStatusFeatures = map[string]time.Duration{ + "sync": time.Duration(0), } type lsMode int @@ -791,6 +782,19 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { glog.Fatalf("%v: %v", flagName, ErrInvalidFlag) } + parseStatusInterval := func(statusModule string, interval string) (tickerInterval time.Duration) { + upcaseModuleName := strings.ToUpper(statusModule) + if interval != "" { + if ti, err := parseDuration(interval); err != nil { + glog.Fatalf("%s %v: could not parse argument: %v", upcaseModuleName, err, interval) + } else { + tickerInterval = ti + } + } + //glog.V(logger.Info).Infof("Rolling %s log interval set: %v", upcaseModuleName, tickerInterval) + return tickerInterval + } + for _, p := range strings.Split(v, ",") { // Ignore hanging or double commas if p == "" { @@ -808,7 +812,7 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { if status, ok := availableLogStatusFeatures[eqs[0]]; !ok { glog.Errorf("%v: %v: unavailable status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) os.Exit(1) - } else if status == StatusFeatRegistered { + } else if status.Seconds() != 0 { glog.Errorf("%v: %v: duplicate status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) os.Exit(1) } @@ -817,10 +821,13 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { if len(eqs) == 1 { eqs = append(eqs, "") } + + d := parseStatusInterval(eqs[0], eqs[1]) + switch eqs[0] { case "sync": - availableLogStatusFeatures["sync"] = StatusFeatRegistered - go runStatusSyncLogs(ctx, ethe, eqs[1], ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + availableLogStatusFeatures["sync"] = d + go runStatusSyncLogs(ctx, ethe, d, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) } } } @@ -828,34 +835,9 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { // runStatusSyncLogs starts STATUS SYNC logging at a given interval. // It should be run as a goroutine. // eg. --log-status="sync=42" logs SYNC information every 42 seconds -func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, interval string, maxPeers int) { - // Establish default interval and parse desired interval from context. - // Includes convenience notifications for UI/UX. - intervalI := 60 - if interval != "" { - i, e := strconv.Atoi(interval) - if e != nil { - glog.Fatalf("SYNC %v: could not parse argument: %v", e, interval) - } - if i < 1 { - glog.Fatalf("SYNC interval value must be a positive integer, got: %d", i) - } - intervalI = i - } - glog.V(logger.Info).Infof("Rolling SYNC log interval set: %d seconds", intervalI) - - // Only use severity=warn if --log-status not in use (ie using defaults) - statIntervalNotice := fmt.Sprintf("Rolling SYNC status logs set to every %d seconds. ", intervalI) - if !ctx.GlobalIsSet(LogStatusFlag.Name) { - statIntervalNotice += fmt.Sprintf("You can adjust this with the --%s flag.", LogStatusFlag.Name) - glog.D(logger.Error).Warnln(statIntervalNotice) - // statIntervalNoticeFn = glog.D(logger.Error).Warnf - } else { - glog.D(logger.Error).Infoln(statIntervalNotice) - } +func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) { // Set up ticker based on established interval. - tickerInterval := time.Second * time.Duration(int32(intervalI)) ticker := time.NewTicker(tickerInterval) var chainEventLastSent time.Time @@ -1051,7 +1033,7 @@ func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, interval string, maxPe // stats will show '0' blocks/second. Looks a little strange; but on the other hand, // precision costs visual space, and normally just looks weird when starting up sync or // syncing slowly. - numBlocksDiffPerSecond = numBlocksDiff / uint64(intervalI) + numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds()) // Don't show initial current / per second val if lastLoggedBlockNumber == 0 { @@ -1060,8 +1042,8 @@ func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, interval string, maxPe } // Divide by interval to yield per-second stats - numTxsDiffPerSecond = numTxsDiff / intervalI - mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(intervalI))) + numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds()) + mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds()))) mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) mGasPerSecondI := mGasPerSecond.Int64() @@ -1108,7 +1090,7 @@ func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, interval string, maxPe for { select { case <-ticker.C: - if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(intervalI/2))) { + if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()/2))) { printIntervalStatusLog() } case <-sigc: diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index cf95dc7d4..83c11ab0e 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -14,6 +14,9 @@ import ( "os" ) +const defaultStatusLog = "sync=30" +var isToFileLoggingEnabled = true + // setupLogging sets default func setupLogging(ctx *cli.Context) error { glog.CopyStandardLogTo("INFO") @@ -118,6 +121,8 @@ func getSizeFlagValue(flag *cli.StringFlag, ctx *cli.Context) (uint64, error) { func parseDuration(str string) (time.Duration, error) { mapping := map[rune]uint64{ 0: uint64(time.Second), // no-suffix means value in seconds + 's': uint64(time.Second), + 'm': uint64(time.Minute), 'h': uint64(time.Hour), 'd': uint64(24 * time.Hour), 'w': uint64(7 * 24 * time.Hour), From 8fcf18d4d074564f4ca474247ec5cf356939aa5d Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 15:24:25 -0600 Subject: [PATCH 24/60] fix: refactor log config setup to file edits flag types and values for easier parsing and conditionals --- cmd/geth/flags.go | 14 ++++----- cmd/geth/log_context.go | 67 +++++++++++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/cmd/geth/flags.go b/cmd/geth/flags.go index 47e4d1417..c637c3f01 100644 --- a/cmd/geth/flags.go +++ b/cmd/geth/flags.go @@ -148,25 +148,25 @@ var ( Name: "neckbeard", Usage: "Use verbose->stderr defaults for logging (verbosity=5,log-status='sync=60')", } - VerbosityFlag = cli.GenericFlag{ + VerbosityFlag = cli.IntFlag{ Name: "verbosity", Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=detail", - Value: glog.GetVerbosity(), + Value: glog.DefaultVerbosity, } DisplayFlag = cli.IntFlag{ Name: "display", Usage: "Display verbosity: 0=silent, 1=basics, 2=status, 3=status+events", - Value: 3, + Value: glog.DefaultDisplay, } - VModuleFlag = cli.GenericFlag{ + VModuleFlag = cli.StringFlag{ Name: "vmodule", Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=6,p2p=5)", - Value: glog.GetVModule(), + Value: "", } - LogDirFlag = DirectoryFlag{ + LogDirFlag = cli.StringFlag{ Name: "log-dir,logdir", Usage: "Directory in which to write log files", - Value: DirectoryString{filepath.Join(common.DefaultDataDir(), "", "logs")}, + Value: filepath.Join(common.DefaultDataDir(), "", glog.DefaultLogDirName), } LogMaxSizeFlag = cli.StringFlag{ Name: "log-max-size,log-maxsize", diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index 83c11ab0e..1930c6f56 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -28,26 +28,49 @@ func setupLogging(ctx *cli.Context) error { glog.SetV(glog.DefaultVerbosity) // Set up file logging. - logDir := filepath.Join(MustMakeChainDataDir(ctx), glog.DefaultLogDirName) + logDir := "" + isToFileLoggingEnabled = toFileLoggingEnabled(ctx) // If '--log-dir' flag is in use, override the default. if ctx.GlobalIsSet(aliasableName(LogDirFlag.Name, ctx)) { ld := ctx.GlobalString(aliasableName(LogDirFlag.Name, ctx)) - ldAbs, err := filepath.Abs(ld) - if err != nil { - return err + if ld == "" { + return fmt.Errorf("--%s cannot be empty", LogDirFlag.Name) } - logDir = ldAbs + if isToFileLoggingEnabled { + ldAbs, err := filepath.Abs(ld) + if err != nil { + return err + } + logDir = ldAbs + } else { + glog.SetD(0) + glog.SetToStderr(true) + } + } else { + logDir = filepath.Join(MustMakeChainDataDir(ctx), glog.DefaultLogDirName) } - // Ensure log dir exists; mkdir -p - if e := os.MkdirAll(logDir, os.ModePerm); e != nil { - return e + + // Allow to-file logging to be disabled + if logDir != "" { + // Ensure log dir exists; mkdir -p + if e := os.MkdirAll(logDir, os.ModePerm); e != nil { + return e + } + + // Before glog.SetLogDir is called, logs are saved to system-default temporary directory. + // If logging is started before this call, the new logDir will be used after file rotation + // (by default after 1800MB of data per file). + glog.SetLogDir(logDir) } - // Before glog.SetLogDir is called, logs are saved to system-default temporary directory. - // If logging is started before this call, the new logDir will be used after file rotation - // (by default after 1800MB of data per file). - glog.SetLogDir(logDir) + + // Handle --neckbeard config overrides if set. + if ctx.GlobalBool(NeckbeardFlag.Name) { + glog.SetD(0) + glog.SetV(5) + glog.SetAlsoToStderr(true) + } // Handle display level configuration. if ctx.GlobalIsSet(DisplayFlag.Name) { @@ -58,20 +81,26 @@ func setupLogging(ctx *cli.Context) error { glog.SetD(i) } - // Handle --neckbeard config overrides if set. - if ctx.GlobalBool(NeckbeardFlag.Name) { - glog.SetD(0) - // Allow manual overrides - if !ctx.GlobalIsSet(VerbosityFlag.Name) { - glog.SetV(5) + // Manual context configs + // Global V verbosity + if ctx.GlobalIsSet(VerbosityFlag.Name) { + nint := ctx.GlobalInt(VerbosityFlag.Name) + if nint <= logger.Detail || nint == logger.Ridiculousness { + glog.SetV(nint) } - glog.SetAlsoToStderr(true) + } + + // Global Vmodule + if ctx.GlobalIsSet(VModuleFlag.Name) { + v := ctx.GlobalString(VModuleFlag.Name) + glog.GetVModule().Set(v) } // If --log-status not set, set default 60s interval if !ctx.GlobalIsSet(LogStatusFlag.Name) { ctx.Set(LogStatusFlag.Name, defaultStatusLog) } + return nil } From d978fda7ac43f0c8a2db7219a5bf022aa82d2131 Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 15:36:47 -0600 Subject: [PATCH 25/60] problem: refactor m/log config and logging - move mlog flags parser and setup funcs to log_context.go - create fn to log logging configs... meta, i know - log verbosity changes when updated on-the-fly via API - fix: actually use the glog D and V verbosity default in the init fn - refactor mlog_file to provide an mlogFormat stringer interface and better getters, setters, and defaults --- cmd/geth/flag.go | 71 ------------------------ cmd/geth/log_context.go | 118 ++++++++++++++++++++++++++++++++++++++++ cmd/geth/main.go | 1 + eth/api.go | 20 ++++++- logger/glog/glog.go | 8 ++- logger/mlog_file.go | 41 ++++++++++---- 6 files changed, 171 insertions(+), 88 deletions(-) diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index d405f77ac..527bc2daa 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -30,8 +30,6 @@ import ( "errors" - "time" - "github.com/ethereumproject/ethash" "github.com/ethereumproject/go-ethereum/accounts" "github.com/ethereumproject/go-ethereum/common" @@ -477,69 +475,6 @@ func makeNodeName(version string, ctx *cli.Context) string { return name } -func mustMakeMLogDir(ctx *cli.Context) string { - if ctx.GlobalIsSet(MLogDirFlag.Name) { - p := ctx.GlobalString(MLogDirFlag.Name) - if p == "" { - glog.Fatalf("Flag %v requires a non-empty argument", MLogDirFlag.Name) - return "" - } - if filepath.IsAbs(p) { - return p - } - ap, e := filepath.Abs(p) - if e != nil { - glog.Fatalf("could not establish absolute path for mlog dir: %v", e) - } - return ap - } - - return filepath.Join(MustMakeChainDataDir(ctx), "mlogs") -} - -func makeMLogFileLogger(ctx *cli.Context) (string, error) { - now := time.Now() - - mlogdir := mustMakeMLogDir(ctx) - logger.SetMLogDir(mlogdir) - - _, filename, err := logger.CreateMLogFile(now) - if err != nil { - return "", err - } - // withTs toggles custom timestamp ISO8601 prefix - // logger print without timestamp header prefix if json - withTs := true - if f := ctx.GlobalString(MLogFlag.Name); logger.MLogStringToFormat[f] == logger.MLOGJSON { - withTs = false - } - logger.BuildNewMLogSystem(mlogdir, filename, 1, 0, withTs) // flags: 0 disables automatic log package time prefix - return filename, nil -} - -func mustRegisterMLogsFromContext(ctx *cli.Context) { - if e := logger.MLogRegisterComponentsFromContext(ctx.GlobalString(MLogComponentsFlag.Name)); e != nil { - // print documentation if user enters unavailable mlog component - var components []string - for k := range logger.MLogRegistryAvailable { - components = append(components, string(k)) - } - glog.V(logger.Error).Errorf("Error: %s", e) - glog.V(logger.Error).Errorf("Available machine log components: %v", components) - os.Exit(1) - } - // Set the global logger mlog format from context - if e := logger.SetMLogFormatFromString(ctx.GlobalString(MLogFlag.Name)); e != nil { - glog.Fatalf("Error setting mlog format: %v, value was: %v", e, ctx.GlobalString(MLogFlag.Name)) - } - fname, e := makeMLogFileLogger(ctx) - if e != nil { - glog.Fatalf("Failed to start machine log: %v", e) - } - glog.V(logger.Info).Infof("Machine logs file: %v", fname) - glog.D(logger.Info).Infof("Machine logs (mlog) file: %v", logger.ColorGreen(fname)) -} - // MakeSystemNode sets up a local node, configures the services to launch and // assembles the P2P protocol stack. func MakeSystemNode(version string, ctx *cli.Context) *node.Node { @@ -585,12 +520,6 @@ func MakeSystemNode(version string, ctx *cli.Context) *node.Node { // If --mlog enabled, configure and create mlog dir and file if ctx.GlobalString(MLogFlag.Name) != "off" { mustRegisterMLogsFromContext(ctx) - } else { - // Just demonstrative code. - if b := logger.SetMlogEnabled(false); b == false && logger.MlogEnabled() == false { - glog.V(logger.Warn).Warnf("Machine logs: disabled") - glog.D(logger.Info).Warnf("Machine logs disabled.") - } } if ctx.GlobalBool(Unused1.Name) { diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index 1930c6f56..905c87d01 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -12,6 +12,7 @@ import ( "github.com/ethereumproject/go-ethereum/logger/glog" "path/filepath" "os" + "github.com/ethereumproject/go-ethereum/logger" ) const defaultStatusLog = "sync=30" @@ -210,3 +211,120 @@ func parseWithSuffix(str string, mapping map[rune]uint64) (uint64, error) { return value * mapping[suffix], nil } + +func toFileLoggingEnabled(ctx *cli.Context) bool { + if ctx.GlobalIsSet(aliasableName(LogDirFlag.Name, ctx)) { + ld := ctx.GlobalString(aliasableName(LogDirFlag.Name, ctx)) + if ld == "off" || ld == "disable" || ld == "disabled" { + return false + } + } + return true +} + +func mustMakeMLogDir(ctx *cli.Context) string { + if ctx.GlobalIsSet(MLogDirFlag.Name) { + p := ctx.GlobalString(MLogDirFlag.Name) + if p == "" { + glog.Fatalf("Flag %v requires a non-empty argument", MLogDirFlag.Name) + return "" + } + if filepath.IsAbs(p) { + return p + } + ap, e := filepath.Abs(p) + if e != nil { + glog.Fatalf("could not establish absolute path for mlog dir: %v", e) + } + return ap + } + + return filepath.Join(MustMakeChainDataDir(ctx), "mlogs") +} + +func makeMLogFileLogger(ctx *cli.Context) (string, error) { + now := time.Now() + + mlogdir := mustMakeMLogDir(ctx) + logger.SetMLogDir(mlogdir) + + _, filename, err := logger.CreateMLogFile(now) + if err != nil { + return "", err + } + // withTs toggles custom timestamp ISO8601 prefix + // logger print without timestamp header prefix if json + withTs := true + if f := ctx.GlobalString(MLogFlag.Name); logger.MLogStringToFormat[f] == logger.MLOGJSON { + withTs = false + } + logger.BuildNewMLogSystem(mlogdir, filename, 1, 0, withTs) // flags: 0 disables automatic log package time prefix + return filename, nil +} + +func mustRegisterMLogsFromContext(ctx *cli.Context) { + if e := logger.MLogRegisterComponentsFromContext(ctx.GlobalString(MLogComponentsFlag.Name)); e != nil { + // print documentation if user enters unavailable mlog component + var components []string + for k := range logger.MLogRegistryAvailable { + components = append(components, string(k)) + } + glog.V(logger.Error).Errorf("Error: %s", e) + glog.V(logger.Error).Errorf("Available machine log components: %v", components) + os.Exit(1) + } + // Set the global logger mlog format from context + if e := logger.SetMLogFormatFromString(ctx.GlobalString(MLogFlag.Name)); e != nil { + glog.Fatalf("Error setting mlog format: %v, value was: %v", e, ctx.GlobalString(MLogFlag.Name)) + } + _, e := makeMLogFileLogger(ctx) + if e != nil { + glog.Fatalf("Failed to start machine log: %v", e) + } + logger.SetMlogEnabled(true) +} + +func logLoggingConfiguration(ctx *cli.Context) { + v := glog.GetVerbosity().String() + logdir := "off" + if isToFileLoggingEnabled { + logdir = glog.GetLogDir() + } + vmodule := glog.GetVModule().String() + // An empty string looks unused, so show * instead, which is equivalent. + if vmodule == "" { + vmodule= "*" + } + d := glog.GetDisplayable().String() + + statusFeats := []string{} + for k, v := range availableLogStatusFeatures { + if v.Seconds() == 0 { + continue + } + statusFeats = append(statusFeats, logger.ColorGreen(fmt.Sprintf("%s=%v", k, v))) + } + statusLine := strings.Join(statusFeats, ",") + + glog.V(logger.Warn).Infoln("Debug log configuration", "v=", v, "logdir=", logdir, "vmodule=", vmodule) + glog.D(logger.Warn).Infof("Debug log config: verbosity=%s log-dir=%s vmodule=%s", + logger.ColorGreen(v), + logger.ColorGreen(logdir), + logger.ColorGreen(vmodule), + ) + + glog.V(logger.Warn).Infoln("Display log configuration", "d=", d, "status=", statusLine) + glog.D(logger.Warn).Infof("Display log config: display=%s status=%s", + logger.ColorGreen(d), + statusLine, + ) + + if logger.MlogEnabled() { + glog.V(logger.Warn).Warnf("Machine log config: mlog=%s mlog-dir=%s", logger.GetMLogFormat().String(), logger.GetMLogDir()) + glog.D(logger.Warn).Infof("Machine log config: mlog=%s mlog-dir=%s", logger.ColorGreen(logger.GetMLogFormat().String()), logger.ColorGreen(logger.GetMLogDir())) + } else { + glog.V(logger.Warn).Warnf("Machine log config: mlog=%s mlog-dir=%s", logger.GetMLogFormat().String(), logger.GetMLogDir()) + glog.D(logger.Warn).Warnf("Machine log config: mlog=%s", logger.ColorYellow("off")) + } + +} \ No newline at end of file diff --git a/cmd/geth/main.go b/cmd/geth/main.go index c2abfd814..95e1a1786 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -316,6 +316,7 @@ func geth(ctx *cli.Context) error { if ctx.GlobalString(LogStatusFlag.Name) != "off" { dispatchStatusLogs(ctx, ethe) } + logLoggingConfiguration(ctx) n.Wait() diff --git a/eth/api.go b/eth/api.go index 7b5119bfa..881e79896 100644 --- a/eth/api.go +++ b/eth/api.go @@ -1757,9 +1757,15 @@ func (api *PublicDebugAPI) Verbosity(n uint64) (int, error) { } if nint <= logger.Detail || nint == logger.Ridiculousness { glog.SetV(nint) - return int(*glog.GetVerbosity()), nil + i := int(*glog.GetVerbosity()) + glog.V(logger.Warn).Infof("Set verbosity: %d", i) + glog.D(logger.Warn).Warnf("Set verbosity: %d", i) + return i, nil } - return -1, errors.New("invalid logging level") + e := errors.New("invalid logging level") + glog.V(logger.Warn).Errorf("Set verbosity failed: %d (%v)", nint, e) + glog.D(logger.Error).Errorf("Set verbosity failed: %d (%v)", nint, e) + return -1, e } // Vmodule implements api method debug_vmodule, enabling setting @@ -1769,7 +1775,15 @@ func (api *PublicDebugAPI) Vmodule(s string) (string, error) { return glog.GetVModule().String(), nil } err := glog.GetVModule().Set(s) - return glog.GetVModule().String(), err + ns := glog.GetVModule().String() + if err != nil { + glog.V(logger.Warn).Infof("Set vmodule: '%s'", ns) + glog.D(logger.Warn).Warnf("Set vmodule: '%s'", ns) + } else { + glog.V(logger.Warn).Infof("Set vmodule failed: '%s' (%v)", ns, err) + glog.D(logger.Warn).Warnf("Set vmodule: '%s' (%v)", ns, err) + } + return ns, err } // ExecutionResult groups all structured logs emitted by the EVM diff --git a/logger/glog/glog.go b/logger/glog/glog.go index b76ce6a7d..23332314d 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -127,6 +127,10 @@ import ( // to-file (debug) logging. var DefaultVerbosity = 5 +// DefaultDisplay establishes the default verbosity Level for +// display (stderr) logging. +var DefaultDisplay = 3 + // DefaultToStdErr establishes the default bool toggling whether logging // should be directed ONLY to stderr. var DefaultToStdErr = false @@ -651,7 +655,7 @@ func init() { logging.severityTraceThreshold.set(2) // Default for verbosity. - logging.setVState(5, nil, false) + logging.setVState(Level(DefaultVerbosity), nil, false) go logging.flushDaemon() display.logTName = displayLog @@ -672,7 +676,7 @@ func init() { // all Fatal, Error, Warn, and Info log levels. // Please don't use Fatal for display; again, Fatal logs should only go through file logging // (they will be printed to stderr anyway). - display.setVState(3, nil, false) + display.setVState(Level(DefaultDisplay), nil, false) go display.flushDaemon() } diff --git a/logger/mlog_file.go b/logger/mlog_file.go index e417f506a..a7e02e82a 100644 --- a/logger/mlog_file.go +++ b/logger/mlog_file.go @@ -34,10 +34,10 @@ import ( "time" ) -type mlogFormat uint +type mlogFormatT uint const ( - mLOGPlain mlogFormat = iota + 1 + mLOGPlain mlogFormatT = iota + 1 mLOGKV MLOGJSON ) @@ -45,8 +45,8 @@ const ( var ( // If non-empty, overrides the choice of directory in which to write logs. // See createLogDirs for the full list of possible destinations. - mLogDir *string = new(string) - mLogFormat mlogFormat = mLOGKV + mLogDir = new(string) + mLogFormat = MLOGJSON errMLogComponentUnavailable = errors.New("provided component name is unavailable") ErrUnkownMLogFormat = errors.New("unknown mlog format") @@ -68,7 +68,7 @@ var ( "DURATION": time.Minute + time.Second*3 + time.Millisecond*42, } - MLogStringToFormat = map[string]mlogFormat{ + MLogStringToFormat = map[string]mlogFormatT{ "plain": mLOGPlain, "kv": mLOGKV, "json": MLOGJSON, @@ -77,9 +77,21 @@ var ( // Global var set to false if "--mlog=off", used to simply/ // speed-up checks to avoid performance penalty if mlog is // off. - isMlogEnabled bool = true + isMlogEnabled bool ) +func (f mlogFormatT) String() string { + switch f { + case MLOGJSON: + return "json" + case mLOGKV: + return "kv" + case mLOGPlain: + return "plain" + } + panic(ErrUnkownMLogFormat) +} + // MLogT defines an mlog LINE type MLogT struct { Description string `json:"-"` @@ -181,7 +193,7 @@ func (c mlogComponent) Send(msg MLogT) { mlogRegLock.RUnlock() } -func (l *Logger) SendFormatted(format mlogFormat, level LogLevel, msg MLogT) { +func (l *Logger) SendFormatted(format mlogFormatT, level LogLevel, msg MLogT) { switch format { case mLOGKV: l.Sendln(level, msg.FormatKV()) @@ -202,10 +214,19 @@ func SetMLogDir(str string) { *mLogDir = str } -func SetMLogFormat(format mlogFormat) { +func GetMLogDir() string { + m := *mLogDir + return m +} + +func SetMLogFormat(format mlogFormatT) { mLogFormat = format } +func GetMLogFormat() mlogFormatT { + return mLogFormat +} + func SetMLogFormatFromString(formatString string) error { if f := MLogStringToFormat[formatString]; f < 1 { return ErrUnkownMLogFormat @@ -215,10 +236,6 @@ func SetMLogFormatFromString(formatString string) error { return nil } -func GetMLogFormat() mlogFormat { - return mLogFormat -} - func createLogDirs() error { if *mLogDir != "" { return os.MkdirAll(*mLogDir, os.ModePerm) From 738df1c24a982b682288188a42b847ce7173195a Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 12 Dec 2017 16:07:30 -0600 Subject: [PATCH 26/60] Revert "the one where I install the progress bar for exporting" This reverts commit d1d37df104fe467b69a08b9f2b1a7cab92308c5a. --- core/blockchain.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 89fe060c0..b9d4655b4 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -45,7 +45,6 @@ import ( "github.com/ethereumproject/go-ethereum/rlp" "github.com/ethereumproject/go-ethereum/trie" "github.com/hashicorp/golang-lru" - "gopkg.in/cheggaaa/pb.v1" ) var ( @@ -932,10 +931,6 @@ func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1) - - bar := pb.New(int(last)) - //bar.Format("[=>-]") - bar.Start() for nr := first; nr <= last; nr++ { block := self.GetBlockByNumber(nr) if block == nil { @@ -945,9 +940,7 @@ func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { if err := block.EncodeRLP(w); err != nil { return err } - bar.Increment() } - bar.Finish() return nil } From d0bfd8156bc2aa385b1bf696149d52b2c733680a Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 13 Dec 2017 20:48:56 -0600 Subject: [PATCH 27/60] fix: use FormatUint instead of Itoa --- cmd/geth/flag.go | 2 +- core/blockchain.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/geth/flag.go b/cmd/geth/flag.go index 527bc2daa..84e2d8ca9 100644 --- a/cmd/geth/flag.go +++ b/cmd/geth/flag.go @@ -779,7 +779,7 @@ func logChainConfiguration(ctx *cli.Context, config *core.SufficientChainConfig) } if chainIsCustom { - sn := strconv.Itoa(int(state.StartingNonce)) + sn := strconv.FormatUint(state.StartingNonce, 10) glog.V(logger.Info).Infof("State starting nonce: %s", logger.ColorGreen(sn)) glog.D(logger.Warn).Infof("State starting nonce: %s", logger.ColorGreen(sn)) } diff --git a/core/blockchain.go b/core/blockchain.go index b9d4655b4..dcc6a8c1c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -695,15 +695,15 @@ func (self *BlockChain) LoadLastState(dryrun bool) error { glog.V(logger.Warn).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd) glog.V(logger.Warn).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd) glog.D(logger.Warn).Infof("Local head header: #%s [%s…] TD=%s", - logger.ColorGreen(strconv.Itoa(int(self.hc.CurrentHeader().Number.Uint64()))), + logger.ColorGreen(strconv.FormatUint(self.hc.CurrentHeader().Number.Uint64(), 10)), logger.ColorGreen(self.hc.CurrentHeader().Hash().Hex()[:8]), logger.ColorGreen(fmt.Sprintf("%v", headerTd))) glog.D(logger.Warn).Infof("Local head full block: #%s [%s…] TD=%s", - logger.ColorGreen(strconv.Itoa(int(self.currentBlock.Number().Uint64()))), + logger.ColorGreen(strconv.FormatUint(self.currentBlock.Number().Uint64(), 10)), logger.ColorGreen(self.currentBlock.Hash().Hex()[:8]), logger.ColorGreen(fmt.Sprintf("%v", blockTd))) glog.D(logger.Warn).Infof("Local head fast block: #%s [%s…] TD=%s", - logger.ColorGreen(strconv.Itoa(int(self.currentFastBlock.Number().Uint64()))), + logger.ColorGreen(strconv.FormatUint(self.currentFastBlock.Number().Uint64(), 10)), logger.ColorGreen(self.currentFastBlock.Hash().Hex()[:8]), logger.ColorGreen(fmt.Sprintf("%v", fastTd))) From bf0ccd948fab65eb3623203751b1b80509f56328 Mon Sep 17 00:00:00 2001 From: ia Date: Thu, 14 Dec 2017 17:45:12 -0600 Subject: [PATCH 28/60] problem: logging output doesn't match initial spec solution: revise log output fmt to fit spec --- cmd/geth/cmd.go | 359 ------------------------ cmd/geth/log_dispatch.go | 579 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 579 insertions(+), 359 deletions(-) create mode 100644 cmd/geth/log_dispatch.go diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index 22548b777..7474d4006 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -22,7 +22,6 @@ import ( "fmt" "io" "io/ioutil" - "math/big" "os" "os/signal" "path/filepath" @@ -37,7 +36,6 @@ import ( "github.com/ethereumproject/go-ethereum/core/state" "github.com/ethereumproject/go-ethereum/core/types" "github.com/ethereumproject/go-ethereum/eth" - "github.com/ethereumproject/go-ethereum/eth/downloader" "github.com/ethereumproject/go-ethereum/event" "github.com/ethereumproject/go-ethereum/logger" "github.com/ethereumproject/go-ethereum/logger/glog" @@ -745,363 +743,6 @@ func version(ctx *cli.Context) error { return nil } -// availableLogStatusFeatures stores state of implemented log STATUS features. -// New features should be registered here, and their status updates by dispatchStatusLogs if in use (to avoid dupe goroutine logging). -var availableLogStatusFeatures = map[string]time.Duration{ - "sync": time.Duration(0), -} - -type lsMode int - -const ( - lsModeDiscover lsMode = iota - lsModeFullSync - lsModeFastSync - lsModeImport -) - -var lsModeName = []string{ - "Discover", - "FullSync", - "FastSync", - "Import", -} - -var lsModeIcon = []string{ - "", - "︎◉", - "◎", - "▶︎", -} - -// dispatchStatusLogs handle parsing --log-status=argument and toggling appropriate goroutine status feature logging. -func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { - flagName := aliasableName(LogStatusFlag.Name, ctx) - v := ctx.GlobalString(flagName) - if v == "" { - glog.Fatalf("%v: %v", flagName, ErrInvalidFlag) - } - - parseStatusInterval := func(statusModule string, interval string) (tickerInterval time.Duration) { - upcaseModuleName := strings.ToUpper(statusModule) - if interval != "" { - if ti, err := parseDuration(interval); err != nil { - glog.Fatalf("%s %v: could not parse argument: %v", upcaseModuleName, err, interval) - } else { - tickerInterval = ti - } - } - //glog.V(logger.Info).Infof("Rolling %s log interval set: %v", upcaseModuleName, tickerInterval) - return tickerInterval - } - - for _, p := range strings.Split(v, ",") { - // Ignore hanging or double commas - if p == "" { - continue - } - - // If possible, split sync=60 into ["sync", "60"], otherwise yields ["sync"], ["60"], or ["someothernonsense"] - eqs := strings.Split(p, "=") - if len(eqs) < 2 { - glog.Errorf("Invalid log status value: %v. Must be comma-separated pairs of module=interval.", eqs) - os.Exit(1) - } - - // Catch unavailable and duplicate status feature logs - if status, ok := availableLogStatusFeatures[eqs[0]]; !ok { - glog.Errorf("%v: %v: unavailable status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) - os.Exit(1) - } else if status.Seconds() != 0 { - glog.Errorf("%v: %v: duplicate status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) - os.Exit(1) - } - - // If user just uses "sync" instead of "sync=42", append empty string and delegate to each status log function how to handle it - if len(eqs) == 1 { - eqs = append(eqs, "") - } - - d := parseStatusInterval(eqs[0], eqs[1]) - - switch eqs[0] { - case "sync": - availableLogStatusFeatures["sync"] = d - go runStatusSyncLogs(ctx, ethe, d, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) - } - } -} - -// runStatusSyncLogs starts STATUS SYNC logging at a given interval. -// It should be run as a goroutine. -// eg. --log-status="sync=42" logs SYNC information every 42 seconds -func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) { - - // Set up ticker based on established interval. - ticker := time.NewTicker(tickerInterval) - var chainEventLastSent time.Time - - // Bookmark vars. - var lastLoggedBlockNumber uint64 - - var lsMode = lsModeDiscover // init - var lsModeN int - var lsModeDiscoverSpinners = []string{"➫", "➬", "➭"} - - var dominoes = []string{"🁣", "🁤", "🁥", "🁦", "🁭", "🁴", "🁻", "🁼", "🂃", "🂄", "🂋", "🂌", "🂓"} // 🁣🁤🁥🁦🁭🁴🁻🁼🂃🂄🂋🂌🂓 - chainIcon := "◼⋯⋯" + logger.ColorGreen("◼") - forkIcon := "◼⋯⦦" + logger.ColorGreen("◼") - headerIcon := "◼⋯⋯" + logger.ColorGreen("❐") - downloaderIcon := "◼⋯⋯" + logger.ColorGreen("⬇") - minedIcon := "◼⋯⋯" + logger.ColorGreen("⟠") - - var sigc = make(chan os.Signal, 1) - signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) - defer signal.Stop(sigc) - - // Should listen for events. - // Proof of concept create event subscription - ethEvents := e.EventMux().Subscribe( - // ChainEvent is called when a single block is inserted into the local blockchain. - // core.ChainEvent{}, - // ChainSideEvent is called when a forked block is inserted into the local blockchain. - core.ChainSideEvent{}, - // NewMinedBlockEvent is called when a new block is mined locally. - core.NewMinedBlockEvent{}, - // ChainInsertEvent is called when a batch of block is finished processing through the bc.InsertChain fn. - // It includes statistics. Processed, queued, ignored, txcount, etc. - core.ChainInsertEvent{}, - // HeaderChainInsertEvent is called when headers are inserted into the headerchain, ie. fastsync. - core.HeaderChainInsertEvent{}, - // StartEvent is called when a peer is selected for synchronisation and sync begins. - downloader.StartEvent{}, - // DoneEvent is called when synchronisation with a peer finishes without error. - downloader.DoneEvent{}, - // FailedEvent is called when synchronisation with a peer finishes with an error. - downloader.FailedEvent{}, - ) - greenParenify := func(s string) string { - return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫") - } - redParenify := func(s string) string { - return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") - } - handleDownloaderEvent := func(e interface{}) { - s := downloaderIcon + " " - switch d := e.(type) { - case downloader.StartEvent: - s += "Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) - glog.D(logger.Info).Infoln(s) - case downloader.DoneEvent: - s += "Done " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) - glog.D(logger.Info).Infoln(s) - case downloader.FailedEvent: - s += "Fail " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error()) - glog.D(logger.Info).Warnln(s) - } - } - - go func() { - for e := range ethEvents.Chan() { - switch d := e.Data.(type) { - // case core.ChainEvent: - // glog.D(logger.Info).Infof("chainevent time=%v block=%v", e.Time, d.Block.NumberU64()) - case core.ChainInsertEvent: - glog.D(logger.Info).Infof(chainIcon+" Insert "+logger.ColorGreen("blocks")+"=%s "+logger.ColorGreen("◼")+"=%s "+logger.ColorGreen("took")+"=%s", - greenParenify(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)), - greenParenify(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))), - greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))), - ) - if bool(glog.D(logger.Info)) { - chainEventLastSent = time.Now() - } - case core.ChainSideEvent: - glog.D(logger.Info).Infof(forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9]))) - case core.HeaderChainInsertEvent: - glog.D(logger.Info).Infof(headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s", - greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)), - greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])), - greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))), - ) - if bool(glog.D(logger.Info)) { - chainEventLastSent = time.Now() - } - case core.NewMinedBlockEvent: - glog.D(logger.Info).Infof(minedIcon+" Mined "+logger.ColorGreen("◼")+"="+greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d", - d.Block.NumberU64(), - d.Block.Hash().Hex()[:9], - d.Block.Coinbase().Hex()[:9], - len(d.Block.Transactions()), - len(d.Block.Uncles()), - ))) - default: - handleDownloaderEvent(d) - } - } - }() - - printIntervalStatusLog := func() { - lenPeers := e.Downloader().GetPeers().Len() - - rtt, ttl, conf := e.Downloader().Qos() - confS := fmt.Sprintf("%01.2f", conf) - qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS) - - _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - mode := e.Downloader().GetMode() - if mode == downloader.FastSync { - current = e.BlockChain().CurrentFastBlock().NumberU64() - } - - // Get our head block - blockchain := e.BlockChain() - currentBlockHex := blockchain.CurrentBlock().Hash().Hex() - - // Discover -> not synchronising (searching for peers) - // FullSync/FastSync -> synchronising - // Import -> synchronising, at full height - fOfHeight := fmt.Sprintf("%7d", height) - - // Calculate and format percent sync of known height - heightRatio := float64(current) / float64(height) - heightRatio = heightRatio * 100 - fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) - - // Wait until syncing because real dl mode will not be engaged until then - lsMode = lsModeDiscover - if e.Downloader().Synchronising() { - switch mode { - case downloader.FullSync: - lsMode = lsModeFullSync - case downloader.FastSync: - lsMode = lsModeFastSync - currentBlockHex = blockchain.CurrentFastBlock().Hash().Hex() - } - } - importMode := lenPeers > 0 && lsMode == lsModeDiscover && current >= height && !(current == 0 && height == 0) - if importMode { - lsMode = lsModeImport - fOfHeight = "" // strings.Repeat(" ", 12) - fHeightRatio = "" // strings.Repeat(" ", 7) - } - if height == 0 { - fOfHeight = "" // strings.Repeat(" ", 12) - fHeightRatio = "" // strings.Repeat(" ", 7) - } - - // Calculate block stats for interval - numBlocksDiff := current - lastLoggedBlockNumber - numTxsDiff := 0 - mGas := new(big.Int) - - var numBlocksDiffPerSecond uint64 - var numTxsDiffPerSecond int - var mGasPerSecond = new(big.Int) - - var dominoGraph string - var nDom int - if numBlocksDiff > 0 && numBlocksDiff != current { - for i := lastLoggedBlockNumber + 1; i <= current; i++ { - b := blockchain.GetBlockByNumber(i) - if b != nil { - txLen := b.Transactions().Len() - // Add to tallies - numTxsDiff += txLen - mGas = new(big.Int).Add(mGas, b.GasUsed()) - // Domino effect - if lsMode == lsModeImport { - if txLen > len(dominoes)-1 { - // prevent slice out of bounds - txLen = len(dominoes) - 1 - } - if nDom <= 20 { - dominoGraph += dominoes[txLen] - } - nDom++ - } - } - } - if nDom > 20 { - dominoGraph += "…" - } - } - dominoGraph = logger.ColorGreen(dominoGraph) - - // Convert to per-second stats - // FIXME(?): Some degree of rounding will happen. - // For example, if interval is 10s and we get 6 blocks imported in that span, - // stats will show '0' blocks/second. Looks a little strange; but on the other hand, - // precision costs visual space, and normally just looks weird when starting up sync or - // syncing slowly. - numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds()) - - // Don't show initial current / per second val - if lastLoggedBlockNumber == 0 { - numBlocksDiffPerSecond = 0 - numBlocksDiff = 0 - } - - // Divide by interval to yield per-second stats - numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds()) - mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds()))) - mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) - mGasPerSecondI := mGasPerSecond.Int64() - - // Update last logged current block number - lastLoggedBlockNumber = current - - // Format head block hex for printing (eg. d4e…fa3) - cbhexstart := currentBlockHex[:9] // trim off '0x' prefix - - localHeadHeight := fmt.Sprintf("#%7d", current) - localHeadHex := fmt.Sprintf("%s…", cbhexstart) - peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) - domOrHeight := fOfHeight + " " + fHeightRatio - if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 { - domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight) - } else { - domOrHeight = "" - } - var blocksprocesseddisplay string - qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay) - if lsMode != lsModeImport { - blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI)) - } else { - blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64())) - domOrHeight = dominoGraph - qosDisplayable = "" - } - - // Log to ERROR. - headDisplay := greenParenify(localHeadHeight + " " + localHeadHex) - peerDisplay := greenParenify(peersOfMax) - - modeIcon := logger.ColorGreen(lsModeIcon[lsMode]) - if lsMode == lsModeDiscover { - modeIcon = lsModeDiscoverSpinners[lsModeN%3] - } - modeIcon = logger.ColorGreen(modeIcon) - lsModeN++ - - // This allows maximum user optionality for desired integration with rest of event-based logging. - glog.D(logger.Warn).Infof("SYNC %s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", lsModeName[lsMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) - } - - for { - select { - case <-ticker.C: - if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()/2))) { - printIntervalStatusLog() - } - case <-sigc: - // Listen for interrupt - ticker.Stop() - glog.D(logger.Warn).Warnln("SYNC Stopping.") - return - } - } -} - func stringInSlice(s string, sl []string) bool { for _, l := range sl { if l == s { diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go new file mode 100644 index 000000000..3c3fa6eb7 --- /dev/null +++ b/cmd/geth/log_dispatch.go @@ -0,0 +1,579 @@ +package main + +import ( + "fmt" + "github.com/ethereumproject/go-ethereum/core" + "github.com/ethereumproject/go-ethereum/core/types" + "github.com/ethereumproject/go-ethereum/eth" + "github.com/ethereumproject/go-ethereum/eth/downloader" + "github.com/ethereumproject/go-ethereum/logger" + "github.com/ethereumproject/go-ethereum/logger/glog" + "gopkg.in/urfave/cli.v1" + "math/big" + "os" + "os/signal" + "strings" + "syscall" + "time" + "strconv" +) + +// availableLogStatusFeatures stores state of implemented log STATUS features. +// New features should be registered here, and their status updates by dispatchStatusLogs if in use (to avoid dupe goroutine logging). +var availableLogStatusFeatures = map[string]time.Duration{ + "sync": time.Duration(0), +} + +type lsMode int + +const ( + lsModeDiscover lsMode = iota + lsModeFullSync + lsModeFastSync + lsModeImport +) + +var lsModeName = []string{ + "Discover", + "Sync", + "Fast", + "Import", +} + +var lsModeIcon = []string{ + "", + "︎◉", + "◎", + "▶︎", +} + +var dominoes = []string{"🁣", "🁤", "🁥", "🁦", "🁭", "🁴", "🁻", "🁼", "🂃", "🂄", "🂋", "🂌", "🂓"} // 🁣🁤🁥🁦🁭🁴🁻🁼🂃🂄🂋🂌🂓 +var chainIcon = "◼⋯⋯" + logger.ColorGreen("◼") +var forkIcon = "◼⋯⦦" + logger.ColorGreen("◼") +var headerIcon = "◼⋯⋯" + logger.ColorGreen("❐") +var downloaderIcon = "◼⋯⋯" + logger.ColorGreen("⬇") +var minedIcon = "◼⋯⋯" + logger.ColorGreen("⟠") +var lsModeDiscoverSpinners = []string{"➫", "➬", "➭"} + +// dispatchStatusLogs handle parsing --log-status=argument and toggling appropriate goroutine status feature logging. +func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { + flagName := aliasableName(LogStatusFlag.Name, ctx) + v := ctx.GlobalString(flagName) + if v == "" { + glog.Fatalf("%v: %v", flagName, ErrInvalidFlag) + } + + parseStatusInterval := func(statusModule string, interval string) (tickerInterval time.Duration) { + upcaseModuleName := strings.ToUpper(statusModule) + if interval != "" { + if ti, err := parseDuration(interval); err != nil { + glog.Fatalf("%s %v: could not parse argument: %v", upcaseModuleName, err, interval) + } else { + tickerInterval = ti + } + } + //glog.V(logger.Info).Infof("Rolling %s log interval set: %v", upcaseModuleName, tickerInterval) + return tickerInterval + } + + for _, p := range strings.Split(v, ",") { + // Ignore hanging or double commas + if p == "" { + continue + } + + // If possible, split sync=60 into ["sync", "60"], otherwise yields ["sync"], ["60"], or ["someothernonsense"] + eqs := strings.Split(p, "=") + if len(eqs) < 2 { + glog.Errorf("Invalid log status value: %v. Must be comma-separated pairs of module=interval.", eqs) + os.Exit(1) + } + + // Catch unavailable and duplicate status feature logs + if status, ok := availableLogStatusFeatures[eqs[0]]; !ok { + glog.Errorf("%v: %v: unavailable status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) + os.Exit(1) + } else if status.Seconds() != 0 { + glog.Errorf("%v: %v: duplicate status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) + os.Exit(1) + } + + // If user just uses "sync" instead of "sync=42", append empty string and delegate to each status log function how to handle it + if len(eqs) == 1 { + eqs = append(eqs, "") + } + + d := parseStatusInterval(eqs[0], eqs[1]) + + switch eqs[0] { + case "sync": + availableLogStatusFeatures["sync"] = d + go runStatusSyncLogs(ctx, ethe, d) + } + } +} + +// runStatusSyncLogs starts STATUS SYNC logging at a given interval. +// It should be run as a goroutine. +// eg. --log-status="sync=42" logs SYNC information every 42 seconds +func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Duration) { + + // Set up ticker based on established interval. + ticker := time.NewTicker(tickerInterval) + var chainEventLastSent time.Time + + // Bookmark vars. + var lastLoggedBlockNumber uint64 + var lastLoggedMode = lsModeDiscover + + var sigc = make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) + defer signal.Stop(sigc) + + // Should listen for events. + // Proof of concept create event subscription + ethEvents := e.EventMux().Subscribe( + //// ChainEvent is called when a single block is inserted into the local blockchain. + //// core.ChainEvent{}, + //// ChainSideEvent is called when a forked block is inserted into the local blockchain. + //core.ChainSideEvent{}, + //// NewMinedBlockEvent is called when a new block is mined locally. + //core.NewMinedBlockEvent{}, + // ChainInsertEvent is called when a batch of block is finished processing through the bc.InsertChain fn. + // It includes statistics. Processed, queued, ignored, txcount, etc. + core.ChainInsertEvent{}, + //// HeaderChainInsertEvent is called when headers are inserted into the headerchain, ie. fastsync. + //core.HeaderChainInsertEvent{}, + // StartEvent is called when a peer is selected for synchronisation and sync begins. + downloader.StartEvent{}, + // DoneEvent is called when synchronisation with a peer finishes without error. + downloader.DoneEvent{}, + // FailedEvent is called when synchronisation with a peer finishes with an error. + downloader.FailedEvent{}, + ) + greenParenify := func(s string) string { + return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫") + } + //redParenify := func(s string) string { + // return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") + //} + handleDownloaderEvent := func(ev interface{}) { + //s := downloaderIcon + " " + switch ev.(type) { + //switch d := ev.(type) { + case downloader.StartEvent: + lastLoggedMode = getLogStatusMode(e) + //s += "Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + //glog.D(logger.Info).Infoln(s) + case downloader.DoneEvent: + lastLoggedMode = getLogStatusMode(e) + //s += "Done " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + //glog.D(logger.Info).Infoln(s) + case downloader.FailedEvent: + lastLoggedMode = getLogStatusMode(e) + //s += "Fail " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error()) + //glog.D(logger.Info).Warnln(s) + } + } + + go func() { + for ev := range ethEvents.Chan() { + switch d := ev.Data.(type) { + // case core.ChainEvent: + // glog.D(logger.Info).Infof("chainevent time=%v block=%v", e.Time, d.Block.NumberU64()) + case core.ChainInsertEvent: + if lastLoggedMode == lsModeImport { + lastLoggedBlockNumber = delegateStatusLogs(ctx, e, lastLoggedMode, lastLoggedBlockNumber, tickerInterval) + } + //glog.D(logger.Info).Infof(chainIcon+" Insert "+logger.ColorGreen("blocks")+"=%s "+logger.ColorGreen("◼")+"=%s "+logger.ColorGreen("took")+"=%s", + // greenParenify(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)), + // greenParenify(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))), + // greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))), + //) + //if bool(glog.D(logger.Info)) { + // chainEventLastSent = time.Now() + //} + case core.ChainSideEvent: + glog.D(logger.Info).Infof(forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9]))) + case core.HeaderChainInsertEvent: + glog.D(logger.Info).Infof(headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s", + greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)), + greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])), + greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))), + ) + if bool(glog.D(logger.Info)) { + chainEventLastSent = time.Now() + } + case core.NewMinedBlockEvent: + glog.D(logger.Info).Infof(minedIcon + " Mined " + logger.ColorGreen("◼") + "=" + greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d", + d.Block.NumberU64(), + d.Block.Hash().Hex()[:9], + d.Block.Coinbase().Hex()[:9], + len(d.Block.Transactions()), + len(d.Block.Uncles()), + ))) + default: + handleDownloaderEvent(d) + } + } + }() + + // Initial log line so user knows is active. + lastLoggedBlockNumber = delegateStatusLogs(ctx, e, lastLoggedMode, lastLoggedBlockNumber, tickerInterval) + + for { + select { + case <-ticker.C: + if lastLoggedMode != lsModeImport { + lastLoggedBlockNumber = delegateStatusLogs(ctx, e, lastLoggedMode, lastLoggedBlockNumber, tickerInterval) + } + case <-sigc: + // Listen for interrupt + ticker.Stop() + glog.D(logger.Warn).Warnln("SYNC Stopping.") + return + } + } +} + +func getLogStatusMode(e *eth.Ethereum) lsMode { + if e.Downloader().Synchronising() { + switch e.Downloader().GetMode() { + case downloader.FullSync: + return lsModeFullSync + case downloader.FastSync: + return lsModeFastSync + } + } + _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + if e.Downloader().GetPeers().Len() > 0 && current >= height && !(current == 0 && height == 0) { + return lsModeImport + } + return lsModeDiscover +} + +//func formatTabbedString(scanLn string, minwidth, tabwidth, padding int, padchar byte, flags uint) string { +// w := new(tabwriter.Writer) +// buf := bytes.NewBufferString("") +// w.Init(buf, minwidth, tabwidth, padding, padchar, flags) +// if w == nil { +// return "" +// } +// fmt.Fprintf(w, scanLn) +// w.Flush() // ignore error +// return buf.String() +//} + +// Spec: +//2017-02-03 16:44:00 Discover 0/25 peers +//2017-02-03 16:45:00 Discover 1/25 peers +//2017-02-03 16:46:00 Fast #2481951 of #3124363 79.4% 1211/ 554 blk/mgas sec 6/25 peers +//2017-02-03 16:47:00 Fast #2689911 of #3124363 86.1% 611/ 981 blk/mgas sec 6/25 peers +//2017-02-03 16:48:00 Fast #2875913 of #3124363 92.0% 502/ 760 blk/mgas sec 4/25 peers +//2017-02-03 16:49:00 Sync #3124227 of #3124363 c76c34e7 77/ 242/ 7 blk/tx/mgas sec 4/25 peers +//2017-02-03 16:50:00 Sync #3124247 of #3124363 75e48eff 51/ 51/ 5 blk/tx/mgas sec 4/25 peers +//2017-02-03 16:51:00 Sync #3124567 of #3124363 9af334ae 117/ 129/11 blk/tx/mgas sec 5/25 peers +//2017-02-03 16:52:00 Sync #3124787 of #3124363 1e3a8351 9/ 6/ 1 blk/tx/mgas sec 7/25 peers +//2017-02-03 16:52:05 Import #3124788 84e11ff4 15/ 7 tx/mgas 10/25 peers +//2017-02-03 16:52:25 Import #3124789 9e45a241 5/ 1 tx/mgas 12/25 peers +//2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers + + +func formatBlockNumber(i uint64) string { + return "#"+strconv.FormatUint(i, 10) +} + +// Examples of spec'd output. +var xlocalOfMaxD = "#92481951 of #93124363" // #2481951 of #3124363 +//var xpercentD = " 92.0%" // 92.0% +var xlocalHeadHashD = "c76c34e7" // c76c34e7 +var xprogressRateD = " 117/ 129/ 11" // 117/ 129/11 +var xprogressRateUnitsD = "blk/txs/mgas sec" // blk/tx/mgas sec +var xpeersD = "18/25 peers" // 18/25 peers + +func strScanLenOf(s string, leftAlign bool) string { + if leftAlign { + return "%" + strconv.Itoa(len(s)) + "s" + } + return "%-" + strconv.Itoa(len(s)) + "s" +} + +type printUnit struct { + value string + example string + leftAlign bool +} + +func (p *printUnit) String() string { + return fmt.Sprintf("%s", p.value) +} + +func calcBlockDiff(e *eth.Ethereum, lastLoggedBlockN uint64, localHead *types.Block) (blks, txs, mgas int) { + // Calculate block stats for interval + localHeadN := localHead.NumberU64() + blks = int(localHeadN - lastLoggedBlockN) + txs = 0 + mGas := new(big.Int) + + for i := lastLoggedBlockN + 1; i <= localHeadN; i++ { + b := e.BlockChain().GetBlockByNumber(i) + if b != nil { + // Add to tallies + txs += b.Transactions().Len() + mGas = mGas.Add(mGas, b.GasUsed()) + } + } + mGas.Div(mGas, big.NewInt(1000000)) + return blks, txs, int(mGas.Int64()) +} + +//getLogStatusMode(e).PrintBasic(e, interval, lastLoggedBlockN, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) +func (l lsMode) PrintBasic(e *eth.Ethereum, d time.Duration, lastLoggedBlockN uint64, maxPeers int) (uint64) { + + localOfMaxD := &printUnit{"", xlocalOfMaxD, true} + percentOrHash := &printUnit{"", xlocalHeadHashD, false} + progressRateD := &printUnit{"", xprogressRateD, false} // 117/ 129/11 + progressRateUnitsD := &printUnit{"", xprogressRateUnitsD, false} // blk/tx/mgas sec + peersD := &printUnit{"", xpeersD, false} // 18/25 peers + + formatLocalOfMaxD := func(localheight, syncheight uint64) string { + if localheight < syncheight { + return fmt.Sprintf("%9s of %9s", formatBlockNumber(localheight), formatBlockNumber(syncheight)) + } + return fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localheight)) + } + + formatPercentD := func(localheight, syncheight uint64) string { + // Calculate and format percent sync of known height + heightRatio := float64(localheight) / float64(syncheight) + heightRatio = heightRatio * 100 + fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) + return fmt.Sprintf(strScanLenOf(xlocalHeadHashD, false), fHeightRatio) + } + + formatBlockHashD := func(b *types.Block) string { + return b.Hash().Hex()[2:2+len(xlocalHeadHashD)] + } + + formatProgressRateD := func(blksN, txsN, mgasN int) string { + if blksN < 0 { + return fmt.Sprintf(" %4d/%2d", txsN, mgasN) + } + if txsN < 0 { + return fmt.Sprintf("%3d/ /%2d", blksN, mgasN) + } + return fmt.Sprintf("%3d/%4d/%2d", blksN, txsN, mgasN) + } + + formatPeersD := func(peersN, maxpeersN int) string { + return fmt.Sprintf("%2d/%2d peers", peersN, maxpeersN) + } + + formatOutput := func(printunits ...*printUnit) string { + o := []string{} + for _, u := range printunits { + o = append(o, strScanLenOf(u.example, u.leftAlign)) + } + return strings.Join(o, " ") + } + + peersD.value = formatPeersD(e.Downloader().GetPeers().Len(), maxPeers) + defer func() { + glog.D(logger.Warn).Infof("%-8s " + formatOutput(localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD), + lsModeName[l], localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) + + }() + if l == lsModeDiscover { + return lastLoggedBlockN + } + + origin, current, chainSyncHeight, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + mode := e.Downloader().GetMode() + if mode == downloader.FastSync { + current = e.BlockChain().CurrentFastBlock().NumberU64() + } + localHead := e.BlockChain().GetBlockByNumber(current) + + // Calculate progress rates + var blks, txs, mgas int + if lastLoggedBlockN == 0 { + blks, txs, mgas = calcBlockDiff(e, origin, localHead) + } else { + blks, txs, mgas = calcBlockDiff(e, lastLoggedBlockN, localHead) + } + + switch l { + case lsModeFastSync: + lh := localHead.NumberU64() + localOfMaxD.value = formatLocalOfMaxD(lh, chainSyncHeight) + percentOrHash.value = formatPercentD(lh, chainSyncHeight) + progressRateD.value = formatProgressRateD(blks / int(d.Seconds()), -1, mgas / int(d.Seconds())) + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/ /mgas sec") + case lsModeFullSync: + localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) + percentOrHash.value = formatBlockHashD(localHead) + progressRateD.value = formatProgressRateD(blks / int(d.Seconds()), txs / int(d.Seconds()), mgas / int(d.Seconds())) + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/txs/mgas sec") + case lsModeImport: + localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) + percentOrHash.value = formatBlockHashD(localHead) + progressRateD.value = fmt.Sprintf(strScanLenOf(xprogressRateD, false), formatProgressRateD(-1, txs, mgas)) + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), " txs/mgas ") + default: + panic("unreachable") + } + return current +} + +func (l lsMode) PrintFancyGreen() { + +} + +func delegateStatusLogs(ctx *cli.Context, e *eth.Ethereum, mode lsMode, lastLoggedBlockN uint64, interval time.Duration) uint64 { + return mode.PrintBasic(e, interval, lastLoggedBlockN, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + + //lenPeers := e.Downloader().GetPeers().Len() + // + //rtt, ttl, conf := e.Downloader().Qos() + //confS := fmt.Sprintf("%01.2f", conf) + //qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS) + // + //_, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + //mode := e.Downloader().GetMode() + //if mode == downloader.FastSync { + // current = e.BlockChain().CurrentFastBlock().NumberU64() + //} + // + //// Get our head block + //blockchain := e.BlockChain() + //currentBlockHex := blockchain.CurrentBlock().Hash().Hex() + // + //// Discover -> not synchronising (searching for peers) + //// FullSync/FastSync -> synchronising + //// Import -> synchronising, at full height + //fOfHeight := fmt.Sprintf("%7d", height) + // + //// Calculate and format percent sync of known height + //heightRatio := float64(current) / float64(height) + //heightRatio = heightRatio * 100 + //fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) + // + //// Wait until syncing because real dl mode will not be engaged until then + //lsMode = lsModeDiscover + //if e.Downloader().Synchronising() { + // switch mode { + // case downloader.FullSync: + // lsMode = lsModeFullSync + // case downloader.FastSync: + // lsMode = lsModeFastSync + // currentBlockHex = blockchain.CurrentFastBlock().Hash().Hex() + // } + //} + //importMode := lenPeers > 0 && lsMode == lsModeDiscover && current >= height && !(current == 0 && height == 0) + //if importMode { + // lsMode = lsModeImport + // fOfHeight = "" // strings.Repeat(" ", 12) + // fHeightRatio = "" // strings.Repeat(" ", 7) + //} + //if height == 0 { + // fOfHeight = "" // strings.Repeat(" ", 12) + // fHeightRatio = "" // strings.Repeat(" ", 7) + //} + // + //// Calculate block stats for interval + //numBlocksDiff := current - lastLoggedBlockN + //numTxsDiff := 0 + //mGas := new(big.Int) + // + //var numBlocksDiffPerSecond uint64 + //var numTxsDiffPerSecond int + //var mGasPerSecond = new(big.Int) + // + //var dominoGraph string + //var nDom int + //if numBlocksDiff > 0 && numBlocksDiff != current { + // for i := lastLoggedBlockN + 1; i <= current; i++ { + // b := blockchain.GetBlockByNumber(i) + // if b != nil { + // txLen := b.Transactions().Len() + // // Add to tallies + // numTxsDiff += txLen + // mGas = new(big.Int).Add(mGas, b.GasUsed()) + // // Domino effect + // if lsMode == lsModeImport { + // if txLen > len(dominoes)-1 { + // // prevent slice out of bounds + // txLen = len(dominoes) - 1 + // } + // if nDom <= 20 { + // dominoGraph += dominoes[txLen] + // } + // nDom++ + // } + // } + // } + // if nDom > 20 { + // dominoGraph += "…" + // } + //} + //dominoGraph = logger.ColorGreen(dominoGraph) + // + //// Convert to per-second stats + //// FIXME(?): Some degree of rounding will happen. + //// For example, if interval is 10s and we get 6 blocks imported in that span, + //// stats will show '0' blocks/second. Looks a little strange; but on the other hand, + //// precision costs visual space, and normally just looks weird when starting up sync or + //// syncing slowly. + //numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds()) + // + //// Don't show initial current / per second val + //if lastLoggedBlockNumber == 0 { + // numBlocksDiffPerSecond = 0 + // numBlocksDiff = 0 + //} + // + //// Divide by interval to yield per-second stats + //numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds()) + //mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds()))) + //mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) + //mGasPerSecondI := mGasPerSecond.Int64() + // + //// Update last logged current block number + //lastLoggedBlockNumber = current + // + //// Format head block hex for printing (eg. d4e…fa3) + //cbhexstart := currentBlockHex[:9] // trim off '0x' prefix + // + //localHeadHeight := fmt.Sprintf("#%7d", current) + //localHeadHex := fmt.Sprintf("%s…", cbhexstart) + //peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) + //domOrHeight := fOfHeight + " " + fHeightRatio + //if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 { + // domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight) + //} else { + // domOrHeight = "" + //} + //var blocksprocesseddisplay string + //qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay) + //if lsMode != lsModeImport { + // blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI)) + //} else { + // blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64())) + // domOrHeight = dominoGraph + // qosDisplayable = "" + //} + // + //// Log to ERROR. + //headDisplay := greenParenify(localHeadHeight + " " + localHeadHex) + //peerDisplay := greenParenify(peersOfMax) + // + //modeIcon := logger.ColorGreen(lsModeIcon[lsMode]) + //if lsMode == lsModeDiscover { + // modeIcon = lsModeDiscoverSpinners[lsModeN%3] + //} + //modeIcon = logger.ColorGreen(modeIcon) + //lsModeN++ + // + //// This allows maximum user optionality for desired integration with rest of event-based logging. + //glog.D(logger.Warn).Infof("SYNC %s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", lsModeName[lsMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) +} From 7532f0af1471025ec207fd35a7693c903f8e1644 Mon Sep 17 00:00:00 2001 From: ia Date: Fri, 15 Dec 2017 11:54:54 -0600 Subject: [PATCH 29/60] problem: log display is inflexible and very hard coded solution: begin abstracting to shareable interfaces and pluggability --- cmd/geth/log_dispatch.go | 696 +++++++++++++++++++++--------------- cmd/geth/migrate_datadir.go | 2 +- 2 files changed, 412 insertions(+), 286 deletions(-) diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 3c3fa6eb7..24dec2c4c 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -2,6 +2,14 @@ package main import ( "fmt" + "math/big" + "os" + "os/signal" + "strconv" + "strings" + "syscall" + "time" + "github.com/ethereumproject/go-ethereum/core" "github.com/ethereumproject/go-ethereum/core/types" "github.com/ethereumproject/go-ethereum/eth" @@ -9,13 +17,6 @@ import ( "github.com/ethereumproject/go-ethereum/logger" "github.com/ethereumproject/go-ethereum/logger/glog" "gopkg.in/urfave/cli.v1" - "math/big" - "os" - "os/signal" - "strings" - "syscall" - "time" - "strconv" ) // availableLogStatusFeatures stores state of implemented log STATUS features. @@ -24,7 +25,7 @@ var availableLogStatusFeatures = map[string]time.Duration{ "sync": time.Duration(0), } -type lsMode int +type lsMode uint const ( lsModeDiscover lsMode = iota @@ -33,6 +34,11 @@ const ( lsModeImport ) +// Global bookmark vars. +var currentMode = lsModeDiscover +var currentBlockNumber uint64 +var chainEventLastSent time.Time + var lsModeName = []string{ "Discover", "Sync", @@ -55,6 +61,222 @@ var downloaderIcon = "◼⋯⋯" + logger.ColorGreen("⬇") var minedIcon = "◼⋯⋯" + logger.ColorGreen("⟠") var lsModeDiscoverSpinners = []string{"➫", "➬", "➭"} +func greenParenify(s string) string { + return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫") +} +func redParenify(s string) string { + return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") +} + +type displayEventHandlerFn func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) +type displayEventHandlerFns []displayEventHandlerFn + +type displayEventHandler struct { + eventName string // used for labeling events and matching to the switch statement + ev interface{} // which event to handle. if nil, will run on the ticker. + // (ctx *cli.Context, e *eth.Ethereum, evData interface{}, mode *lsMode, tickerInterval time.Duration, n *uint64) + handlers displayEventHandlerFns +} +type displayEventHandlers []displayEventHandler + +func (hs displayEventHandlers) getByName(name string) (*displayEventHandler, bool) { + for _, h := range hs { + if h.eventName == name { + return &h, true + } + } + return nil, false +} + +func updateLogStatusModeHandler(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + currentMode = getLogStatusMode(e) +} + +var basicDisplaySystem = displayEventHandlers{ + { + eventName: "CHAIN_INSERT", + ev: core.ChainInsertEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + if currentMode == lsModeImport { + currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + } + }, + }, + }, + { + eventName: "DOWNLOADER_START", + ev: downloader.StartEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + }, + }, + { + eventName: "DOWNLOADER_DONE", + ev: downloader.DoneEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + }, + }, + { + eventName: "DOWNLOADER_FAILED", + ev: downloader.FailedEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + }, + }, + { + eventName: "INTERVAL", + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + if currentMode != lsModeImport { + currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + } + }, + }, + }, +} + +var greenDisplaySystem = displayEventHandlers{ + { + eventName: "CHAIN_INSERT", + ev: core.ChainInsertEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.ChainInsertEvent: + glog.D(logger.Info).Infof(chainIcon+" Insert "+logger.ColorGreen("blocks")+"=%s "+logger.ColorGreen("◼")+"=%s "+logger.ColorGreen("took")+"=%s", + greenParenify(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)), + greenParenify(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))), + greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))), + ) + if bool(glog.D(logger.Info)) { + chainEventLastSent = time.Now() + } + } + }, + }, + }, + { + eventName: "CHAIN_SIDE", + ev: core.ChainSideEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.ChainSideEvent: + glog.D(logger.Info).Infof(forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9]))) + } + }, + }, + }, + { + eventName: "HEADERCHAIN_INSERT", + ev: core.HeaderChainInsertEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.HeaderChainInsertEvent: + glog.D(logger.Info).Infof(headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s", + greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)), + greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])), + greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))), + ) + if bool(glog.D(logger.Info)) { + chainEventLastSent = time.Now() + } + } + }, + }, + }, + { + eventName: "MINED_BLOCK", + ev: core.NewMinedBlockEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.NewMinedBlockEvent: + glog.D(logger.Info).Infof(minedIcon + " Mined " + logger.ColorGreen("◼") + "=" + greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d", + d.Block.NumberU64(), + d.Block.Hash().Hex()[:9], + d.Block.Coinbase().Hex()[:9], + len(d.Block.Transactions()), + len(d.Block.Uncles()), + ))) + } + }, + }, + }, + { + eventName: "DOWNLOADER_START", + ev: downloader.StartEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case downloader.StartEvent: + s := downloaderIcon + " Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + glog.D(logger.Info).Warnln(s) + } + }, + }, + }, + { + eventName: "DOWNLOADER_DONE", + ev: downloader.DoneEvent{}, + //s += + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case downloader.DoneEvent: + s := downloaderIcon + " Done " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + glog.D(logger.Info).Warnln(s) + } + }, + }, + }, + { + eventName: "DOWNLOADER_FAILED", + ev: downloader.FailedEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case downloader.FailedEvent: + s := downloaderIcon + " Fail " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error()) + glog.D(logger.Info).Warnln(s) + } + }, + }, + }, + { + eventName: "INTERVAL", + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()/2))) { + currentBlockNumber = PrintStatusGreen(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + } + }, + }, + }, +} + +func getLogStatusMode(e *eth.Ethereum) lsMode { + if e.Downloader().Synchronising() { + switch e.Downloader().GetMode() { + case downloader.FullSync: + return lsModeFullSync + case downloader.FastSync: + return lsModeFastSync + } + } + _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + if e.Downloader().GetPeers().Len() > 0 && current >= height && !(current == 0 && height == 0) { + return lsModeImport + } + return lsModeDiscover +} + // dispatchStatusLogs handle parsing --log-status=argument and toggling appropriate goroutine status feature logging. func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { flagName := aliasableName(LogStatusFlag.Name, ctx) @@ -108,125 +330,71 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { switch eqs[0] { case "sync": availableLogStatusFeatures["sync"] = d - go runStatusSyncLogs(ctx, ethe, d) + go runDisplayLogs(ctx, ethe, d, greenDisplaySystem) } } } -// runStatusSyncLogs starts STATUS SYNC logging at a given interval. +func (hs *displayEventHandlers) runAllIfAny(ctx *cli.Context, e *eth.Ethereum, d interface{}, tickerInterval time.Duration, name string) { + if h, ok := hs.getByName(name); ok { + for _, handler := range h.handlers { + handler(ctx, e, d, tickerInterval) + } + } +} + +// runDisplayLogs starts STATUS SYNC logging at a given interval. // It should be run as a goroutine. // eg. --log-status="sync=42" logs SYNC information every 42 seconds -func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Duration) { +func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Duration, handles displayEventHandlers) { // Set up ticker based on established interval. ticker := time.NewTicker(tickerInterval) - var chainEventLastSent time.Time - - // Bookmark vars. - var lastLoggedBlockNumber uint64 - var lastLoggedMode = lsModeDiscover var sigc = make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigc) - // Should listen for events. - // Proof of concept create event subscription - ethEvents := e.EventMux().Subscribe( - //// ChainEvent is called when a single block is inserted into the local blockchain. - //// core.ChainEvent{}, - //// ChainSideEvent is called when a forked block is inserted into the local blockchain. - //core.ChainSideEvent{}, - //// NewMinedBlockEvent is called when a new block is mined locally. - //core.NewMinedBlockEvent{}, - // ChainInsertEvent is called when a batch of block is finished processing through the bc.InsertChain fn. - // It includes statistics. Processed, queued, ignored, txcount, etc. - core.ChainInsertEvent{}, - //// HeaderChainInsertEvent is called when headers are inserted into the headerchain, ie. fastsync. - //core.HeaderChainInsertEvent{}, - // StartEvent is called when a peer is selected for synchronisation and sync begins. - downloader.StartEvent{}, - // DoneEvent is called when synchronisation with a peer finishes without error. - downloader.DoneEvent{}, - // FailedEvent is called when synchronisation with a peer finishes with an error. - downloader.FailedEvent{}, - ) - greenParenify := func(s string) string { - return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫") + //// Should listen for events. + //// Proof of concept create event subscription + var handledEvents []interface{} + for _, h := range handles { + handledEvents = append(handledEvents, h.ev) } - //redParenify := func(s string) string { - // return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") - //} - handleDownloaderEvent := func(ev interface{}) { - //s := downloaderIcon + " " - switch ev.(type) { - //switch d := ev.(type) { + ethEvents := e.EventMux().Subscribe(handledEvents...) + + handleDownloaderEvent := func(d interface{}) { + switch d.(type) { case downloader.StartEvent: - lastLoggedMode = getLogStatusMode(e) - //s += "Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) - //glog.D(logger.Info).Infoln(s) + handles.runAllIfAny(ctx, e, d, tickerInterval, "DOWNLOADER_START") case downloader.DoneEvent: - lastLoggedMode = getLogStatusMode(e) - //s += "Done " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) - //glog.D(logger.Info).Infoln(s) + handles.runAllIfAny(ctx, e, d, tickerInterval, "DOWNLOADER_DONE") case downloader.FailedEvent: - lastLoggedMode = getLogStatusMode(e) - //s += "Fail " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error()) - //glog.D(logger.Info).Warnln(s) + handles.runAllIfAny(ctx, e, d, tickerInterval, "DOWNLOADER_FAILED") } } go func() { for ev := range ethEvents.Chan() { switch d := ev.Data.(type) { - // case core.ChainEvent: - // glog.D(logger.Info).Infof("chainevent time=%v block=%v", e.Time, d.Block.NumberU64()) case core.ChainInsertEvent: - if lastLoggedMode == lsModeImport { - lastLoggedBlockNumber = delegateStatusLogs(ctx, e, lastLoggedMode, lastLoggedBlockNumber, tickerInterval) - } - //glog.D(logger.Info).Infof(chainIcon+" Insert "+logger.ColorGreen("blocks")+"=%s "+logger.ColorGreen("◼")+"=%s "+logger.ColorGreen("took")+"=%s", - // greenParenify(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)), - // greenParenify(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))), - // greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))), - //) - //if bool(glog.D(logger.Info)) { - // chainEventLastSent = time.Now() - //} + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "CHAIN_INSERT") case core.ChainSideEvent: - glog.D(logger.Info).Infof(forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9]))) + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "CHAIN_SIDE") case core.HeaderChainInsertEvent: - glog.D(logger.Info).Infof(headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s", - greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)), - greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])), - greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))), - ) - if bool(glog.D(logger.Info)) { - chainEventLastSent = time.Now() - } + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "HEADERCHAIN_INSERT") case core.NewMinedBlockEvent: - glog.D(logger.Info).Infof(minedIcon + " Mined " + logger.ColorGreen("◼") + "=" + greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d", - d.Block.NumberU64(), - d.Block.Hash().Hex()[:9], - d.Block.Coinbase().Hex()[:9], - len(d.Block.Transactions()), - len(d.Block.Uncles()), - ))) + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "MINED_BLOCK") default: handleDownloaderEvent(d) } } }() - // Initial log line so user knows is active. - lastLoggedBlockNumber = delegateStatusLogs(ctx, e, lastLoggedMode, lastLoggedBlockNumber, tickerInterval) - for { select { case <-ticker.C: - if lastLoggedMode != lsModeImport { - lastLoggedBlockNumber = delegateStatusLogs(ctx, e, lastLoggedMode, lastLoggedBlockNumber, tickerInterval) - } + handles.runAllIfAny(ctx, e, nil, tickerInterval, "INTERVAL") case <-sigc: // Listen for interrupt ticker.Stop() @@ -236,34 +404,6 @@ func runStatusSyncLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Du } } -func getLogStatusMode(e *eth.Ethereum) lsMode { - if e.Downloader().Synchronising() { - switch e.Downloader().GetMode() { - case downloader.FullSync: - return lsModeFullSync - case downloader.FastSync: - return lsModeFastSync - } - } - _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - if e.Downloader().GetPeers().Len() > 0 && current >= height && !(current == 0 && height == 0) { - return lsModeImport - } - return lsModeDiscover -} - -//func formatTabbedString(scanLn string, minwidth, tabwidth, padding int, padchar byte, flags uint) string { -// w := new(tabwriter.Writer) -// buf := bytes.NewBufferString("") -// w.Init(buf, minwidth, tabwidth, padding, padchar, flags) -// if w == nil { -// return "" -// } -// fmt.Fprintf(w, scanLn) -// w.Flush() // ignore error -// return buf.String() -//} - // Spec: //2017-02-03 16:44:00 Discover 0/25 peers //2017-02-03 16:45:00 Discover 1/25 peers @@ -278,18 +418,17 @@ func getLogStatusMode(e *eth.Ethereum) lsMode { //2017-02-03 16:52:25 Import #3124789 9e45a241 5/ 1 tx/mgas 12/25 peers //2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers - func formatBlockNumber(i uint64) string { - return "#"+strconv.FormatUint(i, 10) + return "#" + strconv.FormatUint(i, 10) } // Examples of spec'd output. -var xlocalOfMaxD = "#92481951 of #93124363" // #2481951 of #3124363 +var xlocalOfMaxD = "#92481951 of #93124363" // #2481951 of #3124363 //var xpercentD = " 92.0%" // 92.0% -var xlocalHeadHashD = "c76c34e7" // c76c34e7 -var xprogressRateD = " 117/ 129/ 11" // 117/ 129/11 +var xlocalHeadHashD = "c76c34e7" // c76c34e7 +var xprogressRateD = " 117/ 129/ 11" // 117/ 129/11 var xprogressRateUnitsD = "blk/txs/mgas sec" // blk/tx/mgas sec -var xpeersD = "18/25 peers" // 18/25 peers +var xpeersD = "18/25 peers" // 18/25 peers func strScanLenOf(s string, leftAlign bool) string { if leftAlign { @@ -299,8 +438,8 @@ func strScanLenOf(s string, leftAlign bool) string { } type printUnit struct { - value string - example string + value string + example string leftAlign bool } @@ -327,14 +466,17 @@ func calcBlockDiff(e *eth.Ethereum, lastLoggedBlockN uint64, localHead *types.Bl return blks, txs, int(mGas.Int64()) } -//getLogStatusMode(e).PrintBasic(e, interval, lastLoggedBlockN, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) -func (l lsMode) PrintBasic(e *eth.Ethereum, d time.Duration, lastLoggedBlockN uint64, maxPeers int) (uint64) { +// PrintStatusBasic implements the displayStatusPrinter interface +var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { + + l := currentMode + lastLoggedBlockN := currentBlockNumber localOfMaxD := &printUnit{"", xlocalOfMaxD, true} percentOrHash := &printUnit{"", xlocalHeadHashD, false} - progressRateD := &printUnit{"", xprogressRateD, false} // 117/ 129/11 + progressRateD := &printUnit{"", xprogressRateD, false} // 117/ 129/11 progressRateUnitsD := &printUnit{"", xprogressRateUnitsD, false} // blk/tx/mgas sec - peersD := &printUnit{"", xpeersD, false} // 18/25 peers + peersD := &printUnit{"", xpeersD, false} // 18/25 peers formatLocalOfMaxD := func(localheight, syncheight uint64) string { if localheight < syncheight { @@ -352,12 +494,12 @@ func (l lsMode) PrintBasic(e *eth.Ethereum, d time.Duration, lastLoggedBlockN ui } formatBlockHashD := func(b *types.Block) string { - return b.Hash().Hex()[2:2+len(xlocalHeadHashD)] + return b.Hash().Hex()[2 : 2+len(xlocalHeadHashD)] } formatProgressRateD := func(blksN, txsN, mgasN int) string { if blksN < 0 { - return fmt.Sprintf(" %4d/%2d", txsN, mgasN) + return fmt.Sprintf(" %4d/%2d", txsN, mgasN) } if txsN < 0 { return fmt.Sprintf("%3d/ /%2d", blksN, mgasN) @@ -369,7 +511,10 @@ func (l lsMode) PrintBasic(e *eth.Ethereum, d time.Duration, lastLoggedBlockN ui return fmt.Sprintf("%2d/%2d peers", peersN, maxpeersN) } - formatOutput := func(printunits ...*printUnit) string { + // formatOutputScanLn accepts printUnits and returns a scanln based on their example string length and + // printUnit configured alignment. + // eg. %12s %-8s %5s %15s + formatOutputScanLn := func(printunits ...*printUnit) string { o := []string{} for _, u := range printunits { o = append(o, strScanLenOf(u.example, u.leftAlign)) @@ -379,7 +524,7 @@ func (l lsMode) PrintBasic(e *eth.Ethereum, d time.Duration, lastLoggedBlockN ui peersD.value = formatPeersD(e.Downloader().GetPeers().Len(), maxPeers) defer func() { - glog.D(logger.Warn).Infof("%-8s " + formatOutput(localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD), + glog.D(logger.Warn).Infof("%-8s "+formatOutputScanLn(localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD), lsModeName[l], localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) }() @@ -407,12 +552,12 @@ func (l lsMode) PrintBasic(e *eth.Ethereum, d time.Duration, lastLoggedBlockN ui lh := localHead.NumberU64() localOfMaxD.value = formatLocalOfMaxD(lh, chainSyncHeight) percentOrHash.value = formatPercentD(lh, chainSyncHeight) - progressRateD.value = formatProgressRateD(blks / int(d.Seconds()), -1, mgas / int(d.Seconds())) + progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), -1, mgas/int(tickerInterval.Seconds())) progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/ /mgas sec") case lsModeFullSync: localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) percentOrHash.value = formatBlockHashD(localHead) - progressRateD.value = formatProgressRateD(blks / int(d.Seconds()), txs / int(d.Seconds()), mgas / int(d.Seconds())) + progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), txs/int(tickerInterval.Seconds()), mgas/int(tickerInterval.Seconds())) progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/txs/mgas sec") case lsModeImport: localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) @@ -425,155 +570,136 @@ func (l lsMode) PrintBasic(e *eth.Ethereum, d time.Duration, lastLoggedBlockN ui return current } -func (l lsMode) PrintFancyGreen() { +var PrintStatusGreen = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { + lenPeers := e.Downloader().GetPeers().Len() -} + rtt, ttl, conf := e.Downloader().Qos() + confS := fmt.Sprintf("%01.2f", conf) + qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS) -func delegateStatusLogs(ctx *cli.Context, e *eth.Ethereum, mode lsMode, lastLoggedBlockN uint64, interval time.Duration) uint64 { - return mode.PrintBasic(e, interval, lastLoggedBlockN, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) - - //lenPeers := e.Downloader().GetPeers().Len() - // - //rtt, ttl, conf := e.Downloader().Qos() - //confS := fmt.Sprintf("%01.2f", conf) - //qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS) - // - //_, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - //mode := e.Downloader().GetMode() - //if mode == downloader.FastSync { - // current = e.BlockChain().CurrentFastBlock().NumberU64() - //} - // - //// Get our head block - //blockchain := e.BlockChain() - //currentBlockHex := blockchain.CurrentBlock().Hash().Hex() - // - //// Discover -> not synchronising (searching for peers) - //// FullSync/FastSync -> synchronising - //// Import -> synchronising, at full height - //fOfHeight := fmt.Sprintf("%7d", height) - // - //// Calculate and format percent sync of known height - //heightRatio := float64(current) / float64(height) - //heightRatio = heightRatio * 100 - //fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) - // - //// Wait until syncing because real dl mode will not be engaged until then - //lsMode = lsModeDiscover - //if e.Downloader().Synchronising() { - // switch mode { - // case downloader.FullSync: - // lsMode = lsModeFullSync - // case downloader.FastSync: - // lsMode = lsModeFastSync - // currentBlockHex = blockchain.CurrentFastBlock().Hash().Hex() - // } - //} - //importMode := lenPeers > 0 && lsMode == lsModeDiscover && current >= height && !(current == 0 && height == 0) - //if importMode { - // lsMode = lsModeImport - // fOfHeight = "" // strings.Repeat(" ", 12) - // fHeightRatio = "" // strings.Repeat(" ", 7) - //} - //if height == 0 { - // fOfHeight = "" // strings.Repeat(" ", 12) - // fHeightRatio = "" // strings.Repeat(" ", 7) - //} - // - //// Calculate block stats for interval - //numBlocksDiff := current - lastLoggedBlockN - //numTxsDiff := 0 - //mGas := new(big.Int) - // - //var numBlocksDiffPerSecond uint64 - //var numTxsDiffPerSecond int - //var mGasPerSecond = new(big.Int) - // - //var dominoGraph string - //var nDom int - //if numBlocksDiff > 0 && numBlocksDiff != current { - // for i := lastLoggedBlockN + 1; i <= current; i++ { - // b := blockchain.GetBlockByNumber(i) - // if b != nil { - // txLen := b.Transactions().Len() - // // Add to tallies - // numTxsDiff += txLen - // mGas = new(big.Int).Add(mGas, b.GasUsed()) - // // Domino effect - // if lsMode == lsModeImport { - // if txLen > len(dominoes)-1 { - // // prevent slice out of bounds - // txLen = len(dominoes) - 1 - // } - // if nDom <= 20 { - // dominoGraph += dominoes[txLen] - // } - // nDom++ - // } - // } - // } - // if nDom > 20 { - // dominoGraph += "…" - // } - //} - //dominoGraph = logger.ColorGreen(dominoGraph) - // - //// Convert to per-second stats - //// FIXME(?): Some degree of rounding will happen. - //// For example, if interval is 10s and we get 6 blocks imported in that span, - //// stats will show '0' blocks/second. Looks a little strange; but on the other hand, - //// precision costs visual space, and normally just looks weird when starting up sync or - //// syncing slowly. - //numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds()) - // - //// Don't show initial current / per second val - //if lastLoggedBlockNumber == 0 { - // numBlocksDiffPerSecond = 0 - // numBlocksDiff = 0 - //} - // - //// Divide by interval to yield per-second stats - //numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds()) - //mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds()))) - //mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) - //mGasPerSecondI := mGasPerSecond.Int64() - // - //// Update last logged current block number - //lastLoggedBlockNumber = current - // - //// Format head block hex for printing (eg. d4e…fa3) - //cbhexstart := currentBlockHex[:9] // trim off '0x' prefix - // - //localHeadHeight := fmt.Sprintf("#%7d", current) - //localHeadHex := fmt.Sprintf("%s…", cbhexstart) - //peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) - //domOrHeight := fOfHeight + " " + fHeightRatio - //if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 { - // domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight) - //} else { - // domOrHeight = "" - //} - //var blocksprocesseddisplay string - //qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay) - //if lsMode != lsModeImport { - // blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI)) - //} else { - // blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64())) - // domOrHeight = dominoGraph - // qosDisplayable = "" - //} - // - //// Log to ERROR. - //headDisplay := greenParenify(localHeadHeight + " " + localHeadHex) - //peerDisplay := greenParenify(peersOfMax) - // - //modeIcon := logger.ColorGreen(lsModeIcon[lsMode]) - //if lsMode == lsModeDiscover { - // modeIcon = lsModeDiscoverSpinners[lsModeN%3] - //} - //modeIcon = logger.ColorGreen(modeIcon) - //lsModeN++ - // - //// This allows maximum user optionality for desired integration with rest of event-based logging. - //glog.D(logger.Warn).Infof("SYNC %s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", lsModeName[lsMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) + _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + mode := e.Downloader().GetMode() + if mode == downloader.FastSync { + current = e.BlockChain().CurrentFastBlock().NumberU64() + } + + // Get our head block + blockchain := e.BlockChain() + currentBlockHex := blockchain.CurrentBlock().Hash().Hex() + + // Discover -> not synchronising (searching for peers) + // FullSync/FastSync -> synchronising + // Import -> synchronising, at full height + fOfHeight := fmt.Sprintf("%7d", height) + + // Calculate and format percent sync of known height + heightRatio := float64(current) / float64(height) + heightRatio = heightRatio * 100 + fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) + + // Wait until syncing because real dl mode will not be engaged until then + if currentMode == lsModeImport { + fOfHeight = "" // strings.Repeat(" ", 12) + fHeightRatio = "" // strings.Repeat(" ", 7) + } + if height == 0 { + fOfHeight = "" // strings.Repeat(" ", 12) + fHeightRatio = "" // strings.Repeat(" ", 7) + } + + // Calculate block stats for interval + numBlocksDiff := current - currentBlockNumber + numTxsDiff := 0 + mGas := new(big.Int) + + var numBlocksDiffPerSecond uint64 + var numTxsDiffPerSecond int + var mGasPerSecond = new(big.Int) + + var dominoGraph string + var nDom int + if numBlocksDiff > 0 && numBlocksDiff != current { + for i := currentBlockNumber + 1; i <= current; i++ { + b := blockchain.GetBlockByNumber(i) + if b != nil { + txLen := b.Transactions().Len() + // Add to tallies + numTxsDiff += txLen + mGas = new(big.Int).Add(mGas, b.GasUsed()) + // Domino effect + if currentMode == lsModeImport { + if txLen > len(dominoes)-1 { + // prevent slice out of bounds + txLen = len(dominoes) - 1 + } + if nDom <= 20 { + dominoGraph += dominoes[txLen] + } + nDom++ + } + } + } + if nDom > 20 { + dominoGraph += "…" + } + } + dominoGraph = logger.ColorGreen(dominoGraph) + + // Convert to per-second stats + // FIXME(?): Some degree of rounding will happen. + // For example, if interval is 10s and we get 6 blocks imported in that span, + // stats will show '0' blocks/second. Looks a little strange; but on the other hand, + // precision costs visual space, and normally just looks weird when starting up sync or + // syncing slowly. + numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds()) + + // Don't show initial current / per second val + if currentBlockNumber == 0 { + numBlocksDiffPerSecond = 0 + numBlocksDiff = 0 + } + + // Divide by interval to yield per-second stats + numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds()) + mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds()))) + mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) + mGasPerSecondI := mGasPerSecond.Int64() + + // Format head block hex for printing (eg. d4e…fa3) + cbhexstart := currentBlockHex[:9] // trim off '0x' prefix + + localHeadHeight := fmt.Sprintf("#%7d", current) + localHeadHex := fmt.Sprintf("%s…", cbhexstart) + peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) + domOrHeight := fOfHeight + " " + fHeightRatio + if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 { + domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight) + } else { + domOrHeight = "" + } + var blocksprocesseddisplay string + qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay) + if currentMode != lsModeImport { + blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI)) + } else { + blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64())) + domOrHeight = dominoGraph + qosDisplayable = "" + } + + // Log to ERROR. + headDisplay := greenParenify(localHeadHeight + " " + localHeadHex) + peerDisplay := greenParenify(peersOfMax) + + modeIcon := logger.ColorGreen(lsModeIcon[currentMode]) + if currentMode == lsModeDiscover { + // TODO: spin me + modeIcon = lsModeDiscoverSpinners[0] + } + modeIcon = logger.ColorGreen(modeIcon) + + // This allows maximum user optionality for desired integration with rest of event-based logging. + glog.D(logger.Warn).Infof("%s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", + lsModeName[currentMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) + return current } diff --git a/cmd/geth/migrate_datadir.go b/cmd/geth/migrate_datadir.go index 08d513b71..5d96c2551 100644 --- a/cmd/geth/migrate_datadir.go +++ b/cmd/geth/migrate_datadir.go @@ -29,7 +29,7 @@ import ( "path/filepath" ) -// handleIfDataDirSchemaMigrations is a handler for the conditional logic around +// handleIfDataDirSchemaMigrations is a handlers for the conditional logic around // data/chain dir migrations from geth versions < 3.4 and in consideration of EF geth schemas used for ETC. func handleIfDataDirSchemaMigrations(ctx *cli.Context) error { From 298b1681f5b88560430531d059e5a5f755daac15 Mon Sep 17 00:00:00 2001 From: ia Date: Fri, 15 Dec 2017 14:24:57 -0600 Subject: [PATCH 30/60] problem: log display fmt is hardcoded solution: add flag (experimental for development so far) --- cmd/geth/flags.go | 5 +++++ cmd/geth/log_dispatch.go | 10 +++++++--- cmd/geth/main.go | 1 + cmd/geth/usage.go | 1 + 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cmd/geth/flags.go b/cmd/geth/flags.go index c637c3f01..62b76faa0 100644 --- a/cmd/geth/flags.go +++ b/cmd/geth/flags.go @@ -158,6 +158,11 @@ var ( Usage: "Display verbosity: 0=silent, 1=basics, 2=status, 3=status+events", Value: glog.DefaultDisplay, } + DisplayFormatFlag = cli.StringFlag{ + Name: "display-fmt", + Usage: "Display format", + Value: "basic", + } VModuleFlag = cli.StringFlag{ Name: "vmodule", Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=6,p2p=5)", diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 24dec2c4c..0916d45f1 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -223,7 +223,6 @@ var greenDisplaySystem = displayEventHandlers{ { eventName: "DOWNLOADER_DONE", ev: downloader.DoneEvent{}, - //s += handlers: displayEventHandlerFns{ updateLogStatusModeHandler, func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { @@ -327,10 +326,15 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { d := parseStatusInterval(eqs[0], eqs[1]) + displaySystem := basicDisplaySystem + displayFmt := ctx.GlobalString(DisplayFormatFlag.Name) + if displayFmt == "green" { + displaySystem = greenDisplaySystem + } switch eqs[0] { case "sync": availableLogStatusFeatures["sync"] = d - go runDisplayLogs(ctx, ethe, d, greenDisplaySystem) + go runDisplayLogs(ctx, ethe, d, displaySystem) } } } @@ -560,7 +564,7 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), txs/int(tickerInterval.Seconds()), mgas/int(tickerInterval.Seconds())) progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/txs/mgas sec") case lsModeImport: - localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) + localOfMaxD.value = fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localHead.NumberU64())) percentOrHash.value = formatBlockHashD(localHead) progressRateD.value = fmt.Sprintf(strScanLenOf(xprogressRateD, false), formatProgressRateD(-1, txs, mgas)) progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), " txs/mgas ") diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 95e1a1786..5797ce215 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -174,6 +174,7 @@ func makeCLIApp() (app *cli.App) { NeckbeardFlag, VerbosityFlag, DisplayFlag, + DisplayFormatFlag, VModuleFlag, LogDirFlag, LogMaxSizeFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index f9d82402b..7a7b607dc 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -161,6 +161,7 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ WhisperEnabledFlag, NatspecEnabledFlag, + DisplayFormatFlag, }, }, { From f6f1a7d659f96cfa07ccbbb735b8b5c4a4b38c9a Mon Sep 17 00:00:00 2001 From: ia Date: Fri, 15 Dec 2017 14:43:56 -0600 Subject: [PATCH 31/60] solution: add comments --- cmd/geth/log_dispatch.go | 64 ++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 0916d45f1..66757375f 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -35,6 +35,8 @@ const ( ) // Global bookmark vars. +// These are accessible globally to allow inter-communication between display system event handlers. +// TODO: ensure handler cooperation; ie use a mutex, atomic, or something var currentMode = lsModeDiscover var currentBlockNumber uint64 var chainEventLastSent time.Time @@ -68,9 +70,16 @@ func redParenify(s string) string { return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") } +// displayEventHandlerFn is a function that gets called when something happens; where that 'something' +// is decided by the displayEventHandler the fn belongs to. It's type accepts a standard interface signature and +// returns nothing. evData can be nil, and will be, particularly, when the handler is the "INTERVAL" callee. type displayEventHandlerFn func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) type displayEventHandlerFns []displayEventHandlerFn +// displayEventHandler is a unit of "listening" that can be added to the display system handlers to configure +// what is listened for and how to respond to the given event. 'ev' is an event as received from the Ethereum Mux subscription, +// or nil in the case of INTERVAL. Note, as exemplified below, that in order to make use of the ev data it's required +// to use a (hacky) single switch to .(type) the event data type displayEventHandler struct { eventName string // used for labeling events and matching to the switch statement ev interface{} // which event to handle. if nil, will run on the ticker. @@ -79,6 +88,7 @@ type displayEventHandler struct { } type displayEventHandlers []displayEventHandler +// getByName looks up a handler by name to see if it's "registered" for a given display system. func (hs displayEventHandlers) getByName(name string) (*displayEventHandler, bool) { for _, h := range hs { if h.eventName == name { @@ -88,10 +98,38 @@ func (hs displayEventHandlers) getByName(name string) (*displayEventHandler, boo return nil, false } +// runAllIfAny runs all configured fns for a given event, if registered. +func (hs *displayEventHandlers) runAllIfAny(ctx *cli.Context, e *eth.Ethereum, d interface{}, tickerInterval time.Duration, name string) { + if h, ok := hs.getByName(name); ok { + for _, handler := range h.handlers { + handler(ctx, e, d, tickerInterval) + } + } +} + +// updateLogStatusModeHandler is a convenience fn to update the global 'currentMode' var. +// Typically it should be called from downloader events, and uses the 'getLogStatusMode' logic. func updateLogStatusModeHandler(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { currentMode = getLogStatusMode(e) } +// basicDisplaySystem is the basic display system spec'd in #127. +// --- +//2017-02-03 16:44:00 Discover 0/25 peers +//2017-02-03 16:45:00 Discover 1/25 peers +//2017-02-03 16:46:00 Fast #2481951 of #3124363 79.4% 1211/ 554 blk/mgas sec 6/25 peers +//2017-02-03 16:47:00 Fast #2689911 of #3124363 86.1% 611/ 981 blk/mgas sec 6/25 peers +//2017-02-03 16:48:00 Fast #2875913 of #3124363 92.0% 502/ 760 blk/mgas sec 4/25 peers +//2017-02-03 16:49:00 Sync #3124227 of #3124363 c76c34e7 77/ 242/ 7 blk/tx/mgas sec 4/25 peers +//2017-02-03 16:50:00 Sync #3124247 of #3124363 75e48eff 51/ 51/ 5 blk/tx/mgas sec 4/25 peers +//2017-02-03 16:51:00 Sync #3124567 of #3124363 9af334ae 117/ 129/11 blk/tx/mgas sec 5/25 peers +//2017-02-03 16:52:00 Sync #3124787 of #3124363 1e3a8351 9/ 6/ 1 blk/tx/mgas sec 7/25 peers +//2017-02-03 16:52:05 Import #3124788 84e11ff4 15/ 7 tx/mgas 10/25 peers +//2017-02-03 16:52:25 Import #3124789 9e45a241 5/ 1 tx/mgas 12/25 peers +//2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers +// +// FIXME: '16:52:45 Import #3124790 ' aligns right instead of left +// FIXME: date unit (via glog, likely) var basicDisplaySystem = displayEventHandlers{ { eventName: "CHAIN_INSERT", @@ -137,6 +175,7 @@ var basicDisplaySystem = displayEventHandlers{ }, } +// greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic. var greenDisplaySystem = displayEventHandlers{ { eventName: "CHAIN_INSERT", @@ -260,6 +299,8 @@ var greenDisplaySystem = displayEventHandlers{ }, } +// getLogStatusMode gets the "mode" for the ethereum node at any given time. +// It is used to set the global bookmark variable, and influences formatting logic. func getLogStatusMode(e *eth.Ethereum) lsMode { if e.Downloader().Synchronising() { switch e.Downloader().GetMode() { @@ -339,14 +380,6 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { } } -func (hs *displayEventHandlers) runAllIfAny(ctx *cli.Context, e *eth.Ethereum, d interface{}, tickerInterval time.Duration, name string) { - if h, ok := hs.getByName(name); ok { - for _, handler := range h.handlers { - handler(ctx, e, d, tickerInterval) - } - } -} - // runDisplayLogs starts STATUS SYNC logging at a given interval. // It should be run as a goroutine. // eg. --log-status="sync=42" logs SYNC information every 42 seconds @@ -408,20 +441,6 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat } } -// Spec: -//2017-02-03 16:44:00 Discover 0/25 peers -//2017-02-03 16:45:00 Discover 1/25 peers -//2017-02-03 16:46:00 Fast #2481951 of #3124363 79.4% 1211/ 554 blk/mgas sec 6/25 peers -//2017-02-03 16:47:00 Fast #2689911 of #3124363 86.1% 611/ 981 blk/mgas sec 6/25 peers -//2017-02-03 16:48:00 Fast #2875913 of #3124363 92.0% 502/ 760 blk/mgas sec 4/25 peers -//2017-02-03 16:49:00 Sync #3124227 of #3124363 c76c34e7 77/ 242/ 7 blk/tx/mgas sec 4/25 peers -//2017-02-03 16:50:00 Sync #3124247 of #3124363 75e48eff 51/ 51/ 5 blk/tx/mgas sec 4/25 peers -//2017-02-03 16:51:00 Sync #3124567 of #3124363 9af334ae 117/ 129/11 blk/tx/mgas sec 5/25 peers -//2017-02-03 16:52:00 Sync #3124787 of #3124363 1e3a8351 9/ 6/ 1 blk/tx/mgas sec 7/25 peers -//2017-02-03 16:52:05 Import #3124788 84e11ff4 15/ 7 tx/mgas 10/25 peers -//2017-02-03 16:52:25 Import #3124789 9e45a241 5/ 1 tx/mgas 12/25 peers -//2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers - func formatBlockNumber(i uint64) string { return "#" + strconv.FormatUint(i, 10) } @@ -574,6 +593,7 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe return current } +// PrintStatusGreen implements the displayStatusPrinter interface var PrintStatusGreen = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { lenPeers := e.Downloader().GetPeers().Len() From 1255a19c4040b0b09524c9041adc6023adceaf70 Mon Sep 17 00:00:00 2001 From: ia Date: Fri, 15 Dec 2017 15:02:35 -0600 Subject: [PATCH 32/60] problem: log logic is clumped awkwardly solution: refactor to files --- cmd/geth/log_dispatch.go | 537 ---------------------------------- cmd/geth/log_display_basic.go | 230 +++++++++++++++ cmd/geth/log_display_green.go | 295 +++++++++++++++++++ cmd/geth/log_display_util.go | 47 +++ 4 files changed, 572 insertions(+), 537 deletions(-) create mode 100644 cmd/geth/log_display_basic.go create mode 100644 cmd/geth/log_display_green.go create mode 100644 cmd/geth/log_display_util.go diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 66757375f..049e9f4de 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -1,17 +1,13 @@ package main import ( - "fmt" - "math/big" "os" "os/signal" - "strconv" "strings" "syscall" "time" "github.com/ethereumproject/go-ethereum/core" - "github.com/ethereumproject/go-ethereum/core/types" "github.com/ethereumproject/go-ethereum/eth" "github.com/ethereumproject/go-ethereum/eth/downloader" "github.com/ethereumproject/go-ethereum/logger" @@ -34,42 +30,6 @@ const ( lsModeImport ) -// Global bookmark vars. -// These are accessible globally to allow inter-communication between display system event handlers. -// TODO: ensure handler cooperation; ie use a mutex, atomic, or something -var currentMode = lsModeDiscover -var currentBlockNumber uint64 -var chainEventLastSent time.Time - -var lsModeName = []string{ - "Discover", - "Sync", - "Fast", - "Import", -} - -var lsModeIcon = []string{ - "", - "︎◉", - "◎", - "▶︎", -} - -var dominoes = []string{"🁣", "🁤", "🁥", "🁦", "🁭", "🁴", "🁻", "🁼", "🂃", "🂄", "🂋", "🂌", "🂓"} // 🁣🁤🁥🁦🁭🁴🁻🁼🂃🂄🂋🂌🂓 -var chainIcon = "◼⋯⋯" + logger.ColorGreen("◼") -var forkIcon = "◼⋯⦦" + logger.ColorGreen("◼") -var headerIcon = "◼⋯⋯" + logger.ColorGreen("❐") -var downloaderIcon = "◼⋯⋯" + logger.ColorGreen("⬇") -var minedIcon = "◼⋯⋯" + logger.ColorGreen("⟠") -var lsModeDiscoverSpinners = []string{"➫", "➬", "➭"} - -func greenParenify(s string) string { - return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫") -} -func redParenify(s string) string { - return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") -} - // displayEventHandlerFn is a function that gets called when something happens; where that 'something' // is decided by the displayEventHandler the fn belongs to. It's type accepts a standard interface signature and // returns nothing. evData can be nil, and will be, particularly, when the handler is the "INTERVAL" callee. @@ -107,216 +67,6 @@ func (hs *displayEventHandlers) runAllIfAny(ctx *cli.Context, e *eth.Ethereum, d } } -// updateLogStatusModeHandler is a convenience fn to update the global 'currentMode' var. -// Typically it should be called from downloader events, and uses the 'getLogStatusMode' logic. -func updateLogStatusModeHandler(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - currentMode = getLogStatusMode(e) -} - -// basicDisplaySystem is the basic display system spec'd in #127. -// --- -//2017-02-03 16:44:00 Discover 0/25 peers -//2017-02-03 16:45:00 Discover 1/25 peers -//2017-02-03 16:46:00 Fast #2481951 of #3124363 79.4% 1211/ 554 blk/mgas sec 6/25 peers -//2017-02-03 16:47:00 Fast #2689911 of #3124363 86.1% 611/ 981 blk/mgas sec 6/25 peers -//2017-02-03 16:48:00 Fast #2875913 of #3124363 92.0% 502/ 760 blk/mgas sec 4/25 peers -//2017-02-03 16:49:00 Sync #3124227 of #3124363 c76c34e7 77/ 242/ 7 blk/tx/mgas sec 4/25 peers -//2017-02-03 16:50:00 Sync #3124247 of #3124363 75e48eff 51/ 51/ 5 blk/tx/mgas sec 4/25 peers -//2017-02-03 16:51:00 Sync #3124567 of #3124363 9af334ae 117/ 129/11 blk/tx/mgas sec 5/25 peers -//2017-02-03 16:52:00 Sync #3124787 of #3124363 1e3a8351 9/ 6/ 1 blk/tx/mgas sec 7/25 peers -//2017-02-03 16:52:05 Import #3124788 84e11ff4 15/ 7 tx/mgas 10/25 peers -//2017-02-03 16:52:25 Import #3124789 9e45a241 5/ 1 tx/mgas 12/25 peers -//2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers -// -// FIXME: '16:52:45 Import #3124790 ' aligns right instead of left -// FIXME: date unit (via glog, likely) -var basicDisplaySystem = displayEventHandlers{ - { - eventName: "CHAIN_INSERT", - ev: core.ChainInsertEvent{}, - handlers: displayEventHandlerFns{ - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - if currentMode == lsModeImport { - currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) - } - }, - }, - }, - { - eventName: "DOWNLOADER_START", - ev: downloader.StartEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - }, - }, - { - eventName: "DOWNLOADER_DONE", - ev: downloader.DoneEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - }, - }, - { - eventName: "DOWNLOADER_FAILED", - ev: downloader.FailedEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - }, - }, - { - eventName: "INTERVAL", - handlers: displayEventHandlerFns{ - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - if currentMode != lsModeImport { - currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) - } - }, - }, - }, -} - -// greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic. -var greenDisplaySystem = displayEventHandlers{ - { - eventName: "CHAIN_INSERT", - ev: core.ChainInsertEvent{}, - handlers: displayEventHandlerFns{ - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - switch d := evData.(type) { - case core.ChainInsertEvent: - glog.D(logger.Info).Infof(chainIcon+" Insert "+logger.ColorGreen("blocks")+"=%s "+logger.ColorGreen("◼")+"=%s "+logger.ColorGreen("took")+"=%s", - greenParenify(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)), - greenParenify(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))), - greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))), - ) - if bool(glog.D(logger.Info)) { - chainEventLastSent = time.Now() - } - } - }, - }, - }, - { - eventName: "CHAIN_SIDE", - ev: core.ChainSideEvent{}, - handlers: displayEventHandlerFns{ - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - switch d := evData.(type) { - case core.ChainSideEvent: - glog.D(logger.Info).Infof(forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9]))) - } - }, - }, - }, - { - eventName: "HEADERCHAIN_INSERT", - ev: core.HeaderChainInsertEvent{}, - handlers: displayEventHandlerFns{ - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - switch d := evData.(type) { - case core.HeaderChainInsertEvent: - glog.D(logger.Info).Infof(headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s", - greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)), - greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])), - greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))), - ) - if bool(glog.D(logger.Info)) { - chainEventLastSent = time.Now() - } - } - }, - }, - }, - { - eventName: "MINED_BLOCK", - ev: core.NewMinedBlockEvent{}, - handlers: displayEventHandlerFns{ - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - switch d := evData.(type) { - case core.NewMinedBlockEvent: - glog.D(logger.Info).Infof(minedIcon + " Mined " + logger.ColorGreen("◼") + "=" + greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d", - d.Block.NumberU64(), - d.Block.Hash().Hex()[:9], - d.Block.Coinbase().Hex()[:9], - len(d.Block.Transactions()), - len(d.Block.Uncles()), - ))) - } - }, - }, - }, - { - eventName: "DOWNLOADER_START", - ev: downloader.StartEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - switch d := evData.(type) { - case downloader.StartEvent: - s := downloaderIcon + " Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) - glog.D(logger.Info).Warnln(s) - } - }, - }, - }, - { - eventName: "DOWNLOADER_DONE", - ev: downloader.DoneEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - switch d := evData.(type) { - case downloader.DoneEvent: - s := downloaderIcon + " Done " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) - glog.D(logger.Info).Warnln(s) - } - }, - }, - }, - { - eventName: "DOWNLOADER_FAILED", - ev: downloader.FailedEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - switch d := evData.(type) { - case downloader.FailedEvent: - s := downloaderIcon + " Fail " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error()) - glog.D(logger.Info).Warnln(s) - } - }, - }, - }, - { - eventName: "INTERVAL", - handlers: displayEventHandlerFns{ - func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()/2))) { - currentBlockNumber = PrintStatusGreen(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) - } - }, - }, - }, -} - -// getLogStatusMode gets the "mode" for the ethereum node at any given time. -// It is used to set the global bookmark variable, and influences formatting logic. -func getLogStatusMode(e *eth.Ethereum) lsMode { - if e.Downloader().Synchronising() { - switch e.Downloader().GetMode() { - case downloader.FullSync: - return lsModeFullSync - case downloader.FastSync: - return lsModeFastSync - } - } - _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - if e.Downloader().GetPeers().Len() > 0 && current >= height && !(current == 0 && height == 0) { - return lsModeImport - } - return lsModeDiscover -} - // dispatchStatusLogs handle parsing --log-status=argument and toggling appropriate goroutine status feature logging. func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { flagName := aliasableName(LogStatusFlag.Name, ctx) @@ -440,290 +190,3 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat } } } - -func formatBlockNumber(i uint64) string { - return "#" + strconv.FormatUint(i, 10) -} - -// Examples of spec'd output. -var xlocalOfMaxD = "#92481951 of #93124363" // #2481951 of #3124363 -//var xpercentD = " 92.0%" // 92.0% -var xlocalHeadHashD = "c76c34e7" // c76c34e7 -var xprogressRateD = " 117/ 129/ 11" // 117/ 129/11 -var xprogressRateUnitsD = "blk/txs/mgas sec" // blk/tx/mgas sec -var xpeersD = "18/25 peers" // 18/25 peers - -func strScanLenOf(s string, leftAlign bool) string { - if leftAlign { - return "%" + strconv.Itoa(len(s)) + "s" - } - return "%-" + strconv.Itoa(len(s)) + "s" -} - -type printUnit struct { - value string - example string - leftAlign bool -} - -func (p *printUnit) String() string { - return fmt.Sprintf("%s", p.value) -} - -func calcBlockDiff(e *eth.Ethereum, lastLoggedBlockN uint64, localHead *types.Block) (blks, txs, mgas int) { - // Calculate block stats for interval - localHeadN := localHead.NumberU64() - blks = int(localHeadN - lastLoggedBlockN) - txs = 0 - mGas := new(big.Int) - - for i := lastLoggedBlockN + 1; i <= localHeadN; i++ { - b := e.BlockChain().GetBlockByNumber(i) - if b != nil { - // Add to tallies - txs += b.Transactions().Len() - mGas = mGas.Add(mGas, b.GasUsed()) - } - } - mGas.Div(mGas, big.NewInt(1000000)) - return blks, txs, int(mGas.Int64()) -} - -// PrintStatusBasic implements the displayStatusPrinter interface -var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { - - l := currentMode - lastLoggedBlockN := currentBlockNumber - - localOfMaxD := &printUnit{"", xlocalOfMaxD, true} - percentOrHash := &printUnit{"", xlocalHeadHashD, false} - progressRateD := &printUnit{"", xprogressRateD, false} // 117/ 129/11 - progressRateUnitsD := &printUnit{"", xprogressRateUnitsD, false} // blk/tx/mgas sec - peersD := &printUnit{"", xpeersD, false} // 18/25 peers - - formatLocalOfMaxD := func(localheight, syncheight uint64) string { - if localheight < syncheight { - return fmt.Sprintf("%9s of %9s", formatBlockNumber(localheight), formatBlockNumber(syncheight)) - } - return fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localheight)) - } - - formatPercentD := func(localheight, syncheight uint64) string { - // Calculate and format percent sync of known height - heightRatio := float64(localheight) / float64(syncheight) - heightRatio = heightRatio * 100 - fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) - return fmt.Sprintf(strScanLenOf(xlocalHeadHashD, false), fHeightRatio) - } - - formatBlockHashD := func(b *types.Block) string { - return b.Hash().Hex()[2 : 2+len(xlocalHeadHashD)] - } - - formatProgressRateD := func(blksN, txsN, mgasN int) string { - if blksN < 0 { - return fmt.Sprintf(" %4d/%2d", txsN, mgasN) - } - if txsN < 0 { - return fmt.Sprintf("%3d/ /%2d", blksN, mgasN) - } - return fmt.Sprintf("%3d/%4d/%2d", blksN, txsN, mgasN) - } - - formatPeersD := func(peersN, maxpeersN int) string { - return fmt.Sprintf("%2d/%2d peers", peersN, maxpeersN) - } - - // formatOutputScanLn accepts printUnits and returns a scanln based on their example string length and - // printUnit configured alignment. - // eg. %12s %-8s %5s %15s - formatOutputScanLn := func(printunits ...*printUnit) string { - o := []string{} - for _, u := range printunits { - o = append(o, strScanLenOf(u.example, u.leftAlign)) - } - return strings.Join(o, " ") - } - - peersD.value = formatPeersD(e.Downloader().GetPeers().Len(), maxPeers) - defer func() { - glog.D(logger.Warn).Infof("%-8s "+formatOutputScanLn(localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD), - lsModeName[l], localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) - - }() - if l == lsModeDiscover { - return lastLoggedBlockN - } - - origin, current, chainSyncHeight, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - mode := e.Downloader().GetMode() - if mode == downloader.FastSync { - current = e.BlockChain().CurrentFastBlock().NumberU64() - } - localHead := e.BlockChain().GetBlockByNumber(current) - - // Calculate progress rates - var blks, txs, mgas int - if lastLoggedBlockN == 0 { - blks, txs, mgas = calcBlockDiff(e, origin, localHead) - } else { - blks, txs, mgas = calcBlockDiff(e, lastLoggedBlockN, localHead) - } - - switch l { - case lsModeFastSync: - lh := localHead.NumberU64() - localOfMaxD.value = formatLocalOfMaxD(lh, chainSyncHeight) - percentOrHash.value = formatPercentD(lh, chainSyncHeight) - progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), -1, mgas/int(tickerInterval.Seconds())) - progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/ /mgas sec") - case lsModeFullSync: - localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) - percentOrHash.value = formatBlockHashD(localHead) - progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), txs/int(tickerInterval.Seconds()), mgas/int(tickerInterval.Seconds())) - progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/txs/mgas sec") - case lsModeImport: - localOfMaxD.value = fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localHead.NumberU64())) - percentOrHash.value = formatBlockHashD(localHead) - progressRateD.value = fmt.Sprintf(strScanLenOf(xprogressRateD, false), formatProgressRateD(-1, txs, mgas)) - progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), " txs/mgas ") - default: - panic("unreachable") - } - return current -} - -// PrintStatusGreen implements the displayStatusPrinter interface -var PrintStatusGreen = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { - lenPeers := e.Downloader().GetPeers().Len() - - rtt, ttl, conf := e.Downloader().Qos() - confS := fmt.Sprintf("%01.2f", conf) - qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS) - - _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - mode := e.Downloader().GetMode() - if mode == downloader.FastSync { - current = e.BlockChain().CurrentFastBlock().NumberU64() - } - - // Get our head block - blockchain := e.BlockChain() - currentBlockHex := blockchain.CurrentBlock().Hash().Hex() - - // Discover -> not synchronising (searching for peers) - // FullSync/FastSync -> synchronising - // Import -> synchronising, at full height - fOfHeight := fmt.Sprintf("%7d", height) - - // Calculate and format percent sync of known height - heightRatio := float64(current) / float64(height) - heightRatio = heightRatio * 100 - fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) - - // Wait until syncing because real dl mode will not be engaged until then - if currentMode == lsModeImport { - fOfHeight = "" // strings.Repeat(" ", 12) - fHeightRatio = "" // strings.Repeat(" ", 7) - } - if height == 0 { - fOfHeight = "" // strings.Repeat(" ", 12) - fHeightRatio = "" // strings.Repeat(" ", 7) - } - - // Calculate block stats for interval - numBlocksDiff := current - currentBlockNumber - numTxsDiff := 0 - mGas := new(big.Int) - - var numBlocksDiffPerSecond uint64 - var numTxsDiffPerSecond int - var mGasPerSecond = new(big.Int) - - var dominoGraph string - var nDom int - if numBlocksDiff > 0 && numBlocksDiff != current { - for i := currentBlockNumber + 1; i <= current; i++ { - b := blockchain.GetBlockByNumber(i) - if b != nil { - txLen := b.Transactions().Len() - // Add to tallies - numTxsDiff += txLen - mGas = new(big.Int).Add(mGas, b.GasUsed()) - // Domino effect - if currentMode == lsModeImport { - if txLen > len(dominoes)-1 { - // prevent slice out of bounds - txLen = len(dominoes) - 1 - } - if nDom <= 20 { - dominoGraph += dominoes[txLen] - } - nDom++ - } - } - } - if nDom > 20 { - dominoGraph += "…" - } - } - dominoGraph = logger.ColorGreen(dominoGraph) - - // Convert to per-second stats - // FIXME(?): Some degree of rounding will happen. - // For example, if interval is 10s and we get 6 blocks imported in that span, - // stats will show '0' blocks/second. Looks a little strange; but on the other hand, - // precision costs visual space, and normally just looks weird when starting up sync or - // syncing slowly. - numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds()) - - // Don't show initial current / per second val - if currentBlockNumber == 0 { - numBlocksDiffPerSecond = 0 - numBlocksDiff = 0 - } - - // Divide by interval to yield per-second stats - numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds()) - mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds()))) - mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) - mGasPerSecondI := mGasPerSecond.Int64() - - // Format head block hex for printing (eg. d4e…fa3) - cbhexstart := currentBlockHex[:9] // trim off '0x' prefix - - localHeadHeight := fmt.Sprintf("#%7d", current) - localHeadHex := fmt.Sprintf("%s…", cbhexstart) - peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) - domOrHeight := fOfHeight + " " + fHeightRatio - if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 { - domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight) - } else { - domOrHeight = "" - } - var blocksprocesseddisplay string - qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay) - if currentMode != lsModeImport { - blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI)) - } else { - blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64())) - domOrHeight = dominoGraph - qosDisplayable = "" - } - - // Log to ERROR. - headDisplay := greenParenify(localHeadHeight + " " + localHeadHex) - peerDisplay := greenParenify(peersOfMax) - - modeIcon := logger.ColorGreen(lsModeIcon[currentMode]) - if currentMode == lsModeDiscover { - // TODO: spin me - modeIcon = lsModeDiscoverSpinners[0] - } - modeIcon = logger.ColorGreen(modeIcon) - - // This allows maximum user optionality for desired integration with rest of event-based logging. - glog.D(logger.Warn).Infof("%s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", - lsModeName[currentMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) - return current -} diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go new file mode 100644 index 000000000..39e653f7c --- /dev/null +++ b/cmd/geth/log_display_basic.go @@ -0,0 +1,230 @@ +package main + +import ( + "strings" + "github.com/ethereumproject/go-ethereum/logger/glog" + "github.com/ethereumproject/go-ethereum/logger" + "fmt" + "math/big" + "time" + "strconv" + "github.com/ethereumproject/go-ethereum/eth" + "github.com/ethereumproject/go-ethereum/core/types" + "github.com/ethereumproject/go-ethereum/core" + "gopkg.in/urfave/cli.v1" + "github.com/ethereumproject/go-ethereum/eth/downloader" +) + +// basicDisplaySystem is the basic display system spec'd in #127. +// --- +//2017-02-03 16:44:00 Discover 0/25 peers +//2017-02-03 16:45:00 Discover 1/25 peers +//2017-02-03 16:46:00 Fast #2481951 of #3124363 79.4% 1211/ 554 blk/mgas sec 6/25 peers +//2017-02-03 16:47:00 Fast #2689911 of #3124363 86.1% 611/ 981 blk/mgas sec 6/25 peers +//2017-02-03 16:48:00 Fast #2875913 of #3124363 92.0% 502/ 760 blk/mgas sec 4/25 peers +//2017-02-03 16:49:00 Sync #3124227 of #3124363 c76c34e7 77/ 242/ 7 blk/tx/mgas sec 4/25 peers +//2017-02-03 16:50:00 Sync #3124247 of #3124363 75e48eff 51/ 51/ 5 blk/tx/mgas sec 4/25 peers +//2017-02-03 16:51:00 Sync #3124567 of #3124363 9af334ae 117/ 129/11 blk/tx/mgas sec 5/25 peers +//2017-02-03 16:52:00 Sync #3124787 of #3124363 1e3a8351 9/ 6/ 1 blk/tx/mgas sec 7/25 peers +//2017-02-03 16:52:05 Import #3124788 84e11ff4 15/ 7 tx/mgas 10/25 peers +//2017-02-03 16:52:25 Import #3124789 9e45a241 5/ 1 tx/mgas 12/25 peers +//2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers +// +// FIXME: '16:52:45 Import #3124790 ' aligns right instead of left +// FIXME: date unit (via glog, likely) +var basicDisplaySystem = displayEventHandlers{ + { + eventName: "CHAIN_INSERT", + ev: core.ChainInsertEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + if currentMode == lsModeImport { + currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + } + }, + }, + }, + { + eventName: "DOWNLOADER_START", + ev: downloader.StartEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + }, + }, + { + eventName: "DOWNLOADER_DONE", + ev: downloader.DoneEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + }, + }, + { + eventName: "DOWNLOADER_FAILED", + ev: downloader.FailedEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + }, + }, + { + eventName: "INTERVAL", + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + if currentMode != lsModeImport { + currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + } + }, + }, + }, +} + +func formatBlockNumber(i uint64) string { + return "#" + strconv.FormatUint(i, 10) +} + +// Examples of spec'd output. +var xlocalOfMaxD = "#92481951 of #93124363" // #2481951 of #3124363 +//var xpercentD = " 92.0%" // 92.0% +var xlocalHeadHashD = "c76c34e7" // c76c34e7 +var xprogressRateD = " 117/ 129/ 11" // 117/ 129/11 +var xprogressRateUnitsD = "blk/txs/mgas sec" // blk/tx/mgas sec +var xpeersD = "18/25 peers" // 18/25 peers + +func strScanLenOf(s string, leftAlign bool) string { + if leftAlign { + return "%" + strconv.Itoa(len(s)) + "s" + } + return "%-" + strconv.Itoa(len(s)) + "s" +} + +type printUnit struct { + value string + example string + leftAlign bool +} + +func (p *printUnit) String() string { + return fmt.Sprintf("%s", p.value) +} + +func calcBlockDiff(e *eth.Ethereum, lastLoggedBlockN uint64, localHead *types.Block) (blks, txs, mgas int) { + // Calculate block stats for interval + localHeadN := localHead.NumberU64() + blks = int(localHeadN - lastLoggedBlockN) + txs = 0 + mGas := new(big.Int) + + for i := lastLoggedBlockN + 1; i <= localHeadN; i++ { + b := e.BlockChain().GetBlockByNumber(i) + if b != nil { + // Add to tallies + txs += b.Transactions().Len() + mGas = mGas.Add(mGas, b.GasUsed()) + } + } + mGas.Div(mGas, big.NewInt(1000000)) + return blks, txs, int(mGas.Int64()) +} + +// PrintStatusBasic implements the displayEventHandlerFn interface +var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { + + l := currentMode + lastLoggedBlockN := currentBlockNumber + + localOfMaxD := &printUnit{"", xlocalOfMaxD, true} + percentOrHash := &printUnit{"", xlocalHeadHashD, false} + progressRateD := &printUnit{"", xprogressRateD, false} // 117/ 129/11 + progressRateUnitsD := &printUnit{"", xprogressRateUnitsD, false} // blk/tx/mgas sec + peersD := &printUnit{"", xpeersD, false} // 18/25 peers + + formatLocalOfMaxD := func(localheight, syncheight uint64) string { + if localheight < syncheight { + return fmt.Sprintf("%9s of %9s", formatBlockNumber(localheight), formatBlockNumber(syncheight)) + } + return fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localheight)) + } + + formatPercentD := func(localheight, syncheight uint64) string { + // Calculate and format percent sync of known height + heightRatio := float64(localheight) / float64(syncheight) + heightRatio = heightRatio * 100 + fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) + return fmt.Sprintf(strScanLenOf(xlocalHeadHashD, false), fHeightRatio) + } + + formatBlockHashD := func(b *types.Block) string { + return b.Hash().Hex()[2 : 2+len(xlocalHeadHashD)] + } + + formatProgressRateD := func(blksN, txsN, mgasN int) string { + if blksN < 0 { + return fmt.Sprintf(" %4d/%2d", txsN, mgasN) + } + if txsN < 0 { + return fmt.Sprintf("%3d/ /%2d", blksN, mgasN) + } + return fmt.Sprintf("%3d/%4d/%2d", blksN, txsN, mgasN) + } + + formatPeersD := func(peersN, maxpeersN int) string { + return fmt.Sprintf("%2d/%2d peers", peersN, maxpeersN) + } + + // formatOutputScanLn accepts printUnits and returns a scanln based on their example string length and + // printUnit configured alignment. + // eg. %12s %-8s %5s %15s + formatOutputScanLn := func(printunits ...*printUnit) string { + o := []string{} + for _, u := range printunits { + o = append(o, strScanLenOf(u.example, u.leftAlign)) + } + return strings.Join(o, " ") + } + + peersD.value = formatPeersD(e.Downloader().GetPeers().Len(), maxPeers) + defer func() { + glog.D(logger.Warn).Infof("%-8s "+formatOutputScanLn(localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD), + lsModeName[l], localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) + + }() + if l == lsModeDiscover { + return lastLoggedBlockN + } + + origin, current, chainSyncHeight, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + mode := e.Downloader().GetMode() + if mode == downloader.FastSync { + current = e.BlockChain().CurrentFastBlock().NumberU64() + } + localHead := e.BlockChain().GetBlockByNumber(current) + + // Calculate progress rates + var blks, txs, mgas int + if lastLoggedBlockN == 0 { + blks, txs, mgas = calcBlockDiff(e, origin, localHead) + } else { + blks, txs, mgas = calcBlockDiff(e, lastLoggedBlockN, localHead) + } + + switch l { + case lsModeFastSync: + lh := localHead.NumberU64() + localOfMaxD.value = formatLocalOfMaxD(lh, chainSyncHeight) + percentOrHash.value = formatPercentD(lh, chainSyncHeight) + progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), -1, mgas/int(tickerInterval.Seconds())) + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/ /mgas sec") + case lsModeFullSync: + localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) + percentOrHash.value = formatBlockHashD(localHead) + progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), txs/int(tickerInterval.Seconds()), mgas/int(tickerInterval.Seconds())) + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/txs/mgas sec") + case lsModeImport: + localOfMaxD.value = fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localHead.NumberU64())) + percentOrHash.value = formatBlockHashD(localHead) + progressRateD.value = fmt.Sprintf(strScanLenOf(xprogressRateD, false), formatProgressRateD(-1, txs, mgas)) + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), " txs/mgas ") + default: + panic("unreachable") + } + return current +} diff --git a/cmd/geth/log_display_green.go b/cmd/geth/log_display_green.go new file mode 100644 index 000000000..9ce828b56 --- /dev/null +++ b/cmd/geth/log_display_green.go @@ -0,0 +1,295 @@ +package main + +import ( + "time" + "fmt" + "math/big" + "github.com/ethereumproject/go-ethereum/logger" + "strings" + "github.com/ethereumproject/go-ethereum/logger/glog" + "github.com/ethereumproject/go-ethereum/eth" + "github.com/ethereumproject/go-ethereum/eth/downloader" + "gopkg.in/urfave/cli.v1" + "github.com/ethereumproject/go-ethereum/core" +) + +var lsModeIcon = []string{ + "", + "︎◉", + "◎", + "▶︎", +} + +var dominoes = []string{"🁣", "🁤", "🁥", "🁦", "🁭", "🁴", "🁻", "🁼", "🂃", "🂄", "🂋", "🂌", "🂓"} // 🁣🁤🁥🁦🁭🁴🁻🁼🂃🂄🂋🂌🂓 +var chainIcon = "◼⋯⋯" + logger.ColorGreen("◼") +var forkIcon = "◼⋯⦦" + logger.ColorGreen("◼") +var headerIcon = "◼⋯⋯" + logger.ColorGreen("❐") +var downloaderIcon = "◼⋯⋯" + logger.ColorGreen("⬇") +var minedIcon = "◼⋯⋯" + logger.ColorGreen("⟠") +var lsModeDiscoverSpinners = []string{"➫", "➬", "➭"} + +func greenParenify(s string) string { + return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫") +} +func redParenify(s string) string { + return logger.ColorRed("⟪") + s + logger.ColorRed("⟫") +} + +// greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic. +var greenDisplaySystem = displayEventHandlers{ + { + eventName: "CHAIN_INSERT", + ev: core.ChainInsertEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.ChainInsertEvent: + glog.D(logger.Info).Infof(chainIcon+" Insert "+logger.ColorGreen("blocks")+"=%s "+logger.ColorGreen("◼")+"=%s "+logger.ColorGreen("took")+"=%s", + greenParenify(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)), + greenParenify(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))), + greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))), + ) + if bool(glog.D(logger.Info)) { + chainEventLastSent = time.Now() + } + } + }, + }, + }, + { + eventName: "CHAIN_SIDE", + ev: core.ChainSideEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.ChainSideEvent: + glog.D(logger.Info).Infof(forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9]))) + } + }, + }, + }, + { + eventName: "HEADERCHAIN_INSERT", + ev: core.HeaderChainInsertEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.HeaderChainInsertEvent: + glog.D(logger.Info).Infof(headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s", + greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)), + greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])), + greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))), + ) + if bool(glog.D(logger.Info)) { + chainEventLastSent = time.Now() + } + } + }, + }, + }, + { + eventName: "MINED_BLOCK", + ev: core.NewMinedBlockEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.NewMinedBlockEvent: + glog.D(logger.Info).Infof(minedIcon + " Mined " + logger.ColorGreen("◼") + "=" + greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d", + d.Block.NumberU64(), + d.Block.Hash().Hex()[:9], + d.Block.Coinbase().Hex()[:9], + len(d.Block.Transactions()), + len(d.Block.Uncles()), + ))) + } + }, + }, + }, + { + eventName: "DOWNLOADER_START", + ev: downloader.StartEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case downloader.StartEvent: + s := downloaderIcon + " Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + glog.D(logger.Info).Warnln(s) + } + }, + }, + }, + { + eventName: "DOWNLOADER_DONE", + ev: downloader.DoneEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case downloader.DoneEvent: + s := downloaderIcon + " Done " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD)) + glog.D(logger.Info).Warnln(s) + } + }, + }, + }, + { + eventName: "DOWNLOADER_FAILED", + ev: downloader.FailedEvent{}, + handlers: displayEventHandlerFns{ + updateLogStatusModeHandler, + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case downloader.FailedEvent: + s := downloaderIcon + " Fail " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error()) + glog.D(logger.Info).Warnln(s) + } + }, + }, + }, + { + eventName: "INTERVAL", + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()/2))) { + currentBlockNumber = PrintStatusGreen(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + } + }, + }, + }, +} + +// PrintStatusGreen implements the displayEventHandlerFn interface +var PrintStatusGreen = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { + lenPeers := e.Downloader().GetPeers().Len() + + rtt, ttl, conf := e.Downloader().Qos() + confS := fmt.Sprintf("%01.2f", conf) + qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS) + + _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + mode := e.Downloader().GetMode() + if mode == downloader.FastSync { + current = e.BlockChain().CurrentFastBlock().NumberU64() + } + + // Get our head block + blockchain := e.BlockChain() + currentBlockHex := blockchain.CurrentBlock().Hash().Hex() + + // Discover -> not synchronising (searching for peers) + // FullSync/FastSync -> synchronising + // Import -> synchronising, at full height + fOfHeight := fmt.Sprintf("%7d", height) + + // Calculate and format percent sync of known height + heightRatio := float64(current) / float64(height) + heightRatio = heightRatio * 100 + fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) + + // Wait until syncing because real dl mode will not be engaged until then + if currentMode == lsModeImport { + fOfHeight = "" // strings.Repeat(" ", 12) + fHeightRatio = "" // strings.Repeat(" ", 7) + } + if height == 0 { + fOfHeight = "" // strings.Repeat(" ", 12) + fHeightRatio = "" // strings.Repeat(" ", 7) + } + + // Calculate block stats for interval + numBlocksDiff := current - currentBlockNumber + numTxsDiff := 0 + mGas := new(big.Int) + + var numBlocksDiffPerSecond uint64 + var numTxsDiffPerSecond int + var mGasPerSecond = new(big.Int) + + var dominoGraph string + var nDom int + if numBlocksDiff > 0 && numBlocksDiff != current { + for i := currentBlockNumber + 1; i <= current; i++ { + b := blockchain.GetBlockByNumber(i) + if b != nil { + txLen := b.Transactions().Len() + // Add to tallies + numTxsDiff += txLen + mGas = new(big.Int).Add(mGas, b.GasUsed()) + // Domino effect + if currentMode == lsModeImport { + if txLen > len(dominoes)-1 { + // prevent slice out of bounds + txLen = len(dominoes) - 1 + } + if nDom <= 20 { + dominoGraph += dominoes[txLen] + } + nDom++ + } + } + } + if nDom > 20 { + dominoGraph += "…" + } + } + dominoGraph = logger.ColorGreen(dominoGraph) + + // Convert to per-second stats + // FIXME(?): Some degree of rounding will happen. + // For example, if interval is 10s and we get 6 blocks imported in that span, + // stats will show '0' blocks/second. Looks a little strange; but on the other hand, + // precision costs visual space, and normally just looks weird when starting up sync or + // syncing slowly. + numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds()) + + // Don't show initial current / per second val + if currentBlockNumber == 0 { + numBlocksDiffPerSecond = 0 + numBlocksDiff = 0 + } + + // Divide by interval to yield per-second stats + numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds()) + mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds()))) + mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000)) + mGasPerSecondI := mGasPerSecond.Int64() + + // Format head block hex for printing (eg. d4e…fa3) + cbhexstart := currentBlockHex[:9] // trim off '0x' prefix + + localHeadHeight := fmt.Sprintf("#%7d", current) + localHeadHex := fmt.Sprintf("%s…", cbhexstart) + peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers) + domOrHeight := fOfHeight + " " + fHeightRatio + if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 { + domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight) + } else { + domOrHeight = "" + } + var blocksprocesseddisplay string + qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay) + if currentMode != lsModeImport { + blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI)) + } else { + blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64())) + domOrHeight = dominoGraph + qosDisplayable = "" + } + + // Log to ERROR. + headDisplay := greenParenify(localHeadHeight + " " + localHeadHex) + peerDisplay := greenParenify(peersOfMax) + + modeIcon := logger.ColorGreen(lsModeIcon[currentMode]) + if currentMode == lsModeDiscover { + // TODO: spin me + modeIcon = lsModeDiscoverSpinners[0] + } + modeIcon = logger.ColorGreen(modeIcon) + + // This allows maximum user optionality for desired integration with rest of event-based logging. + glog.D(logger.Warn).Infof("%s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", + lsModeName[currentMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) + return current +} diff --git a/cmd/geth/log_display_util.go b/cmd/geth/log_display_util.go new file mode 100644 index 000000000..ea5a4a6ce --- /dev/null +++ b/cmd/geth/log_display_util.go @@ -0,0 +1,47 @@ +package main + +import ( + "time" + "gopkg.in/urfave/cli.v1" + "github.com/ethereumproject/go-ethereum/eth" + "github.com/ethereumproject/go-ethereum/eth/downloader" +) + +var lsModeName = []string{ + "Discover", + "Sync", + "Fast", + "Import", +} + +// Global bookmark vars. +// These are accessible globally to allow inter-communication between display system event handlers. +// TODO: ensure handler cooperation; ie use a mutex, atomic, or something +var currentMode = lsModeDiscover +var currentBlockNumber uint64 +var chainEventLastSent time.Time + +// updateLogStatusModeHandler implements the displayEventHandlerFn signature interface +// It is a convenience fn to update the global 'currentMode' var. +// Typically it should be called from downloader events, and uses the 'getLogStatusMode' logic. +func updateLogStatusModeHandler(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + currentMode = getLogStatusMode(e) +} + +// getLogStatusMode gets the "mode" for the ethereum node at any given time. +// It is used to set the global bookmark variable, and influences formatting logic. +func getLogStatusMode(e *eth.Ethereum) lsMode { + if e.Downloader().Synchronising() { + switch e.Downloader().GetMode() { + case downloader.FullSync: + return lsModeFullSync + case downloader.FastSync: + return lsModeFastSync + } + } + _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known + if e.Downloader().GetPeers().Len() > 0 && current >= height && !(current == 0 && height == 0) { + return lsModeImport + } + return lsModeDiscover +} From b2c60eebf48704ad8f842d92f1e94634a24e27ae Mon Sep 17 00:00:00 2001 From: ia Date: Fri, 15 Dec 2017 15:35:13 -0600 Subject: [PATCH 33/60] problem: log events should not be hardcoded strings solution: use logEventT as type with iota constants --- cmd/geth/log_dispatch.go | 28 ++++++++++++++-------------- cmd/geth/log_display_basic.go | 18 +++++++++--------- cmd/geth/log_display_green.go | 30 +++++++++++++++--------------- cmd/geth/log_display_util.go | 17 +++++++++++++++++ 4 files changed, 55 insertions(+), 38 deletions(-) diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 049e9f4de..6b8426431 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -41,17 +41,17 @@ type displayEventHandlerFns []displayEventHandlerFn // or nil in the case of INTERVAL. Note, as exemplified below, that in order to make use of the ev data it's required // to use a (hacky) single switch to .(type) the event data type displayEventHandler struct { - eventName string // used for labeling events and matching to the switch statement - ev interface{} // which event to handle. if nil, will run on the ticker. + eventT logEventType // used for labeling events and matching to the switch statement + ev interface{} // which event to handle. if nil, will run on the ticker. // (ctx *cli.Context, e *eth.Ethereum, evData interface{}, mode *lsMode, tickerInterval time.Duration, n *uint64) handlers displayEventHandlerFns } type displayEventHandlers []displayEventHandler // getByName looks up a handler by name to see if it's "registered" for a given display system. -func (hs displayEventHandlers) getByName(name string) (*displayEventHandler, bool) { +func (hs displayEventHandlers) getByName(eventType logEventType) (*displayEventHandler, bool) { for _, h := range hs { - if h.eventName == name { + if h.eventT == eventType { return &h, true } } @@ -59,8 +59,8 @@ func (hs displayEventHandlers) getByName(name string) (*displayEventHandler, boo } // runAllIfAny runs all configured fns for a given event, if registered. -func (hs *displayEventHandlers) runAllIfAny(ctx *cli.Context, e *eth.Ethereum, d interface{}, tickerInterval time.Duration, name string) { - if h, ok := hs.getByName(name); ok { +func (hs *displayEventHandlers) runAllIfAny(ctx *cli.Context, e *eth.Ethereum, d interface{}, tickerInterval time.Duration, eventType logEventType) { + if h, ok := hs.getByName(eventType); ok { for _, handler := range h.handlers { handler(ctx, e, d, tickerInterval) } @@ -153,11 +153,11 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat handleDownloaderEvent := func(d interface{}) { switch d.(type) { case downloader.StartEvent: - handles.runAllIfAny(ctx, e, d, tickerInterval, "DOWNLOADER_START") + handles.runAllIfAny(ctx, e, d, tickerInterval, logEventDownloaderStart) case downloader.DoneEvent: - handles.runAllIfAny(ctx, e, d, tickerInterval, "DOWNLOADER_DONE") + handles.runAllIfAny(ctx, e, d, tickerInterval, logEventDownloaderDone) case downloader.FailedEvent: - handles.runAllIfAny(ctx, e, d, tickerInterval, "DOWNLOADER_FAILED") + handles.runAllIfAny(ctx, e, d, tickerInterval, logEventDownloaderFailed) } } @@ -165,13 +165,13 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat for ev := range ethEvents.Chan() { switch d := ev.Data.(type) { case core.ChainInsertEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "CHAIN_INSERT") + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventChainInsert) case core.ChainSideEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "CHAIN_SIDE") + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventChainInsertSide) case core.HeaderChainInsertEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "HEADERCHAIN_INSERT") + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventHeaderChainInsert) case core.NewMinedBlockEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, "MINED_BLOCK") + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventMinedBlock) default: handleDownloaderEvent(d) } @@ -181,7 +181,7 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat for { select { case <-ticker.C: - handles.runAllIfAny(ctx, e, nil, tickerInterval, "INTERVAL") + handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventInterval) case <-sigc: // Listen for interrupt ticker.Stop() diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 39e653f7c..c27206dc5 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -34,8 +34,8 @@ import ( // FIXME: date unit (via glog, likely) var basicDisplaySystem = displayEventHandlers{ { - eventName: "CHAIN_INSERT", - ev: core.ChainInsertEvent{}, + eventT: logEventChainInsert, + ev: core.ChainInsertEvent{}, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { if currentMode == lsModeImport { @@ -45,28 +45,28 @@ var basicDisplaySystem = displayEventHandlers{ }, }, { - eventName: "DOWNLOADER_START", - ev: downloader.StartEvent{}, + eventT: logEventDownloaderStart, + ev: downloader.StartEvent{}, handlers: displayEventHandlerFns{ updateLogStatusModeHandler, }, }, { - eventName: "DOWNLOADER_DONE", - ev: downloader.DoneEvent{}, + eventT: logEventDownloaderDone, + ev: downloader.DoneEvent{}, handlers: displayEventHandlerFns{ updateLogStatusModeHandler, }, }, { - eventName: "DOWNLOADER_FAILED", - ev: downloader.FailedEvent{}, + eventT: logEventDownloaderFailed, + ev: downloader.FailedEvent{}, handlers: displayEventHandlerFns{ updateLogStatusModeHandler, }, }, { - eventName: "INTERVAL", + eventT: logEventInterval, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { if currentMode != lsModeImport { diff --git a/cmd/geth/log_display_green.go b/cmd/geth/log_display_green.go index 9ce828b56..a836a0f19 100644 --- a/cmd/geth/log_display_green.go +++ b/cmd/geth/log_display_green.go @@ -38,8 +38,8 @@ func redParenify(s string) string { // greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic. var greenDisplaySystem = displayEventHandlers{ { - eventName: "CHAIN_INSERT", - ev: core.ChainInsertEvent{}, + eventT: logEventChainInsert, + ev: core.ChainInsertEvent{}, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { switch d := evData.(type) { @@ -57,8 +57,8 @@ var greenDisplaySystem = displayEventHandlers{ }, }, { - eventName: "CHAIN_SIDE", - ev: core.ChainSideEvent{}, + eventT: logEventChainInsertSide, + ev: core.ChainSideEvent{}, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { switch d := evData.(type) { @@ -69,8 +69,8 @@ var greenDisplaySystem = displayEventHandlers{ }, }, { - eventName: "HEADERCHAIN_INSERT", - ev: core.HeaderChainInsertEvent{}, + eventT: logEventHeaderChainInsert, + ev: core.HeaderChainInsertEvent{}, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { switch d := evData.(type) { @@ -88,8 +88,8 @@ var greenDisplaySystem = displayEventHandlers{ }, }, { - eventName: "MINED_BLOCK", - ev: core.NewMinedBlockEvent{}, + eventT: logEventMinedBlock, + ev: core.NewMinedBlockEvent{}, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { switch d := evData.(type) { @@ -106,8 +106,8 @@ var greenDisplaySystem = displayEventHandlers{ }, }, { - eventName: "DOWNLOADER_START", - ev: downloader.StartEvent{}, + eventT: logEventDownloaderStart, + ev: downloader.StartEvent{}, handlers: displayEventHandlerFns{ updateLogStatusModeHandler, func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { @@ -120,8 +120,8 @@ var greenDisplaySystem = displayEventHandlers{ }, }, { - eventName: "DOWNLOADER_DONE", - ev: downloader.DoneEvent{}, + eventT: logEventDownloaderDone, + ev: downloader.DoneEvent{}, handlers: displayEventHandlerFns{ updateLogStatusModeHandler, func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { @@ -134,8 +134,8 @@ var greenDisplaySystem = displayEventHandlers{ }, }, { - eventName: "DOWNLOADER_FAILED", - ev: downloader.FailedEvent{}, + eventT: logEventDownloaderFailed, + ev: downloader.FailedEvent{}, handlers: displayEventHandlerFns{ updateLogStatusModeHandler, func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { @@ -148,7 +148,7 @@ var greenDisplaySystem = displayEventHandlers{ }, }, { - eventName: "INTERVAL", + eventT: logEventInterval, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()/2))) { diff --git a/cmd/geth/log_display_util.go b/cmd/geth/log_display_util.go index ea5a4a6ce..0a7b4f400 100644 --- a/cmd/geth/log_display_util.go +++ b/cmd/geth/log_display_util.go @@ -5,6 +5,7 @@ import ( "gopkg.in/urfave/cli.v1" "github.com/ethereumproject/go-ethereum/eth" "github.com/ethereumproject/go-ethereum/eth/downloader" + "fmt" ) var lsModeName = []string{ @@ -14,6 +15,22 @@ var lsModeName = []string{ "Import", } +type logEventType int +const ( + logEventChainInsert logEventType = iota + logEventChainInsertSide + logEventHeaderChainInsert + logEventMinedBlock + logEventDownloaderStart + logEventDownloaderDone + logEventDownloaderFailed + logEventInterval +) + +func (e logEventType) String() string { + return fmt.Sprintf("%s", e) +} + // Global bookmark vars. // These are accessible globally to allow inter-communication between display system event handlers. // TODO: ensure handler cooperation; ie use a mutex, atomic, or something From 3950b33c6ed334cbe15deba683904862d5adf099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Mon, 18 Dec 2017 13:05:41 +0100 Subject: [PATCH 34/60] problem: `~` not expanded in `--log-dir` --- cmd/geth/log_context.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index 905c87d01..767261fb3 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -9,13 +9,14 @@ import ( "gopkg.in/urfave/cli.v1" + "github.com/ethereumproject/go-ethereum/logger" "github.com/ethereumproject/go-ethereum/logger/glog" - "path/filepath" "os" - "github.com/ethereumproject/go-ethereum/logger" + "path/filepath" ) const defaultStatusLog = "sync=30" + var isToFileLoggingEnabled = true // setupLogging sets default @@ -39,6 +40,7 @@ func setupLogging(ctx *cli.Context) error { return fmt.Errorf("--%s cannot be empty", LogDirFlag.Name) } if isToFileLoggingEnabled { + ld = expandPath(ld) ldAbs, err := filepath.Abs(ld) if err != nil { return err @@ -65,7 +67,6 @@ func setupLogging(ctx *cli.Context) error { glog.SetLogDir(logDir) } - // Handle --neckbeard config overrides if set. if ctx.GlobalBool(NeckbeardFlag.Name) { glog.SetD(0) @@ -293,7 +294,7 @@ func logLoggingConfiguration(ctx *cli.Context) { vmodule := glog.GetVModule().String() // An empty string looks unused, so show * instead, which is equivalent. if vmodule == "" { - vmodule= "*" + vmodule = "*" } d := glog.GetDisplayable().String() @@ -327,4 +328,4 @@ func logLoggingConfiguration(ctx *cli.Context) { glog.D(logger.Warn).Warnf("Machine log config: mlog=%s", logger.ColorYellow("off")) } -} \ No newline at end of file +} From c0a763093341eccefd599134a5f8d076fdf3a8f6 Mon Sep 17 00:00:00 2001 From: ia Date: Fri, 15 Dec 2017 22:48:48 -0600 Subject: [PATCH 35/60] experimental: implement dash display fmt tries out functionality of the log display interface and events dash still experimental because needs to handle and format events/modes like fast syncing better, but is OK proof-of-concept --- cmd/geth/log_context.go | 2 +- cmd/geth/log_dispatch.go | 59 +++++--- cmd/geth/log_display_basic.go | 18 +-- cmd/geth/log_display_dash.go | 270 ++++++++++++++++++++++++++++++++++ cmd/geth/log_display_green.go | 3 - cmd/geth/log_display_util.go | 2 + 6 files changed, 315 insertions(+), 39 deletions(-) create mode 100644 cmd/geth/log_display_dash.go diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index 767261fb3..dd80de73d 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -77,7 +77,7 @@ func setupLogging(ctx *cli.Context) error { // Handle display level configuration. if ctx.GlobalIsSet(DisplayFlag.Name) { i := ctx.GlobalInt(DisplayFlag.Name) - if i > 3 { + if i > 5 { return fmt.Errorf("--%s level must be 0 <= i <= 3, got: %d", DisplayFlag.Name, i) } glog.SetD(i) diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 6b8426431..4da069c18 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -10,7 +10,7 @@ import ( "github.com/ethereumproject/go-ethereum/core" "github.com/ethereumproject/go-ethereum/eth" "github.com/ethereumproject/go-ethereum/eth/downloader" - "github.com/ethereumproject/go-ethereum/logger" + "github.com/ethereumproject/go-ethereum/event" "github.com/ethereumproject/go-ethereum/logger/glog" "gopkg.in/urfave/cli.v1" ) @@ -122,6 +122,9 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { if displayFmt == "green" { displaySystem = greenDisplaySystem } + if displayFmt == "dash" { + displaySystem = dashDisplaySystem + } switch eqs[0] { case "sync": availableLogStatusFeatures["sync"] = d @@ -137,18 +140,23 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat // Set up ticker based on established interval. ticker := time.NewTicker(tickerInterval) + defer ticker.Stop() var sigc = make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigc) - //// Should listen for events. - //// Proof of concept create event subscription + // Listen for events. var handledEvents []interface{} for _, h := range handles { - handledEvents = append(handledEvents, h.ev) + if h.ev != nil { + handledEvents = append(handledEvents, h.ev) + } + } + var ethEvents event.Subscription + if len(handledEvents) > 0 { + ethEvents = e.EventMux().Subscribe(handledEvents...) } - ethEvents := e.EventMux().Subscribe(handledEvents...) handleDownloaderEvent := func(d interface{}) { switch d.(type) { @@ -161,31 +169,36 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat } } - go func() { - for ev := range ethEvents.Chan() { - switch d := ev.Data.(type) { - case core.ChainInsertEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventChainInsert) - case core.ChainSideEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventChainInsertSide) - case core.HeaderChainInsertEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventHeaderChainInsert) - case core.NewMinedBlockEvent: - handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventMinedBlock) - default: - handleDownloaderEvent(d) + // Run any "setup" if configured + handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventBefore) + + if len(handledEvents) > 0 { + go func() { + for ev := range ethEvents.Chan() { + updateLogStatusModeHandler(ctx, e, nil, tickerInterval) + switch ev.Data.(type) { + case core.ChainInsertEvent: + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventChainInsert) + case core.ChainSideEvent: + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventChainInsertSide) + case core.HeaderChainInsertEvent: + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventHeaderChainInsert) + case core.NewMinedBlockEvent: + handles.runAllIfAny(ctx, e, ev.Data, tickerInterval, logEventMinedBlock) + default: + handleDownloaderEvent(ev.Data) + } } - } - }() - + }() + } for { select { case <-ticker.C: + updateLogStatusModeHandler(ctx, e, nil, tickerInterval) handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventInterval) case <-sigc: // Listen for interrupt - ticker.Stop() - glog.D(logger.Warn).Warnln("SYNC Stopping.") + handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventAfter) return } } diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index c27206dc5..7ba01b027 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -47,23 +47,14 @@ var basicDisplaySystem = displayEventHandlers{ { eventT: logEventDownloaderStart, ev: downloader.StartEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - }, }, { eventT: logEventDownloaderDone, ev: downloader.DoneEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - }, }, { eventT: logEventDownloaderFailed, ev: downloader.FailedEvent{}, - handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, - }, }, { eventT: logEventInterval, @@ -125,6 +116,11 @@ func calcBlockDiff(e *eth.Ethereum, lastLoggedBlockN uint64, localHead *types.Bl return blks, txs, int(mGas.Int64()) } +func calcPercent(quotient, divisor uint64) float64 { + out := float64(quotient) / float64(divisor) + return out * 100 +} + // PrintStatusBasic implements the displayEventHandlerFn interface var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { @@ -146,9 +142,7 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe formatPercentD := func(localheight, syncheight uint64) string { // Calculate and format percent sync of known height - heightRatio := float64(localheight) / float64(syncheight) - heightRatio = heightRatio * 100 - fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio) + fHeightRatio := fmt.Sprintf("%4.2f%%", calcPercent(localheight, syncheight)) return fmt.Sprintf(strScanLenOf(xlocalHeadHashD, false), fHeightRatio) } diff --git a/cmd/geth/log_display_dash.go b/cmd/geth/log_display_dash.go new file mode 100644 index 000000000..1efee65df --- /dev/null +++ b/cmd/geth/log_display_dash.go @@ -0,0 +1,270 @@ +package main + +import ( + "time" + "gopkg.in/urfave/cli.v1" + "github.com/ethereumproject/go-ethereum/eth" + "github.com/gizak/termui" + "github.com/ethereumproject/go-ethereum/logger/glog" + "github.com/ethereumproject/go-ethereum/logger" + "github.com/ethereumproject/go-ethereum/core" + "fmt" +) + +const ( + tuiSmallHeight = 3 + tuiMediumHeight = 5 + tuiLargeHeight = 8 + tuiSmallWidth = 20 + tuiMediumWidth = 50 + tuiLargeWidth = 100 + + tuiSpaceHeight = 1 + + tuiDataLimit = 100 +) + +var ( + syncheightGauge *termui.Gauge + + peerCountSpark termui.Sparkline + peerCountSparkHolder *termui.Sparklines + + peerList *termui.List + peerListData []string + + mgasSpark termui.Sparkline + txsSpark termui.Sparkline + blkSpark termui.Sparkline + blkMgasTxsSparkHolder *termui.Sparklines +) + + +func tuiDrawDash(e *eth.Ethereum) { + if currentMode == lsModeImport || currentMode == lsModeDiscover { + syncheightGauge.Label = "" + } + if e != nil && e.IsListening() { + cb := e.BlockChain().GetBlockByNumber(currentBlockNumber) + syncheightGauge.BorderLabel = fmt.Sprintf("%s | local_head ◼ n=%d ⬡=%s txs=%d time=%v ago", lsModeName[currentMode], currentBlockNumber, cb.Hash().Hex()[:10] + "…", cb.Transactions().Len(), time.Since(time.Unix(cb.Time().Int64(), 0)).Round(time.Second)) + } + termui.Render(syncheightGauge, peerCountSparkHolder, peerList, blkMgasTxsSparkHolder) +} + +func tuiSetupDashComponents() { + //// Sync height gauge + syncheightGauge = termui.NewGauge() + syncheightGauge.Percent = 0 + syncheightGauge.BarColor = termui.ColorRed + syncheightGauge.Height = tuiSmallHeight + syncheightGauge.Width = tuiLargeWidth + //// Mgas spark + mgasSpark = termui.Sparkline{} + mgasSpark.Title = "Mgas" + mgasSpark.Data = []int{} + mgasSpark.Height = tuiSmallHeight + mgasSpark.LineColor = termui.ColorYellow + //// Txs spark + txsSpark = termui.Sparkline{} + txsSpark.Title = "Txs" + txsSpark.Data = []int{} + txsSpark.Height = tuiSmallHeight + txsSpark.LineColor = termui.ColorMagenta + //// Blk spark + blkSpark = termui.Sparkline{} + blkSpark.Title = "Blks" + blkSpark.Data = []int{} + blkSpark.Height = tuiSmallHeight + blkSpark.LineColor = termui.ColorGreen + //// MgasTxs spark holder + blkMgasTxsSparkHolder = termui.NewSparklines(blkSpark, mgasSpark, txsSpark) + blkMgasTxsSparkHolder.Height = mgasSpark.Height + txsSpark.Height + blkSpark.Height + tuiSpaceHeight*6 + blkMgasTxsSparkHolder.Width = syncheightGauge.Width + blkMgasTxsSparkHolder.Y = syncheightGauge.Y + syncheightGauge.Height + blkMgasTxsSparkHolder.X = syncheightGauge.X + + //// Peer count spark + peerCountSpark = termui.Sparkline{} + peerCountSpark.LineColor = termui.ColorBlue + peerCountSpark.Data = []int{0} + peerCountSpark.Height = tuiMediumHeight + //// Peer count spark holder + peerCountSparkHolder = termui.NewSparklines(peerCountSpark) + peerCountSparkHolder.BorderLabel = "Peers (0)" + peerCountSparkHolder.BorderLabelFg = termui.ColorBlue + peerCountSparkHolder.BorderBottom = false + peerCountSparkHolder.X = 0 + peerCountSparkHolder.Y = blkMgasTxsSparkHolder.Y + blkMgasTxsSparkHolder.Height + peerCountSparkHolder.Height = tuiMediumHeight + tuiSpaceHeight*3 + peerCountSparkHolder.Width = syncheightGauge.Width + + //// Peer list + peerList = termui.NewList() + peerList.Items = peerListData + peerList.X = 0 + peerList.Y = peerCountSparkHolder.Y + peerCountSparkHolder.Height + peerList.Width = peerCountSparkHolder.Width + peerList.Height = tuiLargeHeight*2 + peerList.BorderTop = false +} + +func addDataWithLimit(sl []int, dataPoint int, maxLen int) []int { + if len(sl) > maxLen { + sl = append(sl[1:], dataPoint) + return sl + } + sl = append(sl, dataPoint) + return sl +} + +// greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic. +var dashDisplaySystem = displayEventHandlers{ + { + eventT: logEventBefore, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + // Disable display logging. + d := glog.GetDisplayable() + glog.SetD(0) + go func() { + // Reset display logs. + defer glog.SetD(int(*d)) + + err := termui.Init() + if err != nil { + panic(err) + } + tuiSetupDashComponents() + tuiSetupHandlers() + if currentBlockNumber == 0 { + _, c, _, _, _ := e.Downloader().Progress() + currentBlockNumber = c + } + tuiDrawDash(e) + + termui.Loop() + }() + }, + }, + }, + { + eventT: logEventChainInsert, + ev: core.ChainInsertEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.ChainInsertEvent: + localheight := d.LastNumber + _, head, syncheight, _ ,_ := e.Downloader().Progress() + if head > localheight { + localheight = head + } + syncheightGauge.Percent = int(calcPercent(localheight, syncheight)) + if localheight >= syncheight { + syncheightGauge.Label = fmt.Sprintf("%d", localheight) + syncheightGauge.BarColor = termui.ColorGreen + } else { + syncheightGauge.Label = fmt.Sprintf("%d / %d", localheight, syncheight) + syncheightGauge.BarColor = termui.ColorRed + } + + if currentBlockNumber != 0 { + b := e.BlockChain().GetBlockByNumber(localheight) + if b == nil { + return + } + blks, txs, mgas := calcBlockDiff(e, currentBlockNumber, b) + // blk + blkMgasTxsSparkHolder.Lines[0].Data = addDataWithLimit(blkMgasTxsSparkHolder.Lines[0].Data, blks, tuiDataLimit) + blkMgasTxsSparkHolder.Lines[0].Title = fmt.Sprintf("n=%d] ∆ blks=%d (inserted_at=%v took=%v)", localheight, blks, time.Now().Round(time.Second), d.Elasped.Round(time.Millisecond)) + // mgas + blkMgasTxsSparkHolder.Lines[1].Data = addDataWithLimit(blkMgasTxsSparkHolder.Lines[1].Data, mgas, tuiDataLimit) + blkMgasTxsSparkHolder.Lines[1].Title = fmt.Sprintf("n=%d] ∑ mgas=%2d/%4dblks", localheight, mgas, blks) + // txs + blkMgasTxsSparkHolder.Lines[2].Data = addDataWithLimit(blkMgasTxsSparkHolder.Lines[2].Data, txs, tuiDataLimit) + blkMgasTxsSparkHolder.Lines[2].Title = fmt.Sprintf("n=%d] ∑ txs=%3d/%4dblks", localheight, txs, blks) + } + currentBlockNumber = localheight + tuiDrawDash(e) + default: + panic(d) + } + }, + }, + }, + { + eventT: logEventAfter, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + termui.StopLoop() + termui.Close() + return + + }, + }, + }, + { + eventT: logEventInterval, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + peers := e.Downloader().GetPeers() + peerCountSparkHolder.Lines[0].Data = addDataWithLimit(peerCountSparkHolder.Lines[0].Data, int(peers.Len()), tuiDataLimit) + peerCountSparkHolder.BorderLabel = fmt.Sprintf("Peers (%d / %d)", int(peers.Len()), ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + + peerListData = []string{} + for _, p := range peers.AllPeers() { + peerListData = append(peerListData, p.String()) + } + peerList.Items = peerListData + tuiDrawDash(e) + }, + }, + }, +} + +func tuiSetupHandlers() { + termui.Handle("interrupt", func(tue termui.Event) { + glog.V(logger.Error).Errorln("interrupt 1") + termui.StopLoop() + termui.Close() + }) + termui.Handle("/interrupt", func(tue termui.Event) { + glog.V(logger.Error).Errorln("interrupt 2") + termui.StopLoop() + termui.Close() + }) + termui.Handle("/sys/interrupt", func(tue termui.Event) { + glog.V(logger.Error).Errorln("interrupt 3") + termui.StopLoop() + termui.Close() + }) + termui.Handle("/sys/kbd/C-c", func(tue termui.Event) { + glog.V(logger.Error).Errorln("/sys/kbd/C-c") + termui.StopLoop() + termui.Close() + }) + termui.Handle("/sys/kbd/C-x", func(tue termui.Event) { + glog.V(logger.Error).Errorln("/sys/kbd/C-x") + termui.StopLoop() + termui.Close() + }) + termui.Handle("/sys/kbd/C-z", func(tue termui.Event) { + glog.V(logger.Error).Errorln("/sys/kbd/C-z") + termui.StopLoop() + termui.Close() + }) + termui.Handle("/sys/kbd/q", func(tue termui.Event) { + glog.V(logger.Error).Errorln("/sys/kbd/q") + termui.StopLoop() + termui.Close() + }) + termui.Handle("/sys/kbd/r", func(tue termui.Event) { + // Just check for len > 1 for an arbitrary set because they're all updated simultaneously. + // This is also a totally opinionated and convenience thing. + if len(blkMgasTxsSparkHolder.Lines[0].Data) > 1 { + blkMgasTxsSparkHolder.Lines[0].Data = blkMgasTxsSparkHolder.Lines[0].Data[1:] + blkMgasTxsSparkHolder.Lines[1].Data = blkMgasTxsSparkHolder.Lines[1].Data[1:] + blkMgasTxsSparkHolder.Lines[2].Data = blkMgasTxsSparkHolder.Lines[2].Data[1:] + } + }) +} \ No newline at end of file diff --git a/cmd/geth/log_display_green.go b/cmd/geth/log_display_green.go index a836a0f19..6ee823155 100644 --- a/cmd/geth/log_display_green.go +++ b/cmd/geth/log_display_green.go @@ -109,7 +109,6 @@ var greenDisplaySystem = displayEventHandlers{ eventT: logEventDownloaderStart, ev: downloader.StartEvent{}, handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { switch d := evData.(type) { case downloader.StartEvent: @@ -123,7 +122,6 @@ var greenDisplaySystem = displayEventHandlers{ eventT: logEventDownloaderDone, ev: downloader.DoneEvent{}, handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { switch d := evData.(type) { case downloader.DoneEvent: @@ -137,7 +135,6 @@ var greenDisplaySystem = displayEventHandlers{ eventT: logEventDownloaderFailed, ev: downloader.FailedEvent{}, handlers: displayEventHandlerFns{ - updateLogStatusModeHandler, func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { switch d := evData.(type) { case downloader.FailedEvent: diff --git a/cmd/geth/log_display_util.go b/cmd/geth/log_display_util.go index 0a7b4f400..e1c00165a 100644 --- a/cmd/geth/log_display_util.go +++ b/cmd/geth/log_display_util.go @@ -25,6 +25,8 @@ const ( logEventDownloaderDone logEventDownloaderFailed logEventInterval + logEventBefore + logEventAfter ) func (e logEventType) String() string { From a4cfc07f4e82b43d1ae6e64f79ef9beccb61c5d1 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 08:15:25 -0600 Subject: [PATCH 36/60] problem: date format for display log not to spec solution: make YYYY-MM-DD and eliminate log-severity information from standard display info+warning logs --- logger/glog/glog.go | 91 +++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/logger/glog/glog.go b/logger/glog/glog.go index 23332314d..a8ff82c9e 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -668,9 +668,11 @@ func init() { // Should never reach... unless we get real fancy with D(levels) display.verbosityTraceThreshold.set(5) - // Only includes traces for severity=fatal logs for dispaly. + // Only includes traces for severity=fatal logs for display. // This should never be reached; fatal logs should ALWAYS be logged to file, // and they will also be written to stderr (anything Error and above is). + // Keep in mind severities are "upside-down" from verbosities; so here 3=error, 4=fatal, and 0=info + // and that here severity>=3 will meet the threshold. display.severityTraceThreshold.set(3) // Set display verbosity default Info. So it will render // all Fatal, Error, Warn, and Info log levels. @@ -807,51 +809,66 @@ func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. // It's worth about 3X. Fprintf is hard. - _, month, day := now.Date() + year, month, day := now.Date() hour, minute, second := now.Clock() // Lmmdd hh:mm:ss.uuuuuu threadid file:line] //buf.nDigits(8, 0, severityColor[s],'') - buf.WriteString(severityColor[s]) - buf.tmp[0] = severityChar[s] - buf.Write(buf.tmp[:1]) - if l.logTName == displayLog { - buf.WriteString(severityColorReset) - // Write dim for rest of line, eg. date and time - buf.WriteString(severityColor[infoLog]) - } else { - buf.WriteString(severityColorReset) - } - buf.twoDigits(0, int(month)) - buf.twoDigits(2, day) - buf.tmp[4] = ' ' - buf.twoDigits(5, hour) - buf.tmp[7] = ':' - buf.twoDigits(8, minute) - buf.tmp[10] = ':' - buf.twoDigits(11, second) - // Only keep nanoseconds for file logs + + // If to-file (debuggable) logs. if l.logTName == fileLog { + buf.tmp[0] = severityChar[s] + buf.Write(buf.tmp[:1]) + buf.twoDigits(0, int(month)) + buf.twoDigits(2, day) + buf.tmp[4] = ' ' + buf.twoDigits(5, hour) + buf.tmp[7] = ':' + buf.twoDigits(8, minute) + buf.tmp[10] = ':' + buf.twoDigits(11, second) + // Only keep nanoseconds for file logs buf.tmp[13] = '.' buf.nDigits(6, 14, now.Nanosecond()/1000, '0') buf.Write(buf.tmp[:20]) + buf.WriteString(" ") + + if l.traceThreshold(s) { + buf.WriteString(file) + buf.tmp[0] = ':' + n := buf.someDigits(1, line) + buf.tmp[n+1] = ']' + buf.tmp[n+2] = ' ' + buf.Write(buf.tmp[:n+3]) + } } else { - buf.Write(buf.tmp[:13]) - buf.WriteString(severityColorReset) - } - buf.WriteString(" ") - if l.traceThreshold(s) { - buf.WriteString(file) - buf.tmp[0] = ':' - n := buf.someDigits(1, line) - buf.tmp[n+1] = ']' - buf.tmp[n+2] = ' ' - buf.Write(buf.tmp[:n+3]) - } - //else { - // buf.tmp[0] = ' ' - // buf.Write(buf.tmp[:1]) - //} + // Write dim. + buf.WriteString(severityColor[infoLog]) + + buf.nDigits(4, 0, year, '_') + buf.nDigits(4, 0, year, '_') + buf.tmp[4] = '-' + buf.twoDigits(5, int(month)) + buf.tmp[7] = '-' + buf.twoDigits(8, day) + buf.tmp[10] = ' ' + buf.twoDigits(11, hour) + buf.tmp[13] = ':' + buf.twoDigits(14, minute) + buf.tmp[16] = ':' + buf.twoDigits(17, second) + buf.Write(buf.tmp[:19]) + + buf.WriteString(severityColorReset + " ") + if l.traceThreshold(s) { + buf.WriteString(severityColor[s]) + buf.Write([]byte{'['}) + buf.WriteString(severityName[s]) + buf.Write([]byte{']'}) + buf.WriteString(severityColorReset) + buf.Write([]byte{' '}) + } + } return buf } From cd58c2983b47f5b29d8f683d37f8b050d4b16480 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 11:01:19 -0600 Subject: [PATCH 37/60] solution: set default log-status =60s per spec --- cmd/geth/log_context.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index dd80de73d..e3d7a2f93 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -15,7 +15,7 @@ import ( "path/filepath" ) -const defaultStatusLog = "sync=30" +const defaultStatusLog = "sync=60s" var isToFileLoggingEnabled = true @@ -301,6 +301,7 @@ func logLoggingConfiguration(ctx *cli.Context) { statusFeats := []string{} for k, v := range availableLogStatusFeatures { if v.Seconds() == 0 { + statusFeats = append(statusFeats, logger.ColorGreen(fmt.Sprintf("%s=%s", k, logger.ColorYellow("off")))) continue } statusFeats = append(statusFeats, logger.ColorGreen(fmt.Sprintf("%s=%v", k, v))) From 1c04a3be3e8ba7018447ea25fd85c741cf7aa7e1 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 11:05:04 -0600 Subject: [PATCH 38/60] problem: improve disabling and default for log status interval solution: use 'off' or '0' to disablog interval-based status logs use 'sync=' as default module for flag since it's the only existing module at this point --- cmd/geth/flags.go | 3 +-- cmd/geth/log_dispatch.go | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/cmd/geth/flags.go b/cmd/geth/flags.go index 62b76faa0..e122f793b 100644 --- a/cmd/geth/flags.go +++ b/cmd/geth/flags.go @@ -142,7 +142,6 @@ var ( Usage: "Password file to use for non-inteactive password input", Value: "", } - // logging and debug settings NeckbeardFlag = cli.BoolFlag{ Name: "neckbeard", @@ -204,7 +203,7 @@ var ( } LogStatusFlag = cli.StringFlag{ Name: "log-status", - Usage: `Toggle interval-based STATUS logs: comma-separated list of =. Use 'off' to disable entirely.'`, + Usage: `Configure interval-based status logs: comma-separated list of =. Use 'off' or '0' to disable.`, Value: defaultStatusLog, } MLogFlag = cli.StringFlag{ diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 4da069c18..cdb213d8f 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -96,6 +96,15 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { // If possible, split sync=60 into ["sync", "60"], otherwise yields ["sync"], ["60"], or ["someothernonsense"] eqs := strings.Split(p, "=") + + // If just given, eg. --log-status=60s, assume as default intended sync=60s, at least until + // there is another status module interval added. + if len(eqs) == 1 { + dur := eqs[0] + if _, err := parseDuration(dur); err == nil { + eqs = append([]string{"sync"}, dur) + } + } if len(eqs) < 2 { glog.Errorf("Invalid log status value: %v. Must be comma-separated pairs of module=interval.", eqs) os.Exit(1) @@ -139,8 +148,14 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Duration, handles displayEventHandlers) { // Set up ticker based on established interval. - ticker := time.NewTicker(tickerInterval) - defer ticker.Stop() + var ticker *time.Ticker + if tickerInterval.Seconds() > 0 { + ticker = time.NewTicker(tickerInterval) + defer ticker.Stop() + } else { + ticker = time.NewTicker(1) + ticker.Stop() + } var sigc = make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) From b0fed84533584e7390368157951a2450c22b0902 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 11:05:52 -0600 Subject: [PATCH 39/60] solution: move experiment logging flag to ex. section of help --- cmd/geth/usage.go | 3 ++- logger/glog/glog.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 7a7b607dc..9532a4808 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -147,7 +147,6 @@ var AppHelpFlagGroups = []flagGroup{ LogMaxAgeFlag, LogCompressFlag, LogStatusFlag, - NeckbeardFlag, MLogFlag, MLogDirFlag, MLogComponentsFlag, @@ -161,7 +160,9 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ WhisperEnabledFlag, NatspecEnabledFlag, + DisplayFlag, DisplayFormatFlag, + NeckbeardFlag, }, }, { diff --git a/logger/glog/glog.go b/logger/glog/glog.go index a8ff82c9e..1700551f5 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -672,7 +672,7 @@ func init() { // This should never be reached; fatal logs should ALWAYS be logged to file, // and they will also be written to stderr (anything Error and above is). // Keep in mind severities are "upside-down" from verbosities; so here 3=error, 4=fatal, and 0=info - // and that here severity>=3 will meet the threshold. + // and that here severity>=3 will meet the threshold. display.severityTraceThreshold.set(3) // Set display verbosity default Info. So it will render // all Fatal, Error, Warn, and Info log levels. From fb9dc2e0348cabc06b9dd95b6b4a60d5ef5ff0c6 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 11:16:37 -0600 Subject: [PATCH 40/60] solution: fix debuglog formatting color --- cmd/geth/log_context.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index e3d7a2f93..454a9bb60 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -301,10 +301,10 @@ func logLoggingConfiguration(ctx *cli.Context) { statusFeats := []string{} for k, v := range availableLogStatusFeatures { if v.Seconds() == 0 { - statusFeats = append(statusFeats, logger.ColorGreen(fmt.Sprintf("%s=%s", k, logger.ColorYellow("off")))) + statusFeats = append(statusFeats, fmt.Sprintf("%s=%s", k, "off")) continue } - statusFeats = append(statusFeats, logger.ColorGreen(fmt.Sprintf("%s=%v", k, v))) + statusFeats = append(statusFeats, fmt.Sprintf("%s=%v", k, v)) } statusLine := strings.Join(statusFeats, ",") @@ -318,7 +318,7 @@ func logLoggingConfiguration(ctx *cli.Context) { glog.V(logger.Warn).Infoln("Display log configuration", "d=", d, "status=", statusLine) glog.D(logger.Warn).Infof("Display log config: display=%s status=%s", logger.ColorGreen(d), - statusLine, + logger.ColorGreen(statusLine), ) if logger.MlogEnabled() { From 1e479fdebee509335ad08ea17c8ed7f7ecf16600 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 13:53:04 -0600 Subject: [PATCH 41/60] solution: use stringer for diplay mode --- cmd/geth/log_dispatch.go | 11 +++++++++++ cmd/geth/log_display_basic.go | 2 +- cmd/geth/log_display_dash.go | 2 +- cmd/geth/log_display_green.go | 2 +- cmd/geth/log_display_util.go | 7 ------- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index cdb213d8f..4f343f39d 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -30,6 +30,17 @@ const ( lsModeImport ) +var lsModeName = []string{ + "Discover", + "Sync", + "Fast", + "Import", +} + +func (m lsMode) String() string { + return lsModeName[m] +} + // displayEventHandlerFn is a function that gets called when something happens; where that 'something' // is decided by the displayEventHandler the fn belongs to. It's type accepts a standard interface signature and // returns nothing. evData can be nil, and will be, particularly, when the handler is the "INTERVAL" callee. diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 7ba01b027..bf28f5809 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -178,7 +178,7 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe peersD.value = formatPeersD(e.Downloader().GetPeers().Len(), maxPeers) defer func() { glog.D(logger.Warn).Infof("%-8s "+formatOutputScanLn(localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD), - lsModeName[l], localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) + l, localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) }() if l == lsModeDiscover { diff --git a/cmd/geth/log_display_dash.go b/cmd/geth/log_display_dash.go index 1efee65df..05294bdb2 100644 --- a/cmd/geth/log_display_dash.go +++ b/cmd/geth/log_display_dash.go @@ -46,7 +46,7 @@ func tuiDrawDash(e *eth.Ethereum) { } if e != nil && e.IsListening() { cb := e.BlockChain().GetBlockByNumber(currentBlockNumber) - syncheightGauge.BorderLabel = fmt.Sprintf("%s | local_head ◼ n=%d ⬡=%s txs=%d time=%v ago", lsModeName[currentMode], currentBlockNumber, cb.Hash().Hex()[:10] + "…", cb.Transactions().Len(), time.Since(time.Unix(cb.Time().Int64(), 0)).Round(time.Second)) + syncheightGauge.BorderLabel = fmt.Sprintf("%s | local_head ◼ n=%d ⬡=%s txs=%d time=%v ago", currentMode, currentBlockNumber, cb.Hash().Hex()[:10] + "…", cb.Transactions().Len(), time.Since(time.Unix(cb.Time().Int64(), 0)).Round(time.Second)) } termui.Render(syncheightGauge, peerCountSparkHolder, peerList, blkMgasTxsSparkHolder) } diff --git a/cmd/geth/log_display_green.go b/cmd/geth/log_display_green.go index 6ee823155..798022d21 100644 --- a/cmd/geth/log_display_green.go +++ b/cmd/geth/log_display_green.go @@ -287,6 +287,6 @@ var PrintStatusGreen = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe // This allows maximum user optionality for desired integration with rest of event-based logging. glog.D(logger.Warn).Infof("%s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s", - lsModeName[currentMode], headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) + currentMode, headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable) return current } diff --git a/cmd/geth/log_display_util.go b/cmd/geth/log_display_util.go index e1c00165a..d712ea131 100644 --- a/cmd/geth/log_display_util.go +++ b/cmd/geth/log_display_util.go @@ -8,13 +8,6 @@ import ( "fmt" ) -var lsModeName = []string{ - "Discover", - "Sync", - "Fast", - "Import", -} - type logEventType int const ( logEventChainInsert logEventType = iota From b95b80a7a2922658744b26f6f1e5b851210eaabf Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 13:55:35 -0600 Subject: [PATCH 42/60] solution: improve log line mode decision for _basic --- cmd/geth/log_display_basic.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index bf28f5809..a37bbfaca 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -31,7 +31,6 @@ import ( //2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers // // FIXME: '16:52:45 Import #3124790 ' aligns right instead of left -// FIXME: date unit (via glog, likely) var basicDisplaySystem = displayEventHandlers{ { eventT: logEventChainInsert, @@ -40,6 +39,7 @@ var basicDisplaySystem = displayEventHandlers{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { if currentMode == lsModeImport { currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + chainEventLastSent = time.Now() } }, }, @@ -60,7 +60,8 @@ var basicDisplaySystem = displayEventHandlers{ eventT: logEventInterval, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - if currentMode != lsModeImport { + // If not in import mode, OR if we haven't yet logged a chain event. + if currentMode != lsModeImport || chainEventLastSent.IsZero() { currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) } }, From b0bfb55004cb9bfd1ec7f125a7128e390159b6d6 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 13:56:35 -0600 Subject: [PATCH 43/60] solution: refactor dispatcher for readability and no races --- cmd/geth/log_dispatch.go | 72 ++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 4f343f39d..7bd6d4e13 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -69,6 +69,24 @@ func (hs displayEventHandlers) getByName(eventType logEventType) (*displayEventH return nil, false } +// mustGetDisplaySystemFromName parses the flag --display-fmt from context and returns an associated +// displayEventHandlers set. This can be considered a temporary solve for handling "registering" or +// "delegating" log interface systems. +func mustGetDisplaySystemFromName(s string) displayEventHandlers { + displaySystem := basicDisplaySystem + switch s { + case "green": + displaySystem = greenDisplaySystem + case "dash": + displaySystem = dashDisplaySystem + case "basic": + // already set as default + default: + glog.Fatalln("%v: --%v", ErrInvalidFlag, DisplayFormatFlag.Name) + } + return displaySystem +} + // runAllIfAny runs all configured fns for a given event, if registered. func (hs *displayEventHandlers) runAllIfAny(ctx *cli.Context, e *eth.Ethereum, d interface{}, tickerInterval time.Duration, eventType logEventType) { if h, ok := hs.getByName(eventType); ok { @@ -117,7 +135,7 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { } } if len(eqs) < 2 { - glog.Errorf("Invalid log status value: %v. Must be comma-separated pairs of module=interval.", eqs) + glog.Errorf("%v: %v. Must be comma-separated pairs of module=interval.", ErrInvalidFlag, eqs) os.Exit(1) } @@ -135,20 +153,13 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { eqs = append(eqs, "") } + // Parse interval from flag value. d := parseStatusInterval(eqs[0], eqs[1]) - - displaySystem := basicDisplaySystem - displayFmt := ctx.GlobalString(DisplayFormatFlag.Name) - if displayFmt == "green" { - displaySystem = greenDisplaySystem - } - if displayFmt == "dash" { - displaySystem = dashDisplaySystem - } switch eqs[0] { case "sync": availableLogStatusFeatures["sync"] = d - go runDisplayLogs(ctx, ethe, d, displaySystem) + dsys := mustGetDisplaySystemFromName(ctx.GlobalString(DisplayFormatFlag.Name)) + go runDisplayLogs(ctx, ethe, d, dsys) } } } @@ -157,21 +168,6 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { // It should be run as a goroutine. // eg. --log-status="sync=42" logs SYNC information every 42 seconds func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Duration, handles displayEventHandlers) { - - // Set up ticker based on established interval. - var ticker *time.Ticker - if tickerInterval.Seconds() > 0 { - ticker = time.NewTicker(tickerInterval) - defer ticker.Stop() - } else { - ticker = time.NewTicker(1) - ticker.Stop() - } - - var sigc = make(chan os.Signal, 1) - signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) - defer signal.Stop(sigc) - // Listen for events. var handledEvents []interface{} for _, h := range handles { @@ -199,6 +195,7 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventBefore) if len(handledEvents) > 0 { + //glog.D(logger.Error).Errorf("handling %d events", len(handledEvents)) go func() { for ev := range ethEvents.Chan() { updateLogStatusModeHandler(ctx, e, nil, tickerInterval) @@ -217,11 +214,28 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat } }() } + + // Set up ticker based on established interval. + if tickerInterval.Seconds() > 0 { + ticker := time.NewTicker(tickerInterval) + defer ticker.Stop() + go func() { + for { + select { + case <-ticker.C: + updateLogStatusModeHandler(ctx, e, nil, tickerInterval) + //glog.D(logger.Error).Errorf("tick tock mode= %v", currentMode) + handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventInterval) + } + } + }() + } + + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) + defer signal.Stop(sigc) for { select { - case <-ticker.C: - updateLogStatusModeHandler(ctx, e, nil, tickerInterval) - handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventInterval) case <-sigc: // Listen for interrupt handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventAfter) From 298ab62c60446fd94ce3cded42434994d086ce17 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 13:57:55 -0600 Subject: [PATCH 44/60] problem: display severity trace thresh is sev=fatal solution: should be Error --- logger/glog/glog.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/logger/glog/glog.go b/logger/glog/glog.go index 67c9d58e3..5b3c218f7 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -523,7 +523,7 @@ func (t *TraceLocation) Get() interface{} { return nil } -var errTraceSyntax = errors.New("syntax error: expect file.go:234") +var errTraceSyntax = errors.New("syntax error: expect 'file.go:234'") // Syntax: -log_backtrace_at=gopherflakes.go:234 // Note that unlike vmodule the file extension is included here. @@ -616,7 +616,7 @@ type loggingT struct { // severityTraceThreshold determines the minimum severity at which // file traces will be logged in the header. See severity const iota above. // Only severities at or above this number will be logged with a trace, - // eg. at severityTraceThreshold = 1, then only severities errorLog and fatalLog + // eg. at severityTraceThreshold = 2, then only severities errorLog and fatalLog // will log with traces. severityTraceThreshold severity @@ -674,7 +674,7 @@ func init() { // and they will also be written to stderr (anything Error and above is). // Keep in mind severities are "upside-down" from verbosities; so here 3=error, 4=fatal, and 0=info // and that here severity>=3 will meet the threshold. - display.severityTraceThreshold.set(3) + display.severityTraceThreshold.set(2) // Set display verbosity default Info. So it will render // all Fatal, Error, Warn, and Info log levels. // Please don't use Fatal for display; again, Fatal logs should only go through file logging From 4fdb9de7e42f82ca91c8b65859d1782f422b4d42 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 15:05:50 -0600 Subject: [PATCH 45/60] problem: update glog tests for new format spec solution: changes to date format for display and removed color from debug file logs requires updates to test expectations --- logger/glog/glog_test.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/logger/glog/glog_test.go b/logger/glog/glog_test.go index 624c87888..adc251697 100644 --- a/logger/glog/glog_test.go +++ b/logger/glog/glog_test.go @@ -149,7 +149,7 @@ func TestInfoDepth(t *testing.T) { } for i, m := range msgs { - if !strings.HasPrefix(m, severityColor[infoLog]+"I") { + if !strings.HasPrefix(m, "I") { t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) } w := fmt.Sprintf("depth-test%d", i) @@ -219,7 +219,7 @@ func TestHeader1ErrorLog(t *testing.T) { pid = 1234 Error("test") line := lineNumber() - 1 // the line directly above - format := severityColor[errorLog] + "E" + severityColorReset + "0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" + format := "E" + "0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" n, err := fmt.Sscanf(loggingContents(errorLog), format, &line) if n != 1 || err != nil { t.Errorf("log format error: %d elements, error %s:\n%s", n, err, loggingContents(errorLog)) @@ -245,7 +245,7 @@ func TestHeader2InfoLog(t *testing.T) { defer logging.verbosityTraceThreshold.set(s) pid = 1234 Info("test") - format := severityColor[infoLog] + "I" + severityColorReset + "0102 15:04:05.067890 logger/glog/glog_test.go:"+strconv.Itoa(lineNumber()-1)+"] test\n" + format := "I" + "0102 15:04:05.067890 logger/glog/glog_test.go:"+strconv.Itoa(lineNumber()-1)+"] test\n" n, err := fmt.Sscanf(loggingContents(infoLog), format) if err != nil { t.Errorf("log format error: %d elements, error %s:\n%s", n, err, loggingContents(infoLog)) @@ -306,8 +306,23 @@ func TestWarningDisplay(t *testing.T) { display.verbosity.Set("3") defer display.verbosity.Set("0") D(2).Warnln("test") - if !displayContains(warningLog, "W", t) { - t.Errorf("Warning has wrong character: %q", displayContents(warningLog)) + if !displayContains(warningLog, "test", t) { + t.Error("Warning failed") + } + str := displayContents(warningLog) + if !displayContains(infoLog, str, t) { + t.Error("Info failed") + } +} + +func TestErrorDisplay(t *testing.T) { + setFlags() + defer display.swapDisplay(display.newDisplayBuffers()) + display.verbosity.Set("3") + defer display.verbosity.Set("0") + D(2).Errorln("test") + if !displayContains(errorLog, "ERROR", t) { + t.Errorf("Error has wrong character: %q", displayContents(errorLog)) } if !displayContains(warningLog, "test", t) { t.Error("Warning failed") @@ -340,7 +355,10 @@ func TestD(t *testing.T) { display.verbosity.Set("2") defer display.verbosity.Set("0") D(2).Infoln("test") - if !displayContains(infoLog, "I", t) { + if !displayContains(infoLog, ":", t) { + t.Errorf("Info has wrong character: %q", displayContents(infoLog)) + } + if displayContains(infoLog, "I", t) { t.Errorf("Info has wrong character: %q", displayContents(infoLog)) } if !displayContains(infoLog, "test", t) { From 159f9f54b0f80f72349d41b1af36e706e8fcd9b4 Mon Sep 17 00:00:00 2001 From: ia Date: Mon, 18 Dec 2017 15:52:40 -0600 Subject: [PATCH 46/60] solution: improve status log condit --- cmd/geth/log_display_basic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index a37bbfaca..7ac1d63c7 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -61,7 +61,7 @@ var basicDisplaySystem = displayEventHandlers{ handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { // If not in import mode, OR if we haven't yet logged a chain event. - if currentMode != lsModeImport || chainEventLastSent.IsZero() { + if currentMode != lsModeImport || time.Since(chainEventLastSent) > tickerInterval { currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) } }, From 6e9fbfba87386a43e3fe41990694a0d452f607ff Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 19 Dec 2017 09:28:47 -0600 Subject: [PATCH 47/60] solution: address pr feedback fromr @tzdybal - adds commented examples for display fmts - moves a few vars to block consts - use glog.Fatal instead of glog.Err + os.Exit - fix typos and deadness in comments - make a few logs more consistent --- cmd/geth/flags.go | 2 +- cmd/geth/log_context.go | 4 +-- cmd/geth/log_dispatch.go | 24 ++++++--------- cmd/geth/log_display_basic.go | 57 +++++++++++++++++++---------------- cmd/geth/log_display_dash.go | 47 ++++++++++++++++++++++++++++- cmd/geth/log_display_green.go | 25 +++++++++++++++ 6 files changed, 114 insertions(+), 45 deletions(-) diff --git a/cmd/geth/flags.go b/cmd/geth/flags.go index e122f793b..121e5d466 100644 --- a/cmd/geth/flags.go +++ b/cmd/geth/flags.go @@ -159,7 +159,7 @@ var ( } DisplayFormatFlag = cli.StringFlag{ Name: "display-fmt", - Usage: "Display format", + Usage: "Display format (experimental). Current possible values are [basic|green|dash].", Value: "basic", } VModuleFlag = cli.StringFlag{ diff --git a/cmd/geth/log_context.go b/cmd/geth/log_context.go index 454a9bb60..8239d4a26 100644 --- a/cmd/geth/log_context.go +++ b/cmd/geth/log_context.go @@ -78,7 +78,7 @@ func setupLogging(ctx *cli.Context) error { if ctx.GlobalIsSet(DisplayFlag.Name) { i := ctx.GlobalInt(DisplayFlag.Name) if i > 5 { - return fmt.Errorf("--%s level must be 0 <= i <= 3, got: %d", DisplayFlag.Name, i) + return fmt.Errorf("--%s level must be 0 <= i <= 5, got: %d", DisplayFlag.Name, i) } glog.SetD(i) } @@ -322,7 +322,7 @@ func logLoggingConfiguration(ctx *cli.Context) { ) if logger.MlogEnabled() { - glog.V(logger.Warn).Warnf("Machine log config: mlog=%s mlog-dir=%s", logger.GetMLogFormat().String(), logger.GetMLogDir()) + glog.V(logger.Warn).Infof("Machine log config: mlog=%s mlog-dir=%s", logger.GetMLogFormat().String(), logger.GetMLogDir()) glog.D(logger.Warn).Infof("Machine log config: mlog=%s mlog-dir=%s", logger.ColorGreen(logger.GetMLogFormat().String()), logger.ColorGreen(logger.GetMLogDir())) } else { glog.V(logger.Warn).Warnf("Machine log config: mlog=%s mlog-dir=%s", logger.GetMLogFormat().String(), logger.GetMLogDir()) diff --git a/cmd/geth/log_dispatch.go b/cmd/geth/log_dispatch.go index 7bd6d4e13..a2015ca82 100644 --- a/cmd/geth/log_dispatch.go +++ b/cmd/geth/log_dispatch.go @@ -73,18 +73,17 @@ func (hs displayEventHandlers) getByName(eventType logEventType) (*displayEventH // displayEventHandlers set. This can be considered a temporary solve for handling "registering" or // "delegating" log interface systems. func mustGetDisplaySystemFromName(s string) displayEventHandlers { - displaySystem := basicDisplaySystem switch s { + case "basic": + return basicDisplaySystem case "green": - displaySystem = greenDisplaySystem + return greenDisplaySystem case "dash": - displaySystem = dashDisplaySystem - case "basic": - // already set as default + return dashDisplaySystem default: glog.Fatalln("%v: --%v", ErrInvalidFlag, DisplayFormatFlag.Name) } - return displaySystem + return displayEventHandlers{} } // runAllIfAny runs all configured fns for a given event, if registered. @@ -135,17 +134,14 @@ func dispatchStatusLogs(ctx *cli.Context, ethe *eth.Ethereum) { } } if len(eqs) < 2 { - glog.Errorf("%v: %v. Must be comma-separated pairs of module=interval.", ErrInvalidFlag, eqs) - os.Exit(1) + glog.Fatalf("%v: %v. Must be comma-separated pairs of module=interval.", ErrInvalidFlag, eqs) } // Catch unavailable and duplicate status feature logs if status, ok := availableLogStatusFeatures[eqs[0]]; !ok { - glog.Errorf("%v: %v: unavailable status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) - os.Exit(1) + glog.Fatalf("%v: %v: unavailable status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) } else if status.Seconds() != 0 { - glog.Errorf("%v: %v: duplicate status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) - os.Exit(1) + glog.Fatalf("%v: %v: duplicate status feature by name of '%v'", flagName, ErrInvalidFlag, eqs[0]) } // If user just uses "sync" instead of "sync=42", append empty string and delegate to each status log function how to handle it @@ -195,7 +191,6 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventBefore) if len(handledEvents) > 0 { - //glog.D(logger.Error).Errorf("handling %d events", len(handledEvents)) go func() { for ev := range ethEvents.Chan() { updateLogStatusModeHandler(ctx, e, nil, tickerInterval) @@ -224,20 +219,19 @@ func runDisplayLogs(ctx *cli.Context, e *eth.Ethereum, tickerInterval time.Durat select { case <-ticker.C: updateLogStatusModeHandler(ctx, e, nil, tickerInterval) - //glog.D(logger.Error).Errorf("tick tock mode= %v", currentMode) handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventInterval) } } }() } + // Listen for interrupt sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigc) for { select { case <-sigc: - // Listen for interrupt handles.runAllIfAny(ctx, e, nil, tickerInterval, logEventAfter) return } diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 7ac1d63c7..7634f0078 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -1,21 +1,3 @@ -package main - -import ( - "strings" - "github.com/ethereumproject/go-ethereum/logger/glog" - "github.com/ethereumproject/go-ethereum/logger" - "fmt" - "math/big" - "time" - "strconv" - "github.com/ethereumproject/go-ethereum/eth" - "github.com/ethereumproject/go-ethereum/core/types" - "github.com/ethereumproject/go-ethereum/core" - "gopkg.in/urfave/cli.v1" - "github.com/ethereumproject/go-ethereum/eth/downloader" -) - -// basicDisplaySystem is the basic display system spec'd in #127. // --- //2017-02-03 16:44:00 Discover 0/25 peers //2017-02-03 16:45:00 Discover 1/25 peers @@ -29,14 +11,35 @@ import ( //2017-02-03 16:52:05 Import #3124788 84e11ff4 15/ 7 tx/mgas 10/25 peers //2017-02-03 16:52:25 Import #3124789 9e45a241 5/ 1 tx/mgas 12/25 peers //2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers -// +//2017-02-03 16:52:46 Mined #3124791 b719f31b 7/ 1 tx/mgas 18/25 peers +// --- // FIXME: '16:52:45 Import #3124790 ' aligns right instead of left + +package main + +import ( + "fmt" + "github.com/ethereumproject/go-ethereum/core" + "github.com/ethereumproject/go-ethereum/core/types" + "github.com/ethereumproject/go-ethereum/eth" + "github.com/ethereumproject/go-ethereum/eth/downloader" + "github.com/ethereumproject/go-ethereum/logger" + "github.com/ethereumproject/go-ethereum/logger/glog" + "gopkg.in/urfave/cli.v1" + "math/big" + "strconv" + "strings" + "time" +) + +// basicDisplaySystem is the basic display system spec'd in #127. var basicDisplaySystem = displayEventHandlers{ { eventT: logEventChainInsert, ev: core.ChainInsertEvent{}, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + // Conditional prevents chain insert event logs during full/fast sync if currentMode == lsModeImport { currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) chainEventLastSent = time.Now() @@ -60,7 +63,7 @@ var basicDisplaySystem = displayEventHandlers{ eventT: logEventInterval, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - // If not in import mode, OR if we haven't yet logged a chain event. + // If not in import mode OR if we haven't logged a chain event lately. if currentMode != lsModeImport || time.Since(chainEventLastSent) > tickerInterval { currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) } @@ -74,12 +77,14 @@ func formatBlockNumber(i uint64) string { } // Examples of spec'd output. -var xlocalOfMaxD = "#92481951 of #93124363" // #2481951 of #3124363 -//var xpercentD = " 92.0%" // 92.0% -var xlocalHeadHashD = "c76c34e7" // c76c34e7 -var xprogressRateD = " 117/ 129/ 11" // 117/ 129/11 -var xprogressRateUnitsD = "blk/txs/mgas sec" // blk/tx/mgas sec -var xpeersD = "18/25 peers" // 18/25 peers +const ( + xlocalOfMaxD = "#92481951 of #93124363" // #2481951 of #3124363 + // xpercentD = " 92.0%" // 92.0% // commented because it is an item in the spec, but shorter than xLocalHeadHashD + xlocalHeadHashD = "c76c34e7" // c76c34e7 + xprogressRateD = " 117/ 129/ 11" // 117/ 129/11 + xprogressRateUnitsD = "blk/txs/mgas sec" // blk/tx/mgas sec + xpeersD = "18/25 peers" // 18/25 peers +) func strScanLenOf(s string, leftAlign bool) string { if leftAlign { diff --git a/cmd/geth/log_display_dash.go b/cmd/geth/log_display_dash.go index 05294bdb2..de80435b6 100644 --- a/cmd/geth/log_display_dash.go +++ b/cmd/geth/log_display_dash.go @@ -1,3 +1,48 @@ +// --- +//┌Import | local_head ◼ n=5047515 ⬡=0xfae68412… txs=18 time=37s ago─────────────────────────────────┐ +//│ │ +//└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +//┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +//│n=5047515] ∆ blks=1 (inserted_at=2017-12-19 09:22:37 -0600 CST took=31ms) │ +//│ │ +//│ │ +//│ │ +//│n=5047515] ∑ mgas= 0/ 1blks │ +//│ │ +//│ │ +//│ │ +//│n=5047515] ∑ txs= 18/ 1blks │ +//│ │ +//│ ▅ ▆ ▅ ▂ │ +//│▂▂▂ ▅▃▄ ▇▂▃▃▁ ▂▂▂▃▆▁ ▁ ▁▄▁▁▄ ▂▁▁ ▁▂▁ ▁▃ ▁▄ │ +//│ │ +//└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +//┌Peers (12 / 25)───────────────────────────────────────────────────────────────────────────────────┐ +//│ │ +//│ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅▅▅▅ ▅▁▁▁▁▁▁▁▁▁▅▅▅ │ +//│ ▃▃▃▃▆▆ ▆▆▆▆▆ │ +//│ ▁ ▇▇▇▇ │ +//│▅▅▅▅▅ ▅▅ │ +//│ │ +// +//│Peer id=8310b1f98b9b7afe eth/63 [Parity/v1.7.0-unstable-5f2cabd-20170727/x86_64-linux-gnu/rustc1.…│ +//│Peer id=5fbfb426fbb46f8b eth/63 [Parity/v1.7.7-stable-eb7c648-20171015/x86_64-linux-gnu/rustc1.20…│ +//│Peer id=1922bfd2acc1e82f eth/63 [Parity/v1.8.2-beta-1b6588c-20171025/x86_64-linux-gnu/rustc1.21.0…│ +//│Peer id=8ed199326f981ae9 eth/63 [Parity/v1.8.0-beta-9882902-20171015/x86_64-linux-gnu/rustc1.20.0…│ +//│Peer id=c89d548e2a55d324 eth/63 [Parity/v1.8.2-beta-1b6588c-20171025/x86_64-linux-gnu/rustc1.21.0…│ +//│Peer id=13c960f1da9a6337 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1…│ +//│Peer id=021eb56de4a7b725 eth/63 [Parity/v1.6.9-stable-d44b008-20170716/x86_64-linux-gnu/rustc1.18…│ +//│Peer id=5701cea16e32cb06 eth/63 [Parity/v1.7.9-stable-12940e4-20171113/x86_64-linux-gnu/rustc1.21…│ +//│Peer id=385ffdfb7f7cd2f5 eth/63 [Geth/v4.0.0/windows/go1.8] [hs 0.00/s, bs 0.15/s, rs 0.00/s, ss …│ +//│Peer id=d5a5d20cb8b77e14 eth/63 [Parity/v1.8.2-beta-1b6588c-20171025/x86_64-linux-gnu/rustc1.21.0…│ +//│Peer id=e7f3c98896581fee eth/63 [Parity/v1.7.9-unstable-12940e4-20171113/x86_64-linux-gnu/rustc1.…│ +//│Peer id=cb0e1d693f950573 eth/63 [Parity/v1.7.10-stable-3931713-20171211/x86_64-linux-gnu/rustc1.2…│ +//│ │ +//│ │ +//│ │ +//└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +// --- + package main import ( @@ -117,7 +162,7 @@ func addDataWithLimit(sl []int, dataPoint int, maxLen int) []int { return sl } -// greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic. +// dashDisplaySystem is an experimental display system intended as a proof of concept for the display dispatch system var dashDisplaySystem = displayEventHandlers{ { eventT: logEventBefore, diff --git a/cmd/geth/log_display_green.go b/cmd/geth/log_display_green.go index 798022d21..c88664898 100644 --- a/cmd/geth/log_display_green.go +++ b/cmd/geth/log_display_green.go @@ -1,3 +1,28 @@ +// --- +//2017-12-19 08:55:03 Discover ➫⟪# 0 0xd4e5674…⟫ ~⟪ 0 blks 0 txs 0 mgas /sec⟫ ✌︎︎︎⟪ 1/25 peers⟫ qos=⟪rtt=18s ttl=1m0s conf=1.00⟫ +//2017-12-19 08:55:17 ◼⋯⋯⬇ Start ⟪Peer id=931f21921fcc7654 eth/63 [Parity/v1.8.3-beta-b49c44a-20171114/x86_64-linux-gnu/rustc1.21.0] [hs 0.00/s, bs 0.00/s, rs 0.00/s, ss 0.00/s, miss 0, rtt 20s]⟫ hash=⟪0xc5ea430…⟫ TD=⟪185549045030301150788⟫ +//2017-12-19 08:55:19 ◼⋯⋯❐ Insert headers=⟪processed= 0 ignored= 192⟫ ❐=⟪n=1836399 hash=0x1ecf991…⟫took=⟪73.546ms⟫ +//2017-12-19 08:55:20 ◼⋯⋯❐ Insert headers=⟪processed= 0 ignored= 192⟫ ❐=⟪n=1836591 hash=0xcff7c3c…⟫took=⟪275.686ms⟫ +//2017-12-19 08:57:25 ◼⋯⋯⬇ Fail ⟪Peer id=931f21921fcc7654 eth/63 [Parity/v1.8.3-beta-b49c44a-20171114/x86_64-linux-gnu/rustc1.21.0] [hs 211.71/s, bs 124.20/s, rs 184.06/s, ss 735.50/s, miss 0, rtt 1.512187158s]⟫ err=⟪content processing canceled (requested)⟫ +//2017-12-19 09:00:43 ◼⋯⋯⬇ Start ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 0.00/s, bs 0.00/s, rs 0.00/s, ss 0.00/s, miss 0, rtt 20s]⟫ hash=⟪0x26f9a91…⟫ TD=⟪185552382866154347587⟫ +//2017-12-19 09:00:46 Sync ︎◉⟪#5043160 0xed71bef…⟫ ~⟪ 0 blks 0 txs 0 mgas /sec⟫ ✌︎︎︎⟪ 5/25 peers⟫ height=⟪5047421 99.92%⟫ qos=⟪rtt=15.942s ttl=1m0s conf=0.70⟫ +//2017-12-19 09:00:51 ◼⋯⋯◼ Insert blocks=⟪processed= 48 queued= 0 ignored= 0 txs= 341⟫ ◼=⟪n= 5043208 hash=0x195123c… time=16h55m40.639s ago⟫ took=⟪5.692s⟫ +//2017-12-19 09:01:47 Sync ︎◉⟪#5044175 0xedb389d…⟫ ~⟪ 16 blks 142 txs 3 mgas /sec⟫ ✌︎︎︎⟪ 8/25 peers⟫ height=⟪5047421 99.94%⟫ qos=⟪rtt=9.585s ttl=36.603s conf=0.87⟫ +//2017-12-19 09:02:43 ◼⋯⋯◼ Insert blocks=⟪processed=2048 queued= 0 ignored= 0 txs=17899⟫ ◼=⟪n= 5045256 hash=0xcf3d3fb… time=8h49m5.014s ago⟫ took=⟪1m51.251s⟫ +//2017-12-19 09:03:47 Sync ︎◉⟪#5046394 0x8b10bdd…⟫ ~⟪ 36 blks 306 txs 9 mgas /sec⟫ ✌︎︎︎⟪11/25 peers⟫ height=⟪5047421 99.98%⟫ qos=⟪rtt=6.695s ttl=22.328s conf=1.00⟫ +//2017-12-19 09:04:43 ◼⋯⋯◼ Insert blocks=⟪processed=2048 queued= 0 ignored= 0 txs=18124⟫ ◼=⟪n= 5047304 hash=0xc0b4780… time=33m45.455s ago⟫ took=⟪2m0.462s⟫ +//2017-12-19 09:04:50 ◼⋯⋯⬇ Done ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 813.97/s, bs 295.27/s, rs 0.00/s, ss 0.00/s, miss 0, rtt 1.918361657s]⟫ hash=⟪0x26f9a91…⟫ TD=⟪185552382866154347587⟫ +//2017-12-19 09:04:50 ◼⋯⋯◼ Insert blocks=⟪processed= 118 queued= 0 ignored= 0 txs=1183⟫ ◼=⟪n= 5047422 hash=0xc47a4fc… time=4m24.711s ago⟫ took=⟪7.258s⟫ +//2017-12-19 09:04:51 ◼⋯⋯⬇ Start ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 0.00/s, bs 0.00/s, rs 0.00/s, ss 0.00/s, miss 0, rtt 1.918361657s]⟫ hash=⟪0x249c344…⟫ TD=⟪185554240788446349916⟫ +//2017-12-19 09:04:52 ◼⋯⋯◼ Insert blocks=⟪processed= 4 queued= 0 ignored= 0 txs= 53⟫ ◼=⟪n= 5047426 hash=0x6aff1b9… time=3m21.897s ago⟫ took=⟪185ms⟫ +//2017-12-19 09:04:53 ◼⋯⋯⬇ Done ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 0.00/s, bs 1.18/s, rs 0.00/s, ss 0.00/s, miss 0, rtt 1.743539409s]⟫ hash=⟪0x249c344…⟫ TD=⟪185554240788446349916⟫ +//2017-12-19 09:04:53 ◼⋯⋯◼ Insert blocks=⟪processed= 11 queued= 0 ignored= 0 txs= 119⟫ ◼=⟪n= 5047437 hash=0x3950d3d… time=1m27.504s ago⟫ took=⟪735ms⟫ +//2017-12-19 09:09:53 ◼⋯⋯◼ Insert blocks=⟪processed= 13 queued= 0 ignored= 0 txs= 124⟫ ◼=⟪n= 5047453 hash=0xff69a28… time=19.599s ago⟫ took=⟪630ms⟫ +//2017-12-19 09:10:11 ◼⋯⋯◼ Insert blocks=⟪processed= 1 queued= 0 ignored= 0 txs= 1⟫ ◼=⟪n= 5047454 hash=0xd03268a… time=34.122s ago⟫ took=⟪8ms⟫ +//2017-12-19 09:10:18 ◼⋯⋯◼ Insert blocks=⟪processed= 1 queued= 0 ignored= 0 txs= 7⟫ ◼=⟪n= 5047455 hash=0x1364b57… time=13.649s ago⟫ took=⟪17ms⟫ +//2017-12-19 09:10:25 ◼⋯⋯◼ Insert blocks=⟪processed= 1 queued= 0 ignored= 0 txs= 6⟫ ◼=⟪n= 5047456 hash=0x00ee977… time=12.328s ago⟫ took=⟪23ms⟫ +// --- + package main import ( From 1abeb28f6558d1cca2875f6071805f027c3a134a Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 19 Dec 2017 10:14:29 -0600 Subject: [PATCH 48/60] (nonfunctional): update comment with fuller example of dash --- cmd/geth/log_display_dash.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd/geth/log_display_dash.go b/cmd/geth/log_display_dash.go index de80435b6..2f08fae15 100644 --- a/cmd/geth/log_display_dash.go +++ b/cmd/geth/log_display_dash.go @@ -1,30 +1,31 @@ // --- -//┌Import | local_head ◼ n=5047515 ⬡=0xfae68412… txs=18 time=37s ago─────────────────────────────────┐ +//┌Import | local_head ◼ n=5047652 ⬡=0x33f6878a… txs=1 time=21s ago──────────────────────────────────┐ //│ │ //└──────────────────────────────────────────────────────────────────────────────────────────────────┘ //┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -//│n=5047515] ∆ blks=1 (inserted_at=2017-12-19 09:22:37 -0600 CST took=31ms) │ +//│n=5047652] ∆ blks=1 (inserted_at=2017-12-19 09:51:35 -0600 CST took=23ms) │ //│ │ //│ │ //│ │ -//│n=5047515] ∑ mgas= 0/ 1blks │ +//│n=5047652] ∑ mgas= 0/ 1blks │ //│ │ //│ │ //│ │ -//│n=5047515] ∑ txs= 18/ 1blks │ -//│ │ -//│ ▅ ▆ ▅ ▂ │ -//│▂▂▂ ▅▃▄ ▇▂▃▃▁ ▂▂▂▃▆▁ ▁ ▁▄▁▁▄ ▂▁▁ ▁▂▁ ▁▃ ▁▄ │ +//│n=5047652] ∑ txs= 1/ 1blks │ +//│ ▃ ▆ ▅ ▇ │ +//│ ▃ ▁ ▁ ▂ ▄ ▆ ▁▂ ▄ ▃ ▅ ▁ ▁ ▇▆ ▂ │ +//│▁▇ ▃ ▆ ▁▁▁ ▁ ▂ ▂▁▂▂▆▂▁▁▁ ▁▄▁ ▃▁▂▅▇ ▅ ▄ ▁ ▂ ▆ ▂▄▇ ▁ ▁ ▃▅▄▇▃ ▃ ▃ ▂ ▁ ▁▁│ //│ │ //└──────────────────────────────────────────────────────────────────────────────────────────────────┘ -//┌Peers (12 / 25)───────────────────────────────────────────────────────────────────────────────────┐ +//┌Peers (13 / 25)───────────────────────────────────────────────────────────────────────────────────┐ +//│ │ +//│ │ +//│ │ +//│ │ //│ │ -//│ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅▅▅▅ ▅▁▁▁▁▁▁▁▁▁▅▅▅ │ -//│ ▃▃▃▃▆▆ ▆▆▆▆▆ │ -//│ ▁ ▇▇▇▇ │ -//│▅▅▅▅▅ ▅▅ │ //│ │ // +//│Peer id=5701cea16e32cb06 eth/63 [Parity/v1.7.9-stable-12940e4-20171113/x86_64-linux-gnu/rustc1.21…│ //│Peer id=8310b1f98b9b7afe eth/63 [Parity/v1.7.0-unstable-5f2cabd-20170727/x86_64-linux-gnu/rustc1.…│ //│Peer id=5fbfb426fbb46f8b eth/63 [Parity/v1.7.7-stable-eb7c648-20171015/x86_64-linux-gnu/rustc1.20…│ //│Peer id=1922bfd2acc1e82f eth/63 [Parity/v1.8.2-beta-1b6588c-20171025/x86_64-linux-gnu/rustc1.21.0…│ @@ -32,14 +33,13 @@ //│Peer id=c89d548e2a55d324 eth/63 [Parity/v1.8.2-beta-1b6588c-20171025/x86_64-linux-gnu/rustc1.21.0…│ //│Peer id=13c960f1da9a6337 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1…│ //│Peer id=021eb56de4a7b725 eth/63 [Parity/v1.6.9-stable-d44b008-20170716/x86_64-linux-gnu/rustc1.18…│ -//│Peer id=5701cea16e32cb06 eth/63 [Parity/v1.7.9-stable-12940e4-20171113/x86_64-linux-gnu/rustc1.21…│ -//│Peer id=385ffdfb7f7cd2f5 eth/63 [Geth/v4.0.0/windows/go1.8] [hs 0.00/s, bs 0.15/s, rs 0.00/s, ss …│ +//│Peer id=385ffdfb7f7cd2f5 eth/63 [Geth/v4.0.0/windows/go1.8] [hs 0.00/s, bs 0.00/s, rs 0.00/s, ss …│ //│Peer id=d5a5d20cb8b77e14 eth/63 [Parity/v1.8.2-beta-1b6588c-20171025/x86_64-linux-gnu/rustc1.21.0…│ +//│Peer id=5f259d71e80f710b eth/63 [Parity/v1.7.9-stable-12940e4-20171113/x86_64-linux-gnu/rustc1.21…│ //│Peer id=e7f3c98896581fee eth/63 [Parity/v1.7.9-unstable-12940e4-20171113/x86_64-linux-gnu/rustc1.…│ //│Peer id=cb0e1d693f950573 eth/63 [Parity/v1.7.10-stable-3931713-20171211/x86_64-linux-gnu/rustc1.2…│ //│ │ //│ │ -//│ │ //└──────────────────────────────────────────────────────────────────────────────────────────────────┘ // --- From 6b31365bd212b154d7b28b7dc1e3908931be44ae Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 19 Dec 2017 10:15:15 -0600 Subject: [PATCH 49/60] problem: no logging for mined block event solution: refactors log_display_basic, adds handler for mined block event - also debugs print basic trying to calc diff for whole chain --- cmd/geth/log_display_basic.go | 78 +++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 7634f0078..563562da3 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -13,7 +13,6 @@ //2017-02-03 16:52:45 Import #3124790 d819f71c 0/ 0 tx/mgas 18/25 peers //2017-02-03 16:52:46 Mined #3124791 b719f31b 7/ 1 tx/mgas 18/25 peers // --- -// FIXME: '16:52:45 Import #3124790 ' aligns right instead of left package main @@ -28,7 +27,6 @@ import ( "gopkg.in/urfave/cli.v1" "math/big" "strconv" - "strings" "time" ) @@ -41,8 +39,31 @@ var basicDisplaySystem = displayEventHandlers{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { // Conditional prevents chain insert event logs during full/fast sync if currentMode == lsModeImport { - currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) - chainEventLastSent = time.Now() + switch d := evData.(type) { + case core.ChainInsertEvent: + currentBlockNumber = PrintStatusBasic(e, tickerInterval, e.BlockChain().GetBlockByNumber(d.LastNumber), ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + chainEventLastSent = time.Now() + } + } + }, + }, + }, + { + eventT: logEventMinedBlock, + ev: core.NewMinedBlockEvent{}, + handlers: displayEventHandlerFns{ + func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { + switch d := evData.(type) { + case core.NewMinedBlockEvent: + glog.D(logger.Warn).Infof(basicScanLn, + "Mined", + formatBlockNumber(d.Block.NumberU64()), + d.Block.Hash().Hex()[2 : 2+len(xlocalHeadHashD)], + fmt.Sprintf("%3d/%2d", d.Block.Transactions().Len(), new(big.Int).Div(d.Block.GasUsed(), big.NewInt(1000000)).Int64()), + "txs/mgas", + fmt.Sprintf("%2d/%2d peers", e.Downloader().GetPeers().Len(), ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))), + ) + currentBlockNumber = d.Block.NumberU64() } }, }, @@ -63,9 +84,13 @@ var basicDisplaySystem = displayEventHandlers{ eventT: logEventInterval, handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { - // If not in import mode OR if we haven't logged a chain event lately. - if currentMode != lsModeImport || time.Since(chainEventLastSent) > tickerInterval { - currentBlockNumber = PrintStatusBasic(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + // If not in import mode OR if we haven't logged a chain event. + if time.Since(chainEventLastSent) > tickerInterval || currentMode != lsModeImport { + glog.D(logger.Error).Infoln("time.Since(chainEventLastSent) > tickerInterval", time.Since(chainEventLastSent) > tickerInterval, + "currentMode != lsModeImport", currentMode != lsModeImport, + currentMode, chainEventLastSent, tickerInterval, + ) + currentBlockNumber = PrintStatusBasic(e, tickerInterval, nil, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) } }, }, @@ -86,11 +111,13 @@ const ( xpeersD = "18/25 peers" // 18/25 peers ) +const basicScanLn = "%-8s %-22s %8s %13s %-16s %11s" + func strScanLenOf(s string, leftAlign bool) string { if leftAlign { - return "%" + strconv.Itoa(len(s)) + "s" + return "%-" + strconv.Itoa(len(s)) + "s" } - return "%-" + strconv.Itoa(len(s)) + "s" + return "%" + strconv.Itoa(len(s)) + "s" } type printUnit struct { @@ -128,7 +155,7 @@ func calcPercent(quotient, divisor uint64) float64 { } // PrintStatusBasic implements the displayEventHandlerFn interface -var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 { +var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB *types.Block, maxPeers int) uint64 { l := currentMode lastLoggedBlockN := currentBlockNumber @@ -143,13 +170,13 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe if localheight < syncheight { return fmt.Sprintf("%9s of %9s", formatBlockNumber(localheight), formatBlockNumber(syncheight)) } - return fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localheight)) + return fmt.Sprintf("%9s", formatBlockNumber(localheight)) } formatPercentD := func(localheight, syncheight uint64) string { // Calculate and format percent sync of known height fHeightRatio := fmt.Sprintf("%4.2f%%", calcPercent(localheight, syncheight)) - return fmt.Sprintf(strScanLenOf(xlocalHeadHashD, false), fHeightRatio) + return fmt.Sprintf("%s", fHeightRatio) } formatBlockHashD := func(b *types.Block) string { @@ -158,10 +185,10 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe formatProgressRateD := func(blksN, txsN, mgasN int) string { if blksN < 0 { - return fmt.Sprintf(" %4d/%2d", txsN, mgasN) + return fmt.Sprintf("%4d/%2d", txsN, mgasN) } if txsN < 0 { - return fmt.Sprintf("%3d/ /%2d", blksN, mgasN) + return fmt.Sprintf("%3d/%2d", blksN, mgasN) } return fmt.Sprintf("%3d/%4d/%2d", blksN, txsN, mgasN) } @@ -170,20 +197,9 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe return fmt.Sprintf("%2d/%2d peers", peersN, maxpeersN) } - // formatOutputScanLn accepts printUnits and returns a scanln based on their example string length and - // printUnit configured alignment. - // eg. %12s %-8s %5s %15s - formatOutputScanLn := func(printunits ...*printUnit) string { - o := []string{} - for _, u := range printunits { - o = append(o, strScanLenOf(u.example, u.leftAlign)) - } - return strings.Join(o, " ") - } - peersD.value = formatPeersD(e.Downloader().GetPeers().Len(), maxPeers) defer func() { - glog.D(logger.Warn).Infof("%-8s "+formatOutputScanLn(localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD), + glog.D(logger.Warn).Infof(basicScanLn, l, localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) }() @@ -200,7 +216,9 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe // Calculate progress rates var blks, txs, mgas int - if lastLoggedBlockN == 0 { + if l == lsModeImport && evB != nil { + blks, txs, mgas = 1, evB.Transactions().Len(), int(new(big.Int).Div(evB.GasUsed(), big.NewInt(1000000)).Uint64()) + } else if lastLoggedBlockN == 0 { blks, txs, mgas = calcBlockDiff(e, origin, localHead) } else { blks, txs, mgas = calcBlockDiff(e, lastLoggedBlockN, localHead) @@ -212,17 +230,17 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, maxPe localOfMaxD.value = formatLocalOfMaxD(lh, chainSyncHeight) percentOrHash.value = formatPercentD(lh, chainSyncHeight) progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), -1, mgas/int(tickerInterval.Seconds())) - progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/ /mgas sec") + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, true), "blk/mgas sec") case lsModeFullSync: localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) percentOrHash.value = formatBlockHashD(localHead) progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), txs/int(tickerInterval.Seconds()), mgas/int(tickerInterval.Seconds())) - progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), "blk/txs/mgas sec") + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, true), "blk/txs/mgas sec") case lsModeImport: localOfMaxD.value = fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localHead.NumberU64())) percentOrHash.value = formatBlockHashD(localHead) progressRateD.value = fmt.Sprintf(strScanLenOf(xprogressRateD, false), formatProgressRateD(-1, txs, mgas)) - progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, false), " txs/mgas ") + progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, true), "txs/mgas") default: panic("unreachable") } From c55bc8a83e6da95bd659eec1105846dad57dca4e Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 19 Dec 2017 13:26:58 -0600 Subject: [PATCH 50/60] solution: refactor display basic logs use global 'current' bookmark vars instead of local copies check that we're not going to try and calc progress rate for whole chain --- cmd/geth/log_display_basic.go | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 563562da3..2c63bf65e 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -85,11 +85,7 @@ var basicDisplaySystem = displayEventHandlers{ handlers: displayEventHandlerFns{ func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) { // If not in import mode OR if we haven't logged a chain event. - if time.Since(chainEventLastSent) > tickerInterval || currentMode != lsModeImport { - glog.D(logger.Error).Infoln("time.Since(chainEventLastSent) > tickerInterval", time.Since(chainEventLastSent) > tickerInterval, - "currentMode != lsModeImport", currentMode != lsModeImport, - currentMode, chainEventLastSent, tickerInterval, - ) + if currentMode != lsModeImport || chainEventLastSent.IsZero() { currentBlockNumber = PrintStatusBasic(e, tickerInterval, nil, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) } }, @@ -157,9 +153,6 @@ func calcPercent(quotient, divisor uint64) float64 { // PrintStatusBasic implements the displayEventHandlerFn interface var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB *types.Block, maxPeers int) uint64 { - l := currentMode - lastLoggedBlockN := currentBlockNumber - localOfMaxD := &printUnit{"", xlocalOfMaxD, true} percentOrHash := &printUnit{"", xlocalHeadHashD, false} progressRateD := &printUnit{"", xprogressRateD, false} // 117/ 129/11 @@ -170,7 +163,7 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB * if localheight < syncheight { return fmt.Sprintf("%9s of %9s", formatBlockNumber(localheight), formatBlockNumber(syncheight)) } - return fmt.Sprintf("%9s", formatBlockNumber(localheight)) + return fmt.Sprintf("%9s ", formatBlockNumber(localheight)) } formatPercentD := func(localheight, syncheight uint64) string { @@ -200,11 +193,11 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB * peersD.value = formatPeersD(e.Downloader().GetPeers().Len(), maxPeers) defer func() { glog.D(logger.Warn).Infof(basicScanLn, - l, localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) + currentMode, localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) }() - if l == lsModeDiscover { - return lastLoggedBlockN + if currentMode == lsModeDiscover { + return currentBlockNumber } origin, current, chainSyncHeight, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known @@ -216,15 +209,17 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB * // Calculate progress rates var blks, txs, mgas int - if l == lsModeImport && evB != nil { + if currentMode == lsModeImport && evB != nil { blks, txs, mgas = 1, evB.Transactions().Len(), int(new(big.Int).Div(evB.GasUsed(), big.NewInt(1000000)).Uint64()) - } else if lastLoggedBlockN == 0 { + } else if currentBlockNumber == 0 && origin > 0 { blks, txs, mgas = calcBlockDiff(e, origin, localHead) + } else if currentBlockNumber != 0 { + blks, txs, mgas = calcBlockDiff(e, currentBlockNumber, localHead) } else { - blks, txs, mgas = calcBlockDiff(e, lastLoggedBlockN, localHead) + blks, txs, mgas = calcBlockDiff(e, localHead.NumberU64() - 1, localHead) } - switch l { + switch currentMode { case lsModeFastSync: lh := localHead.NumberU64() localOfMaxD.value = formatLocalOfMaxD(lh, chainSyncHeight) @@ -237,7 +232,7 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB * progressRateD.value = formatProgressRateD(blks/int(tickerInterval.Seconds()), txs/int(tickerInterval.Seconds()), mgas/int(tickerInterval.Seconds())) progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, true), "blk/txs/mgas sec") case lsModeImport: - localOfMaxD.value = fmt.Sprintf(strScanLenOf(xlocalOfMaxD, true), formatBlockNumber(localHead.NumberU64())) + localOfMaxD.value = formatLocalOfMaxD(localHead.NumberU64(), chainSyncHeight) percentOrHash.value = formatBlockHashD(localHead) progressRateD.value = fmt.Sprintf(strScanLenOf(xprogressRateD, false), formatProgressRateD(-1, txs, mgas)) progressRateUnitsD.value = fmt.Sprintf(strScanLenOf(xprogressRateUnitsD, true), "txs/mgas") From ee7f17038df74aaea53e6d3342c31cb82bd18e62 Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 19 Dec 2017 13:42:34 -0600 Subject: [PATCH 51/60] problem: import mode is weird to happen before sync solution: reorg mode decision logic --- cmd/geth/log_display_util.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/geth/log_display_util.go b/cmd/geth/log_display_util.go index d712ea131..f099acad8 100644 --- a/cmd/geth/log_display_util.go +++ b/cmd/geth/log_display_util.go @@ -51,8 +51,11 @@ func getLogStatusMode(e *eth.Ethereum) lsMode { return lsModeFastSync } } + if e.Downloader().GetPeers().Len() == 0 { + return lsModeDiscover + } _, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known - if e.Downloader().GetPeers().Len() > 0 && current >= height && !(current == 0 && height == 0) { + if height > 0 && height < current { return lsModeImport } return lsModeDiscover From 87aaf6189992fd6d94ce5680e3bf678c18b3bca9 Mon Sep 17 00:00:00 2001 From: ia Date: Tue, 19 Dec 2017 13:47:49 -0600 Subject: [PATCH 52/60] Restart CI From fcbc2604d2f1ae58c2353b581698fd1a8edfc369 Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 20 Dec 2017 08:06:26 -0600 Subject: [PATCH 53/60] Restart CI From 060580dc417fa5aaac2233a92f33369fecdb84bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Thu, 21 Dec 2017 15:12:25 +0100 Subject: [PATCH 54/60] problem: geth killing information not visible --- cmd/geth/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index 7474d4006..c6f26d080 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -107,7 +107,7 @@ func StartNode(stack *node.Node) { for i := 10; i > 0; i-- { <-sigc if i > 1 { - glog.Infof("Already shutting down, interrupt %d more times for panic.", i-1) + glog.D(logger.Warn).Warnf("Already shutting down, interrupt %d more times for panic.", i-1) } } glog.Fatal("boom") From b6e0cc70548dde3d4112585fcef6f44bd3627c97 Mon Sep 17 00:00:00 2001 From: ia Date: Wed, 27 Dec 2017 08:48:30 -0600 Subject: [PATCH 55/60] Restart CI Rel https://github.com/rust-lang-nursery/rustup.rs/issues/1316, testing fix from https://github.com/rust-lang-nursery/rustup.rs/pull/1310 From 1fd96c04dab9b7741a3268c5adb49e2e4e4aafff Mon Sep 17 00:00:00 2001 From: ia Date: Sat, 30 Dec 2017 14:36:54 -0600 Subject: [PATCH 56/60] problem: import logline shows db latest head instead of event block solution: use event block data instead of db currentblock for import log --- cmd/geth/log_display_basic.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 2c63bf65e..936b3f8e7 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -41,7 +41,7 @@ var basicDisplaySystem = displayEventHandlers{ if currentMode == lsModeImport { switch d := evData.(type) { case core.ChainInsertEvent: - currentBlockNumber = PrintStatusBasic(e, tickerInterval, e.BlockChain().GetBlockByNumber(d.LastNumber), ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + currentBlockNumber = PrintStatusBasic(e, tickerInterval, e.BlockChain().GetBlock(d.LastHash), ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) chainEventLastSent = time.Now() } } @@ -202,15 +202,19 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB * origin, current, chainSyncHeight, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known mode := e.Downloader().GetMode() + var localHead *types.Block if mode == downloader.FastSync { current = e.BlockChain().CurrentFastBlock().NumberU64() + } else if currentMode == lsModeImport && evB != nil { + localHead = evB + } else { + localHead = e.BlockChain().GetBlockByNumber(current) } - localHead := e.BlockChain().GetBlockByNumber(current) // Calculate progress rates var blks, txs, mgas int if currentMode == lsModeImport && evB != nil { - blks, txs, mgas = 1, evB.Transactions().Len(), int(new(big.Int).Div(evB.GasUsed(), big.NewInt(1000000)).Uint64()) + blks, txs, mgas = 1, localHead.Transactions().Len(), int(new(big.Int).Div(localHead.GasUsed(), big.NewInt(1000000)).Uint64()) } else if currentBlockNumber == 0 && origin > 0 { blks, txs, mgas = calcBlockDiff(e, origin, localHead) } else if currentBlockNumber != 0 { From 3b4f06880e2b2cc681d63680c72ab85839e03cd5 Mon Sep 17 00:00:00 2001 From: ia Date: Sat, 30 Dec 2017 15:29:23 -0600 Subject: [PATCH 57/60] problem: should show +n blocks imported when n>1 solution: pass event to print fn instead of block, improve logic around bookmark current head printed and to-print block --- cmd/geth/log_display_basic.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 936b3f8e7..6cebe4dce 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -41,7 +41,7 @@ var basicDisplaySystem = displayEventHandlers{ if currentMode == lsModeImport { switch d := evData.(type) { case core.ChainInsertEvent: - currentBlockNumber = PrintStatusBasic(e, tickerInterval, e.BlockChain().GetBlock(d.LastHash), ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) + currentBlockNumber = PrintStatusBasic(e, tickerInterval, &d, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx))) chainEventLastSent = time.Now() } } @@ -151,7 +151,7 @@ func calcPercent(quotient, divisor uint64) float64 { } // PrintStatusBasic implements the displayEventHandlerFn interface -var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB *types.Block, maxPeers int) uint64 { +var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, insertEvent *core.ChainInsertEvent, maxPeers int) uint64 { localOfMaxD := &printUnit{"", xlocalOfMaxD, true} percentOrHash := &printUnit{"", xlocalHeadHashD, false} @@ -163,6 +163,10 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB * if localheight < syncheight { return fmt.Sprintf("%9s of %9s", formatBlockNumber(localheight), formatBlockNumber(syncheight)) } + // Show diff if imported more than one block. + if insertEvent != nil && insertEvent.Processed > 1 { + return fmt.Sprintf("%9s (+%2d) ", formatBlockNumber(localheight), insertEvent.Processed) + } return fmt.Sprintf("%9s ", formatBlockNumber(localheight)) } @@ -205,19 +209,23 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, evB * var localHead *types.Block if mode == downloader.FastSync { current = e.BlockChain().CurrentFastBlock().NumberU64() - } else if currentMode == lsModeImport && evB != nil { - localHead = evB + } + + if insertEvent != nil { + if evB := e.BlockChain().GetBlock(insertEvent.LastHash); evB != nil && currentMode == lsModeImport { + localHead = evB + } } else { localHead = e.BlockChain().GetBlockByNumber(current) } // Calculate progress rates var blks, txs, mgas int - if currentMode == lsModeImport && evB != nil { - blks, txs, mgas = 1, localHead.Transactions().Len(), int(new(big.Int).Div(localHead.GasUsed(), big.NewInt(1000000)).Uint64()) + if currentMode == lsModeImport && insertEvent != nil && insertEvent.Processed == 1 { + blks, txs, mgas = 1, localHead.Transactions().Len(), int(new(big.Int).Div(localHead.GasUsed(), big.NewInt(1000000)).Uint64()) } else if currentBlockNumber == 0 && origin > 0 { blks, txs, mgas = calcBlockDiff(e, origin, localHead) - } else if currentBlockNumber != 0 { + } else if currentBlockNumber != 0 && currentBlockNumber < localHead.NumberU64() { blks, txs, mgas = calcBlockDiff(e, currentBlockNumber, localHead) } else { blks, txs, mgas = calcBlockDiff(e, localHead.NumberU64() - 1, localHead) From b46f10afe256bf053f9f7c7a152fa700ad42e4ce Mon Sep 17 00:00:00 2001 From: ia Date: Sat, 30 Dec 2017 15:30:39 -0600 Subject: [PATCH 58/60] problem: should show shutdown sig received solution: add display line for interrupt signal --- cmd/geth/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/cmd.go b/cmd/geth/cmd.go index c6f26d080..8c5eb2333 100644 --- a/cmd/geth/cmd.go +++ b/cmd/geth/cmd.go @@ -81,7 +81,7 @@ func StartNode(stack *node.Node) { defer signal.Stop(sigc) sig := <-sigc glog.V(logger.Warn).Warnf("Got %v, shutting down...", sig) - + glog.D(logger.Warn).Warnf("Got %v, shutting down...", sig) fails := make(chan error, 1) go func(fs chan error) { for { From 0e4841817de0ffcf6e3270eac5c46c051360baf3 Mon Sep 17 00:00:00 2001 From: ia Date: Sat, 30 Dec 2017 16:03:50 -0600 Subject: [PATCH 59/60] solution: txs/mgas should sum over last n imported blocks --- cmd/geth/log_display_basic.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index 6cebe4dce..b6ecd0a2f 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -200,17 +200,18 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, inser currentMode, localOfMaxD, percentOrHash, progressRateD, progressRateUnitsD, peersD) }() - if currentMode == lsModeDiscover { - return currentBlockNumber - } origin, current, chainSyncHeight, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known mode := e.Downloader().GetMode() - var localHead *types.Block if mode == downloader.FastSync { current = e.BlockChain().CurrentFastBlock().NumberU64() } + if currentMode == lsModeDiscover { + return current + } + + var localHead *types.Block if insertEvent != nil { if evB := e.BlockChain().GetBlock(insertEvent.LastHash); evB != nil && currentMode == lsModeImport { localHead = evB @@ -222,7 +223,9 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, inser // Calculate progress rates var blks, txs, mgas int if currentMode == lsModeImport && insertEvent != nil && insertEvent.Processed == 1 { - blks, txs, mgas = 1, localHead.Transactions().Len(), int(new(big.Int).Div(localHead.GasUsed(), big.NewInt(1000000)).Uint64()) + blks, txs, mgas = 1, localHead.Transactions().Len(), int(new(big.Int).Div(localHead.GasUsed(), big.NewInt(1000000)).Uint64()) + } else if insertEvent != nil && insertEvent.Processed > 1 { + blks, txs, mgas = calcBlockDiff(e, localHead.NumberU64() - uint64(insertEvent.Processed), localHead) } else if currentBlockNumber == 0 && origin > 0 { blks, txs, mgas = calcBlockDiff(e, origin, localHead) } else if currentBlockNumber != 0 && currentBlockNumber < localHead.NumberU64() { From 1248ca438f221d0468f98f912f4717fd47ac2564 Mon Sep 17 00:00:00 2001 From: ia Date: Sat, 30 Dec 2017 16:17:19 -0600 Subject: [PATCH 60/60] problem: spacing on chain insert delta solution: make it 4-digit padded for consistency across all possible cases --- cmd/geth/log_display_basic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/log_display_basic.go b/cmd/geth/log_display_basic.go index b6ecd0a2f..686af4f95 100644 --- a/cmd/geth/log_display_basic.go +++ b/cmd/geth/log_display_basic.go @@ -165,7 +165,7 @@ var PrintStatusBasic = func(e *eth.Ethereum, tickerInterval time.Duration, inser } // Show diff if imported more than one block. if insertEvent != nil && insertEvent.Processed > 1 { - return fmt.Sprintf("%9s (+%2d) ", formatBlockNumber(localheight), insertEvent.Processed) + return fmt.Sprintf("%9s (+%4d) ", formatBlockNumber(localheight), insertEvent.Processed) } return fmt.Sprintf("%9s ", formatBlockNumber(localheight)) }