Skip to content

Commit 03aa3c3

Browse files
authored
Merge pull request #1 from thockin/structured-logs
Convert to structured log support
2 parents 7a7f3ce + 3d8bcc3 commit 03aa3c3

File tree

2 files changed

+116
-23
lines changed

2 files changed

+116
-23
lines changed

example/main.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
6+
"github.com/go-logr/glogr"
7+
"github.com/golang/glog"
8+
)
9+
10+
type E struct {
11+
str string
12+
}
13+
14+
func (e E) Error() string {
15+
return e.str
16+
}
17+
18+
func main() {
19+
flag.Set("v", "3")
20+
flag.Parse()
21+
log := glogr.New().WithName("MyName").WithValues("user", "you")
22+
log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
23+
log.V(3).Info("nice to meet you")
24+
log.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
25+
log.Error(E{"an error occurred"}, "goodbye", "code", -1)
26+
glog.Flush()
27+
}

glogr.go

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,43 @@
33
package glogr
44

55
import (
6+
"bytes"
7+
"encoding/json"
68
"fmt"
79
"runtime"
10+
"sort"
811

12+
"github.com/go-logr/logr"
913
"github.com/golang/glog"
10-
"github.com/thockin/logr"
1114
)
1215

1316
// New returns a logr.Logger which is implemented by glog.
14-
func New() (logr.Logger, error) {
17+
func New() logr.Logger {
1518
return glogger{
1619
level: 0,
1720
prefix: "",
18-
}, nil
21+
values: nil,
22+
}
1923
}
2024

2125
type glogger struct {
2226
level int
2327
prefix string
28+
values []interface{}
29+
}
30+
31+
func (l glogger) clone() glogger {
32+
return glogger{
33+
level: l.level,
34+
prefix: l.prefix,
35+
values: copySlice(l.values),
36+
}
2437
}
2538

26-
func prepend(prefix interface{}, args []interface{}) []interface{} {
27-
return append([]interface{}{prefix}, args...)
39+
func copySlice(in []interface{}) []interface{} {
40+
out := make([]interface{}, len(in))
41+
copy(out, in)
42+
return out
2843
}
2944

3045
// Magic string for intermediate frames that we should ignore.
@@ -44,42 +59,93 @@ func framesToCaller() int {
4459
return 1 // something went wrong, this is safe
4560
}
4661

47-
func (l glogger) Info(args ...interface{}) {
48-
if l.Enabled() {
49-
glog.InfoDepth(framesToCaller(), prepend(l.prefix, args)...)
62+
type kvPair struct {
63+
key string
64+
val interface{}
65+
}
66+
67+
func flatten(kvList ...interface{}) string {
68+
keys := make([]string, 0, len(kvList))
69+
vals := make(map[string]interface{}, len(kvList))
70+
for i := 0; i < len(kvList); i += 2 {
71+
k, ok := kvList[i].(string)
72+
if !ok {
73+
panic(fmt.Sprintf("key is not a string: %s", pretty(kvList[i])))
74+
}
75+
var v interface{}
76+
if i+1 < len(kvList) {
77+
v = kvList[i+1]
78+
}
79+
keys = append(keys, k)
80+
vals[k] = v
5081
}
82+
sort.Strings(keys)
83+
buf := bytes.Buffer{}
84+
for i, k := range keys {
85+
v := vals[k]
86+
if i > 0 {
87+
buf.WriteRune(' ')
88+
}
89+
buf.WriteString(pretty(k))
90+
buf.WriteString("=")
91+
buf.WriteString(pretty(v))
92+
}
93+
return buf.String()
94+
}
95+
96+
func pretty(value interface{}) string {
97+
jb, _ := json.Marshal(value)
98+
return string(jb)
5199
}
52100

53-
func (l glogger) Infof(format string, args ...interface{}) {
101+
func (l glogger) Info(msg string, kvList ...interface{}) {
54102
if l.Enabled() {
55-
glog.InfoDepth(framesToCaller(), fmt.Sprintf("%s"+format, prepend(l.prefix, args)...))
103+
lvlStr := flatten("level", l.level)
104+
msgStr := flatten("msg", msg)
105+
fixedStr := flatten(l.values...)
106+
userStr := flatten(kvList...)
107+
glog.InfoDepth(framesToCaller(), l.prefix, " ", lvlStr, " ", msgStr, " ", fixedStr, " ", userStr)
56108
}
57109
}
58110

59111
func (l glogger) Enabled() bool {
60112
return bool(glog.V(glog.Level(l.level)))
61113
}
62114

63-
func (l glogger) Error(args ...interface{}) {
64-
glog.ErrorDepth(framesToCaller(), prepend(l.prefix, args)...)
115+
func (l glogger) Error(err error, msg string, kvList ...interface{}) {
116+
msgStr := flatten("msg", msg)
117+
var loggableErr interface{}
118+
if err != nil {
119+
loggableErr = err.Error()
120+
}
121+
errStr := flatten("error", loggableErr)
122+
fixedStr := flatten(l.values...)
123+
userStr := flatten(kvList...)
124+
glog.ErrorDepth(framesToCaller(), l.prefix, " ", msgStr, " ", errStr, " ", fixedStr, " ", userStr)
65125
}
66126

67-
func (l glogger) Errorf(format string, args ...interface{}) {
68-
glog.ErrorDepth(framesToCaller(), fmt.Sprintf("%s"+format, prepend(l.prefix, args)...))
127+
func (l glogger) V(level int) logr.InfoLogger {
128+
new := l.clone()
129+
new.level = level
130+
return new
69131
}
70132

71-
func (l glogger) V(level int) logr.InfoLogger {
72-
return glogger{
73-
level: level,
74-
prefix: l.prefix,
133+
// WithName returns a new logr.Logger with the specified name appended. glogr
134+
// uses '/' characters to separate name elements. Callers should not pass '/'
135+
// in the provided name string, but this library does not actually enforce that.
136+
func (l glogger) WithName(name string) logr.Logger {
137+
new := l.clone()
138+
if len(l.prefix) > 0 {
139+
new.prefix = l.prefix + "/"
75140
}
141+
new.prefix += name
142+
return new
76143
}
77144

78-
func (l glogger) NewWithPrefix(prefix string) logr.Logger {
79-
return glogger{
80-
level: l.level,
81-
prefix: prefix,
82-
}
145+
func (l glogger) WithValues(kvList ...interface{}) logr.Logger {
146+
new := l.clone()
147+
new.values = append(new.values, kvList...)
148+
return new
83149
}
84150

85151
var _ logr.Logger = glogger{}

0 commit comments

Comments
 (0)