renaming from "package flog" to "package log"

master
Charles Iliya Krempeaux 2022-06-29 23:46:50 -07:00
parent 1665d92ed6
commit 34c0062438
33 changed files with 2 additions and 2893 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}
}
}

View File

@ -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
}

View File

@ -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++
}
}
}

View File

@ -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
}

View File

@ -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
}
}
}
}
}

View File

@ -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
}

View File

@ -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
View File

@ -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

View File

@ -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
}
}
}

View File

@ -8,5 +8,4 @@ import (
var ( var (
errNilReceiver = errors.New("Nil Receiver") errNilReceiver = errors.New("Nil Receiver")
errNilRouter = errors.New("Nil Router")
) )

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}
}
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}
}
}
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}
}
}
}

View File

@ -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
} }

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -1,6 +0,0 @@
package log
type Router interface {
Route(message string, context map[string]interface{}) error
}