Skip to content

Instantly share code, notes, and snippets.

@graphaelli
Created March 23, 2018 21:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save graphaelli/43ae22d3cff33f71bb3ecca0237d11a4 to your computer and use it in GitHub Desktop.
Save graphaelli/43ae22d3cff33f71bb3ecca0237d11a4 to your computer and use it in GitHub Desktop.
messing around with the prometheus client
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
[prune]
go-tests = true
unused-packages = true
[[constraint]]
name = "github.com/prometheus/client_golang"
version = "0.8.0"
[[constraint]]
branch = "master"
name = "github.com/prometheus/common"
package main
import (
"expvar"
"flag"
"fmt"
"log"
"math/rand"
"net"
"net/http"
"net/http/pprof"
"os"
"strconv"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
"github.com/rcrowley/go-metrics"
)
type responseWriter struct {
http.ResponseWriter
code int
}
func (w *responseWriter) WriteHeader(code int) {
w.code = code
w.ResponseWriter.WriteHeader(code)
}
func timeit(f func()) time.Duration {
ts := time.Now()
f()
return time.Now().Sub(ts)
}
func logHandler(logger *log.Logger, route string, h http.Handler) http.Handler {
// prometheus
reqprom := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "http_request",
Help: "http request details",
ConstLabels: map[string]string{"route": route},
},
)
resprom := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_response",
Help: "http response details",
ConstLabels: map[string]string{"route": route},
},
[]string{"code", "method", "url"},
)
prometheus.MustRegister(reqprom, resprom)
// go-metrics
reqm := metrics.GetOrRegisterMeter("http.request.count", nil)
resm := metrics.GetOrRegisterMeter("http.response.count", nil)
t := metrics.GetOrRegisterTimer("http.response.time", nil)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// prometheus
reqprom.Add(1)
// go-metrics
reqm.Mark(1)
rw := &responseWriter{
ResponseWriter: w,
code: http.StatusOK, // WriteHeader not called by default
}
dur := timeit(func() {
h.ServeHTTP(rw, r)
})
logger.Println(r.URL, rw.code, dur)
// prometheus
resprom.WithLabelValues(strconv.Itoa(rw.code), r.Method, r.URL.Path).Add(1)
// go-metrics
t.Update(dur)
resm.Mark(1)
c := metrics.GetOrRegisterCounter(fmt.Sprintf("http.response.code.%d", rw.code), nil)
c.Inc(1)
})
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
// work
time.Sleep(100*time.Millisecond + time.Duration(rand.Intn(1000)) * time.Millisecond)
fmt.Fprint(w, "OK")
return
}
p := strings.Trim(r.URL.Path, "/")
if i, err := strconv.Atoi(p); err == nil && i >= 200 && i < 600 {
http.Error(w, http.StatusText(i), i)
return
}
http.NotFound(w, r)
}
func main() {
var port = flag.Int("port", 1234, "port")
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port))
if err != nil {
panic(err)
}
logger := log.New(os.Stderr, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)
// prometheus
var pmLogger log.Logger
pmLogger = *logger
pmLogger.SetPrefix("[prometheus] ")
go func() {
ticker := time.NewTicker(10 * time.Second)
g := prometheus.DefaultGatherer
var mfs []*io_prometheus_client.MetricFamily
var vec model.Vector
for range ticker.C {
if mfs, err = g.Gather(); err != nil {
pmLogger.Println("while gathering", err)
continue
}
for _, mf := range mfs {
pmLogger.Println(strings.ToLower((mf.Type).String()), *mf.Name)
if vec, err = expfmt.ExtractSamples(&expfmt.DecodeOptions{}, mf); err != nil {
pmLogger.Println("while extracting", err)
continue
}
for _, sample := range vec {
pmLogger.Println(" ", sample)
}
}
}
}()
// go-metrics
var gmLogger log.Logger
gmLogger = *logger
gmLogger.SetPrefix("[go-metrics] ")
go metrics.LogScaled(metrics.DefaultRegistry, 10*time.Second, time.Millisecond, &gmLogger)
url := fmt.Sprintf("http://%s", lis.Addr())
logger.Printf("listening on %s", url )
go func() {
ticker := time.NewTicker(2*time.Second)
for range ticker.C{
rsp, _ := http.Get(url)
rsp.Body.Close()
}
}()
mux := http.NewServeMux()
reg := func(pattern string, handler http.Handler) {
mux.Handle(pattern, logHandler(logger, pattern, handler))
}
reg("/debug/vars", expvar.Handler())
reg("/debug/pprof/", http.HandlerFunc(pprof.Index))
reg("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
reg("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
reg("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
reg("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
reg("/metrics", promhttp.Handler())
reg("/", http.HandlerFunc(handler))
server := &http.Server{
Handler: mux,
}
log.Fatal(server.Serve(lis))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment