Codebase list golang-github-go-logr-logr / c666772
Structured Logging Example This introduces a little example app and a very basic example implementation to demonstrate the usage of this library. Solly Ross 5 years ago
2 changed file(s) with 223 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 package main
1
2 import (
3 "os"
4 "fmt"
5 "text/tabwriter"
6
7 "github.com/go-logr/logr"
8 )
9
10 // TabLogger is a sample logr.Logger that logs to stderr.
11 // It's terribly inefficient, and is *only* a basic example.
12 type TabLogger struct{
13 name string
14 tags map[string]interface{}
15
16 writer *tabwriter.Writer
17 }
18
19 var _ logr.Logger = &TabLogger{}
20
21 func (l *TabLogger) Info(msg string, kvs ...interface{}) {
22 fmt.Fprintf(l.writer, "%s\t%s\t", l.name, msg)
23 for k, v := range l.tags {
24 fmt.Fprintf(l.writer, "%s: %+v ", k, v)
25 }
26 for i := 0; i < len(kvs); i += 2 {
27 fmt.Fprintf(l.writer, "%s: %+v ", kvs[i], kvs[i+1])
28 }
29 fmt.Fprintf(l.writer, "\n")
30 l.writer.Flush()
31 }
32
33 func (_ *TabLogger) Enabled() bool {
34 return true
35 }
36
37 func (l *TabLogger) Error(err error, msg string, kvs ...interface{}) {
38 kvs = append(kvs, "error", err)
39 l.Info(msg, kvs...)
40 }
41
42 func (l *TabLogger) V(_ int) logr.InfoLogger {
43 return l
44 }
45
46 func (l *TabLogger) WithName(name string) logr.Logger {
47 return &TabLogger{
48 name: l.name+"."+name,
49 tags: l.tags,
50 writer: l.writer,
51 }
52 }
53
54 func (l *TabLogger) WithTags(kvs ...interface{}) logr.Logger {
55 newMap := make(map[string]interface{}, len(l.tags)+len(kvs)/2)
56 for k, v := range l.tags {
57 newMap[k] = v
58 }
59 for i := 0; i < len(kvs); i += 2 {
60 newMap[kvs[i].(string)] = kvs[i+1]
61 }
62 return &TabLogger{
63 name: l.name,
64 tags: newMap,
65 writer: l.writer,
66 }
67 }
68
69 func NewTabLogger() logr.Logger {
70 return &TabLogger{
71 writer: tabwriter.NewWriter(os.Stderr, 40, 8, 2, '\t', 0),
72 }
73 }
0 package main
1
2 import (
3 "fmt"
4 "math/rand"
5 "time"
6
7 "github.com/go-logr/logr"
8 )
9
10 // This application demonstrates the usage of logger.
11 // It's a simple reconciliation loop that pretends to
12 // receive notifications about updates from a some API
13 // server, make some changes, and then submit updates of
14 // its own.
15
16 // This uses object-based logging. It's also possible
17 // (but a bit trickier) to use file-level "base" loggers.
18
19 var objectMap = map[string]Object{
20 "obj1": Object{
21 Name: "obj1",
22 Kind: "one",
23 Details: 33,
24 },
25 "obj2": Object{
26 Name: "obj2",
27 Kind: "two",
28 Details: "hi",
29 },
30 "obj3": Object{
31 Name: "obj3",
32 Kind: "one",
33 Details: 1,
34 },
35 }
36
37 type Object struct {
38 Name string
39 Kind string
40 Details interface{}
41 }
42
43 type Client struct {
44 objects map[string]Object
45 log logr.Logger
46 }
47
48 func (c *Client) Get(key string) (Object, error) {
49 c.log.V(1).Info("fetching object", "key", key)
50 obj, ok := c.objects[key]
51 if !ok {
52 return Object{}, fmt.Errorf("no object %s exists", key)
53 }
54 c.log.V(1).Info("pretending to deserialize object", "key", key, "json", "[insert real json here]")
55 return obj, nil
56 }
57
58 func (c *Client) Save(obj Object) error {
59 c.log.V(1).Info("saving object", "key", obj.Name, "object", obj)
60 if rand.Intn(2) == 0 {
61 return fmt.Errorf("couldn't save to %s", obj.Name)
62 }
63 c.log.V(1).Info("pretending to post object", "key", obj.Name, "url", "https://fake.test")
64 return nil
65 }
66
67 func (c *Client) WatchNext() string {
68 time.Sleep(2*time.Second)
69
70 keyInd := rand.Intn(len(c.objects))
71
72 currInd := 0
73 for key := range c.objects {
74 if currInd == keyInd {
75 return key
76 }
77 currInd++
78 }
79
80 c.log.Info("watch ended")
81 return ""
82 }
83
84 type Controller struct {
85 log logr.Logger
86 expectedKind string
87 client *Client
88 }
89
90 func (c *Controller) Run() {
91 c.log.Info("starting reconciliation")
92
93 for key := c.client.WatchNext(); key != ""; key = c.client.WatchNext() {
94 // we can make more specific loggers if we always want to attach a particular tag
95 log := c.log.WithTags("key", key)
96
97 // fetch our object
98 obj, err := c.client.Get(key)
99 if err != nil {
100 log.Error(err, "unable to reconcile object")
101 continue
102 }
103
104 // make sure it's as expected
105 if obj.Kind != c.expectedKind {
106 log.Error(nil, "got object that wasn't expected kind", "actual-kind", obj.Kind, "object", obj)
107 continue
108 }
109
110 // always log the object with log messages
111 log = log.WithTags("object", obj)
112 log.V(1).Info("reconciling object for key")
113
114 // Do some complicated updates updates
115 obj.Details = obj.Details.(int) * 2
116
117 // actually save the updates
118 log.V(1).Info("updating object", "details", obj.Details)
119 if err := c.client.Save(obj); err != nil {
120 log.Error(err, "unable to reconcile object")
121 }
122 }
123
124 c.log.Info("stopping reconciliation")
125 }
126
127 func NewController(log logr.Logger, objectKind string) *Controller {
128 ctrlLogger := log.WithName("controller").WithName(objectKind)
129 client := &Client{
130 log: ctrlLogger.WithName("client"),
131 objects: objectMap,
132 }
133 return &Controller{
134 log: ctrlLogger,
135 expectedKind: objectKind,
136 client: client,
137 }
138 }
139
140 func main() {
141 // use a fake implementation just for demonstration purposes
142 log := NewTabLogger()
143
144 // update objects with the "one" kind
145 ctrl := NewController(log, "one")
146
147 ctrl.Run()
148 }