-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
agent: add per-endpoint proxy metrics (#220)
Fixes #216, where if the agent runs multiple endpoints, it attempts to register the same metrics multiple times so panics. This extends the proxy metrics to include an endpoint label, such as: ``` piko_agent_requests_total{endpoint="endpoint1",method="GET",status="200"} 1034 piko_agent_requests_total{endpoint="endpoint2",method="GET",status="200"} 1234 ``` It also registers the metrics only once to avoid panicing.
- Loading branch information
1 parent
6ace8bf
commit c1c4fdf
Showing
3 changed files
with
142 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package reverseproxy | ||
|
||
import ( | ||
"net/http" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/prometheus/client_golang/prometheus" | ||
) | ||
|
||
type Metrics struct { | ||
RequestsInFlight *prometheus.GaugeVec | ||
RequestsTotal *prometheus.CounterVec | ||
RequestLatency *prometheus.HistogramVec | ||
RequestSize *prometheus.HistogramVec | ||
ResponseSize *prometheus.HistogramVec | ||
} | ||
|
||
func NewMetrics(subsystem string) *Metrics { | ||
sizeBuckets := prometheus.ExponentialBuckets(256, 4, 8) | ||
return &Metrics{ | ||
RequestsInFlight: prometheus.NewGaugeVec( | ||
prometheus.GaugeOpts{ | ||
Namespace: "piko", | ||
Subsystem: subsystem, | ||
Name: "requests_in_flight", | ||
Help: "Number of requests currently handled by this server.", | ||
}, | ||
[]string{"endpoint"}, | ||
), | ||
RequestsTotal: prometheus.NewCounterVec( | ||
prometheus.CounterOpts{ | ||
Namespace: "piko", | ||
Subsystem: subsystem, | ||
Name: "requests_total", | ||
Help: "Total requests.", | ||
}, | ||
[]string{"status", "method", "endpoint"}, | ||
), | ||
RequestLatency: prometheus.NewHistogramVec( | ||
prometheus.HistogramOpts{ | ||
Namespace: "piko", | ||
Subsystem: subsystem, | ||
Name: "request_latency_seconds", | ||
Help: "Request latency.", | ||
Buckets: prometheus.DefBuckets, | ||
}, | ||
[]string{"status", "method", "endpoint"}, | ||
), | ||
RequestSize: prometheus.NewHistogramVec( | ||
prometheus.HistogramOpts{ | ||
Namespace: "piko", | ||
Subsystem: subsystem, | ||
Name: "request_size_bytes", | ||
Help: "Request size", | ||
Buckets: sizeBuckets, | ||
}, | ||
[]string{"endpoint"}, | ||
), | ||
ResponseSize: prometheus.NewHistogramVec( | ||
prometheus.HistogramOpts{ | ||
Namespace: "piko", | ||
Subsystem: subsystem, | ||
Name: "response_size_bytes", | ||
Help: "Response size", | ||
Buckets: sizeBuckets, | ||
}, | ||
[]string{"endpoint"}, | ||
), | ||
} | ||
} | ||
|
||
func (m *Metrics) Handler(endpoint string) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
m.RequestsInFlight.With(prometheus.Labels{ | ||
"endpoint": endpoint, | ||
}).Inc() | ||
defer m.RequestsInFlight.With(prometheus.Labels{ | ||
"endpoint": endpoint, | ||
}).Dec() | ||
|
||
start := time.Now() | ||
|
||
// Process request. | ||
c.Next() | ||
|
||
m.RequestsTotal.With(prometheus.Labels{ | ||
"status": strconv.Itoa(c.Writer.Status()), | ||
"method": c.Request.Method, | ||
"endpoint": endpoint, | ||
}).Inc() | ||
m.RequestLatency.With(prometheus.Labels{ | ||
"status": strconv.Itoa(c.Writer.Status()), | ||
"method": c.Request.Method, | ||
"endpoint": endpoint, | ||
}).Observe(float64(time.Since(start).Milliseconds()) / 1000) | ||
m.RequestSize.With(prometheus.Labels{ | ||
"endpoint": endpoint, | ||
}).Observe(float64(computeApproximateRequestSize(c.Request))) | ||
m.ResponseSize.With(prometheus.Labels{ | ||
"endpoint": endpoint, | ||
}).Observe(float64(c.Writer.Size())) | ||
} | ||
} | ||
|
||
func (m *Metrics) Register(registry *prometheus.Registry) { | ||
registry.MustRegister( | ||
m.RequestsInFlight, | ||
m.RequestsTotal, | ||
m.RequestLatency, | ||
m.RequestSize, | ||
m.ResponseSize, | ||
) | ||
} | ||
|
||
func computeApproximateRequestSize(r *http.Request) int { | ||
s := 0 | ||
if r.URL != nil { | ||
s += len(r.URL.String()) | ||
} | ||
|
||
s += len(r.Method) | ||
s += len(r.Proto) | ||
for name, values := range r.Header { | ||
s += len(name) | ||
for _, value := range values { | ||
s += len(value) | ||
} | ||
} | ||
s += len(r.Host) | ||
|
||
if r.ContentLength != -1 { | ||
s += int(r.ContentLength) | ||
} | ||
return s | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters