首页 > 解决方案 > 除指标端点外,无法抓取 Prometheus http 指标

问题描述

我正在尝试在 Go 中抓取 Prometheus http 指标。我只能抓取“/metrics”而不是其他两个端点“/ok”和“/world”。我正在使用下面的源代码。

package main

import (
    "bufio"
    "fmt"
    "github.com/go-chi/chi/middleware"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "io"
    "math/rand"

    //"math/rand"

    //"math/rand"
    "net"
    "net/http"
    "strconv"
    //"strings"
    "time"

    //"github.com/go-chi/chi"

    //chiprometheus "github.com/766b/chi-prometheus"
    "github.com/felixge/httpsnoop"
    //"github.com/prometheus/client_golang/prometheus"
)

func Instrument(next http.Handler) http.Handler {
    buckets := []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
    responseTimeHistogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{
        Namespace: "prometheus",
        Name:      "http_server_request_duration_seconds",
        Help:      "Histogram of response time for handler in seconds",
        Buckets:   buckets,
    }, []string{"method", "code"})
    prometheus.MustRegister(responseTimeHistogram)
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        m := httpsnoop.CaptureMetrics(next, w, r)
        duration := float64(m.Duration / time.Millisecond)

        //rctx := chi.RouteContext(r.Context())
        //routePattern := strings.Join(rctx.RoutePatterns, "")
        //routePattern = strings.Replace(routePattern, "/*/", "/", -1)

        responseTimeHistogram.WithLabelValues(
            r.Method, strconv.Itoa(m.Code),
        ).Observe(duration)
    })
}


// NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to
// hook into various parts of the response process.
func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter {
    _, fl := w.(http.Flusher)

    bw := basicWriter{ResponseWriter: w}

    if protoMajor == 2 {
        _, ps := w.(http.Pusher)
        if fl || ps {
            return &http2FancyWriter{bw}
        }
    } else {
        _, hj := w.(http.Hijacker)
        _, rf := w.(io.ReaderFrom)
        if fl || hj || rf {
            return &httpFancyWriter{bw}
        }
    }

    return &bw
}

// WrapResponseWriter is a proxy around an http.ResponseWriter that allows you to hook
// into various parts of the response process.
type WrapResponseWriter interface {
    http.ResponseWriter
    // Status returns the HTTP status of the request, or 0 if one has not
    // yet been sent.
    Status() int
    // BytesWritten returns the total number of bytes sent to the client.
    BytesWritten() int
    // Tee causes the response body to be written to the given io.Writer in
    // addition to proxying the writes through. Only one io.Writer can be
    // tee'd to at once: setting a second one will overwrite the first.
    // Writes will be sent to the proxy before being written to this
    // io.Writer. It is illegal for the tee'd writer to be modified
    // concurrently with writes.
    Tee(io.Writer)
    // Unwrap returns the original proxied target.
    Unwrap() http.ResponseWriter
}

// basicWriter wraps a http.ResponseWriter that implements the minimal
// http.ResponseWriter interface.
type basicWriter struct {
    http.ResponseWriter
    wroteHeader bool
    code        int
    bytes       int
    tee         io.Writer
}

func (b *basicWriter) WriteHeader(code int) {
    if !b.wroteHeader {
        b.code = code
        b.wroteHeader = true
        b.ResponseWriter.WriteHeader(code)
    }
}

func (b *basicWriter) Write(buf []byte) (int, error) {
    b.maybeWriteHeader()
    n, err := b.ResponseWriter.Write(buf)
    if b.tee != nil {
        _, err2 := b.tee.Write(buf[:n])
        // Prefer errors generated by the proxied writer.
        if err == nil {
            err = err2
        }
    }
    b.bytes += n
    return n, err
}

func (b *basicWriter) maybeWriteHeader() {
    if !b.wroteHeader {
        b.WriteHeader(http.StatusOK)
    }
}

func (b *basicWriter) Status() int {
    return b.code
}

func (b *basicWriter) BytesWritten() int {
    return b.bytes
}

