Skip to content

Commit

Permalink
Add histogram metrics and remove deperecated lib (#56)
Browse files Browse the repository at this point in the history
- Remove "io/ioutil" which is deprecated since go-1.16.
- Add histogram metrics as a duplicate of existing summaries. Summaries
  are still required/used for in-app results calculation and reporting
  (when using CLI). But if you scrape/push metrics with Prometheus, you
  now have an option to use summaries or histograms.
  • Loading branch information
adidenko authored Oct 16, 2023
1 parent 1eaf2c3 commit 98c9703
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 9 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.5.0] - 2023-10-11

### Added

- New histogram metrics. Every existing summary Prometheus metric now has a corresponding
histogram metric, with the `_hist_` in the metrics name.

### Removed

- "io/ioutil" library which is deprecated since go-1.16.

## [0.4.3] - 2023-02-28

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Minigun

[![Minigun Version](https://img.shields.io/badge/Minigun-0.4.3-7f187f.svg)](https://github.com/wayfair-incubator/minigun/blob/main/CHANGELOG.md)
[![Minigun Version](https://img.shields.io/badge/Minigun-0.5.0-7f187f.svg)](https://github.com/wayfair-incubator/minigun/blob/main/CHANGELOG.md)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](CODE_OF_CONDUCT.md)

## About The Project
Expand Down
21 changes: 13 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"io"
"math/rand"
"net"
"net/http"
Expand All @@ -35,18 +35,16 @@ import (
)

// Constants and vars
const version = "0.4.3"
const version = "0.5.0"
const workersCannelSize = 1024
const errorBadHTTPCode = "Bad HTTP status code"

var applog *logger.Logger
var workerStatuses []workerStatus
var registry = prometheus.NewRegistry()

// NOTES: histograms have been replaced with summaries. I'm keeping it just in case I decide
// to use histograms again.
// Let's use the same buckets for histograms as NGINX Ingress controller
// var secondsDurationBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}
var secondsDurationBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}

// Objectives for summary distributions
var summaryObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.95: 0.005, 0.99: 0.001}
Expand Down Expand Up @@ -297,31 +295,36 @@ func sendDataHTTP(data []byte, config appConfig, client *http.Client) error {
trace := &httptrace.ClientTrace{
DNSStart: func(dsi httptrace.DNSStartInfo) { dns = time.Now() },
DNSDone: func(ddi httptrace.DNSDoneInfo) {
config.metrics.histDNSDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(dns).Seconds())
config.metrics.summaryDNSDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(dns).Seconds())
applog.Infof("DNS Done: %v\n", time.Since(dns))
applog.Infof("DNS Result: %v\n", ddi.Addrs)
},

TLSHandshakeStart: func() { tlsHandshake = time.Now() },
TLSHandshakeDone: func(cs tls.ConnectionState, err error) {
config.metrics.histTLSHandshakeDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(tlsHandshake).Seconds())
config.metrics.summaryTLSHandshakeDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(tlsHandshake).Seconds())
applog.Infof("TLS Handshake: %v\n", time.Since(tlsHandshake))
},

ConnectStart: func(network, addr string) { connect = time.Now() },
ConnectDone: func(network, addr string, err error) {
config.metrics.histConnectDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(connect).Seconds())
config.metrics.summaryConnectDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(connect).Seconds())
applog.Infof("Connect time: %v\n", time.Since(connect))
},

WroteHeaders: func() { headers = time.Now() },
WroteRequest: func(wri httptrace.WroteRequestInfo) {
config.metrics.histWroteRequestBodyDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(headers).Seconds())
config.metrics.summaryWroteRequestBodyDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(headers).Seconds())
wroteRequest = time.Now()
applog.Infof("Wrote request body time: %v\n", time.Since(headers))
},

GotFirstResponseByte: func() {
config.metrics.histGotFirstByteDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(start).Seconds())
config.metrics.summaryGotFirstByteDuration.WithLabelValues(config.metrics.labelValues...).Observe(time.Since(start).Seconds())
applog.Infof("Time from start to first byte: %v\n", time.Since(start))
},
Expand All @@ -335,13 +338,15 @@ func sendDataHTTP(data []byte, config appConfig, client *http.Client) error {
responseTime := time.Since(wroteRequest)
if err == nil && resp != nil {
localLabelValues := append(config.metrics.labelValues, fmt.Sprintf("%v", resp.StatusCode))
config.metrics.histResponseDuration.WithLabelValues(localLabelValues...).Observe(responseTime.Seconds())
config.metrics.summaryResponseDuration.WithLabelValues(localLabelValues...).Observe(responseTime.Seconds())
}
}

totalTime := time.Since(start)

config.metrics.requestsSendBytesSum.WithLabelValues(config.metrics.labelValues...).Add(float64(len(config.sendPayload)))
config.metrics.histRequestsDuration.WithLabelValues(config.metrics.labelValues...).Observe(totalTime.Seconds())
config.metrics.summaryRequestsDuration.WithLabelValues(config.metrics.labelValues...).Observe(totalTime.Seconds())

