renaming from "package flog" to "package log"
parent
1665d92ed6
commit
34c0062438
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2015 Charles Iliya Krempeaux <charles@reptile.ca> :: http://changelog.ca/
|
Copyright (c) 2015 Charles Iliya Krempeaux :: http://changelog.ca/
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
24
context.go
24
context.go
|
@ -1,24 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
func newContext(cascade ...interface{}) map[string]interface{} {
|
|
||||||
|
|
||||||
context := make(map[string]interface{})
|
|
||||||
|
|
||||||
for _,x := range cascade {
|
|
||||||
switch xx := x.(type) {
|
|
||||||
case map[string]string:
|
|
||||||
for key, value := range xx {
|
|
||||||
context[key] = value
|
|
||||||
}
|
|
||||||
case map[string]interface{}:
|
|
||||||
for key, value := range xx {
|
|
||||||
context[key] = value
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
context["text"] = xx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context
|
|
||||||
}
|
|
154
context_test.go
154
context_test.go
|
@ -1,154 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestNewContext(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
Cascade []interface{}
|
|
||||||
Expected map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{},
|
|
||||||
Expected: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"apple",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
"apple",
|
|
||||||
"banana",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"banana",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
"apple",
|
|
||||||
"banana",
|
|
||||||
"cherry",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"cherry",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"fig":"THIS SHOULD BE REPLACED",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"fig":"THIS SHOULD REMAIN",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"fig":"THIS SHOULD REMAIN",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TestLoop:
|
|
||||||
for testNumber, test := range tests {
|
|
||||||
|
|
||||||
context := newContext(test.Cascade...)
|
|
||||||
|
|
||||||
if expected, actual := len(test.Expected), len(context); expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected length to be %d but actually was %d.", testNumber, expected, actual)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
for expectedKey, expectedValue := range test.Expected {
|
|
||||||
if _, ok := context[expectedKey]; !ok {
|
|
||||||
t.Errorf("For test #%d, expected key %q to be in resulting context but wasn't.", testNumber, expectedKey)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := expectedValue, context[expectedKey]; expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected value for key %q to be %q in resulting context, but was actually %q.", testNumber, expectedKey, expected, actual)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
// NewCopyingRouter returns an initialized CopyingRouter.
|
|
||||||
func NewCopyingRouter(subrouter Router) *CopyingRouter {
|
|
||||||
copies := make([]struct{Message string ; Context map[string]interface{}}, 0, 8)
|
|
||||||
|
|
||||||
router := CopyingRouter {
|
|
||||||
subrouter:subrouter,
|
|
||||||
copies:copies,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// CopyingRouter is a Router that copies a message (and its context) to memory, and then
|
|
||||||
// re-routes a message (and its context) to a sub-router.
|
|
||||||
//
|
|
||||||
// This router is NOT designed to be used for an indefinite number of routings, and instead
|
|
||||||
// should only be used for a limited amount of routings, as it stores all the copies in
|
|
||||||
// memory.
|
|
||||||
type CopyingRouter struct {
|
|
||||||
subrouter Router
|
|
||||||
copies []struct{Message string ; Context map[string]interface{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (router *CopyingRouter) Route(message string, context map[string]interface{}) error {
|
|
||||||
if nil == router {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
copy := struct{Message string ; Context map[string]interface{}}{
|
|
||||||
Message: message,
|
|
||||||
Context: context,
|
|
||||||
}
|
|
||||||
router.copies = append(router.copies, copy)
|
|
||||||
|
|
||||||
return router.subrouter.Route(message, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (router *CopyingRouter) Copies() <-chan struct{Message string ; Context map[string]interface{}} {
|
|
||||||
ch := make(chan struct{Message string ; Context map[string]interface{}})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for _, copy := range router.copies {
|
|
||||||
ch <- copy
|
|
||||||
}
|
|
||||||
close(ch)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return ch
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestNewCopyingRouter(t *testing.T) {
|
|
||||||
|
|
||||||
randomness := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
|
|
||||||
|
|
||||||
|
|
||||||
router := NewCopyingRouter(NewDiscardingRouter())
|
|
||||||
if nil == router {
|
|
||||||
t.Errorf("After trying to create a copying router, expected it to be not nil, but was: %v", router)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
lenInitialCopies := 0
|
|
||||||
for range router.Copies() {
|
|
||||||
lenInitialCopies++
|
|
||||||
}
|
|
||||||
if expected, actual := 0, lenInitialCopies; expected != actual {
|
|
||||||
t.Errorf("After creating a copying router, expected copies to be length %d, but was actually %d,", expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
message := fmt.Sprintf("%x", randomness.Int63n(9999999999))
|
|
||||||
|
|
||||||
context := make(map[string]interface{})
|
|
||||||
limit := randomness.Int63n(30)
|
|
||||||
for i:=int64(0); i<limit; i++ {
|
|
||||||
context[ fmt.Sprintf("%x", randomness.Int63n(1000*limit)) ] = fmt.Sprintf("%x", randomness.Int63n(999999999999999))
|
|
||||||
}
|
|
||||||
|
|
||||||
router.Route(message, context) // Just make sure it doesn't panic or deadlok, by calling this.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestCopyingRouterRoute(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
Data []struct{Message string ; Context map[string]interface{}}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Data: []struct{Message string ; Context map[string]interface{}}{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Data: []struct{Message string ; Context map[string]interface{}}{
|
|
||||||
struct{Message string ; Context map[string]interface{}}{
|
|
||||||
Message: "apple banana cherry",
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"one":1,
|
|
||||||
"two":2,
|
|
||||||
"three":3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Data: []struct{Message string ; Context map[string]interface{}}{
|
|
||||||
struct{Message string ; Context map[string]interface{}}{
|
|
||||||
Message: "apple",
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"one":1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
struct{Message string ; Context map[string]interface{}}{
|
|
||||||
Message: "banana",
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"two":2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
struct{Message string ; Context map[string]interface{}}{
|
|
||||||
Message: "cherry",
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"cherry":3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TLoop: for testNumber, test := range tests {
|
|
||||||
|
|
||||||
router := NewCopyingRouter(NewDiscardingRouter())
|
|
||||||
|
|
||||||
for _, datum := range test.Data {
|
|
||||||
router.Route(datum.Message, datum.Context)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
lenCopies := 0
|
|
||||||
for range router.Copies() {
|
|
||||||
lenCopies++
|
|
||||||
}
|
|
||||||
if expected, actual := len(test.Data), lenCopies; expected != actual {
|
|
||||||
t.Errorf("For test #%d, after creating a copying router and (potentially) doing some routing, expected copies to be length %d, but was actually %d.", testNumber, expected, actual)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
datumNumber := 0
|
|
||||||
for actualDatum := range router.Copies() {
|
|
||||||
if expected, actual := test.Data[datumNumber].Message, actualDatum.Message; expected != actual {
|
|
||||||
t.Errorf("For test #%d, after creating a copying router and (potentially) doing some routing, expected message for copies datum #%d to be %q, but was actually %q.", testNumber, datumNumber, expected, actual)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := len(test.Data[datumNumber].Context), len(actualDatum.Context); expected != actual {
|
|
||||||
t.Errorf("For test #%d, after creating a copying router and (potentially) doing some routing, expected length of context for copies datum #%d to be %d, but was actually %d.", testNumber, datumNumber, expected, actual)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
for expectedKey, expectedValue := range test.Data[datumNumber].Context {
|
|
||||||
if _, ok := actualDatum.Context[expectedKey]; !ok {
|
|
||||||
t.Errorf("For test #%d, after creating a copying router and (potentially) doing some routing, expected context for copies datum #%d to have key %q, but didn't.", testNumber, datumNumber, expectedKey)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualValue := actualDatum.Context[expectedKey]; expectedValue != actualValue {
|
|
||||||
t.Errorf("For test #%d, after creating a copying router and (potentially) doing some routing, expected value for context for copies datum #%d at key %q to have value [%v], but actually had [%v].", testNumber, datumNumber, expectedKey, expectedValue, actualValue)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
datumNumber++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/reiver/go-dotquote"
|
|
||||||
"github.com/reiver/go-oi"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDefaultWritingRouter returns an initialized DefaultWritingRouter
|
|
||||||
func NewDefaultWritingRouter(writer io.Writer) *DefaultWritingRouter {
|
|
||||||
return NewDefaultWritingRouterWithPrefix(writer, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDefaultWritingRouterWithPrefix returns an initialized DefaultWritingRouter
|
|
||||||
func NewDefaultWritingRouterWithPrefix(writer io.Writer, prefix map[string]interface{}) *DefaultWritingRouter {
|
|
||||||
var prefixBuffer []byte
|
|
||||||
if 0 < len(prefix) {
|
|
||||||
prefixBuffer = dotquote.AppendMap(prefixBuffer, prefix)
|
|
||||||
prefixBuffer = append(prefixBuffer, ' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
router := DefaultWritingRouter{
|
|
||||||
writer:writer,
|
|
||||||
prefix:prefixBuffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
prefix []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
if prefix := router.prefix; 0 < len(prefix) {
|
|
||||||
p = append(p, prefix...)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
p = dotquote.AppendString(p, message, "text")
|
|
||||||
p = append(p, ' ')
|
|
||||||
p = dotquote.AppendString(p, time.Now().String(), "when")
|
|
||||||
if trace := calltrace(); nil != trace {
|
|
||||||
p = append(p, ' ')
|
|
||||||
p = dotquote.AppendStrings(p, trace, "calltrace")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if 0 < len(context) {
|
|
||||||
p = append(p, ' ')
|
|
||||||
p = dotquote.AppendMap(p, context, "ctx")
|
|
||||||
}
|
|
||||||
|
|
||||||
p = append(p, '\n')
|
|
||||||
|
|
||||||
_,_ = oi.LongWrite(router.writer, p)
|
|
||||||
|
|
||||||
//@TODO: Should this be checking for errors from oi.LongWrite()?
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
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"="one" "ctx"."banana"="2" "ctx"."cherry"="3.300000" "ctx"."kiwi"="true"`,
|
|
||||||
` "error"."type"="*errors.errorString" "error"."text"="test error" `,
|
|
||||||
` "when"="`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Message: "Apple\tBANANA\nCherry",
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": 3.3,
|
|
||||||
"kiwi": true,
|
|
||||||
"~error": errors.New("test error"),
|
|
||||||
"more": map[string]interface{}{
|
|
||||||
"ONE": "1",
|
|
||||||
"TWO": "2",
|
|
||||||
"THREE": "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExpectContains: []string{
|
|
||||||
`"text"="Apple\tBANANA\nCherry"`,
|
|
||||||
` "ctx"."apple"="one" "ctx"."banana"="2" "ctx"."cherry"="3.300000" "ctx"."kiwi"="true"`,
|
|
||||||
` "error"."type"="*errors.errorString" "error"."text"="test error" `,
|
|
||||||
` "ctx"."more"."ONE"="1" "ctx"."more"."THREE"="3" "ctx"."more"."TWO"="2"`,
|
|
||||||
` "when"="`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Message: "Apple\tBANANA\nCherry",
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": 3.3,
|
|
||||||
"kiwi": true,
|
|
||||||
"~error": errors.New("test error"),
|
|
||||||
"more": map[string]interface{}{
|
|
||||||
"ONE": "1",
|
|
||||||
"TWO": "2",
|
|
||||||
"THREE": "3",
|
|
||||||
"FOUR": map[string]interface{}{
|
|
||||||
"a": "1st",
|
|
||||||
"b": "2nd",
|
|
||||||
"c": []string{
|
|
||||||
"th",
|
|
||||||
"i",
|
|
||||||
"rd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExpectContains: []string{
|
|
||||||
`"text"="Apple\tBANANA\nCherry"`,
|
|
||||||
` "ctx"."apple"="one" "ctx"."banana"="2" "ctx"."cherry"="3.300000" "ctx"."kiwi"="true"`,
|
|
||||||
` "error"."type"="*errors.errorString" "error"."text"="test error" `,
|
|
||||||
` "ctx"."more"."FOUR"."a"="1st" "ctx"."more"."FOUR"."b"="2nd" "ctx"."more"."FOUR"."c"=["th","i","rd"] "ctx"."more"."ONE"="1" "ctx"."more"."THREE"="3" "ctx"."more"."TWO"="2"`,
|
|
||||||
` "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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestDefaultWritingRouterWithPrefixRoute(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
Message string
|
|
||||||
Context map[string]interface{}
|
|
||||||
Prefix 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"),
|
|
||||||
},
|
|
||||||
Prefix: map[string]interface{}{
|
|
||||||
"name": "backendapi",
|
|
||||||
"number": "123",
|
|
||||||
},
|
|
||||||
ExpectContains: []string{
|
|
||||||
`"name"="backendapi" "number"="123" "text"="Hello world!" "when"="`,
|
|
||||||
` "ctx"."apple"="one" "ctx"."banana"="2" "ctx"."cherry"="3.300000" "ctx"."kiwi"="true"`,
|
|
||||||
` "error"."type"="*errors.errorString" "error"."text"="test error" `,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Message: "Apple\tBANANA\nCherry",
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": 3.3,
|
|
||||||
"kiwi": true,
|
|
||||||
"~error": errors.New("test error"),
|
|
||||||
"more": map[string]interface{}{
|
|
||||||
"ONE": "1",
|
|
||||||
"TWO": "2",
|
|
||||||
"THREE": "3",
|
|
||||||
"FOUR": map[string]interface{}{
|
|
||||||
"a": "1st",
|
|
||||||
"b": "2nd",
|
|
||||||
"c": []string{
|
|
||||||
"th",
|
|
||||||
"i",
|
|
||||||
"rd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Prefix: map[string]interface{}{
|
|
||||||
"app": map[string]interface{}{
|
|
||||||
"name": "backendapi",
|
|
||||||
"build": map[string]interface{}{
|
|
||||||
"number": 123,
|
|
||||||
"hash": "4a844b2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExpectContains: []string{
|
|
||||||
`"app"."build"."hash"="4a844b2" "app"."build"."number"="123" "app"."name"="backendapi" "text"="Apple\tBANANA\nCherry" "when"="`,
|
|
||||||
` "ctx"."apple"="one" "ctx"."banana"="2" "ctx"."cherry"="3.300000" "ctx"."kiwi"="true"`,
|
|
||||||
` "error"."type"="*errors.errorString" "error"."text"="test error" `,
|
|
||||||
` "ctx"."more"."FOUR"."a"="1st" "ctx"."more"."FOUR"."b"="2nd" "ctx"."more"."FOUR"."c"=["th","i","rd"] "ctx"."more"."ONE"="1" "ctx"."more"."THREE"="3" "ctx"."more"."TWO"="2"`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for testNumber, test := range tests {
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
|
|
||||||
var router Router = NewDefaultWritingRouterWithPrefix(&buffer, test.Prefix)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
singltonDiscardingRouter = DiscardingRouter{}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// NewDiscardingRouter returns an initialized DiscardingRouter.
|
|
||||||
func NewDiscardingRouter() *DiscardingRouter {
|
|
||||||
return &singltonDiscardingRouter
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// DiscardingRouter is a Router that discards any message (and its context)
|
|
||||||
// it is asked to route.
|
|
||||||
//
|
|
||||||
// Conceptually it is similar to /dev/null
|
|
||||||
type DiscardingRouter struct{}
|
|
||||||
|
|
||||||
|
|
||||||
func (router *DiscardingRouter) Route(message string, context map[string]interface{}) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestNewDiscardingRouter(t *testing.T) {
|
|
||||||
|
|
||||||
randomness := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
|
|
||||||
|
|
||||||
|
|
||||||
router := NewDiscardingRouter()
|
|
||||||
if nil == router {
|
|
||||||
t.Errorf("After trying to create a discard router, expected it to be not nil, but was: %v", router)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
message := fmt.Sprintf("%x", randomness.Int63n(9999999999))
|
|
||||||
|
|
||||||
context := make(map[string]interface{})
|
|
||||||
limit := randomness.Int63n(30)
|
|
||||||
for i:=int64(0); i<limit; i++ {
|
|
||||||
context[ fmt.Sprintf("%x", randomness.Int63n(1000*limit)) ] = fmt.Sprintf("%x", randomness.Int63n(999999999999999))
|
|
||||||
}
|
|
||||||
|
|
||||||
router.Route(message, context) // Just make sure it doesn't panic or deadlok, by calling this.
|
|
||||||
}
|
|
104
doc.go
104
doc.go
|
@ -1,104 +0,0 @@
|
||||||
/*
|
|
||||||
Package flog provides structured and formatted logging.
|
|
||||||
|
|
||||||
Basic Usage
|
|
||||||
|
|
||||||
Basic usage of the flogger looks like this:
|
|
||||||
|
|
||||||
router := flog.NewPrettyWritingRouter(os.Stdout)
|
|
||||||
|
|
||||||
flogger := flog.New(router)
|
|
||||||
|
|
||||||
Once you have the flogger, you can do things such as:
|
|
||||||
|
|
||||||
flogger.Print("Hello world!")
|
|
||||||
flogger.Println("Hello world!")
|
|
||||||
flogger.Printf("Hello %s!", name)
|
|
||||||
|
|
||||||
flogger.Panic("Uh oh!")
|
|
||||||
flogger.Panicln("Uh oh!")
|
|
||||||
flogger.Panicf("Uh oh, had a problem happen: %s.", problemDescription)
|
|
||||||
|
|
||||||
flogger.Fatal("Something really bad happened!")
|
|
||||||
flogger.Fatalln("Something really bad happened!")
|
|
||||||
flogger.Fatalf("Something really bad happened: %s.", problemDescription)
|
|
||||||
|
|
||||||
BTW, if the PrettyWritingRouter was being used, then this:
|
|
||||||
|
|
||||||
flogger.Print("Hello world!")
|
|
||||||
|
|
||||||
Would generate output like the following:
|
|
||||||
|
|
||||||
Hello world! (2015-10-10 17:28:49.397356044 -0700 PDT)
|
|
||||||
|
|
||||||
(Although note that in actual usage this would have color.)
|
|
||||||
|
|
||||||
(Note that for for other routers the actual output would look very different!
|
|
||||||
What the output looks like is router dependent.)
|
|
||||||
|
|
||||||
Structured Logging
|
|
||||||
|
|
||||||
But those method calls all generated unstructure data.
|
|
||||||
|
|
||||||
To include structured data the flogger's With method needs to be used.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
newFlogger := flogger.With(map[string]interface{}{
|
|
||||||
"method":"Toil",
|
|
||||||
"secret_note":"Hi there! How are you?",
|
|
||||||
})
|
|
||||||
|
|
||||||
Then if the PrettyWritingRouter was being used, then this:
|
|
||||||
|
|
||||||
newFlogger.Print("Hello world!")
|
|
||||||
|
|
||||||
Would generate output like the following:
|
|
||||||
|
|
||||||
Hello world! (2015-10-10 17:28:49.397356044 -0700 PDT) method="Toil" secret_note="Hi there! How are you?"
|
|
||||||
|
|
||||||
(Again, note that in actual usage this would have color.)
|
|
||||||
|
|
||||||
Deployment Environment
|
|
||||||
|
|
||||||
Of course in a real application system you should (probably) create a different kind
|
|
||||||
of flogger for each deployment environment.
|
|
||||||
|
|
||||||
Even though the PrettyWritingRouter is great for a development deployment environment
|
|
||||||
(i.e., "DEV") it is probably not appropriate for a production deployment environment
|
|
||||||
(i.e., "PROD").
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
var flogger flog.Flogger
|
|
||||||
|
|
||||||
switch deploymentEnvironment {
|
|
||||||
case "DEV":
|
|
||||||
router := flog.NewPrettyWritingRouter(os.Stdout)
|
|
||||||
|
|
||||||
flogger = flog.New(router)
|
|
||||||
case "PROD":
|
|
||||||
verboseRouter = flog.NewDiscardingRouter()
|
|
||||||
if isVerboseMode {
|
|
||||||
verboseRouter = NewCustomVerboseRouter()
|
|
||||||
}
|
|
||||||
|
|
||||||
panicDetectionRouter := flog.NewFilteringRouter(NewCustomerPanicRecordingRouter(), filterOnlyPanicsFunc)
|
|
||||||
|
|
||||||
errorDetectionRouter := flog.NewFilteringRouter(NewCustomerPanicRecordingRouter(), filterOnlyErrorsFunc)
|
|
||||||
|
|
||||||
router := NewFanoutRouter(verboseRouter, panicDetectionRouter, errorDetectionRouter)
|
|
||||||
|
|
||||||
flogger = flog.New(router)
|
|
||||||
}
|
|
||||||
|
|
||||||
More Routers
|
|
||||||
|
|
||||||
In addition to the builtin routers that go-flog comes with, other external
|
|
||||||
routers are also available. These include:
|
|
||||||
|
|
||||||
go-slackchannelrouter:
|
|
||||||
Makes it so log messages get posted to a Slack channel.
|
|
||||||
https://github.com/reiver/go-slackchannelrouter
|
|
||||||
|
|
||||||
*/
|
|
||||||
package log
|
|
198
error_test.go
198
error_test.go
|
@ -1,198 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestNewErrorContext(t *testing.T) {
|
|
||||||
|
|
||||||
err1 := errors.New("error 1")
|
|
||||||
err2 := errors.New("error 2")
|
|
||||||
err3 := errors.New("error 3")
|
|
||||||
err4 := errors.New("error 4")
|
|
||||||
err5 := errors.New("error 5")
|
|
||||||
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
BaseContext map[string]interface{}
|
|
||||||
V []interface{}
|
|
||||||
Expected map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
// Nothing here.
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
1,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
1, "two",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
1, "two", 3.0,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
1, "two", 3.0,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
err1,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
"~errors": []error{
|
|
||||||
err1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
err1, err2,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
"~errors": []error{
|
|
||||||
err1, err2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
err1, err2, err3,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
"~errors": []error{
|
|
||||||
err1, err2, err3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
err1, err2, err3, err4,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
"~errors": []error{
|
|
||||||
err1, err2, err3, err4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
err1, err2, err3, err4, err5,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
"~errors": []error{
|
|
||||||
err1, err2, err3, err4, err5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
BaseContext: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
},
|
|
||||||
V: []interface{}{
|
|
||||||
1, err1, "two", err2, 3.0, err3, '4',
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
"~errors": []error{
|
|
||||||
err1, err2, err3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for testNumber, test := range tests {
|
|
||||||
|
|
||||||
actualContext := newErrorContext(test.BaseContext, test.V...)
|
|
||||||
if actual := actualContext; nil == actual {
|
|
||||||
t.Errorf("For test #%d, did not expected nil, but actually got %v", testNumber, actual)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := len(test.Expected), len(actualContext); expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected %d, but actually got %d.", testNumber, expected, actual)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := test.Expected, actualContext; !reflect.DeepEqual(expected, actual) {
|
|
||||||
t.Errorf("For test #%d, expected...\n%#v\nbut actually got...\n%#v", testNumber, expected, actual)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,5 +8,4 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNilReceiver = errors.New("Nil Receiver")
|
errNilReceiver = errors.New("Nil Receiver")
|
||||||
errNilRouter = errors.New("Nil Router")
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/reiver/go-manyerrors"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// NewFanoutRouter returns an initialized FanoutRouter.
|
|
||||||
func NewFanoutRouter(subrouters ...Router) *FanoutRouter {
|
|
||||||
router := FanoutRouter{
|
|
||||||
subrouters:subrouters,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// FanoutRouter is a Router that re-routes any message (and its context) it
|
|
||||||
// receives to all of its sub-routers.
|
|
||||||
type FanoutRouter struct {
|
|
||||||
subrouters []Router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (router *FanoutRouter) Route(message string, context map[string]interface{}) error {
|
|
||||||
if nil == router {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
errors := []error{}
|
|
||||||
|
|
||||||
for _, subrouter := range router.subrouters {
|
|
||||||
if err := subrouter.Route(message, context); nil != err {
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if 0 < len(errors) {
|
|
||||||
return manyerrors.New(errors...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,350 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/reiver/go-manyerrors"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestNewFanoutRouter(t *testing.T) {
|
|
||||||
|
|
||||||
randomness := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
|
|
||||||
|
|
||||||
|
|
||||||
router := NewFanoutRouter()
|
|
||||||
if nil == router {
|
|
||||||
t.Errorf("After trying to create a discard router, expected it to be not nil, but was: %v", router)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
message := fmt.Sprintf("%x", randomness.Int63n(9999999999))
|
|
||||||
|
|
||||||
context := make(map[string]interface{})
|
|
||||||
limit := randomness.Int63n(30)
|
|
||||||
for i:=int64(0); i<limit; i++ {
|
|
||||||
context[ fmt.Sprintf("%x", randomness.Int63n(1000*limit)) ] = fmt.Sprintf("%x", randomness.Int63n(999999999999999))
|
|
||||||
}
|
|
||||||
|
|
||||||
router.Route(message, context) // Just make sure it doesn't panic or deadlok, by calling this.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestFanoutRouterRoute(t *testing.T) {
|
|
||||||
|
|
||||||
numApples := 0
|
|
||||||
numBananas := 0
|
|
||||||
numCherries := 0
|
|
||||||
numFigs := 0
|
|
||||||
numKiwis := 0
|
|
||||||
numApplesOrFigs := 0
|
|
||||||
|
|
||||||
appleRouter := NewFilteringRouter(NewDiscardingRouter(), func(message string, context map[string]interface{}) bool {
|
|
||||||
if "apple" == message {
|
|
||||||
numApples++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
bananaRouter := NewFilteringRouter(NewDiscardingRouter(), func(message string, context map[string]interface{}) bool {
|
|
||||||
if "banana" == message {
|
|
||||||
numBananas++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
cherryRouter := NewFilteringRouter(NewDiscardingRouter(), func(message string, context map[string]interface{}) bool {
|
|
||||||
if "cherry" == message {
|
|
||||||
numCherries++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
figRouter := NewFilteringRouter(NewDiscardingRouter(), func(message string, context map[string]interface{}) bool {
|
|
||||||
if "fig" == message {
|
|
||||||
numFigs++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
kiwiRouter := NewFilteringRouter(NewDiscardingRouter(), func(message string, context map[string]interface{}) bool {
|
|
||||||
if "kiwi" == message {
|
|
||||||
numKiwis++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
appleOrFigRouter := NewFilteringRouter(NewDiscardingRouter(), func(message string, context map[string]interface{}) bool {
|
|
||||||
if "apple" == message || "fig" == message {
|
|
||||||
numApplesOrFigs++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
router := NewFanoutRouter(appleRouter, bananaRouter, cherryRouter, figRouter, kiwiRouter, appleOrFigRouter)
|
|
||||||
|
|
||||||
if expected, actual := 0, numApples; expected != actual {
|
|
||||||
t.Errorf("Initially expected numApples to be %d, but actually was %d.", expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numBananas; expected != actual {
|
|
||||||
t.Errorf("Initially expected numBananas to be %d, but actually was %d.", expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numCherries; expected != actual {
|
|
||||||
t.Errorf("Initially expected numCherries to be %d, but actually was %d.", expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numFigs; expected != actual {
|
|
||||||
t.Errorf("Initially expected numFigs to be %d, but actually was %d.", expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numKiwis; expected != actual {
|
|
||||||
t.Errorf("Initially expected numKiwis to be %d, but actually was %d.", expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numApplesOrFigs; expected != actual {
|
|
||||||
t.Errorf("Initially expected numApplesOrFigs to be %d, but actually was %d.", expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var message string
|
|
||||||
var context map[string]interface{} = map[string]interface{}{}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message = "apple"
|
|
||||||
if err := router.Route(message, context); nil != err {
|
|
||||||
switch errs := err.(type) {
|
|
||||||
case manyerrors.Errors:
|
|
||||||
t.Errorf("Received many error when trying to send message %q: %#v", message, errs.Errors())
|
|
||||||
default:
|
|
||||||
t.Errorf("Received error when trying to send message %q: %v", message, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := 1, numApples; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApples to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numBananas; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numBananas to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numCherries; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numCherries to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numKiwis; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numKiwis to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numApplesOrFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApplesOrFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message = "banana"
|
|
||||||
if err := router.Route(message, context); nil != err {
|
|
||||||
switch errs := err.(type) {
|
|
||||||
case manyerrors.Errors:
|
|
||||||
t.Errorf("Received many error when trying to send message %q: %#v", message, errs.Errors())
|
|
||||||
default:
|
|
||||||
t.Errorf("Received error when trying to send message %q: %v", message, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := 1, numApples; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApples to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numBananas; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numBananas to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numCherries; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numCherries to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numKiwis; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numKiwis to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numApplesOrFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApplesOrFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message = "cherry"
|
|
||||||
if err := router.Route(message, context); nil != err {
|
|
||||||
switch errs := err.(type) {
|
|
||||||
case manyerrors.Errors:
|
|
||||||
t.Errorf("Received many error when trying to send message %q: %#v", message, errs.Errors())
|
|
||||||
default:
|
|
||||||
t.Errorf("Received error when trying to send message %q: %v", message, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := 1, numApples; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApples to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numBananas; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numBananas to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numCherries; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numCherries to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numKiwis; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numKiwis to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numApplesOrFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApplesOrFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message = "fig"
|
|
||||||
if err := router.Route(message, context); nil != err {
|
|
||||||
switch errs := err.(type) {
|
|
||||||
case manyerrors.Errors:
|
|
||||||
t.Errorf("Received many error when trying to send message %q: %#v", message, errs.Errors())
|
|
||||||
default:
|
|
||||||
t.Errorf("Received error when trying to send message %q: %v", message, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := 1, numApples; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApples to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numBananas; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numBananas to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numCherries; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numCherries to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 0, numKiwis; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numKiwis to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 2, numApplesOrFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApplesOrFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message = "kiwi"
|
|
||||||
if err := router.Route(message, context); nil != err {
|
|
||||||
switch errs := err.(type) {
|
|
||||||
case manyerrors.Errors:
|
|
||||||
t.Errorf("Received many error when trying to send message %q: %#v", message, errs.Errors())
|
|
||||||
default:
|
|
||||||
t.Errorf("Received error when trying to send message %q: %v", message, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := 1, numApples; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApples to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numBananas; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numBananas to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numCherries; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numCherries to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numKiwis; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numKiwis to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 2, numApplesOrFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApplesOrFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message = "fig"
|
|
||||||
if err := router.Route(message, context); nil != err {
|
|
||||||
switch errs := err.(type) {
|
|
||||||
case manyerrors.Errors:
|
|
||||||
t.Errorf("Received many error when trying to send message %q: %#v", message, errs.Errors())
|
|
||||||
default:
|
|
||||||
t.Errorf("Received error when trying to send message %q: %v", message, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := 1, numApples; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApples to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numBananas; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numBananas to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numCherries; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numCherries to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 2, numFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 1, numKiwis; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numKiwis to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if expected, actual := 3, numApplesOrFigs; expected != actual {
|
|
||||||
t.Errorf("After sending message %q expected numApplesOrFigs to be %d, but actually was %d.", message, expected, actual)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
// NewFilteringRouter returns an initialized FilteringRouter.
|
|
||||||
//
|
|
||||||
// 'subrouter' is the sub-router that a FilteringRouter will
|
|
||||||
// re-Route a 'message' (and 'context') to, but only on the
|
|
||||||
// condition that 'filterFn' returns 'true' for the 'message'
|
|
||||||
// and 'context' passed to it.
|
|
||||||
//
|
|
||||||
// An example for 'filterFn' is the following.
|
|
||||||
//
|
|
||||||
// func filterError(message string, context map[string]interface{})bool) bool {
|
|
||||||
// if datum, ok := context["error"]; !ok {
|
|
||||||
// return false
|
|
||||||
// } else if _, ok := datum.(error); !ok {
|
|
||||||
// return false
|
|
||||||
// } else {
|
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// This func will cause the router it only re-route messages whose context #1 has the
|
|
||||||
// key "error" and #2 where the value of the context at key "key" fits the builtin Go
|
|
||||||
// 'error' interface.
|
|
||||||
//
|
|
||||||
// So, for example, for 'filterError' this would pass:
|
|
||||||
//
|
|
||||||
// context := map[string]interface{}{
|
|
||||||
// "apple":1,
|
|
||||||
// "banana":2,
|
|
||||||
// "cherry":3,
|
|
||||||
// "error": errors.New("Something bad happened :-("),
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// But, again for 'filterError', this would NOT pass:
|
|
||||||
//
|
|
||||||
// context := map[string]interface{}{
|
|
||||||
// "apple":1,
|
|
||||||
// "banana":2,
|
|
||||||
// "cherry":3,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Also, a rather useless example, but a 'filterFn' that would reject all messages (and
|
|
||||||
// contexts) is:
|
|
||||||
//
|
|
||||||
// func filterRejectAll(message string, context map[string]interface{})bool) bool {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// And also, another rather useless example, but a 'filterFn' that would allow all messages
|
|
||||||
// (and contexts) is:
|
|
||||||
//
|
|
||||||
// func filterAcceptAll(message string, context map[string]interface{})bool) bool {
|
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func NewFilteringRouter(subrouter Router, filterFn func(string, map[string]interface{})bool) *FilteringRouter {
|
|
||||||
router := FilteringRouter{
|
|
||||||
subrouter:subrouter,
|
|
||||||
filterFn:filterFn,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// FilteringRouter is a Router that conditionally re-routes or discards a message (and its context).
|
|
||||||
type FilteringRouter struct {
|
|
||||||
subrouter Router
|
|
||||||
filterFn func(string, map[string]interface{})bool
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (router *FilteringRouter) Route(message string, context map[string]interface{}) error {
|
|
||||||
if nil == router {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
if router.filterFn(message, context) {
|
|
||||||
return router.subrouter.Route(message, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestFilteringRouterJustCreated(t *testing.T) {
|
|
||||||
|
|
||||||
randomness := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
|
|
||||||
|
|
||||||
|
|
||||||
router := NewFilteringRouter(NewDiscardingRouter(), func(string, map[string]interface{}) bool {
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if nil == router {
|
|
||||||
t.Errorf("After trying to create a filtering router, expected it to be not nil, but was: %v", router)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
message := fmt.Sprintf("%x", randomness.Int63n(9999999999))
|
|
||||||
|
|
||||||
context := make(map[string]interface{})
|
|
||||||
limit := randomness.Int63n(30)
|
|
||||||
for i:=int64(0); i<limit; i++ {
|
|
||||||
context[ fmt.Sprintf("%x", randomness.Int63n(1000*limit)) ] = fmt.Sprintf("%x", randomness.Int63n(999999999999999))
|
|
||||||
}
|
|
||||||
|
|
||||||
router.Route(message, context) // Just make sure it doesn't panic or deadlok, by calling this.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestFilteringRouterJustFilterParameters(t *testing.T) {
|
|
||||||
|
|
||||||
randomness := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
|
|
||||||
|
|
||||||
|
|
||||||
var filterMessage string
|
|
||||||
var filterContext map[string] interface{}
|
|
||||||
var filterResult = false
|
|
||||||
|
|
||||||
router := NewFilteringRouter(NewDiscardingRouter(), func(message string, context map[string]interface{}) bool {
|
|
||||||
filterMessage = message
|
|
||||||
filterContext = context
|
|
||||||
|
|
||||||
filterResult = !filterResult
|
|
||||||
|
|
||||||
return filterResult
|
|
||||||
})
|
|
||||||
|
|
||||||
const NUM_TESTS = 20
|
|
||||||
TLoop: for testNumber:=0; testNumber<NUM_TESTS; testNumber++ {
|
|
||||||
msg := fmt.Sprintf("string with random numbers: %d", randomness.Int63n(9999999999))
|
|
||||||
|
|
||||||
ctx := make(map[string]interface{})
|
|
||||||
lenCtx := randomness.Int63n(30)
|
|
||||||
for i:=int64(0); i<lenCtx; i++ {
|
|
||||||
ctx[ fmt.Sprintf("%x", randomness.Int63n(1000*lenCtx)) ] = fmt.Sprintf("%x", randomness.Int63n(999999999999999))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
router.Route(msg, ctx)
|
|
||||||
|
|
||||||
if expected, actual := msg, filterMessage; expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected message passed to filter func to be %q, but actually was %q.", testNumber, expected, actual)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := len(ctx), len(filterContext); expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected context passed to filter func to be len %d, but actually was %d.", testNumber, expected, actual)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
for expectedKey, expectedValue := range ctx {
|
|
||||||
if _, ok := filterContext[expectedKey]; !ok {
|
|
||||||
t.Errorf("For test #%d, expected context passed to filter func to have key %q, but didn't.", testNumber, expectedKey)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := expectedValue.(string), filterContext[expectedKey].(string); expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected context passed to filter func for key %q to have value %q, but actuall had %q.", testNumber, expectedKey, expected, actual)
|
|
||||||
continue TLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
36
flogger.go
36
flogger.go
|
@ -1,36 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
type Flogger interface {
|
|
||||||
Debug(...interface{})
|
|
||||||
Debugf(string, ...interface{})
|
|
||||||
Debugln(...interface{})
|
|
||||||
|
|
||||||
Error(...interface{})
|
|
||||||
Errorf(string, ...interface{})
|
|
||||||
Errorfe(error, string, ...interface{})
|
|
||||||
Errorln(...interface{})
|
|
||||||
|
|
||||||
Fatal(...interface{})
|
|
||||||
Fatalf(string, ...interface{})
|
|
||||||
Fatalln(...interface{})
|
|
||||||
|
|
||||||
Panic(...interface{})
|
|
||||||
Panicf(string, ...interface{})
|
|
||||||
Panicfv(interface{}, string, ...interface{})
|
|
||||||
Panicln(...interface{})
|
|
||||||
|
|
||||||
Print(...interface{})
|
|
||||||
Printf(string, ...interface{})
|
|
||||||
Println(...interface{})
|
|
||||||
|
|
||||||
Trace(...interface{})
|
|
||||||
Tracef(string, ...interface{})
|
|
||||||
Traceln(...interface{})
|
|
||||||
|
|
||||||
Warn(...interface{})
|
|
||||||
Warnf(string, ...interface{})
|
|
||||||
Warnln(...interface{})
|
|
||||||
|
|
||||||
With(...interface{}) Flogger
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
type internalFlogger struct {
|
|
||||||
context map[string]interface{}
|
|
||||||
router Router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// New returns an initialized Flogger.
|
|
||||||
func New(router Router, cascade ...interface{}) Flogger {
|
|
||||||
context := newContext(cascade...)
|
|
||||||
|
|
||||||
flogger := internalFlogger{
|
|
||||||
context:context,
|
|
||||||
router:router,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &flogger
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) route(message string, moreContext map[string]interface{}) error {
|
|
||||||
if nil == flogger {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
context := newContext(flogger.context, moreContext)
|
|
||||||
|
|
||||||
router := flogger.router
|
|
||||||
if nil == router {
|
|
||||||
return errNilRouter
|
|
||||||
}
|
|
||||||
|
|
||||||
return router.Route(message, context)
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
debugContext = map[string]interface{}{
|
|
||||||
"~type":"debug",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Debug(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprint(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, debugContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Debugf(format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, debugContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Debugln(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintln(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, debugContext)
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
errorContext = map[string]interface{}{
|
|
||||||
"~type":"error",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Error(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprint(v...)
|
|
||||||
|
|
||||||
errCtx := newErrorContext(errorContext, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, errCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Errorf(format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
errCtx := newErrorContext(errorContext, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, errCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Errorfe(err error, format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
context := map[string]interface{}{}
|
|
||||||
for k,v := range errorContext {
|
|
||||||
context[k] = v
|
|
||||||
}
|
|
||||||
context["~error"] = err
|
|
||||||
|
|
||||||
flogger.route(msg, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Errorln(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintln(v...)
|
|
||||||
|
|
||||||
errCtx := newErrorContext(errorContext, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, errCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func newErrorContext(baseContext map[string]interface{}, v ...interface{}) map[string]interface{} {
|
|
||||||
|
|
||||||
|
|
||||||
// Collect any errors.
|
|
||||||
errs := []error{}
|
|
||||||
for _, datum := range v {
|
|
||||||
if err, ok := datum.(error); ok {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if 0 == len(errs) {
|
|
||||||
return baseContext
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Copy the base context.
|
|
||||||
context := map[string]interface{}{}
|
|
||||||
for k,v := range baseContext {
|
|
||||||
context[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Put the collected errors in this new context.
|
|
||||||
context["~errors"] = errs
|
|
||||||
|
|
||||||
|
|
||||||
return context
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
fatalContext = map[string]interface{}{
|
|
||||||
"~type":"fatal",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Fatal(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprint(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, fatalContext)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Fatalf(format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, fatalContext)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Fatalln(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintln(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, fatalContext)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
panicContext = map[string]interface{}{
|
|
||||||
"~type":"panic",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Panic(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprint(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, panicContext)
|
|
||||||
panic(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Panicf(format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, panicContext)
|
|
||||||
panic(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Panicfv(panicValue interface{}, format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, panicContext)
|
|
||||||
panic(panicValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Panicln(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintln(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, panicContext)
|
|
||||||
panic(msg)
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
printContext = map[string]interface{}{
|
|
||||||
"~type":"print",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Print(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprint(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, printContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Printf(format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, printContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Println(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintln(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, printContext)
|
|
||||||
}
|
|
|
@ -1,352 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
|
|
||||||
flogger := New(NewDiscardingRouter())
|
|
||||||
if nil == flogger {
|
|
||||||
t.Errorf("Expected created flogger to not be nil, but was: %v", flogger)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestNewForContext(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
Cascade []interface{}
|
|
||||||
Expected map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{},
|
|
||||||
Expected: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"apple",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
"apple",
|
|
||||||
"banana",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"banana",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
"apple",
|
|
||||||
"banana",
|
|
||||||
"cherry",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"cherry",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"fig":"THIS SHOULD BE REPLACED",
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"fig":"THIS SHOULD REMAIN",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"fig":"THIS SHOULD REMAIN",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TestLoop:
|
|
||||||
for testNumber, test := range tests {
|
|
||||||
|
|
||||||
flogger := New(NewDiscardingRouter(), test.Cascade...)
|
|
||||||
|
|
||||||
context := flogger.(*internalFlogger).context
|
|
||||||
|
|
||||||
if expected, actual := len(test.Expected), len(context); expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected length to be %d but actually was %d.", testNumber, expected, actual)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
for expectedKey, expectedValue := range test.Expected {
|
|
||||||
if _, ok := context[expectedKey]; !ok {
|
|
||||||
t.Errorf("For test #%d, expected key %q to be in resulting context but wasn't.", testNumber, expectedKey)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := expectedValue, context[expectedKey]; expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected value for key %q to be %q in resulting context, but was actually %q.", testNumber, expectedKey, expected, actual)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestInternalFloggerRouteNilReceiver(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
Message string
|
|
||||||
MoreContext map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Message: "",
|
|
||||||
MoreContext: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "",
|
|
||||||
MoreContext: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "",
|
|
||||||
MoreContext: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": '3',
|
|
||||||
"kiwi": 4.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Message: "Hello world!",
|
|
||||||
MoreContext: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "Hello world!",
|
|
||||||
MoreContext: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "Hello world!",
|
|
||||||
MoreContext: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": '3',
|
|
||||||
"kiwi": 4.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Message: " ",
|
|
||||||
MoreContext: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: " ",
|
|
||||||
MoreContext: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: " ",
|
|
||||||
MoreContext: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": '3',
|
|
||||||
"kiwi": 4.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Message: "one\ntwo\tthree\r\n",
|
|
||||||
MoreContext: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "one\ntwo\tthree\r\n",
|
|
||||||
MoreContext: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "one\ntwo\tthree\r\n",
|
|
||||||
MoreContext: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": '3',
|
|
||||||
"kiwi": 4.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for testNumber, test := range tests {
|
|
||||||
|
|
||||||
var flogger *internalFlogger = nil
|
|
||||||
|
|
||||||
err := flogger.route(test.Message, test.MoreContext)
|
|
||||||
if nil == err {
|
|
||||||
t.Errorf("For test #%d, expected an error, but did not actually get one: %v", testNumber, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if expected, actual := errNilReceiver, err; expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected an error (%T) %q, but actually got (%T) %q", testNumber, expected, expected, actual, actual)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestInternalFloggerRouteNilRouter(t *testing.T) {
|
|
||||||
|
|
||||||
moreContexts := []map[string]interface{}{
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": '3',
|
|
||||||
"kiwi": 4.0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
messages := []string{
|
|
||||||
"",
|
|
||||||
"Hello world!",
|
|
||||||
" ",
|
|
||||||
"one\ntwo\tthree\r\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
Context map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Context: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Context: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Context: map[string]interface{}{
|
|
||||||
"apple": "one",
|
|
||||||
"banana": 2,
|
|
||||||
"cherry": '3',
|
|
||||||
"kiwi": 4.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for testNumber, test := range tests {
|
|
||||||
var flogger internalFlogger
|
|
||||||
|
|
||||||
flogger.context = test.Context
|
|
||||||
flogger.router = nil
|
|
||||||
|
|
||||||
for messageNumber, message := range messages {
|
|
||||||
for moreContextNumber, moreContext := range moreContexts {
|
|
||||||
err := flogger.route(message, moreContext)
|
|
||||||
if nil == err {
|
|
||||||
t.Errorf("For test #%d and message #%d and more context #%d, expected an error, but did not actually get one: %v", testNumber, messageNumber, moreContextNumber, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if expected, actual := errNilRouter, err; expected != actual {
|
|
||||||
t.Errorf("For test #%d and message #%d and more context #%d, expected an error (%T) %q, but actually got (%T) %q", testNumber, messageNumber, moreContextNumber, expected, expected, actual, actual)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
traceContext = map[string]interface{}{
|
|
||||||
"~type":"trace",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Trace(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprint(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, traceContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Tracef(format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, traceContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Traceln(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintln(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, traceContext)
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
warnContext = map[string]interface{}{
|
|
||||||
"~type":"warn",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Warn(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprint(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, warnContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Warnf(format string, v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(format, v...)
|
|
||||||
|
|
||||||
flogger.route(msg, warnContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) Warnln(v ...interface{}) {
|
|
||||||
|
|
||||||
msg := fmt.Sprintln(v...)
|
|
||||||
|
|
||||||
flogger.route(msg, warnContext)
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
func (flogger *internalFlogger) With(cascade ...interface{}) Flogger {
|
|
||||||
|
|
||||||
var x interface{} = flogger.context
|
|
||||||
var xs []interface{} = []interface{}{x}
|
|
||||||
|
|
||||||
newCascade := append(xs, cascade...)
|
|
||||||
|
|
||||||
newFlogger := New(flogger.router, newCascade...)
|
|
||||||
|
|
||||||
return newFlogger
|
|
||||||
}
|
|
|
@ -1,329 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestWith(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct{
|
|
||||||
Cascade1 []interface{}
|
|
||||||
Cascade2 []interface{}
|
|
||||||
Expected map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{},
|
|
||||||
Cascade2: []interface{}{},
|
|
||||||
Expected: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"apple",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"apple",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"apple",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
"banana",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"banana",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
"apple",
|
|
||||||
"banana",
|
|
||||||
"cherry",
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"cherry",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
"apple",
|
|
||||||
"banana",
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
"cherry",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"cherry",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
"apple",
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
"banana",
|
|
||||||
"cherry",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"cherry",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
"apple",
|
|
||||||
"banana",
|
|
||||||
"cherry",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"text":"cherry",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Cascade1: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"fig":"THIS SHOULD BE REPLACED",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Cascade2: []interface{}{
|
|
||||||
map[string]string{
|
|
||||||
"fig":"THIS SHOULD REMAIN",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"apple":"one",
|
|
||||||
"banana":"two",
|
|
||||||
"cherry":"three",
|
|
||||||
"fig":"THIS SHOULD REMAIN",
|
|
||||||
"kiwi":"four",
|
|
||||||
"watermelon":"five",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TestLoop:
|
|
||||||
for testNumber, test := range tests {
|
|
||||||
|
|
||||||
flogger1 := New(NewDiscardingRouter(), test.Cascade1...)
|
|
||||||
|
|
||||||
flogger2 := flogger1.With(test.Cascade2...)
|
|
||||||
|
|
||||||
context := flogger2.(*internalFlogger).context
|
|
||||||
|
|
||||||
if expected, actual := len(test.Expected), len(context); expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected length to be %d but actually was %d.", testNumber, expected, actual)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
for expectedKey, expectedValue := range test.Expected {
|
|
||||||
if _, ok := context[expectedKey]; !ok {
|
|
||||||
t.Errorf("For test #%d, expected key %q to be in resulting context but wasn't.", testNumber, expectedKey)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected, actual := expectedValue, context[expectedKey]; expected != actual {
|
|
||||||
t.Errorf("For test #%d, expected value for key %q to be %q in resulting context, but was actually %q.", testNumber, expectedKey, expected, actual)
|
|
||||||
continue TestLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -66,7 +66,7 @@ func TestLoggerPrefix(t *testing.T) {
|
||||||
|
|
||||||
newInternalLogger, casted := newLogger.(*internalLogger)
|
newInternalLogger, casted := newLogger.(*internalLogger)
|
||||||
if !casted {
|
if !casted {
|
||||||
t.Errorf("For test #%d, could not cast to flog.internalLogger.", testNumber)
|
t.Errorf("For test #%d, could not cast to log.internalLogger.", testNumber)
|
||||||
t.Logf("TYPE: %T", newLogger)
|
t.Logf("TYPE: %T", newLogger)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
// NewMappingRouter returns an initialized MappingRouter.
|
|
||||||
func NewMappingRouter(subrouter Router, fn func(string, map[string]interface{})(string, map[string]interface{})) *MappingRouter {
|
|
||||||
router := MappingRouter{
|
|
||||||
subrouter:subrouter,
|
|
||||||
fn:fn,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MappingRouter is a Router that can modify the message and context before
|
|
||||||
// re-routing it to its sub-router.
|
|
||||||
//
|
|
||||||
// Conceptually this is somewhat similar to "map" functions in functional
|
|
||||||
// programming.
|
|
||||||
type MappingRouter struct {
|
|
||||||
subrouter Router
|
|
||||||
fn func(string, map[string]interface{})(string, map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (router *MappingRouter) Route(message string, context map[string]interface{}) error {
|
|
||||||
if nil == router {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
return router.subrouter.Route(router.fn(message, context))
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
|
|
||||||
// NewNonBlockingRouter returns an initialized NonBlockingRouter.
|
|
||||||
func NewNonBlockingRouter(subrouter Router) *NonBlockingRouter {
|
|
||||||
router := NonBlockingRouter{
|
|
||||||
subrouter:subrouter,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// NonBlockingRouter is a Router when its Route method is call its does not
|
|
||||||
// block and dealing with the routing in parallel.
|
|
||||||
//
|
|
||||||
// Note that this means that if the application could terminate before this
|
|
||||||
// completes.
|
|
||||||
type NonBlockingRouter struct {
|
|
||||||
subrouter Router
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (router *NonBlockingRouter) Route(message string, context map[string]interface{}) error {
|
|
||||||
if nil == router {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
router.subrouter.Route(message, context)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PrettyWritingRouter returns an initialized PrettyWritingRouter
|
|
||||||
func NewPrettyWritingRouter(writer io.Writer) *PrettyWritingRouter {
|
|
||||||
router := PrettyWritingRouter{
|
|
||||||
writer:writer,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &router
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrettyWritingRouter is a router that writes a pretty version of
|
|
||||||
// the log it was give (including COLORS!) to the writer it was
|
|
||||||
// given when it was created.
|
|
||||||
//
|
|
||||||
// A PrettyWritingRouter is appropriate for a development (i.e., "DEV")
|
|
||||||
// deployment enviornment. (And probably not appropriate a production
|
|
||||||
// (i.e., "PROD") deployment environment.)
|
|
||||||
type PrettyWritingRouter struct {
|
|
||||||
writer io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (router *PrettyWritingRouter) Route(message string, context map[string]interface{}) error {
|
|
||||||
if nil == router {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
const STYLE_FATAL = "\x1b[40;33;1m" // BG BLACK, FG YELLOW, BOLD
|
|
||||||
const STYLE_PANIC = "\x1b[40;31;1m" // BG BLACK, FG RED, BOLD
|
|
||||||
const STYLE_ERROR = "\x1b[41;37;1m" // BG RED, FG WHITE, BOLD
|
|
||||||
const STYLE_WARNING = "\x1b[43;37;1m" // BG YELLOW, FG WHITE, BOLD
|
|
||||||
const STYLE_NOTICE = "\x1b[42;33;1m" // BG GREEN, FG YELLOW, BOLD
|
|
||||||
const STYLE_TIMESTAMP = "\x1b[2m" // FAINT
|
|
||||||
const STYLE_MESSAGE = "\x1b[44;37;1m" // BG BLUE, FG WHITE, BOLD
|
|
||||||
const STYLE_DEFAULT = "\033[95m" // HEADER
|
|
||||||
const STYLE_RESET = "\033[0m" // RESET
|
|
||||||
const STYLE_CALLTRACE = "\033[40;36;1m" // BG BLACK, FG CYAN, BOLD
|
|
||||||
|
|
||||||
str := ""
|
|
||||||
|
|
||||||
if nil != context {
|
|
||||||
if _, ok := context["~fatal"]; ok {
|
|
||||||
str = fmt.Sprintf("%s 💀 💀 💀 💀 💀 %s\t%s", STYLE_FATAL, STYLE_RESET, str)
|
|
||||||
} else if _, ok := context["~panic"]; ok {
|
|
||||||
str = fmt.Sprintf("%s ☠ ☠ ☠ ☠ ☠ %s\t%s", STYLE_PANIC, STYLE_RESET, str)
|
|
||||||
} else if _, ok := context["~error"]; ok {
|
|
||||||
str = fmt.Sprintf("%s 😨 😨 😨 😨 😨 %s\t%s", STYLE_ERROR, STYLE_RESET, str)
|
|
||||||
} else if _, ok := context["~warn"]; ok {
|
|
||||||
str = fmt.Sprintf("%s 😟 😟 😟 😟 😟 %s\t%s", STYLE_WARNING, STYLE_RESET, str)
|
|
||||||
} else if _, ok := context["~print"]; ok {
|
|
||||||
str = fmt.Sprintf("%s 😮 😮 😮 😮 😮 %s\t%s", STYLE_NOTICE, STYLE_RESET, str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str = fmt.Sprintf("%s%s%s%s\t(%s%v%s)", str, STYLE_MESSAGE, message, STYLE_RESET, STYLE_TIMESTAMP, time.Now(), STYLE_RESET)
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
context["~~error"] = fmt.Sprintf("%s, {{%T}}", err.Error(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//@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]
|
|
||||||
|
|
||||||
style := STYLE_DEFAULT
|
|
||||||
|
|
||||||
switch key {
|
|
||||||
case "~fatal", "~fatals":
|
|
||||||
style = STYLE_FATAL
|
|
||||||
case "~panic", "~panics":
|
|
||||||
style = STYLE_PANIC
|
|
||||||
case "~error", "~errors", "~~error":
|
|
||||||
style = STYLE_ERROR
|
|
||||||
case "~warning", "~warnings":
|
|
||||||
style = STYLE_WARNING
|
|
||||||
case "~print", "~prints":
|
|
||||||
style = STYLE_NOTICE
|
|
||||||
}
|
|
||||||
|
|
||||||
str = fmt.Sprintf("%s\t%s%s%s=%s%#v%s", str, style, key, STYLE_RESET, style, value, STYLE_RESET)
|
|
||||||
}
|
|
||||||
|
|
||||||
if trace := calltrace(); nil != trace {
|
|
||||||
str = fmt.Sprintf("%s\t⟨⟨", str)
|
|
||||||
for _, s := range trace {
|
|
||||||
str = fmt.Sprintf("%s %s%s%s,", str, STYLE_CALLTRACE, s, STYLE_RESET)
|
|
||||||
}
|
|
||||||
str = fmt.Sprintf("%s ⟩⟩", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(router.writer, str)
|
|
||||||
|
|
||||||
//@TODO: Should this be checking for errors from fmt.Fprintln()?
|
|
||||||
return nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue