github.com/oam-dev/kubevela@v1.9.11/pkg/monitor/watcher/application.go (about) 1 /* 2 Copyright 2022 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package watcher 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "strings" 23 "sync" 24 "time" 25 26 "k8s.io/client-go/tools/cache" 27 "k8s.io/klog/v2" 28 ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache" 29 30 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 31 "github.com/oam-dev/kubevela/pkg/monitor/metrics" 32 ) 33 34 type applicationMetricsWatcher struct { 35 mu sync.Mutex 36 phaseCounter map[string]int 37 stepPhaseCounter map[string]int 38 phaseDirty map[string]struct{} 39 stepPhaseDirty map[string]struct{} 40 informer ctrlcache.Informer 41 stopCh chan struct{} 42 } 43 44 func (watcher *applicationMetricsWatcher) getApp(obj interface{}) *v1beta1.Application { 45 app := &v1beta1.Application{} 46 bs, _ := json.Marshal(obj) 47 _ = json.Unmarshal(bs, app) 48 return app 49 } 50 51 func (watcher *applicationMetricsWatcher) getPhase(phase string) string { 52 if phase == "" { 53 return "-" 54 } 55 return phase 56 } 57 58 func (watcher *applicationMetricsWatcher) inc(app *v1beta1.Application, delta int) { 59 watcher.mu.Lock() 60 defer watcher.mu.Unlock() 61 phase := watcher.getPhase(string(app.Status.Phase)) 62 watcher.phaseCounter[phase] += delta 63 watcher.phaseDirty[phase] = struct{}{} 64 if app.Status.Workflow != nil { 65 for _, step := range app.Status.Workflow.Steps { 66 stepPhase := watcher.getPhase(string(step.Phase)) 67 key := fmt.Sprintf("%s/%s#%s", step.Type, stepPhase, step.Reason) 68 watcher.stepPhaseCounter[key] += delta 69 watcher.stepPhaseDirty[key] = struct{}{} 70 } 71 } 72 } 73 74 func (watcher *applicationMetricsWatcher) report() { 75 watcher.mu.Lock() 76 defer watcher.mu.Unlock() 77 for phase := range watcher.phaseDirty { 78 metrics.ApplicationPhaseCounter.WithLabelValues(phase).Set(float64(watcher.phaseCounter[phase])) 79 } 80 for stepPhase := range watcher.stepPhaseDirty { 81 metrics.WorkflowStepPhaseGauge.WithLabelValues(strings.Split(stepPhase, "/")[:2]...).Set(float64(watcher.stepPhaseCounter[stepPhase])) 82 } 83 watcher.phaseDirty = map[string]struct{}{} 84 watcher.stepPhaseDirty = map[string]struct{}{} 85 } 86 87 func (watcher *applicationMetricsWatcher) run() { 88 go func() { 89 for { 90 select { 91 case <-watcher.stopCh: 92 return 93 default: 94 time.Sleep(time.Second) 95 watcher.report() 96 } 97 } 98 }() 99 } 100 101 // StartApplicationMetricsWatcher start metrics watcher for reporting application stats 102 func StartApplicationMetricsWatcher(informer ctrlcache.Informer) { 103 watcher := &applicationMetricsWatcher{ 104 phaseCounter: map[string]int{}, 105 stepPhaseCounter: map[string]int{}, 106 phaseDirty: map[string]struct{}{}, 107 stepPhaseDirty: map[string]struct{}{}, 108 informer: informer, 109 stopCh: make(chan struct{}, 1), 110 } 111 _, err := watcher.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 112 AddFunc: func(obj interface{}) { 113 app := watcher.getApp(obj) 114 watcher.inc(app, 1) 115 }, 116 UpdateFunc: func(oldObj, obj interface{}) { 117 oldApp := watcher.getApp(oldObj) 118 app := watcher.getApp(obj) 119 watcher.inc(oldApp, -1) 120 watcher.inc(app, 1) 121 }, 122 DeleteFunc: func(obj interface{}) { 123 app := watcher.getApp(obj) 124 watcher.inc(app, -1) 125 }, 126 }) 127 if err != nil { 128 klog.ErrorS(err, "failed to add event handler for application metrics watcher") 129 } 130 watcher.run() 131 }