See Part 1 for the Custom Metric Setup
2. Collect and Harvest Custom Metric with Golang
For this example, we're trivally tracking the line count. It can be expanded
to parse success vs error lines and send those as two metrics, e.g.
backup-count
vs error-backup-count
The code below creates a 5-sec timer which counts the file's lines and sends
that count to stackdriver. stackdriver reporting will allow you to run
statistics e.g. avg() & max() and allow you to see the backup-count growth
over time
package main
import (
"bytes"
"context"
"fmt"
"io"
"log"
"os"
"time"
monitoring "cloud.google.com/go/monitoring/apiv3"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
metricpb "google.golang.org/genproto/googleapis/api/metric"
monitoredres "google.golang.org/genproto/googleapis/api/monitoredres"
monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3"
)
const metricType = "custom.googleapis.com/photos/backup-count"
const (
projectID = "YOUR-GCP-PROJECT-ID"
instanceID = "INSTANCE-ID-FOR-TESTING"
INTERVAL = 3
zone = "us-east1-c"
)
func writeTimeSeriesValue(projectID, metricType string, value int) error {
ctx := context.Background()
c, err := monitoring.NewMetricClient(ctx)
if err != nil {
return err
}
now := ×tamp.Timestamp{
Seconds: time.Now().Unix(),
}
req := &monitoringpb.CreateTimeSeriesRequest{
Name: "projects/" + projectID,
TimeSeries: []*monitoringpb.TimeSeries{{
Metric: &metricpb.Metric{
Type: metricType,
},
Resource: &monitoredres.MonitoredResource{
Type: "gce_instance",
Labels: map[string]string{
"project_id": projectID,
"instance_id": instanceID,
"zone": zone,
},
},
Points: []*monitoringpb.Point{{
Interval: &monitoringpb.TimeInterval{
StartTime: now,
EndTime: now,
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_Int64Value{
Int64Value: int64(value),
},
},
}},
}},
}
log.Printf("writeTimeseriesRequest: %+v\n", req)
err = c.CreateTimeSeries(ctx, req)
if err != nil {
return fmt.Errorf("could not write time series value, %v ", err)
}
return nil
}
// [END monitoring_read_timeseries_simple]
func LineCounter(r io.Reader) (int, error) {
buf := make([]byte, 32*1024)
count := 0
lineSep := []byte{'\n'}
for {
c, err := r.Read(buf)
count += bytes.Count(buf[:c], lineSep)
switch {
case err == io.EOF:
return count, nil
case err != nil:
return count, err
}
}
}
// given interval, return the # of lines in the file
func WatchFile(interval time.Duration, filename string) <-chan int {
handle, err := os.Open(filename)
ticker := time.NewTicker(interval)
sizeChan := make(chan int)
go func() {
for _ = range ticker.C {
lineCount, _ := LineCounter(handle)
sizeChan <- lineCount
if err != nil {
fmt.Println("error")
}
handle.Seek(0, 0)
}
}()
return sizeChan
}
func main() {
if len(os.Args) < 2 {
fmt.Printf("%s FILENAME\n", os.Args[0])
os.Exit(-1)
}
sizeChan := WatchFile(INTERVAL*time.Second, os.Args[1])
for {
select {
case <-sizeChan:
curSize := <-sizeChan
fmt.Printf("Lines in %s : %d\n", os.Args[1], curSize)
writeTimeSeriesValue(projectID, metricType, curSize)
}
}
}
Top comments (1)
Thanks, this really helped me out.