| package main |
| |
| import ( |
| "fmt" |
| "math/rand" |
| "time" |
| |
| "github.com/go-logr/logr" |
| ) |
| |
| // This application demonstrates the usage of logger. |
| // It's a simple reconciliation loop that pretends to |
| // receive notifications about updates from a some API |
| // server, make some changes, and then submit updates of |
| // its own. |
| |
| // This uses object-based logging. It's also possible |
| // (but a bit trickier) to use file-level "base" loggers. |
| |
| var objectMap = map[string]Object{ |
| "obj1": Object{ |
| Name: "obj1", |
| Kind: "one", |
| Details: 33, |
| }, |
| "obj2": Object{ |
| Name: "obj2", |
| Kind: "two", |
| Details: "hi", |
| }, |
| "obj3": Object{ |
| Name: "obj3", |
| Kind: "one", |
| Details: 1, |
| }, |
| } |
| |
| type Object struct { |
| Name string |
| Kind string |
| Details interface{} |
| } |
| |
| type Client struct { |
| objects map[string]Object |
| log logr.Logger |
| } |
| |
| func (c *Client) Get(key string) (Object, error) { |
| c.log.V(1).Info("fetching object", "key", key) |
| obj, ok := c.objects[key] |
| if !ok { |
| return Object{}, fmt.Errorf("no object %s exists", key) |
| } |
| c.log.V(1).Info("pretending to deserialize object", "key", key, "json", "[insert real json here]") |
| return obj, nil |
| } |
| |
| func (c *Client) Save(obj Object) error { |
| c.log.V(1).Info("saving object", "key", obj.Name, "object", obj) |
| if rand.Intn(2) == 0 { |
| return fmt.Errorf("couldn't save to %s", obj.Name) |
| } |
| c.log.V(1).Info("pretending to post object", "key", obj.Name, "url", "https://fake.test") |
| return nil |
| } |
| |
| func (c *Client) WatchNext() string { |
| time.Sleep(2*time.Second) |
| |
| keyInd := rand.Intn(len(c.objects)) |
| |
| currInd := 0 |
| for key := range c.objects { |
| if currInd == keyInd { |
| return key |
| } |
| currInd++ |
| } |
| |
| c.log.Info("watch ended") |
| return "" |
| } |
| |
| type Controller struct { |
| log logr.Logger |
| expectedKind string |
| client *Client |
| } |
| |
| func (c *Controller) Run() { |
| c.log.Info("starting reconciliation") |
| |
| for key := c.client.WatchNext(); key != ""; key = c.client.WatchNext() { |
| // we can make more specific loggers if we always want to attach a particular named value |
| log := c.log.WithValues("key", key) |
| |
| // fetch our object |
| obj, err := c.client.Get(key) |
| if err != nil { |
| log.Error(err, "unable to reconcile object") |
| continue |
| } |
| |
| // make sure it's as expected |
| if obj.Kind != c.expectedKind { |
| log.Error(nil, "got object that wasn't expected kind", "actual-kind", obj.Kind, "object", obj) |
| continue |
| } |
| |
| // always log the object with log messages |
| log = log.WithValues("object", obj) |
| log.V(1).Info("reconciling object for key") |
| |
| // Do some complicated updates updates |
| obj.Details = obj.Details.(int) * 2 |
| |
| // actually save the updates |
| log.V(1).Info("updating object", "details", obj.Details) |
| if err := c.client.Save(obj); err != nil { |
| log.Error(err, "unable to reconcile object") |
| } |
| } |
| |
| c.log.Info("stopping reconciliation") |
| } |
| |
| func NewController(log logr.Logger, objectKind string) *Controller { |
| ctrlLogger := log.WithName("controller").WithName(objectKind) |
| client := &Client{ |
| log: ctrlLogger.WithName("client"), |
| objects: objectMap, |
| } |
| return &Controller{ |
| log: ctrlLogger, |
| expectedKind: objectKind, |
| client: client, |
| } |
| } |
| |
| func main() { |
| // use a fake implementation just for demonstration purposes |
| log := NewTabLogger() |
| |
| // update objects with the "one" kind |
| ctrl := NewController(log, "one") |
| |
| ctrl.Run() |
| } |