Gauges Example

Table of Contents

Summary

top

This example shows how to use gauge metrics. The program records two gauges.

  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. Metrics can be viewed at file:///tmp/metrics.log once the program is running. Alternatively you could do tail -f /tmp/metrics.log on Linux/OSx.

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.

Run the example

$ go get go.opencensus.io/examples/gauges/...

then:

$ go run $(go env GOPATH)/src/go.opencensus.io/examples/gauges/gauge.go

How to use gauges?

Initialize Metric Registry

Create a new metric registry for all your metrics. This step is a general step for any kind of metrics and not specific to gauges. Register newly created registry with global producer manager.

r := metric.NewRegistry()
metricproducer.GlobalManager().AddProducer(r)

Create gauge metric

Create a gauge metric. In this example we have two metrics.

process_heap_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)
}

process_heap_idle_to_alloc_ratio

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)
}

Create gauge entry

Now, create or get a unique entry (equivalent of a row in a table) for a given set of tags. Since we are not using any tags in this example we only have one entry for each gauge metric.

entry for process_heap_alloc

allocEntry, err = allocGauge.GetEntry()
if err != nil {
	log.Fatalf("error getting heap allocation gauge entry, error %v\n", err)
}

entry for process_heap_idle_to_alloc_ratio

ratioEntry, err = ratioGauge.GetEntry()
if err != nil {
	log.Fatalf("error getting process heap idle to allocate ratio gauge entry, error %v\n", err)
}

Set gauge values

Use Set or Add function to update the value of gauge entries. You can call these methods anytime based on your metric and your application. In this example, Set is called periodically.

			allocEntry.Set(int64(getAlloc()))     // int64 gauge
			ratioEntry.Set(getIdleToAllocRatio()) // float64 gauge

Complete Example


// 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. allocEntry.Set(int64(getAlloc())) // int64 gauge ratioEntry.Set(getIdleToAllocRatio()) // float64 gauge } } } 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. r := metric.NewRegistry() metricproducer.GlobalManager().AddProducer(r) // Create Int64Gauge to report memory usage of a process. 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) } allocEntry, err = allocGauge.GetEntry() if err != nil { log.Fatalf("error getting heap allocation gauge entry, error %v\n", err) } // Create Float64Gauge to report fractional cpu consumed by Garbage Collection. 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) } ratioEntry, err = ratioGauge.GetEntry() if err != nil { log.Fatalf("error getting process heap idle to allocate ratio gauge entry, error %v\n", err) } // 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() }