initial commit. this is still a work in progress.
commit
f151008688
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2015 Charles Iliya Krempeaux <charles@reptile.ca> :: http://changelog.ca/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,11 @@
|
|||
# go-flog
|
||||
|
||||
A library that provides structured and formatted logging for the Go programming language.
|
||||
|
||||
|
||||
## Documention
|
||||
|
||||
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-flog
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/reiver/go-flog?status.svg)](https://godoc.org/github.com/reiver/go-flog)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package flog
|
||||
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package flog
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package flog
|
||||
|
||||
|
||||
func NewDiscardRouter() *DiscardRouter {
|
||||
router := DiscardRouter{}
|
||||
|
||||
return &router
|
||||
}
|
||||
|
||||
|
||||
type DiscardRouter struct{}
|
||||
|
||||
|
||||
func (router *DiscardRouter) Route(message string, context map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package flog
|
||||
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestNewDiscardRouter(t *testing.T) {
|
||||
|
||||
router := NewDiscardRouter()
|
||||
if nil == router {
|
||||
t.Errorf("After trying to create a discard router, expected it to be not nil, but was: %v", router)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package flog
|
||||
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
|
||||
func (flogger *internalFlogger) Fatal(v ...interface{}) {
|
||||
|
||||
flogger.Print(v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (flogger *internalFlogger) Fatalf(format string, v ...interface{}) {
|
||||
|
||||
flogger.Printf(format, v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (flogger *internalFlogger) Fatalln(v ...interface{}) {
|
||||
|
||||
flogger.Println(v...)
|
||||
os.Exit(1)
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package flog
|
||||
|
||||
|
||||
func NewFilteredRouter() *FilteredRouter {
|
||||
registry := make([]struct{FilterFn func(string, map[string]interface{})bool ; Subrouter Router}, 0, 2)
|
||||
|
||||
router := FilteredRouter{
|
||||
registry:registry,
|
||||
}
|
||||
|
||||
return &router
|
||||
}
|
||||
|
||||
|
||||
type FilteredRouter struct {
|
||||
registry []struct{FilterFn func(string, map[string]interface{})bool ; Subrouter Router}
|
||||
}
|
||||
|
||||
|
||||
func (router *FilteredRouter) Route(message string, context map[string]interface{}) error {
|
||||
for _, datum := range router.registry {
|
||||
if datum.FilterFn(message, context) {
|
||||
return datum.Subrouter.Route(message, context)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (router *FilteredRouter) Register(subrouter Router, filterFn func(string, map[string]interface{})bool) {
|
||||
datum := struct{FilterFn func(string, map[string]interface{})bool ; Subrouter Router}{
|
||||
FilterFn: filterFn,
|
||||
Subrouter: subrouter,
|
||||
}
|
||||
|
||||
router.registry = append(router.registry, datum)
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package flog
|
||||
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
func TestFilteredRouterJustCreated(t *testing.T) {
|
||||
|
||||
router := NewFilteredRouter()
|
||||
router.Register(NewDiscardRouter(), func(string, map[string]interface{}) bool {
|
||||
return false
|
||||
})
|
||||
if nil == router {
|
||||
t.Errorf("After trying to create a filtered router, expected it to be not nil, but was: %v", router)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func TestFilteredRouterJustFilterParameters(t *testing.T) {
|
||||
|
||||
randomness := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
|
||||
|
||||
|
||||
var filterMessage string
|
||||
var filterContext map[string] interface{}
|
||||
var filterResult = false
|
||||
|
||||
router := NewFilteredRouter()
|
||||
|
||||
router.Register(NewDiscardRouter(), 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package flog
|
||||
|
||||
|
||||
type internalFlogger struct {
|
||||
//@TODO: Do we really want this here?
|
||||
// logs []string
|
||||
context map[string]interface{}
|
||||
router Router
|
||||
}
|
||||
|
||||
|
||||
func New(router Router, cascade ...interface{}) Flogger {
|
||||
//@TODO: Do we really want this here?
|
||||
// logs := make([]string, 0, 8)
|
||||
|
||||
context := newContext(cascade...)
|
||||
|
||||
flogger := internalFlogger{
|
||||
//@TODO: Do we really want this here?
|
||||
// logs:logs,
|
||||
context:context,
|
||||
router:router,
|
||||
}
|
||||
|
||||
return &flogger
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
package flog
|
||||
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
|
||||
flogger := New(NewDiscardRouter())
|
||||
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(NewDiscardRouter(), 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package flog
|
||||
|
||||
|
||||
type Flogger interface {
|
||||
Fatal(...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
Fatalln(...interface{})
|
||||
|
||||
Panic(...interface{})
|
||||
Panicf(string, ...interface{})
|
||||
Panicln(...interface{})
|
||||
|
||||
Print(...interface{})
|
||||
Printf(string, ...interface{})
|
||||
Println(...interface{})
|
||||
|
||||
With(...interface{}) Flogger
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package flog
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
func (flogger *internalFlogger) Panic(v ...interface{}) {
|
||||
|
||||
flogger.Print(v...)
|
||||
panic(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (flogger *internalFlogger) Panicf(format string, v ...interface{}) {
|
||||
|
||||
flogger.Printf(format, v...)
|
||||
panic(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (flogger *internalFlogger) Panicln(v ...interface{}) {
|
||||
|
||||
flogger.Println(v...)
|
||||
panic(fmt.Sprintln(v...))
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package flog
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
func (flogger *internalFlogger) Print(v ...interface{}) {
|
||||
|
||||
msg := fmt.Sprint(v...)
|
||||
//@TODO: Do we really want this here?
|
||||
// flogger.logs = append(flogger.logs, msg)
|
||||
|
||||
flogger.router.Route(msg, flogger.context)
|
||||
}
|
||||
|
||||
func (flogger *internalFlogger) Printf(format string, v ...interface{}) {
|
||||
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
//@TODO: Do we really want this here?
|
||||
// flogger.logs = append(flogger.logs, msg)
|
||||
|
||||
flogger.router.Route(msg, flogger.context)
|
||||
}
|
||||
|
||||
func (flogger *internalFlogger) Println(v ...interface{}) {
|
||||
|
||||
msg := fmt.Sprintln(v...)
|
||||
//@TODO: Do we really want this here?
|
||||
// flogger.logs = append(flogger.logs, msg)
|
||||
|
||||
flogger.router.Route(msg, flogger.context)
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package flog
|
||||
|
||||
|
||||
type Router interface {
|
||||
Route(message string, context map[string]interface{}) error
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package flog
|
||||
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
package flog
|
||||
|
||||
|
||||
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(NewDiscardRouter(), 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package flog
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
func NewWritingRouter(writer io.Writer) *WritingRouter {
|
||||
router := WritingRouter{
|
||||
writer:writer,
|
||||
}
|
||||
|
||||
return &router
|
||||
}
|
||||
|
||||
|
||||
|
||||
type WritingRouter struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (router *WritingRouter) Route(message string, context map[string]interface{}) error {
|
||||
const BOLD = "\033[1m"
|
||||
const HEADER = "\033[95m"
|
||||
const UNDERLINE = "\033[4m"
|
||||
const ENDC = "\033[0m"
|
||||
|
||||
str := fmt.Sprintf("%s%s%s\t(%s%v%s)", UNDERLINE, message, ENDC, HEADER, time.Now(), ENDC)
|
||||
for key, value := range context {
|
||||
str = fmt.Sprintf("%s\t%s%s%s=%q", str, HEADER, key, ENDC, value)
|
||||
}
|
||||
|
||||
fmt.Fprintln(router.writer, str)
|
||||
|
||||
//@TODO: Should this be checking for errors from fmt.Fprintln()?
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue