| /* | |
| Copyright 2019 The logr Authors. | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| 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": { | |
| Name: "obj1", | |
| Kind: "one", | |
| Details: 33, | |
| }, | |
| "obj2": { | |
| Name: "obj2", | |
| Kind: "two", | |
| Details: "hi", | |
| }, | |
| "obj3": { | |
| Name: "obj3", | |
| Kind: "one", | |
| Details: 1, | |
| }, | |
| } | |
| // Object is an app construct that might want to be logged. | |
| type Object struct { | |
| Name string | |
| Kind string | |
| Details any | |
| } | |
| // Client is a simulated client in this example app. | |
| type Client struct { | |
| objects map[string]Object | |
| log logr.Logger | |
| } | |
| // Get retrieves an object. | |
| 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 | |
| } | |
| // Save stores an object. | |
| 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 | |
| } | |
| // WatchNext waits for object updates. | |
| 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 "" | |
| } | |
| // Controller is the main point of this example. | |
| type Controller struct { | |
| log logr.Logger | |
| expectedKind string | |
| client *Client | |
| } | |
| // Run starts the example controller. | |
| 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 //nolint:forcetypeassert | |
| // 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") | |
| } | |
| // NewController allocates and initializes a Controller. | |
| 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() | |
| } |