applog.Infof("Total time: %v\n", totalTime)
Expand All @@ -357,7 +362,7 @@ func sendDataHTTP(data []byte, config appConfig, client *http.Client) error {

if resp.StatusCode >= 200 && resp.StatusCode <= 299 {

bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
applog.Errorf("Failed to read response body from %q, error: %s", config.sendEndpoint, err.Error())
return err
Expand Down Expand Up @@ -676,7 +681,7 @@ func main() {
workerStatuses = make([]workerStatus, config.workers)

// Logger
applog = logger.Init("minigun", config.verbose, false, ioutil.Discard)
applog = logger.Init("minigun", config.verbose, false, io.Discard)

// Some checks
if config.sendEndpoint == "" {
Expand All @@ -697,7 +702,7 @@ func main() {
// Load file if requested
if config.sendFile != "" {
applog.Infof("Reading file %q", config.sendFile)
if data, err := ioutil.ReadFile(config.sendFile); err == nil {
if data, err := os.ReadFile(config.sendFile); err == nil {
config.sendPayload = data
} else {
applog.Fatalf("Error reading file %q: %s", config.sendFile, err.Error())
Expand Down
86 changes: 86 additions & 0 deletions metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ type appMetrics struct {
channelLength *prometheus.GaugeVec
channelConfigLength *prometheus.GaugeVec

// Histograms
histRequestsDuration *prometheus.HistogramVec
histDNSDuration *prometheus.HistogramVec
histConnectDuration *prometheus.HistogramVec
histGotFirstByteDuration *prometheus.HistogramVec
histTLSHandshakeDuration *prometheus.HistogramVec
histWroteRequestBodyDuration *prometheus.HistogramVec
histResponseDuration *prometheus.HistogramVec

// Summaries
summaryRequestsDuration *prometheus.SummaryVec
summaryDNSDuration *prometheus.SummaryVec
Expand Down Expand Up @@ -95,6 +104,17 @@ func initMetrics(config appConfig, labelNames, labelValues []string) appMetrics
am.labelNames,
)

am.histRequestsDuration = promauto.With(registry).NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "minigun",
Subsystem: "requests",
Name: "hist_duration_seconds",
Help: "Histogram distribution of request durations, in seconds",
Buckets: secondsDurationBuckets,
},
am.labelNames,
)

am.summaryRequestsDuration = promauto.With(registry).NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "minigun",
Expand All @@ -107,6 +127,17 @@ func initMetrics(config appConfig, labelNames, labelValues []string) appMetrics
)

// DNS metrics
am.histDNSDuration = promauto.With(registry).NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "minigun",
Subsystem: "httptrace",
Name: "hist_dns_duration_seconds",
Help: "Histogram distribution of DNS durations, in seconds",
Buckets: secondsDurationBuckets,
},
am.labelNames,
)

am.summaryDNSDuration = promauto.With(registry).NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "minigun",
Expand All @@ -119,6 +150,17 @@ func initMetrics(config appConfig, labelNames, labelValues []string) appMetrics
)

// Connection metrics
am.histConnectDuration = promauto.With(registry).NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "minigun",
Subsystem: "httptrace",
Name: "hist_connect_duration_seconds",
Help: "Histogram distribution of connection durations, in seconds",
Buckets: secondsDurationBuckets,
},
am.labelNames,
)

am.summaryConnectDuration = promauto.With(registry).NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "minigun",
Expand All @@ -131,6 +173,17 @@ func initMetrics(config appConfig, labelNames, labelValues []string) appMetrics
)

// Response first byte metrics
am.histGotFirstByteDuration = promauto.With(registry).NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "minigun",
Subsystem: "httptrace",
Name: "hist_time_to_first_byte_seconds",
Help: "Histogram distribution of time to first byte durations, in seconds",
Buckets: secondsDurationBuckets,
},
am.labelNames,
)

am.summaryGotFirstByteDuration = promauto.With(registry).NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "minigun",
Expand All @@ -143,6 +196,17 @@ func initMetrics(config appConfig, labelNames, labelValues []string) appMetrics
)

// TLS handshake metrics
am.histTLSHandshakeDuration = promauto.With(registry).NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "minigun",
Subsystem: "httptrace",
Name: "hist_tls_handshake_duration_seconds",
Help: "Histogram distribution of TLS Handshake durations, in seconds",
Buckets: secondsDurationBuckets,
},
am.labelNames,
)

am.summaryTLSHandshakeDuration = promauto.With(registry).NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "minigun",
Expand All @@ -155,6 +219,17 @@ func initMetrics(config appConfig, labelNames, labelValues []string) appMetrics
)

// Request body sent metrics
am.histWroteRequestBodyDuration = promauto.With(registry).NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "minigun",
Subsystem: "httptrace",
Name: "hist_write_request_body_duration_seconds",
Help: "Histogram distribution of WroteRequestBody durations, in seconds",
Buckets: secondsDurationBuckets,
},
am.labelNames,
)

am.summaryWroteRequestBodyDuration = promauto.With(registry).NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "minigun",
Expand Down Expand Up @@ -187,6 +262,17 @@ func initMetrics(config appConfig, labelNames, labelValues []string) appMetrics
am.labelNames,
)

am.histResponseDuration = promauto.With(registry).NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "minigun",
Subsystem: "response",
Name: "hist_duration_seconds",
Help: "Histogram distribution of response durations, in seconds",
Buckets: secondsDurationBuckets,
},
append(am.labelNames, "status"),
)

am.summaryResponseDuration = promauto.With(registry).NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "minigun",
Expand Down

0 comments on commit 98c9703

Please sign in to comment.