observability/workload-metrics/main.go (90 lines of code) (raw):
// # Copyright 2021 Google LLC
// #
// # 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.
package main
import (
"flag"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
)
var (
logger, _ = zap.NewProduction()
reg = prometheus.NewRegistry()
requestCount = promauto.With(reg).NewCounterVec(
prometheus.CounterOpts{
Name: "example_requests_total",
Help: "Total number of HTTP requests by status code and method.",
},
[]string{"code", "method"},
)
histogram = promauto.With(reg).NewHistogram(prometheus.HistogramOpts{
Name: "example_random_numbers",
Help: "A histogram of normally distributed random numbers.",
// TODO: support negative bounds
// Buckets: prometheus.LinearBuckets(-3, .1, 61),
Buckets: prometheus.LinearBuckets(0, .1, 61),
})
)
// Generates random data for a histogram
func Random() {
logger.Sugar().Info("Started number generator")
for {
val := rand.NormFloat64()
if val < 0 {
val = -val
}
histogram.Observe(val)
}
}
// PollItself polls the HTTP endpoint to generate synthetic "traffic"
func PollItself() {
for {
resp, err := http.Get("http://localhost:1234/")
if err != nil {
logger.Sugar().Errorf("HTTP request failed: %w", err)
} else {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logger.Sugar().Errorf("Failed to read response: %w", err)
} else {
logger.Sugar().Debugf("Response: %s", string(body))
_ = resp.Body.Close()
}
}
time.Sleep(time.Second * time.Duration(rand.Intn(3)))
}
}
func parseFlags() {
enableProcessMetrics := flag.Bool("process-metrics", false, "Enables process metrics")
enableGoMetrics := flag.Bool("go-metrics", false, "Enables Go metrics")
flag.Parse()
if *enableProcessMetrics {
reg.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
}
if *enableGoMetrics {
reg.MustRegister(prometheus.NewGoCollector())
}
}
func main() {
parseFlags()
go Random()
go PollItself()
// Example HTTP handler
http.Handle("/", promhttp.InstrumentHandlerCounter(
requestCount,
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
random := rand.Intn(100)
if random < 20 {
w.WriteHeader(500)
_, _ = fmt.Fprint(w, "Something went wrong :(")
return
}
_, _ = fmt.Fprint(w, "Hello, world!")
}),
))
// Expose Prometheus metrics
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
logger.Info("Starting HTTP server")
err := http.ListenAndServe(":1234", nil)
if err != nil {
logger.Sugar().Errorf("ListenAndServe failed: %w", err)
}
}