Skip to content

Commit 6d3409b

Browse files
author
Dean Karn
authored
Merge pull request #15 from go-playground/configurable-witherror
Configurable WithError function
2 parents c2be0ef + 663bc8a commit 6d3409b

File tree

10 files changed

+323
-64
lines changed

10 files changed

+323
-64
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## log
2-
<img align="right" src="https://raw.githubusercontent.com/go-playground/log/master/logo.png">![Project status](https://img.shields.io/badge/version-5.0.2-green.svg)
2+
<img align="right" src="https://raw.githubusercontent.com/go-playground/log/master/logo.png">![Project status](https://img.shields.io/badge/version-6.0.0-green.svg)
33
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/log/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/log)
44
[![Coverage Status](https://coveralls.io/repos/github/go-playground/log/badge.svg?branch=master)](https://coveralls.io/github/go-playground/log?branch=master)
55
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/log)](https://goreportcard.com/report/github.com/go-playground/log)
@@ -21,6 +21,9 @@ Features
2121
- [x] Handlers are simple to write + easy to register
2222
- [x] Logger is a singleton ( one of the few instances a singleton is desired ) so the root package registers which handlers are used and any libraries just follow suit.
2323
- [x] Convenient context helpers `GetContext` & `SetContext`
24+
- [x] Works with go-playground/errors extracting types and tags when used with `WithError`, is the default
25+
- [x] Works with pkg/errors when used with `WithError`, must set using `SetWithErrFn`
26+
- [x] Works with segmentio/errors-go extracting types and tags when used with `WithError`, must set using `SetWithErrFn`
2427

2528
Installation
2629
-----------

entry.go

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ package log
22

33
import (
44
"fmt"
5-
"strings"
65
"time"
7-
8-
"github.com/pkg/errors"
96
)
107

118
// Fields is the type to send to WithFields
@@ -48,154 +45,130 @@ func (e Entry) WithFields(fields ...Field) Entry {
4845
return ne
4946
}
5047

51-
// WithTrace withh add duration of how long the between this function call and
52-
// the susequent log
48+
// WithTrace with add duration of how long the between this function call and
49+
// the subsequent log
5350
func (e Entry) WithTrace() Entry {
5451
e.start = time.Now()
5552
return e
5653
}
5754

5855
// WithError add a minimal stack trace to the log Entry
5956
func (e Entry) WithError(err error) Entry {
60-
return e.withError(err)
61-
}
62-
63-
// WithError add a minimal stack trace to the log Entry
64-
func (e Entry) withError(err error) Entry {
65-
ne := newEntry(e)
66-
ne.Fields = append(ne.Fields, Field{Key: "error", Value: err.Error()})
67-
68-
var frame errors.Frame
69-
70-
if s, ok := err.(stackTracer); ok {
71-
frame = s.StackTrace()[0]
72-
} else {
73-
frame = errors.WithStack(err).(stackTracer).StackTrace()[2:][0]
74-
}
75-
76-
name := fmt.Sprintf("%n", frame)
77-
file := fmt.Sprintf("%+s", frame)
78-
line := fmt.Sprintf("%d", frame)
79-
parts := strings.Split(file, "\n\t")
80-
if len(parts) > 1 {
81-
file = parts[1]
82-
}
83-
ne.Fields = append(ne.Fields, Field{Key: "source", Value: fmt.Sprintf("%s: %s:%s", name, file, line)})
84-
return ne
57+
return withErrFn(e, err)
8558
}
8659

8760
// Debug logs a debug entry
8861
func (e Entry) Debug(v ...interface{}) {
8962
e.Message = fmt.Sprint(v...)
9063
e.Level = DebugLevel
91-
handleEntry(e)
64+
HandleEntry(e)
9265
}
9366

9467
// Debugf logs a debug entry with formatting
9568
func (e Entry) Debugf(s string, v ...interface{}) {
9669
e.Message = fmt.Sprintf(s, v...)
9770
e.Level = DebugLevel
98-
handleEntry(e)
71+
HandleEntry(e)
9972
}
10073

10174
// Info logs a normal. information, entry
10275
func (e Entry) Info(v ...interface{}) {
10376
e.Message = fmt.Sprint(v...)
10477
e.Level = InfoLevel
105-
handleEntry(e)
78+
HandleEntry(e)
10679
}
10780

10881
// Infof logs a normal. information, entry with formatting
10982
func (e Entry) Infof(s string, v ...interface{}) {
11083
e.Message = fmt.Sprintf(s, v...)
11184
e.Level = InfoLevel
112-
handleEntry(e)
85+
HandleEntry(e)
11386
}
11487

11588
// Notice logs a notice log entry
11689
func (e Entry) Notice(v ...interface{}) {
11790
e.Message = fmt.Sprint(v...)
11891
e.Level = NoticeLevel
119-
handleEntry(e)
92+
HandleEntry(e)
12093
}
12194

12295
// Noticef logs a notice log entry with formatting
12396
func (e Entry) Noticef(s string, v ...interface{}) {
12497
e.Message = fmt.Sprintf(s, v...)
12598
e.Level = NoticeLevel
126-
handleEntry(e)
99+
HandleEntry(e)
127100
}
128101

129102
// Warn logs a warn log entry
130103
func (e Entry) Warn(v ...interface{}) {
131104
e.Message = fmt.Sprint(v...)
132105
e.Level = WarnLevel
133-
handleEntry(e)
106+
HandleEntry(e)
134107
}
135108

136109
// Warnf logs a warn log entry with formatting
137110
func (e Entry) Warnf(s string, v ...interface{}) {
138111
e.Message = fmt.Sprintf(s, v...)
139112
e.Level = WarnLevel
140-
handleEntry(e)
113+
HandleEntry(e)
141114
}
142115

143116
// Panic logs a panic log entry
144117
func (e Entry) Panic(v ...interface{}) {
145118
e.Message = fmt.Sprint(v...)
146119
e.Level = PanicLevel
147-
handleEntry(e)
120+
HandleEntry(e)
148121
exitFunc(1)
149122
}
150123

151124
// Panicf logs a panic log entry with formatting
152125
func (e Entry) Panicf(s string, v ...interface{}) {
153126
e.Message = fmt.Sprintf(s, v...)
154127
e.Level = PanicLevel
155-
handleEntry(e)
128+
HandleEntry(e)
156129
exitFunc(1)
157130
}
158131

159132
// Alert logs an alert log entry
160133
func (e Entry) Alert(v ...interface{}) {
161134
e.Message = fmt.Sprint(v...)
162135
e.Level = AlertLevel
163-
handleEntry(e)
136+
HandleEntry(e)
164137
}
165138

166139
// Alertf logs an alert log entry with formatting
167140
func (e Entry) Alertf(s string, v ...interface{}) {
168141
e.Message = fmt.Sprintf(s, v...)
169142
e.Level = AlertLevel
170-
handleEntry(e)
143+
HandleEntry(e)
171144
}
172145

173146
// Fatal logs a fatal log entry
174147
func (e Entry) Fatal(v ...interface{}) {
175148
e.Message = fmt.Sprint(v...)
176149
e.Level = FatalLevel
177-
handleEntry(e)
150+
HandleEntry(e)
178151
exitFunc(1)
179152
}
180153

181154
// Fatalf logs a fatal log entry with formatting
182155
func (e Entry) Fatalf(s string, v ...interface{}) {
183156
e.Message = fmt.Sprintf(s, v...)
184157
e.Level = FatalLevel
185-
handleEntry(e)
158+
HandleEntry(e)
186159
exitFunc(1)
187160
}
188161

189162
// Error logs an error log entry
190163
func (e Entry) Error(v ...interface{}) {
191164
e.Message = fmt.Sprint(v...)
192165
e.Level = ErrorLevel
193-
handleEntry(e)
166+
HandleEntry(e)
194167
}
195168

196169
// Errorf logs an error log entry with formatting
197170
func (e Entry) Errorf(s string, v ...interface{}) {
198171
e.Message = fmt.Sprintf(s, v...)
199172
e.Level = ErrorLevel
200-
handleEntry(e)
173+
HandleEntry(e)
201174
}

errors.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package log
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/go-playground/errors"
8+
)
9+
10+
func errorsWithError(e Entry, err error) Entry {
11+
ne := newEntry(e)
12+
13+
if w, ok := err.(*errors.Wrapped); ok {
14+
cause := errors.Cause(w).(*errors.Wrapped)
15+
ne.Fields = append(ne.Fields, Field{Key: "error", Value: fmt.Sprintf("%s: %s", cause.Prefix, cause.Err)})
16+
ne.Fields = append(ne.Fields, Field{Key: "source", Value: cause.Source})
17+
if len(w.Errors) > 0 {
18+
// top level error
19+
types := make([]string, 0, len(w.Errors))
20+
for _, e := range w.Errors {
21+
for _, tag := range e.Tags {
22+
ne.Fields = append(ne.Fields, Field{Key: tag.Key, Value: tag.Value})
23+
}
24+
types = append(types, e.Types...)
25+
}
26+
if len(types) > 0 {
27+
ne.Fields = append(ne.Fields, Field{Key: "types", Value: strings.Join(types, ",")})
28+
}
29+
} else {
30+
// not top level, probably cause
31+
for _, tag := range w.Tags {
32+
ne.Fields = append(ne.Fields, Field{Key: tag.Key, Value: tag.Value})
33+
}
34+
if len(w.Types) > 0 {
35+
ne.Fields = append(ne.Fields, Field{Key: "types", Value: strings.Join(w.Types, ",")})
36+
}
37+
}
38+
39+
} else {
40+
ne.Fields = append(ne.Fields, Field{Key: "error", Value: err.Error()})
41+
frame := errors.StackLevel(2)
42+
name := fmt.Sprintf("%n", frame)
43+
file := fmt.Sprintf("%+s", frame)
44+
line := fmt.Sprintf("%d", frame)
45+
parts := strings.Split(file, "\n\t")
46+
if len(parts) > 1 {
47+
file = parts[1]
48+
}
49+
ne.Fields = append(ne.Fields, Field{Key: "source", Value: fmt.Sprintf("%s: %s:%s", name, file, line)})
50+
}
51+
return ne
52+
}

errors/pkg/pkg.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package pkg
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/go-playground/log"
8+
"github.com/pkg/errors"
9+
)
10+
11+
type stackTracer interface {
12+
StackTrace() errors.StackTrace
13+
}
14+
15+
// ErrorsWithError is a custom WithError function that can be used by using log's
16+
// SetWithErrorFn function.
17+
func ErrorsWithError(e log.Entry, err error) log.Entry {
18+
// normally would call newEntry, but instead will shallow copy
19+
// because it's not exposed.
20+
ne := new(log.Entry)
21+
*ne = *(&e)
22+
23+
flds := make([]log.Field, 0, len(e.Fields))
24+
flds = append(flds, e.Fields...)
25+
flds = append(flds, log.Field{Key: "error", Value: err.Error()})
26+
ne.Fields = flds
27+
28+
var frame errors.Frame
29+
30+
if s, ok := err.(stackTracer); ok {
31+
frame = s.StackTrace()[0]
32+
} else {
33+
frame = errors.WithStack(err).(stackTracer).StackTrace()[2:][0]
34+
}
35+
36+
name := fmt.Sprintf("%n", frame)
37+
file := fmt.Sprintf("%+s", frame)
38+
line := fmt.Sprintf("%d", frame)
39+
parts := strings.Split(file, "\n\t")
40+
if len(parts) > 1 {
41+
file = parts[1]
42+
}
43+
ne.Fields = append(ne.Fields, log.Field{Key: "source", Value: fmt.Sprintf("%s: %s:%s", name, file, line)})
44+
return *ne
45+
}

