Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion metrics/prometheus/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package prometheus

import "github.com/prometheus/client_golang/prometheus"
import (
"github.com/prometheus/client_golang/prometheus"
)

type Config struct {
// Namespace is the prefix that will be set on the metrics, by default it will be empty.
Expand Down Expand Up @@ -47,3 +49,25 @@ func (c *Config) defaults() {
c.MethodLabel = "method"
}
}

type ConfigTrace struct {
// Namespace is the prefix that will be set on the metrics, by default it will be empty.
Namespace string
// DurationBuckets are the buckets used by Prometheus for the HTTP request duration metrics,
// by default uses Prometheus default buckets (from 5ms to 10s).
DetailLatencyBuckets []float64
DNSLatencyBuckets []float64
TLSLatencyBuckets []float64
}

func (c *ConfigTrace) defaults() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

U1000: func (*ConfigTrace).defaults is unused (from unused)

if len(c.DetailLatencyBuckets) == 0 {
c.DetailLatencyBuckets = []float64{.005, .01, .025, .05}
}
if len(c.DNSLatencyBuckets) == 0 {
c.DNSLatencyBuckets = []float64{.005, .01, .025, .05}
}
if len(c.TLSLatencyBuckets) == 0 {
c.TLSLatencyBuckets = []float64{.05, .1, .25, .5}
}
}
89 changes: 89 additions & 0 deletions metrics/prometheus/trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package prometheus

import (
"context"
"time"

"github.com/prometheus/client_golang/prometheus"

"github.com/gol4ng/httpware/metrics"
)

func NewInstrumentTrace(config ConfigTrace) *metrics.InstrumentTrace {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function NewInstrumentTrace has 72 lines of code (exceeds 50 allowed). Consider refactoring.

clientDetailsLatencyVec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: config.Namespace,
Subsystem: "http",
Name: "details_duration_seconds",
Help: "Trace request latency histogram.",
Buckets: config.DetailLatencyBuckets,
},
[]string{"event"},
)
clientDNSLatencyVec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: config.Namespace,
Subsystem: "http",
Name: "dns_duration_seconds",
Help: "Trace dns latency histogram.",
Buckets: config.DNSLatencyBuckets,
},
[]string{"event"},
)
clientTLSLatencyVec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: config.Namespace,
Subsystem: "http",
Name: "tls_duration_seconds",
Help: "Trace tls latency histogram.",
Buckets: config.TLSLatencyBuckets,
},
[]string{"event"},
)

confTrace := &metrics.InstrumentTrace{}

confTrace.DNSStart = func(ctx context.Context, t time.Duration) {
clientDNSLatencyVec.WithLabelValues("dns_start")
}
confTrace.DNSDone = func(ctx context.Context, t time.Duration) {
clientDNSLatencyVec.WithLabelValues("dns_done")
}
confTrace.ConnectStart = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("connect_start")
}
confTrace.ConnectDone = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("connect_done")
}
confTrace.TLSHandshakeStart = func(ctx context.Context, t time.Duration) {
clientTLSLatencyVec.WithLabelValues("tls_handshake_start")
}
confTrace.TLSHandshakeDone = func(ctx context.Context, t time.Duration) {
clientTLSLatencyVec.WithLabelValues("tls_handshake_done")
}
confTrace.GotConn = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("got_conn")
}
confTrace.PutIdleConn = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("put_idle_conn")
}
confTrace.WroteHeaders = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("wrote_headers")
}
confTrace.WroteRequest = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("wrote_request")
}
confTrace.Got100Continue = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("got_100_continue")
}
confTrace.Wait100Continue = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("wait_100_continue")
}
confTrace.GotFirstResponseByte = func(ctx context.Context, t time.Duration) {
clientDetailsLatencyVec.WithLabelValues("got_first_response_byte")
}

prometheus.MustRegister(clientDetailsLatencyVec, clientDNSLatencyVec, clientTLSLatencyVec)

