// Copyright 2019, OpenCensus 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. // START entire // This example shows how to use gauge metrics. The program records two gauges, one to demonstrate // a gauge with int64 value and the other to demonstrate a gauge with float64 value. // // Metrics // // 1. process_heap_alloc (int64): Total bytes used by objects allocated in the heap. // It includes objects currently used and objects that are freed but not garbage collected. // // 2. process_heap_idle_to_alloc_ratio (float64): It is the ratio of Idle bytes to allocated // bytes in the heap. // // It periodically runs a function that retrieves the memory stats and updates the above two // metrics. These metrics are then exported using log exporter. // The program lets you choose the amount of memory (in MB) to consume. Choose different values // and query the metrics to see the change in metrics. package main import ( "bufio" "fmt" "log" "os" "runtime" "strconv" "strings" "time" "go.opencensus.io/examples/exporter" "go.opencensus.io/metric" "go.opencensus.io/metric/metricdata" "go.opencensus.io/metric/metricproducer" ) const ( metricsLogFile = "/tmp/metrics.log" ) var ( mem = &runtime.MemStats{} ) type memObj struct { size int b []byte } func newMemObj(size int) *memObj { n := &memObj{size: size, b: make([]byte, size)} for i := 0; i < n.size; i++ { n.b[i] = byte(i) } return n } var allocEntry *metric.Int64GaugeEntry var ratioEntry *metric.Float64Entry var arr []*memObj func getAlloc() uint64 { runtime.ReadMemStats(mem) return mem.HeapAlloc } func getIdleToAllocRatio() float64 { runtime.ReadMemStats(mem) return float64(mem.HeapIdle) / float64(mem.HeapAlloc) } func consumeMem(sizeMB int) { arr = make([]*memObj, sizeMB) for i := 0; i < sizeMB; i++ { arr = append(arr, newMemObj(1000000)) } } func doSomeWork(sizeMB int) { // do some work consumeMem(sizeMB) } func recordMetrics(delay int, done chan int) { tick := time.NewTicker(time.Duration(delay) * time.Second) for { select { case <-done: return case <-tick.C: // record heap allocation and idle to allocation ratio. // START record allocEntry.Set(int64(getAlloc())) // int64 gauge ratioEntry.Set(getIdleToAllocRatio()) // float64 gauge // END record } } } func getInput() int { reader := bufio.NewReader(os.Stdin) limit := 50 for { fmt.Printf("Enter memory (in MB between 1-%d): ", limit) text, _ := reader.ReadString('\n') sizeMB, err := strconv.Atoi(strings.TrimSuffix(text, "\n")) if err == nil { if sizeMB < 1 || sizeMB > limit { fmt.Printf("invalid value %s\n", text) continue } fmt.Printf("consuming %dMB\n", sizeMB) return sizeMB } fmt.Printf("error %v\n", err) } } func work() { fmt.Printf("Program periodically records following gauge metrics.\n") fmt.Printf(" 1. process_heap_alloc = the heap allocation (used + freed but not garbage collected)\n") fmt.Printf(" 2. process_idle_to_alloc_ratio = heap idle (unused) /allocation ratio\n") fmt.Printf("\nGo to file://%s to see the metrics. OR do `tail -f %s` in another terminal\n\n\n", metricsLogFile, metricsLogFile) fmt.Printf("Enter memory you would like to allocate in MB to change the value of above metrics.\n") // Do some work and record gauge metrics. for { sizeMB := getInput() doSomeWork(sizeMB) fmt.Printf("press CTRL+C to terminate the program\n") } } func main() { // Using log exporter to export metrics but you can choose any supported exporter. exporter, err := exporter.NewLogExporter(exporter.Options{ ReportingInterval: time.Duration(10 * time.Second), MetricsLogFile: metricsLogFile, }) if err != nil { log.Fatalf("Error creating log exporter: %v", err) } exporter.Start() defer exporter.Stop() defer exporter.Close() // Create metric registry and register it with global producer manager. // START reg r := metric.NewRegistry() metricproducer.GlobalManager().AddProducer(r) // END reg // Create Int64Gauge to report memory usage of a process. // START alloc allocGauge, err := r.AddInt64Gauge( "process_heap_alloc", metric.WithDescription("Process heap allocation"), metric.WithUnit(metricdata.UnitBytes)) if err != nil { log.Fatalf("error creating heap allocation gauge, error %v\n", err) } // END alloc // START entryAlloc allocEntry, err = allocGauge.GetEntry() if err != nil { log.Fatalf("error getting heap allocation gauge entry, error %v\n", err) } // END entryAlloc // Create Float64Gauge to report fractional cpu consumed by Garbage Collection. // START idle ratioGauge, err := r.AddFloat64Gauge( "process_heap_idle_to_alloc_ratio", metric.WithDescription("process heap idle to allocate ratio"), metric.WithUnit(metricdata.UnitDimensionless)) if err != nil { log.Fatalf("error creating process heap idle to allocate ratio gauge, error %v\n", err) } // END idle // START entryIdle ratioEntry, err = ratioGauge.GetEntry() if err != nil { log.Fatalf("error getting process heap idle to allocate ratio gauge entry, error %v\n", err) } // END entryIdle // record gauge metrics every 5 seconds. This example records the gauges periodically. However, // depending on the application it can be non-periodic and can be recorded at any time. done := make(chan int) defer close(done) go recordMetrics(1, done) // do your work. work() } // END entire