errors/pkg/pkg_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package pkg
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"strings"
8+
"testing"
9+
10+
"github.com/go-playground/log"
11+
"github.com/pkg/errors"
12+
)
13+
14+
type testHandler struct {
15+
writer io.Writer
16+
}
17+
18+
func (th *testHandler) Log(e log.Entry) {
19+
s := e.Level.String() + " "
20+
s += e.Message
21+
22+
for _, f := range e.Fields {
23+
s += fmt.Sprintf(" %s=%v", f.Key, f.Value)
24+
}
25+
s += "\n"
26+
if _, err := th.writer.Write([]byte(s)); err != nil {
27+
panic(err)
28+
}
29+
}
30+
31+
func TestWrappedError(t *testing.T) {
32+
log.SetExitFunc(func(int) {})
33+
log.SetWithErrorFn(ErrorsWithError)
34+
buff := new(bytes.Buffer)
35+
th := &testHandler{
36+
writer: buff,
37+
}
38+
log.AddHandler(th, log.AllLevels...)
39+
40+
err := fmt.Errorf("this is an %s", "error")
41+
err = errors.Wrap(err, "prefix")
42+
log.WithError(err).Error("test")
43+
expected := "pkg_test.go:41\n"
44+
if !strings.HasSuffix(buff.String(), expected) {
45+
t.Errorf("got %s Expected %s", buff.String(), expected)
46+
}
47+
buff.Reset()
48+
expected = "pkg_test.go:50\n"
49+
err = fmt.Errorf("this is an %s", "error")
50+
log.WithError(err).Error("test")
51+
if !strings.HasSuffix(buff.String(), expected) {
52+
t.Errorf("got %s Expected %s", buff.String(), expected)
53+
}
54+
}

0 commit comments

Comments
 (0)