From f151008688bae77967e4cc30974bfe1c60744c67 Mon Sep 17 00:00:00 2001 From: Charles Iliya Krempeaux Date: Sat, 10 Oct 2015 00:29:18 -0700 Subject: [PATCH] initial commit. this is still a work in progress. --- LICENSE | 19 +++ README.md | 11 ++ context.go | 24 +++ context_test.go | 154 +++++++++++++++++++ discard_router.go | 16 ++ discard_router_test.go | 16 ++ fatal.go | 25 +++ filtered_router.go | 38 +++++ filtered_router_test.go | 82 ++++++++++ flog.go | 26 ++++ flog_test.go | 179 ++++++++++++++++++++++ flogger.go | 18 +++ panic.go | 25 +++ print.go | 34 +++++ router.go | 6 + with.go | 14 ++ with_test.go | 329 ++++++++++++++++++++++++++++++++++++++++ writing_router.go | 42 +++++ 18 files changed, 1058 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 context.go create mode 100644 context_test.go create mode 100644 discard_router.go create mode 100644 discard_router_test.go create mode 100644 fatal.go create mode 100644 filtered_router.go create mode 100644 filtered_router_test.go create mode 100644 flog.go create mode 100644 flog_test.go create mode 100644 flogger.go create mode 100644 panic.go create mode 100644 print.go create mode 100644 router.go create mode 100644 with.go create mode 100644 with_test.go create mode 100644 writing_router.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ff0d4ea --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Charles Iliya Krempeaux :: 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3380249 --- /dev/null +++ b/README.md @@ -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) + diff --git a/context.go b/context.go new file mode 100644 index 0000000..0f48e4e --- /dev/null +++ b/context.go @@ -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 +} diff --git a/context_test.go b/context_test.go new file mode 100644 index 0000000..dfd99a7 --- /dev/null +++ b/context_test.go @@ -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 + } + } + + } +} diff --git a/discard_router.go b/discard_router.go new file mode 100644 index 0000000..a36970c --- /dev/null +++ b/discard_router.go @@ -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 +} diff --git a/discard_router_test.go b/discard_router_test.go new file mode 100644 index 0000000..5b1a2d1 --- /dev/null +++ b/discard_router_test.go @@ -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) + } + +} diff --git a/fatal.go b/fatal.go new file mode 100644 index 0000000..4da7632 --- /dev/null +++ b/fatal.go @@ -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) +} diff --git a/filtered_router.go b/filtered_router.go new file mode 100644 index 0000000..6958540 --- /dev/null +++ b/filtered_router.go @@ -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) +} diff --git a/filtered_router_test.go b/filtered_router_test.go new file mode 100644 index 0000000..cd1df3a --- /dev/null +++ b/filtered_router_test.go @@ -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