github.com/cs3org/reva/v2@v2.27.7/pkg/sysinfo/metrics.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package sysinfo 20 21 import ( 22 "context" 23 "fmt" 24 "reflect" 25 "strings" 26 27 "github.com/pkg/errors" 28 "go.opencensus.io/stats" 29 "go.opencensus.io/stats/view" 30 "go.opencensus.io/tag" 31 32 "github.com/cs3org/reva/v2/pkg/utils" 33 ) 34 35 type sysInfoMetricsLabels = map[tag.Key]string 36 37 func registerSystemInfoMetrics() error { 38 labels := getSystemInfoMetricsLabels("", SysInfo) 39 40 // Collect all labels and their values; the values are stored as mutators 41 tagKeys := make([]tag.Key, 0, len(labels)) 42 mutators := make([]tag.Mutator, 0, len(labels)) 43 for key, value := range labels { 44 tagKeys = append(tagKeys, key) 45 mutators = append(mutators, tag.Insert(key, value)) 46 } 47 48 // Create the OpenCensus statistics and a corresponding view 49 sysInfoStats := stats.Int64("sys_info", "A metric with a constant '1' value labeled by various system information elements", stats.UnitDimensionless) 50 sysInfoView := &view.View{ 51 Name: sysInfoStats.Name(), 52 Description: sysInfoStats.Description(), 53 Measure: sysInfoStats, 54 TagKeys: tagKeys, 55 Aggregation: view.LastValue(), 56 } 57 58 if err := view.Register(sysInfoView); err != nil { 59 return errors.Wrap(err, "unable to register the system info metrics view") 60 } 61 62 // Create a new context to serve the metrics 63 if ctx, err := tag.New(context.Background(), mutators...); err == nil { 64 // Just record a simple hardcoded '1' to expose the system info as a metric 65 stats.Record(ctx, sysInfoStats.M(1)) 66 } else { 67 return errors.Wrap(err, "unable to create a context for the system info metrics") 68 } 69 70 return nil 71 } 72 73 func getSystemInfoMetricsLabels(root string, i interface{}) sysInfoMetricsLabels { 74 labels := sysInfoMetricsLabels{} 75 76 // Iterate over each field of the given interface, recursively collecting the values as labels 77 v := reflect.ValueOf(i).Elem() 78 for i := 0; i < v.NumField(); i++ { 79 // Check if the field was tagged with 'sysinfo:omitlabel'; if so, skip this field 80 tags := v.Type().Field(i).Tag.Get("sysinfo") 81 if strings.Contains(tags, "omitlabel") { 82 continue 83 } 84 85 // Get the name of the field from the parent structure 86 fieldName := utils.ToSnakeCase(v.Type().Field(i).Name) 87 if len(root) > 0 { 88 fieldName = "_" + fieldName 89 } 90 fieldName = root + fieldName 91 92 // Check if the field is either a struct or a pointer to a struct; in that case, process the field recursively 93 f := v.Field(i) 94 if f.Kind() == reflect.Struct || (f.Kind() == reflect.Ptr && f.Elem().Kind() == reflect.Struct) { 95 // Merge labels recursively 96 for key, val := range getSystemInfoMetricsLabels(fieldName, f.Interface()) { 97 labels[key] = val 98 } 99 } else { // Store the value of the field in the labels 100 key := tag.MustNewKey(fieldName) 101 labels[key] = fmt.Sprintf("%v", f) 102 } 103 } 104 105 return labels 106 }