func (b *basicWriter) Tee(w io.Writer) {
    b.tee = w
}

func (b *basicWriter) Unwrap() http.ResponseWriter {
    return b.ResponseWriter
}

// httpFancyWriter is a HTTP writer that additionally satisfies
// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
// of wrapping the http.ResponseWriter that package http gives you, in order to
// make the proxied object support the full method set of the proxied object.
type httpFancyWriter struct {
    basicWriter
}

func (f *httpFancyWriter) Flush() {
    f.wroteHeader = true
    fl := f.basicWriter.ResponseWriter.(http.Flusher)
    fl.Flush()
}

func (f *httpFancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
    hj := f.basicWriter.ResponseWriter.(http.Hijacker)
    return hj.Hijack()
}

func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) {
    if f.basicWriter.tee != nil {
        n, err := io.Copy(&f.basicWriter, r)
        f.basicWriter.bytes += int(n)
        return n, err
    }
    rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
    f.basicWriter.maybeWriteHeader()
    n, err := rf.ReadFrom(r)
    f.basicWriter.bytes += int(n)
    return n, err
}

var _ http.Flusher = &httpFancyWriter{}
var _ http.Hijacker = &httpFancyWriter{}
var _ io.ReaderFrom = &httpFancyWriter{}

// http2FancyWriter is a HTTP2 writer that additionally satisfies
// http.Flusher, and io.ReaderFrom. It exists for the common case
// of wrapping the http.ResponseWriter that package http gives you, in order to
// make the proxied object support the full method set of the proxied object.
type http2FancyWriter struct {
    basicWriter
}

func (f *http2FancyWriter) Flush() {
    f.wroteHeader = true
    fl := f.basicWriter.ResponseWriter.(http.Flusher)
    fl.Flush()
}

func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error {
    return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts)
}

var _ http.Flusher = &http2FancyWriter{}
var _ http.Pusher = &http2FancyWriter{}

func homeLink(w http.ResponseWriter,r *http.Request){
    fmt.Fprintf(w,"welcome home");
}



func  handler(next http.Handler) http.Handler {
    buckets := []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
    reqs := prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name:        "http_requests_total",
            Help:        "How many HTTP requests processed, partitioned by status code, method and HTTP path.",
            ConstLabels: prometheus.Labels{"service": "hello"},
        },
        []string{"code", "method", "path"},
    )
    prometheus.MustRegister(reqs)

    latency := prometheus.NewHistogramVec(prometheus.HistogramOpts{
        Name:        "http_server_request_duration_seconds",
        Help:        "How long it took to process the request, partitioned by status code, method and HTTP path.",
        ConstLabels: prometheus.Labels{"service": "hello"},
        Buckets:     buckets,
    },
        []string{"code", "method", "path"},
    )
    prometheus.MustRegister(latency)
    fn := func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
        next.ServeHTTP(ww, r)
        reqs.WithLabelValues(http.StatusText(ww.Status()), r.Method, r.URL.Path).Inc()
        latency.WithLabelValues(http.StatusText(ww.Status()), r.Method, r.URL.Path).Observe(float64(time.Since(start).Nanoseconds()) / 1000000)
    }
    return http.HandlerFunc(fn)
}
func main() {
     n := http.NewServeMux()
     n.Handle("/metrics",handler(promhttp.Handler()))
     n.HandleFunc(`/ok`, func(w http.ResponseWriter, r *http.Request) {
        sleep := rand.Intn(4999) + 1
        time.Sleep(time.Duration(sleep) * time.Millisecond)
        w.WriteHeader(http.StatusOK)

        w.Write([]byte("200 - Something bad happened!"))
        fmt.Fprintf(w, "slept %d milliseconds\n", sleep)
    })

    http.HandleFunc("/world",homeLink)
    http.ListenAndServe(":3001", n)
}

谁能建议我如何获取“/metrics”以外的端点的指标。还有一个问题我可以从在不同端口和不同文件中运行的应用程序中抓取指标吗?

标签: goprometheus

解决方案


推荐阅读