added flog.DefaultWritingRouter
							parent
							
								
									01305ab350
								
							
						
					
					
						commit
						0c315d3937
					
				|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in New Issue