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