go - 除指标端点外,无法抓取 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”以外的端点的指标。还有一个问题我可以从在不同端口和不同文件中运行的应用程序中抓取指标吗?
解决方案
推荐阅读
- uber-api - UBER - API/Deeeplink - 是否有添加多个停靠点的参数,比如pickup-location > stop > drop-off,就像在应用程序中一样?
- php - 条纹计划、客户、订阅创建。它不会创建重复项吗?
- python - 如何使用烧瓶下载 PDF
- django - 在 django-filter 表单上添加标签或修改小部件
- rust - 如何在 Rust 中禁用未使用的变量警告?
- asp.net-core - 防止每个类 AuthorizationHandler
- flutter - 如何在 Flutter 中使用 API 填充数据行?
- javascript - PixelArt 挑战:无法在“窗口”上执行“getComputedStyle”:参数 1 不是“元素”类型
- apache-spark - 向 Spark 集群提交 Gremlin OLAP 遍历查询并写入遍历结果而不是整个图
- github - 代码在 VSCode 中编译,但不在 ubuntu 服务器上