aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/Xe/ln
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2017-12-13 10:43:58 -0800
committerChristine Dodrill <me@christine.website>2017-12-13 11:42:37 -0800
commit3a21ef192628f6952eaa981bcdf718a35a4b43c7 (patch)
tree9c88a3ddc57ab5014f436ec2c08c96280872632e /vendor/github.com/Xe/ln
parent3b4b6cede9bc30008b0f40989a1564b26e64fd05 (diff)
downloadxesite-3a21ef192628f6952eaa981bcdf718a35a4b43c7.tar.xz
xesite-3a21ef192628f6952eaa981bcdf718a35a4b43c7.zip
convert to go buildpack
Diffstat (limited to 'vendor/github.com/Xe/ln')
-rw-r--r--vendor/github.com/Xe/ln/LICENSE25
-rw-r--r--vendor/github.com/Xe/ln/README.md29
-rw-r--r--vendor/github.com/Xe/ln/action.go11
-rw-r--r--vendor/github.com/Xe/ln/context.go38
-rw-r--r--vendor/github.com/Xe/ln/doc.go25
-rw-r--r--vendor/github.com/Xe/ln/ex/doc.go7
-rw-r--r--vendor/github.com/Xe/ln/ex/gotrace.go68
-rw-r--r--vendor/github.com/Xe/ln/ex/http.go36
-rw-r--r--vendor/github.com/Xe/ln/ex/l2met.go25
-rw-r--r--vendor/github.com/Xe/ln/example/http.go51
-rw-r--r--vendor/github.com/Xe/ln/filter.go15
-rw-r--r--vendor/github.com/Xe/ln/formatter.go5
-rw-r--r--vendor/github.com/Xe/ln/logger.go95
-rw-r--r--vendor/github.com/Xe/ln/logger_test.go111
14 files changed, 504 insertions, 37 deletions
diff --git a/vendor/github.com/Xe/ln/LICENSE b/vendor/github.com/Xe/ln/LICENSE
new file mode 100644
index 0000000..7202b64
--- /dev/null
+++ b/vendor/github.com/Xe/ln/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2015, Andrew Gwozdziewycz
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/vendor/github.com/Xe/ln/README.md b/vendor/github.com/Xe/ln/README.md
new file mode 100644
index 0000000..61fc941
--- /dev/null
+++ b/vendor/github.com/Xe/ln/README.md
@@ -0,0 +1,29 @@
+# ln: The Natural Logger for Go
+
+`ln` provides a simple interface to logging, and metrics, and
+obviates the need to utilize purpose built metrics packages, like
+`go-metrics` for simple use cases.
+
+The design of `ln` centers around the idea of key-value pairs, which
+can be interpreted on the fly, but "Filters" to do things such as
+aggregated metrics, and report said metrics to, say Librato, or
+statsd.
+
+"Filters" are like WSGI, or Rack Middleware. They are run "top down"
+and can abort an emitted log's output at any time, or continue to let
+it through the chain. However, the interface is slightly different
+than that. Rather than encapsulating the chain with partial function
+application, we utilize a simpler method, namely, each plugin defines
+an `Apply` function, which takes as an argument the log event, and
+performs the work of the plugin, only if the Plugin "Applies" to this
+log event.
+
+If `Apply` returns `false`, the iteration through the rest of the
+filters is aborted, and the log is dropped from further processing.
+
+## Current Status: Initial Development / Concept
+
+## Copyright
+
+(c) 2015, Andrew Gwozdziewycz, BSD Licensed. See LICENSE for more
+info.
diff --git a/vendor/github.com/Xe/ln/action.go b/vendor/github.com/Xe/ln/action.go
new file mode 100644
index 0000000..54f8954
--- /dev/null
+++ b/vendor/github.com/Xe/ln/action.go
@@ -0,0 +1,11 @@
+package ln
+
+// Action is a convenience helper for logging the "action" being performed as
+// part of a log line.
+//
+// It is a convenience wrapper for the following:
+//
+// ln.Log(ctx, fer, f, ln.Action("writing frozberry sales reports to database"))
+func Action(act string) Fer {
+ return F{"action": act}
+}
diff --git a/vendor/github.com/Xe/ln/context.go b/vendor/github.com/Xe/ln/context.go
new file mode 100644
index 0000000..0ea3229
--- /dev/null
+++ b/vendor/github.com/Xe/ln/context.go
@@ -0,0 +1,38 @@
+package ln
+
+import (
+ "context"
+)
+
+type ctxKey int
+
+const (
+ fKey = iota
+)
+
+// WithF stores or appends a given F instance into a context.
+func WithF(ctx context.Context, f F) context.Context {
+ pf, ok := FFromContext(ctx)
+ if !ok {
+ return context.WithValue(ctx, fKey, f)
+ }
+
+ pf.Extend(f)
+
+ return context.WithValue(ctx, fKey, pf)
+}
+
+// FFromContext fetches the `F` out of the context if it exists.
+func FFromContext(ctx context.Context) (F, bool) {
+ fvp := ctx.Value(fKey)
+ if fvp == nil {
+ return nil, false
+ }
+
+ f, ok := fvp.(F)
+ if !ok {
+ return nil, false
+ }
+
+ return f, true
+}
diff --git a/vendor/github.com/Xe/ln/doc.go b/vendor/github.com/Xe/ln/doc.go
new file mode 100644
index 0000000..ab81c3c
--- /dev/null
+++ b/vendor/github.com/Xe/ln/doc.go
@@ -0,0 +1,25 @@
+/*
+Package ln is the Natural Logger for Go
+
+`ln` provides a simple interface to logging, and metrics, and
+obviates the need to utilize purpose built metrics packages, like
+`go-metrics` for simple use cases.
+
+The design of `ln` centers around the idea of key-value pairs, which
+can be interpreted on the fly, but "Filters" to do things such as
+aggregated metrics, and report said metrics to, say Librato, or
+statsd.
+
+"Filters" are like WSGI, or Rack Middleware. They are run "top down"
+and can abort an emitted log's output at any time, or continue to let
+it through the chain. However, the interface is slightly different
+than that. Rather than encapsulating the chain with partial function
+application, we utilize a simpler method, namely, each plugin defines
+an `Apply` function, which takes as an argument the log event, and
+performs the work of the plugin, only if the Plugin "Applies" to this
+log event.
+
+If `Apply` returns `false`, the iteration through the rest of the
+filters is aborted, and the log is dropped from further processing.
+*/
+package ln
diff --git a/vendor/github.com/Xe/ln/ex/doc.go b/vendor/github.com/Xe/ln/ex/doc.go
new file mode 100644
index 0000000..932ed42
--- /dev/null
+++ b/vendor/github.com/Xe/ln/ex/doc.go
@@ -0,0 +1,7 @@
+/*
+Package ex is a set of extensions and middleware for ln.
+
+This package will (inevitably) have a lot of third-party dependencies and
+as such might be broken apart into other packages in the future.
+*/
+package ex
diff --git a/vendor/github.com/Xe/ln/ex/gotrace.go b/vendor/github.com/Xe/ln/ex/gotrace.go
new file mode 100644
index 0000000..5579879
--- /dev/null
+++ b/vendor/github.com/Xe/ln/ex/gotrace.go
@@ -0,0 +1,68 @@
+package ex
+
+import (
+ "context"
+ "log"
+
+ "github.com/Xe/ln"
+ "golang.org/x/net/trace"
+)
+
+type goEventLogger struct {
+ ev trace.EventLog
+}
+
+// NewGoEventLogger will log ln information to a given trace.EventLog instance.
+func NewGoEventLogger(ev trace.EventLog) ln.Filter {
+ return &goEventLogger{ev: ev}
+}
+
+func (gel *goEventLogger) Apply(ctx context.Context, e ln.Event) bool {
+ data, err := ln.DefaultFormatter.Format(ctx, e)
+ if err != nil {
+ log.Printf("wtf: error in log formatting: %v", err)
+ return false
+ }
+
+ if everr := e.Data["err"]; everr != nil {
+ gel.ev.Errorf("%s", string(data))
+ return true
+ }
+
+ gel.ev.Printf("%s", string(data))
+ return true
+}
+
+func (gel *goEventLogger) Close() { gel.ev.Finish() }
+func (gel *goEventLogger) Run() {}
+
+type sst string
+
+func (s sst) String() string { return string(s) }
+
+func goTraceLogger(ctx context.Context, e ln.Event) bool {
+ sp, ok := trace.FromContext(ctx)
+ if !ok {
+ return true // no trace in context
+ }
+
+ data, err := ln.DefaultFormatter.Format(ctx, e)
+ if err != nil {
+ log.Printf("wtf: error in log formatting: %v", err)
+ return false
+ }
+
+ if everr := e.Data["err"]; everr != nil {
+ sp.SetError()
+ }
+
+ sp.LazyLog(sst(string(data)), false)
+
+ return true
+}
+
+// NewGoTraceLogger will log ln information to a golang.org/x/net/trace.Trace
+// if it is present in the context of ln calls.
+func NewGoTraceLogger() ln.Filter {
+ return ln.FilterFunc(goTraceLogger)
+}
diff --git a/vendor/github.com/Xe/ln/ex/http.go b/vendor/github.com/Xe/ln/ex/http.go
new file mode 100644
index 0000000..c5715a3
--- /dev/null
+++ b/vendor/github.com/Xe/ln/ex/http.go
@@ -0,0 +1,36 @@
+package ex
+
+import (
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/Xe/ln"
+)
+
+func HTTPLog(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ host, _, _ := net.SplitHostPort(r.RemoteAddr)
+ f := ln.F{
+ "remote_ip": host,
+ "x_forwarded_for": r.Header.Get("X-Forwarded-For"),
+ "path": r.URL.Path,
+ }
+ ctx := ln.WithF(r.Context(), f)
+ st := time.Now()
+
+ next.ServeHTTP(w, r.WithContext(ctx))
+
+ af := time.Now()
+ f["request_duration"] = af.Sub(st)
+
+ ws, ok := w.(interface {
+ Status() int
+ })
+ if ok {
+ f["status"] = ws.Status()
+ }
+
+ ln.Log(r.Context(), f)
+ })
+}
diff --git a/vendor/github.com/Xe/ln/ex/l2met.go b/vendor/github.com/Xe/ln/ex/l2met.go
new file mode 100644
index 0000000..e2a7f19
--- /dev/null
+++ b/vendor/github.com/Xe/ln/ex/l2met.go
@@ -0,0 +1,25 @@
+package ex
+
+import (
+ "time"
+
+ "github.com/Xe/ln"
+)
+
+// This file deals with formatting of [l2met] style metrics.
+// [l2met]: https://r.32k.io/l2met-introduction
+
+// Counter formats a value as a metrics counter.
+func Counter(name string, value int) ln.Fer {
+ return ln.F{"count#" + name: value}
+}
+
+// Gauge formats a value as a metrics gauge.
+func Gauge(name string, value int) ln.Fer {
+ return ln.F{"gauge#" + name: value}
+}
+
+// Measure formats a value as a metrics measure.
+func Measure(name string, ts time.Time) ln.Fer {
+ return ln.F{"measure#" + name: time.Since(ts)}
+}
diff --git a/vendor/github.com/Xe/ln/example/http.go b/vendor/github.com/Xe/ln/example/http.go
new file mode 100644
index 0000000..7fb98a3
--- /dev/null
+++ b/vendor/github.com/Xe/ln/example/http.go
@@ -0,0 +1,51 @@
+// +build ignore
+
+package main
+
+import (
+ "context"
+ "flag"
+ "net/http"
+ "time"
+
+ "github.com/Xe/ln"
+ "github.com/Xe/ln/ex"
+ "github.com/facebookgo/flagenv"
+ "golang.org/x/net/trace"
+)
+
+var (
+ port = flag.String("port", "2145", "http port to listen on")
+ tracingFamily = flag.String("trace-family", "ln example", "tracing family to use for x/net/trace")
+)
+
+func main() {
+ flagenv.Parse()
+ flag.Parse()
+
+ ln.DefaultLogger.Filters = append(ln.DefaultLogger.Filters, ex.NewGoTraceLogger())
+
+ http.HandleFunc("/", handleIndex)
+ http.ListenAndServe(":"+*port, middlewareSpan(ex.HTTPLog(http.DefaultServeMux)))
+}
+
+func middlewareSpan(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ sp := trace.New(*tracingFamily, "HTTP request")
+ defer sp.Finish()
+ ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
+ defer cancel()
+
+ ctx = trace.NewContext(ctx, sp)
+
+ next.ServeHTTP(w, r.WithContext(ctx))
+ })
+}
+
+func handleIndex(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ ln.Log(ctx, ln.Action("index"), ln.F{"there_is": "no_danger"})
+
+ http.Error(w, "There is no danger citizen", http.StatusOK)
+}
diff --git a/vendor/github.com/Xe/ln/filter.go b/vendor/github.com/Xe/ln/filter.go
index 586efef..4f2d006 100644
--- a/vendor/github.com/Xe/ln/filter.go
+++ b/vendor/github.com/Xe/ln/filter.go
@@ -1,23 +1,24 @@
package ln
import (
+ "context"
"io"
"sync"
)
// Filter interface for defining chain filters
type Filter interface {
- Apply(Event) bool
+ Apply(ctx context.Context, e Event) bool
Run()
Close()
}
// FilterFunc allows simple functions to implement the Filter interface
-type FilterFunc func(e Event) bool
+type FilterFunc func(ctx context.Context, e Event) bool
// Apply implements the Filter interface
-func (ff FilterFunc) Apply(e Event) bool {
- return ff(e)
+func (ff FilterFunc) Apply(ctx context.Context, e Event) bool {
+ return ff(ctx, e)
}
// Run implements the Filter interface
@@ -45,8 +46,8 @@ func NewWriterFilter(out io.Writer, format Formatter) *WriterFilter {
}
// Apply implements the Filter interface
-func (w *WriterFilter) Apply(e Event) bool {
- output, err := w.Formatter.Format(e)
+func (w *WriterFilter) Apply(ctx context.Context, e Event) bool {
+ output, err := w.Formatter.Format(ctx, e)
if err == nil {
w.Lock()
w.Out.Write(output)
@@ -63,4 +64,4 @@ func (w *WriterFilter) Run() {}
func (w *WriterFilter) Close() {}
// NilFilter is safe to return as a Filter, but does nothing
-var NilFilter = FilterFunc(func(e Event) bool { return true })
+var NilFilter = FilterFunc(func(_ context.Context, e Event) bool { return true })
diff --git a/vendor/github.com/Xe/ln/formatter.go b/vendor/github.com/Xe/ln/formatter.go
index ecd4743..70313fc 100644
--- a/vendor/github.com/Xe/ln/formatter.go
+++ b/vendor/github.com/Xe/ln/formatter.go
@@ -2,6 +2,7 @@ package ln
import (
"bytes"
+ "context"
"fmt"
"time"
)
@@ -13,7 +14,7 @@ var (
// Formatter defines the formatting of events
type Formatter interface {
- Format(Event) ([]byte, error)
+ Format(ctx context.Context, e Event) ([]byte, error)
}
// DefaultFormatter is the default way in which to format events
@@ -36,7 +37,7 @@ func NewTextFormatter() Formatter {
}
// Format implements the Formatter interface
-func (t *TextFormatter) Format(e Event) ([]byte, error) {
+func (t *TextFormatter) Format(_ context.Context, e Event) ([]byte, error) {
var writer bytes.Buffer
writer.WriteString("time=\"")
diff --git a/vendor/github.com/Xe/ln/logger.go b/vendor/github.com/Xe/ln/logger.go
index cdfe89e..79a9a63 100644
--- a/vendor/github.com/Xe/ln/logger.go
+++ b/vendor/github.com/Xe/ln/logger.go
@@ -1,7 +1,7 @@
package ln
import (
- "fmt"
+ "context"
"os"
"time"
@@ -30,14 +30,28 @@ func init() {
DefaultLogger = &Logger{
Filters: defaultFilters,
}
-
}
// F is a key-value mapping for structured data.
type F map[string]interface{}
+// Extend concatentates one F with one or many Fer instances.
+func (f F) Extend(other ...Fer) {
+ for _, ff := range other {
+ for k, v := range ff.F() {
+ f[k] = v
+ }
+ }
+}
+
+// F makes F an Fer
+func (f F) F() F {
+ return f
+}
+
+// Fer allows any type to add fields to the structured logging key->value pairs.
type Fer interface {
- F() map[string]interface{}
+ F() F
}
// Event represents an event
@@ -48,8 +62,7 @@ type Event struct {
}
// Log is the generic logging method.
-func (l *Logger) Log(xs ...interface{}) {
- var bits []interface{}
+func (l *Logger) Log(ctx context.Context, xs ...Fer) {
event := Event{Time: time.Now()}
addF := func(bf F) {
@@ -62,18 +75,14 @@ func (l *Logger) Log(xs ...interface{}) {
}
}
- // Assemble the event
- for _, b := range xs {
- if bf, ok := b.(F); ok {
- addF(bf)
- } else if fer, ok := b.(Fer); ok {
- addF(F(fer.F()))
- } else {
- bits = append(bits, b)
- }
+ for _, f := range xs {
+ addF(f.F())
}
- event.Message = fmt.Sprint(bits...)
+ ctxf, ok := FFromContext(ctx)
+ if ok {
+ addF(ctxf)
+ }
if os.Getenv("LN_DEBUG_ALL_EVENTS") == "1" {
frame := callersFrame()
@@ -85,19 +94,19 @@ func (l *Logger) Log(xs ...interface{}) {
event.Data["_filename"] = frame.filename
}
- l.filter(event)
+ l.filter(ctx, event)
}
-func (l *Logger) filter(e Event) {
+func (l *Logger) filter(ctx context.Context, e Event) {
for _, f := range l.Filters {
- if !f.Apply(e) {
+ if !f.Apply(ctx, e) {
return
}
}
}
// Error logs an error and information about the context of said error.
-func (l *Logger) Error(err error, xs ...interface{}) {
+func (l *Logger) Error(ctx context.Context, err error, xs ...Fer) {
data := F{}
frame := callersFrame()
@@ -113,12 +122,37 @@ func (l *Logger) Error(err error, xs ...interface{}) {
xs = append(xs, data)
- l.Log(xs...)
+ l.Log(ctx, xs...)
}
// Fatal logs this set of values, then exits with status code 1.
-func (l *Logger) Fatal(xs ...interface{}) {
- l.Log(xs...)
+func (l *Logger) Fatal(ctx context.Context, xs ...Fer) {
+ xs = append(xs, F{"fatal": true})
+
+ l.Log(ctx, xs...)
+
+ os.Exit(1)
+}
+
+// FatalErr combines Fatal and Error.
+func (l *Logger) FatalErr(ctx context.Context, err error, xs ...Fer) {
+ xs = append(xs, F{"fatal": true})
+
+ data := F{}
+ frame := callersFrame()
+
+ data["_lineno"] = frame.lineno
+ data["_function"] = frame.function
+ data["_filename"] = frame.filename
+ data["err"] = err
+
+ cause := errors.Cause(err)
+ if cause != nil {
+ data["cause"] = cause.Error()
+ }
+
+ xs = append(xs, data)
+ l.Log(ctx, xs...)
os.Exit(1)
}
@@ -126,16 +160,21 @@ func (l *Logger) Fatal(xs ...interface{}) {
// Default Implementation
// Log is the generic logging method.
-func Log(xs ...interface{}) {
- DefaultLogger.Log(xs...)
+func Log(ctx context.Context, xs ...Fer) {
+ DefaultLogger.Log(ctx, xs...)
}
// Error logs an error and information about the context of said error.
-func Error(err error, xs ...interface{}) {
- DefaultLogger.Error(err, xs...)
+func Error(ctx context.Context, err error, xs ...Fer) {
+ DefaultLogger.Error(ctx, err, xs...)
}
// Fatal logs this set of values, then exits with status code 1.
-func Fatal(xs ...interface{}) {
- DefaultLogger.Fatal(xs...)
+func Fatal(ctx context.Context, xs ...Fer) {
+ DefaultLogger.Fatal(ctx, xs...)
+}
+
+// FatalErr combines Fatal and Error.
+func FatalErr(ctx context.Context, err error, xs ...Fer) {
+ DefaultLogger.FatalErr(ctx, err, xs...)
}
diff --git a/vendor/github.com/Xe/ln/logger_test.go b/vendor/github.com/Xe/ln/logger_test.go
new file mode 100644
index 0000000..800ed90
--- /dev/null
+++ b/vendor/github.com/Xe/ln/logger_test.go
@@ -0,0 +1,111 @@
+package ln
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "testing"
+ "time"
+)
+
+var ctx context.Context
+
+func setup(t *testing.T) (*bytes.Buffer, func()) {
+ ctx = context.Background()
+
+ out := bytes.Buffer{}
+ oldFilters := DefaultLogger.Filters
+ DefaultLogger.Filters = []Filter{NewWriterFilter(&out, nil)}
+ return &out, func() {
+ DefaultLogger.Filters = oldFilters
+ }
+}
+
+func TestSimpleError(t *testing.T) {
+ out, teardown := setup(t)
+ defer teardown()
+
+ Log(ctx, F{"err": fmt.Errorf("This is an Error!!!")}, F{"msg": "fooey", "bar": "foo"})
+ data := []string{
+ `err="This is an Error!!!"`,
+ `fooey`,
+ `bar=foo`,
+ }
+
+ for _, line := range data {
+ if !bytes.Contains(out.Bytes(), []byte(line)) {
+ t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
+ }
+ }
+}
+
+func TestTimeConversion(t *testing.T) {
+ out, teardown := setup(t)
+ defer teardown()
+
+ var zeroTime time.Time
+
+ Log(ctx, F{"zero": zeroTime})
+ data := []string{
+ `zero=0001-01-01T00:00:00Z`,
+ }
+
+ for _, line := range data {
+ if !bytes.Contains(out.Bytes(), []byte(line)) {
+ t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
+ }
+ }
+}
+
+func TestDebug(t *testing.T) {
+ out, teardown := setup(t)
+ defer teardown()
+
+ // set priority to Debug
+ Error(ctx, fmt.Errorf("This is an Error!!!"), F{})
+
+ data := []string{
+ `err="This is an Error!!!"`,
+ `_lineno=`,
+ `_function=ln.TestDebug`,
+ `_filename=github.com/Xe/ln/logger_test.go`,
+ `cause="This is an Error!!!"`,
+ }
+
+ for _, line := range data {
+ if !bytes.Contains(out.Bytes(), []byte(line)) {
+ t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
+ }
+ }
+}
+
+func TestFer(t *testing.T) {
+ out, teardown := setup(t)
+ defer teardown()
+
+ underTest := foobar{Foo: 1, Bar: "quux"}
+
+ Log(ctx, underTest)
+ data := []string{
+ `foo=1`,
+ `bar=quux`,
+ }
+
+ for _, line := range data {
+ if !bytes.Contains(out.Bytes(), []byte(line)) {
+ t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
+ }
+ }
+}
+
+type foobar struct {
+ Foo int
+ Bar string
+}
+
+func (f foobar) F() F {
+ return F{
+ "foo": f.Foo,
+ "bar": f.Bar,
+ }
+}