return confTrace
}
22 changes: 22 additions & 0 deletions metrics/trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package metrics

import (
"context"
"time"
)

type InstrumentTrace struct {
DNSStart func(context.Context, time.Duration)
DNSDone func(context.Context, time.Duration)
ConnectStart func(context.Context, time.Duration)
ConnectDone func(context.Context, time.Duration)
TLSHandshakeStart func(context.Context, time.Duration)
TLSHandshakeDone func(context.Context, time.Duration)
GotConn func(context.Context, time.Duration)
PutIdleConn func(context.Context, time.Duration)
WroteHeaders func(context.Context, time.Duration)
WroteRequest func(context.Context, time.Duration)
Got100Continue func(context.Context, time.Duration)
Wait100Continue func(context.Context, time.Duration)
GotFirstResponseByte func(context.Context, time.Duration)
}
90 changes: 90 additions & 0 deletions tripperware/metrics.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package tripperware

import (
"crypto/tls"
"fmt"
"net/http"
"net/http/httptrace"
"strconv"
"time"

Expand Down Expand Up @@ -44,3 +46,91 @@ func Metrics(config *metrics.Config) httpware.Tripperware {
})
}
}

func MetricsTrace(it *metrics.InstrumentTrace) httpware.Tripperware {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function MetricsTrace has 83 lines of code (exceeds 50 allowed). Consider refactoring.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function MetricsTrace has a Cognitive Complexity of 64 (exceeds 20 allowed). Consider refactoring.

return func(next http.RoundTripper) http.RoundTripper {
return httpware.RoundTripFunc(func(req *http.Request) (resp *http.Response, err error) {
start := time.Now()
ctx := req.Context()

trace := &httptrace.ClientTrace{
GotConn: func(_ httptrace.GotConnInfo) {
if it.GotConn != nil {
it.GotConn(ctx, time.Since(start))
}
},
PutIdleConn: func(err error) {
if err != nil {
return
}
if it.PutIdleConn != nil {
it.PutIdleConn(ctx, time.Since(start))
}
},
DNSStart: func(_ httptrace.DNSStartInfo) {
if it.DNSStart != nil {
it.DNSStart(ctx, time.Since(start))
}
},
DNSDone: func(_ httptrace.DNSDoneInfo) {
if it.DNSDone != nil {
it.DNSDone(ctx, time.Since(start))
}
},
ConnectStart: func(_, _ string) {
if it.ConnectStart != nil {
it.ConnectStart(ctx, time.Since(start))
}
},
ConnectDone: func(_, _ string, err error) {
if err != nil {
return
}
if it.ConnectDone != nil {
it.ConnectDone(ctx, time.Since(start))
}
},
GotFirstResponseByte: func() {
if it.GotFirstResponseByte != nil {
it.GotFirstResponseByte(ctx, time.Since(start))
}
},
Got100Continue: func() {
if it.Got100Continue != nil {
it.Got100Continue(ctx, time.Since(start))
}
},
TLSHandshakeStart: func() {
if it.TLSHandshakeStart != nil {
it.TLSHandshakeStart(ctx, time.Since(start))
}
},
TLSHandshakeDone: func(_ tls.ConnectionState, err error) {
if err != nil {
return
}
if it.TLSHandshakeDone != nil {
it.TLSHandshakeDone(ctx, time.Since(start))
}
},
WroteHeaders: func() {
if it.WroteHeaders != nil {
it.WroteHeaders(ctx, time.Since(start))
}
},
Wait100Continue: func() {
if it.Wait100Continue != nil {
it.Wait100Continue(ctx, time.Since(start))
}
},
WroteRequest: func(_ httptrace.WroteRequestInfo) {
if it.WroteRequest != nil {
it.WroteRequest(ctx, time.Since(start))
}
},
}

return next.RoundTrip(req.WithContext(httptrace.WithClientTrace(req.Context(), trace)))
})
}
}