diff options
| author | Felix Hanley <felix@userspace.com.au> | 2020-05-20 01:35:50 +0000 |
|---|---|---|
| committer | Felix Hanley <felix@userspace.com.au> | 2020-05-20 01:36:03 +0000 |
| commit | 21b31fdacf93bd5bb54937fb7c25a73b61205344 (patch) | |
| tree | f1c50ff4a8dab9bd6e86ed3a8402ac0b1ef34ba0 | |
| parent | 0b93ba86bf1b495ebb765a6cdca20a121ac1e9c7 (diff) | |
| download | logger-21b31fdacf93bd5bb54937fb7c25a73b61205344.tar.gz logger-21b31fdacf93bd5bb54937fb7c25a73b61205344.tar.bz2 | |
Reduce levels to debug and not debug
| -rw-r--r-- | README.md | 43 | ||||
| -rw-r--r-- | comparison_test.go | 4 | ||||
| -rw-r--r-- | example_test.go | 31 | ||||
| -rw-r--r-- | internal/strings.go | 54 | ||||
| -rw-r--r-- | internal/strings_test.go | 54 | ||||
| -rw-r--r-- | logger.go | 104 | ||||
| -rw-r--r-- | logger_test.go | 68 | ||||
| -rw-r--r-- | message/level.go | 39 | ||||
| -rw-r--r-- | message/level_test.go | 25 | ||||
| -rw-r--r-- | message/message.go | 6 | ||||
| -rw-r--r-- | options.go | 14 | ||||
| -rw-r--r-- | std.go | 26 | ||||
| -rw-r--r-- | writers/json/json.go | 19 | ||||
| -rw-r--r-- | writers/json/json_test.go | 36 | ||||
| -rw-r--r-- | writers/kv/key_value.go | 27 | ||||
| -rw-r--r-- | writers/kv/key_value_test.go | 23 | ||||
| -rw-r--r-- | writers/rabbitmq/rabbitmq.go | 20 |
17 files changed, 136 insertions, 457 deletions
@@ -1,6 +1,6 @@ # Simple structured logger for Go -A simple logger package that provides levels, a number of output formats, and +A simple logger package that provides a number of output formats, and named sub-logs. Output formats include key/value, JSON, null and AMQP/RabbitMQ ## Installation @@ -11,38 +11,38 @@ Documentation is available at http://godoc.org/src.userspace.com.au/logger ## Usage -There is a package level logger that is set to level 'WARN'. +There is a package level logger with two levels, debug and not! ### Create a key/value logger ```go -log := logger.New(logger.Name("app"), logger.Level(logger.DEBUG)) -log.Error("unable to do anything") +log := logger.New(logger.Name("app")) +log.Log("unable to do anything") ``` ```text -... [info] app: unable to do anything +... app: unable to do anything ``` ### Add structure ```go -log.Warn("invalid something", "id", 344, "error", "generally broken") +log.Log("invalid something", "id", 344, "error", "generally broken") ``` ```text -... [warn] app: invalid something id=344 error="generally broken" +... app: invalid something id=344 error="generally broken" ``` ### Create a named sub-logger ```go sublog := log.Named("database") -sublog.Info("connection initialised") +sublog.Log("connection initialised") ``` ```text -... [info] app.database: connection initialised +... app.database: connection initialised ``` ### Create a new Logger with pre-defined values @@ -52,30 +52,19 @@ For major sub-systems there is no need to repeat values for each log call: ```go reqID := "555" msgLog := sublog.Field("request", reqID) -msgLog.Error("failed to process message") +msgLog.Log("failed to process message") ``` ```text -... [info] app.database: failed to process message request=555 -``` - -There is also a Log command with no defined level. These messages are always -printed: - -```go -log.Log("metrics or whatnot", "something", large) -``` - -```text -... metrics or whatnot something="12345678" +... app.database: failed to process message request=555 ``` ## Comparison ``` -BenchmarkCoreLogger-12 5000000 288 ns/op -BenchmarkLocal-12 2000000 654 ns/op -BenchmarkLogrus-12 1000000 1738 ns/op -BenchmarkFieldsLocal-12 1000000 1024 ns/op -BenchmarkFieldsLogrus-12 1000000 2061 ns/op +BenchmarkCoreLogger-12 4731555 243 ns/op +BenchmarkLocal-12 2035790 597 ns/op +BenchmarkLogrus-12 698662 1725 ns/op +BenchmarkFieldsLocal-12 1000000 1010 ns/op +BenchmarkFieldsLogrus-12 592014 2022 ns/op ``` diff --git a/comparison_test.go b/comparison_test.go index 8e4a021..3dcc331 100644 --- a/comparison_test.go +++ b/comparison_test.go @@ -28,7 +28,7 @@ func BenchmarkCoreLogger(b *testing.B) { func BenchmarkLocal(b *testing.B) { l, _ := New(Writer(dummyWriter())) for n := 0; n < b.N; n++ { - l.Error("Some text") + l.Log("Some text") } } @@ -44,7 +44,7 @@ func BenchmarkFieldsLocal(b *testing.B) { l.Field("key", "value") l.Field("one", "two") for n := 0; n < b.N; n++ { - l.Error("Some text") + l.Info("Some text") } } diff --git a/example_test.go b/example_test.go index 14521c0..d4af463 100644 --- a/example_test.go +++ b/example_test.go @@ -1,27 +1,28 @@ package logger import ( - "src.userspace.com.au/logger/message" + "os" + "src.userspace.com.au/logger/writers/json" "src.userspace.com.au/logger/writers/kv" ) -func ExampleLevel() { +func ExampleDebug() { keyValue, _ := kv.New(kv.SetTimeFormat("")) + os.Setenv("DEBUG", "true") log, _ := New( Name("app"), - Level(message.DEBUG), Writer(keyValue), ) - log.Error("unable to do anything") - // Output: [error] app: unable to do anything + log.Debug("unable to do anything") + // Output: app: unable to do anything } func Example_structure() { keyValue, _ := kv.New(kv.SetTimeFormat("")) log, _ := New(Writer(keyValue)) - log.Warn("invalid something", "id", 344, "error", "generally broken") - // Output: [warn] invalid something id=344 error="generally broken" + log.Fields("id", 344, "error", "generally broken").Log("invalid something") + // Output: invalid something id=344 error="generally broken" } func ExampleNamed() { @@ -30,8 +31,8 @@ func ExampleNamed() { Name("database"), Writer(keyValue), ) - log.Error("connection initialised") - // Output: [error] database: connection initialised + log.Info("connection initialised") + // Output: database: connection initialised } func ExampleField() { @@ -40,14 +41,6 @@ func ExampleField() { // Create a new Logger with pre-defined values reqID := "555" msgLog := log.Field("request", reqID) - msgLog.Error("failed to process message") - // Output: {"_level":"error","_message":"failed to process message","_name":"app.database","_time":"","request":"555"} -} - -func Example_nolevel() { - keyValue, _ := kv.New(kv.SetTimeFormat("")) - log, _ := New(Writer(keyValue)) - large := 12345678 - log.Log("metrics or whatnot", "something", large) - // Output: metrics or whatnot something=12345678 + msgLog.Log("failed to process message") + // Output: {"_message":"failed to process message","_name":"app.database","_time":"","request":"555"} } diff --git a/internal/strings.go b/internal/strings.go deleted file mode 100644 index 8924734..0000000 --- a/internal/strings.go +++ /dev/null @@ -1,54 +0,0 @@ -package internal - -import ( - "fmt" - "strconv" - "time" -) - -// ToString converts interface to string -func ToString(v interface{}) string { - if v == nil { - return "" - } - switch c := v.(type) { - case string: - return c - case *string: - return *c - case int: - return strconv.FormatInt(int64(c), 10) - case int64: - return strconv.FormatInt(int64(c), 10) - case int32: - return strconv.FormatInt(int64(c), 10) - case int16: - return strconv.FormatInt(int64(c), 10) - case int8: - return strconv.FormatInt(int64(c), 10) - case uint: - return strconv.FormatUint(uint64(c), 10) - case uint64: - return strconv.FormatUint(uint64(c), 10) - case uint32: - return strconv.FormatUint(uint64(c), 10) - case uint16: - return strconv.FormatUint(uint64(c), 10) - case uint8: - return strconv.FormatUint(uint64(c), 10) - case float32: - return strconv.FormatFloat(float64(c), 'g', -1, 32) - case float64: - return strconv.FormatFloat(c, 'g', -1, 64) - case bool: - return strconv.FormatBool(c) - case *bool: - return strconv.FormatBool(*c) - case *time.Time: - return fmt.Sprintf("%s", c) - case fmt.Stringer: - return c.String() - default: - return fmt.Sprintf("%v", c) - } -} diff --git a/internal/strings_test.go b/internal/strings_test.go deleted file mode 100644 index 4f1a0c8..0000000 --- a/internal/strings_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package internal - -import ( - "testing" - "time" -) - -type stringer struct{} - -func (s stringer) String() string { - return "I am a stringer" -} - -func TestToString(t *testing.T) { - epoch := time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC) - s := "string" - b := true - tests := []struct { - in interface{} - expected string - }{ - {"string", "string"}, - {1, "1"}, - {0, "0"}, - {false, "false"}, - {int(-3), "-3"}, - {int8(3), "3"}, - {int16(30), "30"}, - {int32(30), "30"}, - {int64(30), "30"}, - {uint(3), "3"}, - {uint8(3), "3"}, - {uint16(30), "30"}, - {uint32(30), "30"}, - {uint64(30), "30"}, - {float32(3.0001), "3.0001"}, - {float64(3.0000001), "3.0000001"}, - {nil, ""}, - {new(stringer), "I am a stringer"}, - {epoch, "-0001-11-30 00:00:00 +0000 UTC"}, - {struct{ string }{"test"}, "{test}"}, - // Pointers - {&s, "string"}, - {&epoch, "-0001-11-30 00:00:00 +0000 UTC"}, - {&b, "true"}, - } - - for _, tt := range tests { - actual := ToString(tt.in) - if actual != tt.expected { - t.Errorf("ToString(%v) => %s, expected %s", tt.in, actual, tt.expected) - } - } -} @@ -1,6 +1,7 @@ package logger import ( + "fmt" "os" "strings" "sync" @@ -10,11 +11,11 @@ import ( "src.userspace.com.au/logger/writers/kv" ) -// Logger is a simple levelled logger. +// Logger is a simple logger with optional structured. type Logger struct { + debug bool name string - min message.Level - fields map[string]interface{} + fields map[string]string writers []message.Writer lock *sync.RWMutex } @@ -22,8 +23,8 @@ type Logger struct { // New creates a new logger instance func New(opts ...Option) (*Logger, error) { l := &Logger{ - min: message.WARN, - fields: make(map[string]interface{}), + debug: (os.Getenv("DEBUG") != ""), + fields: make(map[string]string), lock: new(sync.RWMutex), writers: []message.Writer{}, } @@ -46,28 +47,20 @@ func New(opts ...Option) (*Logger, error) { return l, nil } -// Log a message with no level. -func (l *Logger) Log(msg string, args ...interface{}) *Logger { - l.LogAtLevel(message.NONE, msg, args...) - return l +// Log a message. +func (l *Logger) Log(args ...interface{}) *Logger { + return l.log(args...) } -// LogAtLevel logs a message with a specified level. -func (l *Logger) LogAtLevel(lvl message.Level, msg string, args ...interface{}) *Logger { - if l.min < lvl { +func (l *Logger) log(args ...interface{}) *Logger { + if len(args) == 0 { return l } - - l.lock.RLock() - defer l.lock.RUnlock() - m := message.Message{ Name: l.name, Time: time.Now(), - Level: lvl, - Content: msg, + Content: toString(args...), Fields: l.fields, - Extras: args, } for _, w := range l.writers { @@ -76,56 +69,67 @@ func (l *Logger) LogAtLevel(lvl message.Level, msg string, args ...interface{}) return l } -// Error logs an error message. -func (l *Logger) Error(msg string, args ...interface{}) *Logger { - return l.LogAtLevel(message.ERROR, msg, args...) -} - -// Warn logs an information message. -func (l *Logger) Warn(msg string, args ...interface{}) *Logger { - return l.LogAtLevel(message.WARN, msg, args...) +// toString converts interface to string +func toString(args ...interface{}) string { + var buf strings.Builder + last := len(args) + for i, a := range args { + buf.WriteString(fmt.Sprint(a)) + if i < last-1 { + buf.WriteByte(' ') + } + } + return buf.String() } -// Info logs an information message. -func (l *Logger) Info(msg string, args ...interface{}) *Logger { - return l.LogAtLevel(message.INFO, msg, args...) +// Info is an alias for Log. +func (l *Logger) Info(args ...interface{}) *Logger { + return l.log(args...) } // Debug logs a debug message. -func (l *Logger) Debug(msg string, args ...interface{}) *Logger { - return l.LogAtLevel(message.DEBUG, msg, args...) +func (l *Logger) Debug(args ...interface{}) *Logger { + if l.debug { + return l.log(args...) + } + return l } -// IsWarn determines the info status for a logger instance. -// Use this to conditionally execute blocks of code depending on the log verbosity. -func (l *Logger) IsWarn() bool { return l.min >= message.WARN } - -// IsInfo determines the info status for a logger instance. -// Use this to conditionally execute blocks of code depending on the log verbosity. -func (l *Logger) IsInfo() bool { return l.min >= message.INFO } - // IsDebug determines the debug status for a logger instance. // Use this to conditionally execute blocks of code depending on the log verbosity. -func (l *Logger) IsDebug() bool { return l.min >= message.DEBUG } +func (l *Logger) IsDebug() bool { return l.debug } + +// Field enables setting or changing the default fields for a logger instance. +func (l *Logger) Field(k string, v interface{}) *Logger { + l.lock.Lock() + defer l.lock.Unlock() -// SetLevelAsString enables changing the minimum level for a logger instance. -func (l *Logger) SetLevelAsString(lvl string) *Logger { - l.SetLevel(message.Levels[strings.ToUpper(lvl)]) + l.fields[k] = toString(v) return l } -// SetLevel enables changing the minimum level for a logger instance. -func (l *Logger) SetLevel(lvl message.Level) *Logger { - l.min = lvl +// Fields enables setting or changing the default fields for a logger instance. +func (l *Logger) Fields(args ...interface{}) *Logger { + l.lock.Lock() + defer l.lock.Unlock() + + if len(args)%2 != 0 { + args = append(args, "") + } + for i := 0; i < len(args); i += 2 { + l.fields[toString(args[i])] = toString(args[i+1]) + } return l } -// Field enables changing the default fields for a logger instance. -func (l *Logger) Field(k string, v interface{}) *Logger { +// FieldMap enables setting or changing the default fields for a logger instance. +func (l *Logger) FieldMap(f map[string]interface{}) *Logger { l.lock.Lock() defer l.lock.Unlock() - l.fields[k] = v + for k, v := range f { + l.fields[k] = toString(v) + } return l } diff --git a/logger_test.go b/logger_test.go index 1ea794d..f517d06 100644 --- a/logger_test.go +++ b/logger_test.go @@ -6,20 +6,12 @@ import ( "strings" "testing" - "src.userspace.com.au/logger/message" "src.userspace.com.au/logger/writers/kv" ) func TestLoggerOptions(t *testing.T) { - l, err := New(Level(message.INFO)) - if err != nil { - t.Errorf("New() failed: %s", err) - } - if !l.IsInfo() { - t.Errorf("IsInfo() => %t, expected true", l.IsInfo()) - } - - err = Level(message.DEBUG)(l) + l, err := New() + err = ForceDebug(true)(l) if err != nil { t.Errorf("level option failed: %s", err) } @@ -28,38 +20,15 @@ func TestLoggerOptions(t *testing.T) { } } -func TestLevels(t *testing.T) { +func TestLog(t *testing.T) { tests := []struct { - min message.Level - level message.Level - output bool + input []interface{} + expected string }{ - // error - {message.ERROR, message.ERROR, true}, - {message.ERROR, message.WARN, false}, - {message.ERROR, message.INFO, false}, - {message.ERROR, message.DEBUG, false}, - // warn - {message.WARN, message.ERROR, true}, - {message.WARN, message.WARN, true}, - {message.WARN, message.INFO, false}, - {message.WARN, message.DEBUG, false}, - // info - {message.INFO, message.ERROR, true}, - {message.INFO, message.WARN, true}, - {message.INFO, message.INFO, true}, - {message.INFO, message.DEBUG, false}, - // debug - {message.DEBUG, message.ERROR, true}, - {message.DEBUG, message.WARN, true}, - {message.DEBUG, message.INFO, true}, - {message.DEBUG, message.DEBUG, true}, + {[]interface{}{"one", "two"}, "one two"}, } var buf bytes.Buffer - kv, err := kv.New( - kv.SetOutput(&buf), - kv.SetTimeFormat(""), // Ignore time - ) + kv, err := kv.New(kv.SetOutput(&buf)) if err != nil { t.Fatal("failed to create keyvalue: ", err) } @@ -68,22 +37,13 @@ func TestLevels(t *testing.T) { t.Fatal("failed to create logger: ", err) } - for i, tt := range tests { - log.SetLevel(tt.min) - log.LogAtLevel(tt.level, "test") - actual := strings.TrimSpace(buf.String()) - buf.Reset() - expected := fmt.Sprintf("[%s] test", tt.level) - if (actual == expected) != tt.output { - t.Errorf("invalid levelled output for test %d, got %q", i, actual) - } + for _, tt := range tests { + log.Log(tt.input...) - // Test logging without a level - log.Log("test") - actual = strings.TrimSpace(buf.String()) + actual := buf.String() buf.Reset() - if actual != "test" { - t.Errorf("invalid Log output for test %d, got %q", i, actual) + if !strings.Contains(actual, tt.expected) { + t.Errorf("expected %q got %q", tt.expected, actual) } } } @@ -111,11 +71,11 @@ func TestNamed(t *testing.T) { for _, tt := range tests { log.name = tt.existing named := log.Named(tt.name) - named.Error("test") + named.Log("test") actual := buf.String() buf.Reset() - expected := fmt.Sprintf("[error] %s: test", tt.expected) + expected := fmt.Sprintf("%s: test", tt.expected) if !strings.Contains(actual, expected) { t.Errorf("expected %q got %q", expected, actual) } diff --git a/message/level.go b/message/level.go deleted file mode 100644 index 8438cc1..0000000 --- a/message/level.go +++ /dev/null @@ -1,39 +0,0 @@ -package message - -// Level defines the output level -type Level int - -// Log levels -const ( - NONE Level = iota // Always log it - ERROR // Wake someone up - WARN // Something failed but don't wake anyone up - INFO // Good to know - DEBUG // Not for production -) - -func (l Level) String() string { - switch l { - case 0: - return "" - case 1: - return "error" - case 2: - return "warn" - case 3: - return "info" - case 4: - return "debug" - default: - return "unknown" - } -} - -// Levels is a convenience for string -> level -var Levels = map[string]Level{ - "NONE": NONE, - "ERROR": ERROR, - "WARN": WARN, - "INFO": INFO, - "DEBUG": DEBUG, -} diff --git a/message/level_test.go b/message/level_test.go deleted file mode 100644 index d13f944..0000000 --- a/message/level_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package message - -import ( - "testing" -) - -func TestLevel(t *testing.T) { - tests := []struct { - in string - expected string - }{ - {"ERROR", "error"}, - {"DEBUG", "debug"}, - {"WARN", "warn"}, - {"INFO", "info"}, - } - - for _, tt := range tests { - l := Levels[tt.in] - actual := l.String() - if actual != tt.expected { - t.Errorf("got %s, expected %s", actual, tt.expected) - } - } -} diff --git a/message/message.go b/message/message.go index 72e3c40..c4049da 100644 --- a/message/message.go +++ b/message/message.go @@ -8,14 +8,10 @@ type Message struct { Name string // The time log() was called Time time.Time - // The log level - Level Level // The message content Content string // Optional fields for the logger - Fields map[string]interface{} - // Optional extras for this log message - Extras []interface{} + Fields map[string]string } // Writer interface for writing messages. @@ -1,6 +1,8 @@ package logger import ( + "os" + "src.userspace.com.au/logger/message" ) @@ -15,18 +17,18 @@ func Writer(f message.Writer) Option { } } -// Level configures the minimum level to log. -func Level(lvl message.Level) Option { +// ForceDebug sets debug. +func ForceDebug(b bool) Option { return func(l *Logger) error { - l.SetLevel(lvl) + l.debug = b return nil } } -// LevelAsString configures the minimum level to log. -func LevelAsString(lvl string) Option { +// DebugEnvVar sets debug if the envvar 'v' is not empty. +func DebugEnvVar(v string) Option { return func(l *Logger) error { - l.SetLevelAsString(lvl) + l.debug = (os.Getenv(v) != "") return nil } } @@ -7,39 +7,19 @@ import ( var std *Logger func init() { - std, _ = New(Level(message.WARN)) + std, _ = New() } -// Error logs an error message. -func Error(msg string, args ...interface{}) { std.Error(msg, args...) } - -// Warn logs an information message. -func Warn(msg string, args ...interface{}) { std.Warn(msg, args...) } - // Info logs an information message. -func Info(msg string, args ...interface{}) { std.Info(msg, args...) } +func Info(args ...interface{}) { std.Info(args...) } // Debug logs a debug message. -func Debug(msg string, args ...interface{}) { std.Debug(msg, args...) } - -// IsWarn determines the info status for a logger instance. -// Use this to conditionally execute blocks of code depending on the log verbosity. -func IsWarn() bool { return std.IsWarn() } - -// IsInfo determines the info status for a logger instance. -// Use this to conditionally execute blocks of code depending on the log verbosity. -func IsInfo() bool { return std.IsInfo() } +func Debug(args ...interface{}) { std.Debug(args...) } // IsDebug determines the debug status for a logger instance. // Use this to conditionally execute blocks of code depending on the log verbosity. func IsDebug() bool { return std.IsDebug() } -// SetLevelAsString enables changing the minimum level for a logger instance. -func SetLevelAsString(lvl string) { std.SetLevelAsString(lvl) } - -// SetLevel enables changing the minimum level for a logger instance. -func SetLevel(lvl message.Level) { std.SetLevel(lvl) } - // Field enables changing the default fields for a logger instance. func Field(k string, v interface{}) *Logger { return std.Field(k, v) } diff --git a/writers/json/json.go b/writers/json/json.go index ecc2278..0dff446 100644 --- a/writers/json/json.go +++ b/writers/json/json.go @@ -6,7 +6,6 @@ import ( "io" "os" - "src.userspace.com.au/logger/internal" "src.userspace.com.au/logger/message" ) @@ -39,28 +38,10 @@ func (w Writer) Write(m message.Message) { "_message": m.Content, } - if l := m.Level.String(); l != "" { - vals["_level"] = m.Level.String() - } - for k, v := range m.Fields { vals[k] = v } - if len(m.Extras) > 0 { - // Allow for an odd number of extras - offset := len(m.Extras) % 2 - if offset != 0 { - for k, v := range m.Extras { - vals[fmt.Sprintf("extra%02d", k)] = v - } - } else { - for i := offset; i < len(m.Extras); i = i + 2 { - vals[internal.ToString(m.Extras[i])] = m.Extras[i+1] - } - } - } - if err := json.NewEncoder(w.writer).Encode(vals); err != nil { fmt.Fprintf(w.writer, "\"failed to encode JSON: %s\"", err) } diff --git a/writers/json/json_test.go b/writers/json/json_test.go index 0bd29b1..5778f51 100644 --- a/writers/json/json_test.go +++ b/writers/json/json_test.go @@ -14,17 +14,16 @@ func TestWriter(t *testing.T) { now, _ := time.Parse(time.RFC3339, "2019-05-03T13:38:29.987249+10:00") var tests = []struct { in message.Message - expected map[string]interface{} + expected map[string]string }{ { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, Content: "msg", }, - expected: map[string]interface{}{ - "_level": "error", "_name": "test", "_message": "msg", + expected: map[string]string{ + "_name": "test", "_message": "msg", "_time": "2019-05-03T13:38:29.987+1000", }, }, @@ -32,12 +31,10 @@ func TestWriter(t *testing.T) { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, Content: "msg", - Extras: []interface{}{"one"}, }, - expected: map[string]interface{}{ - "_level": "error", "_name": "test", "_message": "msg", "extra00": "one", + expected: map[string]string{ + "_name": "test", "_message": "msg", "_time": "2019-05-03T13:38:29.987+1000", }, }, @@ -45,12 +42,11 @@ func TestWriter(t *testing.T) { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, Content: "msg", - Fields: map[string]interface{}{"one": "1"}, + Fields: map[string]string{"one": "1"}, }, - expected: map[string]interface{}{ - "_level": "error", "_name": "test", "_message": "msg", "one": "1", + expected: map[string]string{ + "_name": "test", "_message": "msg", "one": "1", "_time": "2019-05-03T13:38:29.987+1000", }, }, @@ -58,11 +54,10 @@ func TestWriter(t *testing.T) { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, - Content: "msg", Extras: []interface{}{"one", "1", "two", "2", "three", 3, "fo ur", "# 4"}, + Content: "msg", }, - expected: map[string]interface{}{ - "_level": "error", "_name": "test", "_message": "msg", "one": "1", "two": "2", "three": float64(3), "fo ur": "# 4", + expected: map[string]string{ + "_name": "test", "_message": "msg", "_time": "2019-05-03T13:38:29.987+1000", }, }, @@ -70,12 +65,11 @@ func TestWriter(t *testing.T) { in: message.Message{ Time: now, Name: "test", - Level: message.DEBUG, Content: "msg", - Extras: []interface{}{"one"}, Fields: map[string]interface{}{"f1": "v1"}, + Fields: map[string]string{"f1": "v1"}, }, - expected: map[string]interface{}{ - "_level": "debug", "_name": "test", "_message": "msg", "f1": "v1", "extra00": "one", + expected: map[string]string{ + "_name": "test", "_message": "msg", "f1": "v1", "_time": "2019-05-03T13:38:29.987+1000", }, }, @@ -90,7 +84,7 @@ func TestWriter(t *testing.T) { for _, tt := range tests { l.Write(tt.in) - var raw map[string]interface{} + var raw map[string]string if err := json.Unmarshal(buf.Bytes(), &raw); err != nil { t.Fatal(err) } diff --git a/writers/kv/key_value.go b/writers/kv/key_value.go index a3369c5..b4434be 100644 --- a/writers/kv/key_value.go +++ b/writers/kv/key_value.go @@ -6,7 +6,6 @@ import ( "os" "strings" - "src.userspace.com.au/logger/internal" "src.userspace.com.au/logger/message" ) @@ -33,11 +32,7 @@ func New(opts ...Option) (*Writer, error) { // Write implements the message.Writer interface. func (w Writer) Write(m message.Message) { - //fmt.Fprintf(w, "%s [%-5s] ", m.Time, m.Level) fmt.Fprintf(w.writer, "%s ", m.Time.Format(w.timeFormat)) - if l := m.Level.String(); l != "" { - fmt.Fprintf(w.writer, "[%s] ", l) - } if m.Name != "" { fmt.Fprintf(w.writer, "%s: ", m.Name) } @@ -45,32 +40,14 @@ func (w Writer) Write(m message.Message) { // Write message content first w.writer.Write([]byte(m.Content)) - // Write fields before extras for k, v := range m.Fields { writeKV(w.writer, k, v) } - - if len(m.Extras) > 0 { - // Allow for an odd number of extras - offset := len(m.Extras) % 2 - if offset != 0 { - for k, v := range m.Extras { - writeKV(w.writer, fmt.Sprintf("extra%02d", k), v) - } - } else { - for i := offset; i < len(m.Extras); i = i + 2 { - writeKV(w.writer, m.Extras[i], m.Extras[i+1]) - } - } - } w.writer.Write([]byte{'\n'}) } -func writeKV(w io.Writer, k, v interface{}) (int, error) { - return fmt.Fprintf(w, " %s=%s", - maybeQuote(internal.ToString(k)), - maybeQuote(internal.ToString(v)), - ) +func writeKV(w io.Writer, k, v string) (int, error) { + return fmt.Fprintf(w, " %s=%s", maybeQuote(k), maybeQuote(v)) } func maybeQuote(s string) string { diff --git a/writers/kv/key_value_test.go b/writers/kv/key_value_test.go index adf8bcd..241da87 100644 --- a/writers/kv/key_value_test.go +++ b/writers/kv/key_value_test.go @@ -19,50 +19,43 @@ func TestWriter(t *testing.T) { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, Content: "msg", }, - expected: "2019-05-03T13:38:29.987+1000 [error] test: msg", + expected: "2019-05-03T13:38:29.987+1000 test: msg", }, { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, Content: "msg", - Extras: []interface{}{"one"}, }, - expected: "2019-05-03T13:38:29.987+1000 [error] test: msg extra00=one", + expected: "2019-05-03T13:38:29.987+1000 test: msg", }, { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, Content: "msg", - Fields: map[string]interface{}{"one": "1"}, + Fields: map[string]string{"one": "1"}, }, - expected: "2019-05-03T13:38:29.987+1000 [error] test: msg one=1", + expected: "2019-05-03T13:38:29.987+1000 test: msg one=1", }, { in: message.Message{ Time: now, Name: "test", - Level: message.ERROR, - Content: "msg", Extras: []interface{}{"one", "1", "two", "2", "three", 3, "fo ur", "# 4"}, + Content: "msg", }, - expected: `2019-05-03T13:38:29.987+1000 [error] test: msg one=1 two=2 three=3 "fo ur"="# 4"`, + expected: `2019-05-03T13:38:29.987+1000 test: msg`, }, { in: message.Message{ Time: now, Name: "test", - Level: message.DEBUG, Content: "msg", - Fields: map[string]interface{}{"f1": "v1"}, - Extras: []interface{}{"one"}, + Fields: map[string]string{"f1": "v1"}, }, - expected: "2019-05-03T13:38:29.987+1000 [debug] test: msg f1=v1 extra00=one", + expected: "2019-05-03T13:38:29.987+1000 test: msg f1=v1", }, } diff --git a/writers/rabbitmq/rabbitmq.go b/writers/rabbitmq/rabbitmq.go index be11f28..871a176 100644 --- a/writers/rabbitmq/rabbitmq.go +++ b/writers/rabbitmq/rabbitmq.go @@ -2,12 +2,10 @@ package amqp import ( "encoding/json" - "fmt" "strings" "time" "github.com/streadway/amqp" - "src.userspace.com.au/logger/internal" "src.userspace.com.au/logger/message" ) @@ -32,7 +30,7 @@ func New(url, exchange string, opts ...WriterOpt) (*Writer, error) { exchangeName: exchange, passive: false, exchangeType: "topic", - routingFormat: "{name}.{level}", + routingFormat: "{name}", contentType: "application/json", } @@ -85,7 +83,6 @@ func New(url, exchange string, opts ...WriterOpt) (*Writer, error) { func (w Writer) Write(m message.Message) { vals := map[string]interface{}{ "@name": m.Name, - "@level": m.Level.String(), "@message": m.Content, "@time": m.Time, } @@ -94,20 +91,6 @@ func (w Writer) Write(m message.Message) { vals[k] = v } - if len(m.Extras) > 0 { - // Allow for an odd number of extras - offset := len(m.Extras) % 2 - if offset != 0 { - for k, v := range m.Extras { - vals[fmt.Sprintf("extra%02d", k)] = v - } - } else { - for i := offset; i < len(m.Extras); i = i + 2 { - vals[internal.ToString(m.Extras[i])] = m.Extras[i+1] - } - } - } - d, err := json.Marshal(vals) if err != nil { panic(err) @@ -121,7 +104,6 @@ func (w Writer) Write(m message.Message) { } routingKey := strings.Replace(w.routingFormat, "{name}", m.Name, 0) - routingKey = strings.Replace(w.routingFormat, "{level}", m.Level.String(), 0) if err := w.channel.Publish( w.exchangeName, |
