Coder Social home page Coder Social logo

negroni-prometheus's Introduction

negroni-prometheus GoDoc Go Report Card

Prometheus middleware for negroni.

Warning

THIS PROJECT IS ARCHIVED AND IS NO LONGER MAINTAINED SINCE I DO NOT USE NEGRONI ANYMORE.

Why

Logging v. instrumentation

Instead of logging request times, it is considered best practice to provide an endpoint for instrumentation tools (like prometheus).

Installation

$ go get github.com/zbindenren/negroni-prometheus

Usage

Use this middleware like the negroni.Logger middleware (after negroni.Recovery, before every other middleware).

Take a look at the example.

What do you get

An endpoint with the following information (stripped output):

...
# HELP negroni_request_duration_milliseconds How long it took to process the request, partitioned by status code, method and HTTP path.
# TYPE negroni_request_duration_milliseconds histogram
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/metrics",service="serviceName",le="300"} 1
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/metrics",service="serviceName",le="1200"} 1
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/metrics",service="serviceName",le="5000"} 1
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/metrics",service="serviceName",le="+Inf"} 1
negroni_request_duration_milliseconds_sum{code="OK",method="GET",path="/metrics",service="serviceName"} 2.003123
negroni_request_duration_milliseconds_count{code="OK",method="GET",path="/metrics",service="serviceName"} 1
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/ok",service="serviceName",le="300"} 0
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/ok",service="serviceName",le="1200"} 0
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/ok",service="serviceName",le="5000"} 2
negroni_request_duration_milliseconds_bucket{code="OK",method="GET",path="/ok",service="serviceName",le="+Inf"} 2
negroni_request_duration_milliseconds_sum{code="OK",method="GET",path="/ok",service="serviceName"} 4747.529026
negroni_request_duration_milliseconds_count{code="OK",method="GET",path="/ok",service="serviceName"} 2
# HELP negroni_requests_total How many HTTP requests processed, partitioned by status code, method and HTTP path.
# TYPE negroni_requests_total counter
negroni_requests_total{code="OK",method="GET",path="/metrics",service="serviceName"} 1
negroni_requests_total{code="OK",method="GET",path="/ok",service="serviceName"} 2
...

negroni-prometheus's People

Contributors

augier avatar zbindenren avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

negroni-prometheus's Issues

DoS attack protection

Please force user to filter routes by supplying []string or map[string]filterFunc

otherwise every dynamically-generated route will be added to Prometheus label, even routes that were not registered, but returned 404.

This could easily cause performance issues on Prometheus side.
https://prometheus.io/docs/practices/naming/

CAUTION: Remember that every unique combination of key-value label pairs represents a new time series, which can dramatically increase the amount of data stored. Do not use labels to store dimensions with high cardinality (many different label values), such as user IDs, email addresses, or other unbounded sets of values.

I've done it like this^

func NewMiddleware(name string, routes []string, buckets ...float64) *Middleware {
	m.routes = routes
func (m *Middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        start := time.Now()
	res := negroni.NewResponseWriter(rw)
	next(res, r)
	url := "other"
	for i, u := range m.routes {
		if strings.HasPrefix(r.URL.Path, u) {
			url = u
			break
		}
	}
	m.reqs.WithLabelValues(http.StatusText(res.Status()), r.Method, url).Inc()

Also note the usage of res and rw variables - you should pass res to middleware, not an original writer.

"code" metric label is always empty

Hi

Something seems wrong with the code metric label exposed by your middleware, as when querying the /metrics endpoint the value for this label is always empty. Considering the following dummy HTTP API code:

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/urfave/negroni"
	"github.com/zbindenren/negroni-prometheus"
)

func main() {
	router := mux.NewRouter()

	router.HandleFunc("/a", handleA).
		Methods("GET", "POST", "PUT")

	router.HandleFunc("/b", handleB).
		Methods("GET")

	router.HandleFunc("/c", handleC).
		Methods("GET")

	router.Handle("/metrics", promhttp.Handler()).
		Methods("GET")

	n := negroni.New()

	n.UseHandler(router)
	n.Use(negroniprometheus.NewMiddleware("flapi", 0.001, 0.01, 0.1, 1, 5, 10))

	http.ListenAndServe(":8000", n)
}

func handleDefault(rw http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(rw, "Default")
}

func handleA(rw http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "POST", "PUT":
		time.Sleep(100 * time.Millisecond)

	case "GET":
		time.Sleep(10 * time.Millisecond)
	}

	fmt.Fprintf(rw, "A\n")
}

