diff --git a/default_writing_router.go b/default_writing_router.go new file mode 100644 index 0000000..494a41e --- /dev/null +++ b/default_writing_router.go @@ -0,0 +1,92 @@ +package flog + + +import ( + "github.com/reiver/go-dotquote" + "github.com/reiver/go-oi" + + "fmt" + "io" + "sort" + "time" +) + + +// DefaultWritingRouter returns an initialized DefaultWritingRouter +func NewDefaultWritingRouter(writer io.Writer) *DefaultWritingRouter { + router := DefaultWritingRouter{ + writer:writer, + } + + return &router +} + + +// DefaultWritingRouter is a router that writes the log in a default format. +// +// A DefaultWritingRouter is appropriate for a production (i.e., "PROD") +// deployment enviornment. +type DefaultWritingRouter struct { + writer io.Writer +} + + + +func (router *DefaultWritingRouter) Route(message string, context map[string]interface{}) error { + if nil == router { + return errNilReceiver + } + + + writer := router.writer + if nil == writer { + // Nothing to do, so just return silently. + return nil + } + + + var buffer [512]byte + p := buffer[:0] + + + p = dotquote.AppendString(p, message, "text") + p = append(p, ' ') + p = dotquote.AppendString(p, time.Now().String(), "when") + + // If we have an error, then get the error.Error() into the log too. + if errorFieldValue, ok := context["~error"]; ok { + if err, ok := errorFieldValue.(error); ok { + p = append(p, ' ') + p = dotquote.AppendString(p, fmt.Sprintf("%T", err), "error", "type") + p = append(p, ' ') + p = dotquote.AppendString(p, err.Error(), "error", "text") + } + } + + +//@TODO: This is a potential heavy operation. Is there a better way +// to get the ultimate result this is trying to archive? +// + sortedKeys := make([]string, len(context)) + i := 0 + for key, _ := range context { + sortedKeys[i] = key + i++ + } + sort.Strings(sortedKeys) + + for _, key := range sortedKeys { + + value := context[key] + + p = append(p, ' ') + p = dotquote.AppendString(p, fmt.Sprintf("%T", value), "ctx", key, "type") + p = append(p, ' ') + p = dotquote.AppendString(p, fmt.Sprintf("%v", value), "ctx", key, "value") + } + + _,_ = oi.LongWrite(router.writer, p) + +//@TODO: Should this be checking for errors from oi.LongWrite()? + return nil +} diff --git a/default_writing_router_test.go b/default_writing_router_test.go new file mode 100644 index 0000000..d072827 --- /dev/null +++ b/default_writing_router_test.go @@ -0,0 +1,66 @@ +package flog + + +import ( + "bytes" + "errors" + "strings" + + "testing" +) + + +func TestDefaultWritingRouterRoute(t *testing.T) { + + tests := []struct{ + Message string + Context map[string]interface{} + ExpectContains []string + }{ + { + Message: "Hello world!", + Context: map[string]interface{}{ + "apple": "one", + "banana": 2, + "cherry": 3.3, + "kiwi": true, + "~error": errors.New("test error"), + }, + ExpectContains: []string{ + `"text"="Hello world!"`, + ` "ctx"."apple"."type"="string" "ctx"."apple"."value"="one" "ctx"."banana"."type"="int" "ctx"."banana"."value"="2" "ctx"."cherry"."type"="float64" "ctx"."cherry"."value"="3.3" "ctx"."kiwi"."type"="bool" "ctx"."kiwi"."value"="true"`, + ` "error"."type"="*errors.errorString" "error"."text"="test error" `, + ` "when"="`, + }, + }, + } + + + for testNumber, test := range tests { + + var buffer bytes.Buffer + + var router Router = NewDefaultWritingRouter(&buffer) + + ctx := map[string]interface{}{} + for n, v := range test.Context { + ctx[n] = v + } + + if err := router.Route(test.Message, ctx); nil != err { + t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %v", testNumber, err, err) + continue + } + + { + actual := buffer.String() + for expectedNumber, expectContains := range test.ExpectContains { + + if !strings.Contains(actual, expectContains) { + t.Errorf("For test #%d and expected #%d, expect to contain, actual:\n==)>%s<(==\n==)>%s<(==", testNumber, expectedNumber, expectContains, actual) + continue + } + } + } + } +}