diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..82f77436 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab + +[*.sh] +indent_style = tab + +[*.proto] +indent_style = space +indent_size = 2 diff --git a/CHANGELOG.md b/CHANGELOG.md index c380fdcd..b0aa90d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 0.4.1 (November 27, 2017) + +FEATURES: + - [common] `Keys()` method on `CMap` + +IMPROVEMENTS: + - [log] complex types now encoded as "%+v" by default if `String()` method is undefined (previously resulted in error) + - [log] logger logs its own errors + +BUG FIXES: + - [common] fixed `Kill()` to build on Windows (Windows does not have `syscall.Kill`) + ## 0.4.0 (October 26, 2017) BREAKING: diff --git a/clist/clist_test.go b/clist/clist_test.go index 2063cf46..9d5272de 100644 --- a/clist/clist_test.go +++ b/clist/clist_test.go @@ -149,7 +149,7 @@ func _TestGCRandom(t *testing.T) { func TestScanRightDeleteRandom(t *testing.T) { const numElements = 10000 - const numTimes = 100000 + const numTimes = 1000 const numScanners = 10 l := New() diff --git a/db/mem_db.go b/db/mem_db.go index 07742750..2f507321 100644 --- a/db/mem_db.go +++ b/db/mem_db.go @@ -2,6 +2,7 @@ package db import ( "fmt" + "sort" "strings" "sync" ) @@ -127,6 +128,8 @@ func (db *MemDB) IteratorPrefix(prefix []byte) Iterator { it.keys = append(it.keys, key) } } + // and we need to sort them + sort.Strings(it.keys) return it } diff --git a/log/tm_logger.go b/log/tm_logger.go index dc6932dd..d49e8d22 100644 --- a/log/tm_logger.go +++ b/log/tm_logger.go @@ -52,19 +52,28 @@ func NewTMLoggerWithColorFn(w io.Writer, colorFn func(keyvals ...interface{}) te // Info logs a message at level Info. func (l *tmLogger) Info(msg string, keyvals ...interface{}) { lWithLevel := kitlevel.Info(l.srcLogger) - kitlog.With(lWithLevel, msgKey, msg).Log(keyvals...) + if err := kitlog.With(lWithLevel, msgKey, msg).Log(keyvals...); err != nil { + errLogger := kitlevel.Error(l.srcLogger) + kitlog.With(errLogger, msgKey, msg).Log("err", err) + } } // Debug logs a message at level Debug. func (l *tmLogger) Debug(msg string, keyvals ...interface{}) { lWithLevel := kitlevel.Debug(l.srcLogger) - kitlog.With(lWithLevel, msgKey, msg).Log(keyvals...) + if err := kitlog.With(lWithLevel, msgKey, msg).Log(keyvals...); err != nil { + errLogger := kitlevel.Error(l.srcLogger) + kitlog.With(errLogger, msgKey, msg).Log("err", err) + } } // Error logs a message at level Error. func (l *tmLogger) Error(msg string, keyvals ...interface{}) { lWithLevel := kitlevel.Error(l.srcLogger) - kitlog.With(lWithLevel, msgKey, msg).Log(keyvals...) + lWithMsg := kitlog.With(lWithLevel, msgKey, msg) + if err := lWithMsg.Log(keyvals...); err != nil { + lWithMsg.Log("err", err) + } } // With returns a new contextual logger with keyvals prepended to those passed diff --git a/log/tm_logger_test.go b/log/tm_logger_test.go index 8cd2f827..b2b600ad 100644 --- a/log/tm_logger_test.go +++ b/log/tm_logger_test.go @@ -1,12 +1,26 @@ package log_test import ( + "bytes" "io/ioutil" + "strings" "testing" + "github.com/go-logfmt/logfmt" "github.com/tendermint/tmlibs/log" ) +func TestLoggerLogsItsErrors(t *testing.T) { + var buf bytes.Buffer + + logger := log.NewTMLogger(&buf) + logger.Info("foo", "baz baz", "bar") + msg := strings.TrimSpace(buf.String()) + if !strings.Contains(msg, logfmt.ErrInvalidKey.Error()) { + t.Errorf("Expected logger msg to contain ErrInvalidKey, got %s", msg) + } +} + func BenchmarkTMLoggerSimple(b *testing.B) { benchmarkRunner(b, log.NewTMLogger(ioutil.Discard), baseInfoMessage) } diff --git a/log/tmfmt_logger.go b/log/tmfmt_logger.go index 2b464a6b..d0397971 100644 --- a/log/tmfmt_logger.go +++ b/log/tmfmt_logger.go @@ -35,7 +35,8 @@ type tmfmtLogger struct { } // NewTMFmtLogger returns a logger that encodes keyvals to the Writer in -// Tendermint custom format. +// Tendermint custom format. Note complex types (structs, maps, slices) +// formatted as "%+v". // // Each log event produces no more than one call to w.Write. // The passed Writer must be safe for concurrent use by multiple goroutines if @@ -103,7 +104,10 @@ KeyvalueLoop: } } - if err := enc.EncodeKeyval(keyvals[i], keyvals[i+1]); err != nil { + err := enc.EncodeKeyval(keyvals[i], keyvals[i+1]) + if err == logfmt.ErrUnsupportedValueType { + enc.EncodeKeyval(keyvals[i], fmt.Sprintf("%+v", keyvals[i+1])) + } else if err != nil { return err } } diff --git a/log/tmfmt_logger_test.go b/log/tmfmt_logger_test.go index 62eb32a0..a07b323c 100644 --- a/log/tmfmt_logger_test.go +++ b/log/tmfmt_logger_test.go @@ -30,8 +30,10 @@ func TestTMFmtLogger(t *testing.T) { assert.Regexp(t, regexp.MustCompile(`N\[.+\] unknown \s+ a=1 err=error\n$`), buf.String()) buf.Reset() - err := logger.Log("std_map", map[int]int{1: 2}, "my_map", mymap{0: 0}) - assert.NotNil(t, err) + if err := logger.Log("std_map", map[int]int{1: 2}, "my_map", mymap{0: 0}); err != nil { + t.Fatal(err) + } + assert.Regexp(t, regexp.MustCompile(`N\[.+\] unknown \s+ std_map=map\[1:2\] my_map=special_behavior\n$`), buf.String()) buf.Reset() if err := logger.Log("level", "error"); err != nil { diff --git a/process/process.go b/process/process.go deleted file mode 100644 index 7d2ae914..00000000 --- a/process/process.go +++ /dev/null @@ -1,76 +0,0 @@ -package process - -import ( - "fmt" - "io" - "os" - "os/exec" - "time" -) - -type Process struct { - Label string - ExecPath string - Args []string - Pid int - StartTime time.Time - EndTime time.Time - Cmd *exec.Cmd `json:"-"` - ExitState *os.ProcessState `json:"-"` - InputFile io.Reader `json:"-"` - OutputFile io.WriteCloser `json:"-"` - WaitCh chan struct{} `json:"-"` -} - -// execPath: command name -// args: args to command. (should not include name) -func StartProcess(label string, dir string, execPath string, args []string, inFile io.Reader, outFile io.WriteCloser) (*Process, error) { - cmd := exec.Command(execPath, args...) - cmd.Dir = dir - cmd.Stdout = outFile - cmd.Stderr = outFile - cmd.Stdin = inFile - if err := cmd.Start(); err != nil { - return nil, err - } - proc := &Process{ - Label: label, - ExecPath: execPath, - Args: args, - Pid: cmd.Process.Pid, - StartTime: time.Now(), - Cmd: cmd, - ExitState: nil, - InputFile: inFile, - OutputFile: outFile, - WaitCh: make(chan struct{}), - } - go func() { - err := proc.Cmd.Wait() - if err != nil { - // fmt.Printf("Process exit: %v\n", err) - if exitError, ok := err.(*exec.ExitError); ok { - proc.ExitState = exitError.ProcessState - } - } - proc.ExitState = proc.Cmd.ProcessState - proc.EndTime = time.Now() // TODO make this goroutine-safe - err = proc.OutputFile.Close() - if err != nil { - fmt.Printf("Error closing output file for %v: %v\n", proc.Label, err) - } - close(proc.WaitCh) - }() - return proc, nil -} - -func (proc *Process) StopProcess(kill bool) error { - defer proc.OutputFile.Close() - if kill { - // fmt.Printf("Killing process %v\n", proc.Cmd.Process) - return proc.Cmd.Process.Kill() - } else { - // fmt.Printf("Stopping process %v\n", proc.Cmd.Process) - return proc.Cmd.Process.Signal(os.Interrupt) - } -} diff --git a/process/util.go b/process/util.go deleted file mode 100644 index 24cf3528..00000000 --- a/process/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package process - -import ( - . "github.com/tendermint/tmlibs/common" -) - -// Runs a command and gets the result. -func Run(dir string, command string, args []string) (string, bool, error) { - outFile := NewBufferCloser(nil) - proc, err := StartProcess("", dir, command, args, nil, outFile) - if err != nil { - return "", false, err - } - - <-proc.WaitCh - - if proc.ExitState.Success() { - return outFile.String(), true, nil - } else { - return outFile.String(), false, nil - } -} diff --git a/version/version.go b/version/version.go index c1635d20..c30887b4 100644 --- a/version/version.go +++ b/version/version.go @@ -1,3 +1,3 @@ package version -const Version = "0.4.0" +const Version = "0.4.1"