blob: 287d0b202e11ba9108461955bfe3a4d41fb68454 [file] [log] [blame]
// 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 prometheus 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"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
"go.opencensus.io/exporter/prometheus"
"go.opencensus.io/metric"
"go.opencensus.io/metric/metricdata"
"go.opencensus.io/metric/metricproducer"
)
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 http://localhost:9090/metrics to see the metrics.\n\n\n")
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 createAndStartExporter() {
// Create Prometheus metrics exporter to verify gauge metrics in this example.
exporter, err := prometheus.NewExporter(prometheus.Options{})
if err != nil {
log.Fatalf("Failed to create the prometheus metrics exporter: %v", err)
}
http.Handle("/metrics", exporter)
go func() {
log.Fatal(http.ListenAndServe(":9090", nil))
}()
}
func main() {
createAndStartExporter()
// 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