func handleB(rw http.ResponseWriter, r *http.Request) {
	time.Sleep(50 * time.Millisecond)
	fmt.Fprintf(rw, "B\n")
}

func handleC(rw http.ResponseWriter, r *http.Request) {
	time.Sleep(10 * time.Millisecond)
	fmt.Fprintf(rw, "C\n")
}

type MetricsHandler struct {
}

After sending some HTTP requests, here is the result of the /metrics endpoint:

HTTP Requests:

$ curl -i -X POST localhost:8000/a
HTTP/1.1 200 OK
Date: Thu, 14 Dec 2017 12:29:13 GMT
Content-Length: 2
Content-Type: text/plain; charset=utf-8

A

$ curl -i localhost:8000/b
HTTP/1.1 200 OK
Date: Thu, 14 Dec 2017 12:29:18 GMT
Content-Length: 2
Content-Type: text/plain; charset=utf-8

B

$ curl -i localhost:8000/c
HTTP/1.1 200 OK
Date: Thu, 14 Dec 2017 12:29:22 GMT
Content-Length: 2
Content-Type: text/plain; charset=utf-8

C

$ curl -i -X POST localhost:8000/c
HTTP/1.1 405 Method Not Allowed
Date: Thu, 14 Dec 2017 12:31:51 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8

Prometheus /metrics endpoint query result:

$ curl -i localhost:8000/metrics
HTTP/1.1 200 OK
Content-Length: 9866
Content-Type: text/plain; version=0.0.4
Date: Thu, 14 Dec 2017 12:31:52 GMT

# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 7
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.9.2"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 611008
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 611008
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 2750
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 458
# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started.
# TYPE go_memstats_gc_cpu_fraction gauge
go_memstats_gc_cpu_fraction 0
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
# TYPE go_memstats_gc_sys_bytes gauge
go_memstats_gc_sys_bytes 137216
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
# TYPE go_memstats_heap_alloc_bytes gauge
go_memstats_heap_alloc_bytes 611008
# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used.
# TYPE go_memstats_heap_idle_bytes gauge
go_memstats_heap_idle_bytes 65536
# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use.
# TYPE go_memstats_heap_inuse_bytes gauge
go_memstats_heap_inuse_bytes 1.671168e+06
# HELP go_memstats_heap_objects Number of allocated objects.
# TYPE go_memstats_heap_objects gauge
go_memstats_heap_objects 7006
# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS.
# TYPE go_memstats_heap_released_bytes gauge
go_memstats_heap_released_bytes 0
# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system.
# TYPE go_memstats_heap_sys_bytes gauge
go_memstats_heap_sys_bytes 1.736704e+06
# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection.
# TYPE go_memstats_last_gc_time_seconds gauge
go_memstats_last_gc_time_seconds 0
# HELP go_memstats_lookups_total Total number of pointer lookups.
# TYPE go_memstats_lookups_total counter
go_memstats_lookups_total 15
# HELP go_memstats_mallocs_total Total number of mallocs.
# TYPE go_memstats_mallocs_total counter
go_memstats_mallocs_total 7464
# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures.
# TYPE go_memstats_mcache_inuse_bytes gauge
go_memstats_mcache_inuse_bytes 6944
# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system.
# TYPE go_memstats_mcache_sys_bytes gauge
go_memstats_mcache_sys_bytes 16384
# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures.
# TYPE go_memstats_mspan_inuse_bytes gauge
go_memstats_mspan_inuse_bytes 27208
# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system.
# TYPE go_memstats_mspan_sys_bytes gauge
go_memstats_mspan_sys_bytes 32768
# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place.
# TYPE go_memstats_next_gc_bytes gauge
go_memstats_next_gc_bytes 4.473924e+06
# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations.
# TYPE go_memstats_other_sys_bytes gauge
go_memstats_other_sys_bytes 798018
# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator.
# TYPE go_memstats_stack_inuse_bytes gauge
go_memstats_stack_inuse_bytes 360448
# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator.
# TYPE go_memstats_stack_sys_bytes gauge
go_memstats_stack_sys_bytes 360448
# HELP go_memstats_sys_bytes Number of bytes obtained from system.
# TYPE go_memstats_sys_bytes gauge
go_memstats_sys_bytes 3.084288e+06
# HELP go_threads Number of OS threads created.
# TYPE go_threads gauge
go_threads 7
# HELP negroni_request_duration_milliseconds How long it took to process the request, partitioned by status code, method and HTTP path.
# TYPE negroni_request_duration_milliseconds histogram
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/b",service="flapi",le="0.001"} 0
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/b",service="flapi",le="0.01"} 0
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/b",service="flapi",le="0.1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/b",service="flapi",le="1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/b",service="flapi",le="5"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/b",service="flapi",le="10"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/b",service="flapi",le="+Inf"} 1
negroni_request_duration_milliseconds_sum{code="",method="GET",path="/b",service="flapi"} 0.044683
negroni_request_duration_milliseconds_count{code="",method="GET",path="/b",service="flapi"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/c",service="flapi",le="0.001"} 0
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/c",service="flapi",le="0.01"} 0
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/c",service="flapi",le="0.1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/c",service="flapi",le="1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/c",service="flapi",le="5"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/c",service="flapi",le="10"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/c",service="flapi",le="+Inf"} 1
negroni_request_duration_milliseconds_sum{code="",method="GET",path="/c",service="flapi"} 0.028517
negroni_request_duration_milliseconds_count{code="",method="GET",path="/c",service="flapi"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/metrics",service="flapi",le="0.001"} 0
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/metrics",service="flapi",le="0.01"} 0
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/metrics",service="flapi",le="0.1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/metrics",service="flapi",le="1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/metrics",service="flapi",le="5"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/metrics",service="flapi",le="10"} 1
negroni_request_duration_milliseconds_bucket{code="",method="GET",path="/metrics",service="flapi",le="+Inf"} 1
negroni_request_duration_milliseconds_sum{code="",method="GET",path="/metrics",service="flapi"} 0.011603
negroni_request_duration_milliseconds_count{code="",method="GET",path="/metrics",service="flapi"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/a",service="flapi",le="0.001"} 0
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/a",service="flapi",le="0.01"} 0
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/a",service="flapi",le="0.1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/a",service="flapi",le="1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/a",service="flapi",le="5"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/a",service="flapi",le="10"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/a",service="flapi",le="+Inf"} 1
negroni_request_duration_milliseconds_sum{code="",method="POST",path="/a",service="flapi"} 0.0384
negroni_request_duration_milliseconds_count{code="",method="POST",path="/a",service="flapi"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/c",service="flapi",le="0.001"} 0
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/c",service="flapi",le="0.01"} 0
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/c",service="flapi",le="0.1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/c",service="flapi",le="1"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/c",service="flapi",le="5"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/c",service="flapi",le="10"} 1
negroni_request_duration_milliseconds_bucket{code="",method="POST",path="/c",service="flapi",le="+Inf"} 1
negroni_request_duration_milliseconds_sum{code="",method="POST",path="/c",service="flapi"} 0.022877
negroni_request_duration_milliseconds_count{code="",method="POST",path="/c",service="flapi"} 1
# HELP negroni_requests_total How many HTTP requests processed, partitioned by status code, method and HTTP path.
# TYPE negroni_requests_total counter
negroni_requests_total{code="",method="GET",path="/b",service="flapi"} 1
negroni_requests_total{code="",method="GET",path="/c",service="flapi"} 1
negroni_requests_total{code="",method="GET",path="/metrics",service="flapi"} 1
negroni_requests_total{code="",method="POST",path="/a",service="flapi"} 1
negroni_requests_total{code="",method="POST",path="/c",service="flapi"} 1

Is there something wrong in my implementation? Let me know if you need me to provide details about my environment.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.