123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- package prometheus
- import (
- "context"
- "fmt"
- "net/http"
- "strconv"
- "time"
- "github.com/felixge/httpsnoop"
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promhttp"
- log "github.com/sirupsen/logrus"
- "github.com/imgproxy/imgproxy/v3/config"
- "github.com/imgproxy/imgproxy/v3/metrics/stats"
- "github.com/imgproxy/imgproxy/v3/reuseport"
- )
- var (
- enabled = false
- requestsTotal prometheus.Counter
- statusCodesTotal *prometheus.CounterVec
- errorsTotal *prometheus.CounterVec
- requestDuration prometheus.Histogram
- requestSpanDuration *prometheus.HistogramVec
- downloadDuration prometheus.Histogram
- processingDuration prometheus.Histogram
- bufferSize *prometheus.HistogramVec
- bufferDefaultSize *prometheus.GaugeVec
- bufferMaxSize *prometheus.GaugeVec
- workers prometheus.Gauge
- requestsInProgress prometheus.GaugeFunc
- imagesInProgress prometheus.GaugeFunc
- workersUtilization prometheus.GaugeFunc
- )
- func Init() {
- if len(config.PrometheusBind) == 0 {
- return
- }
- requestsTotal = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: config.PrometheusNamespace,
- Name: "requests_total",
- Help: "A counter of the total number of HTTP requests imgproxy processed.",
- })
- statusCodesTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
- Namespace: config.PrometheusNamespace,
- Name: "status_codes_total",
- Help: "A counter of the response status codes.",
- }, []string{"status"})
- errorsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
- Namespace: config.PrometheusNamespace,
- Name: "errors_total",
- Help: "A counter of the occurred errors separated by type.",
- }, []string{"type"})
- requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
- Namespace: config.PrometheusNamespace,
- Name: "request_duration_seconds",
- Help: "A histogram of the response latency.",
- })
- requestSpanDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
- Namespace: config.PrometheusNamespace,
- Name: "request_span_duration_seconds",
- Help: "A histogram of the queue latency.",
- }, []string{"span"})
- downloadDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
- Namespace: config.PrometheusNamespace,
- Name: "download_duration_seconds",
- Help: "A histogram of the source image downloading latency.",
- })
- processingDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
- Namespace: config.PrometheusNamespace,
- Name: "processing_duration_seconds",
- Help: "A histogram of the image processing latency.",
- })
- bufferSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
- Namespace: config.PrometheusNamespace,
- Name: "buffer_size_bytes",
- Help: "A histogram of the buffer size in bytes.",
- Buckets: prometheus.ExponentialBuckets(1024, 2, 14),
- }, []string{"type"})
- bufferDefaultSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{
- Namespace: config.PrometheusNamespace,
- Name: "buffer_default_size_bytes",
- Help: "A gauge of the buffer default size in bytes.",
- }, []string{"type"})
- bufferMaxSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{
- Namespace: config.PrometheusNamespace,
- Name: "buffer_max_size_bytes",
- Help: "A gauge of the buffer max size in bytes.",
- }, []string{"type"})
- workers = prometheus.NewGauge(prometheus.GaugeOpts{
- Namespace: config.PrometheusNamespace,
- Name: "workers",
- Help: "A gauge of the number of running workers.",
- })
- workers.Set(float64(config.Workers))
- requestsInProgress = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
- Namespace: config.PrometheusNamespace,
- Name: "requests_in_progress",
- Help: "A gauge of the number of requests currently being in progress.",
- }, stats.RequestsInProgress)
- imagesInProgress = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
- Namespace: config.PrometheusNamespace,
- Name: "images_in_progress",
- Help: "A gauge of the number of images currently being in progress.",
- }, stats.ImagesInProgress)
- workersUtilization = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
- Namespace: config.PrometheusNamespace,
- Name: "workers_utilization",
- Help: "A gauge of the workers utilization in percents.",
- }, stats.WorkersUtilization)
- prometheus.MustRegister(
- requestsTotal,
- statusCodesTotal,
- errorsTotal,
- requestDuration,
- requestSpanDuration,
- downloadDuration,
- processingDuration,
- bufferSize,
- bufferDefaultSize,
- bufferMaxSize,
- workers,
- requestsInProgress,
- imagesInProgress,
- workersUtilization,
- )
- enabled = true
- }
- func Enabled() bool {
- return enabled
- }
- func StartServer(cancel context.CancelFunc) error {
- if !enabled {
- return nil
- }
- s := http.Server{Handler: promhttp.Handler()}
- l, err := reuseport.Listen("tcp", config.PrometheusBind)
- if err != nil {
- return fmt.Errorf("Can't start Prometheus metrics server: %s", err)
- }
- go func() {
- log.Infof("Starting Prometheus server at %s", config.PrometheusBind)
- if err := s.Serve(l); err != nil && err != http.ErrServerClosed {
- log.Error(err)
- }
- cancel()
- }()
- return nil
- }
- func StartRequest(rw http.ResponseWriter) (context.CancelFunc, http.ResponseWriter) {
- if !enabled {
- return func() {}, rw
- }
- requestsTotal.Inc()
- newRw := httpsnoop.Wrap(rw, httpsnoop.Hooks{
- WriteHeader: func(next httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc {
- return func(statusCode int) {
- statusCodesTotal.With(prometheus.Labels{"status": strconv.Itoa(statusCode)}).Inc()
- next(statusCode)
- }
- },
- })
- return startDuration(requestDuration), newRw
- }
- func StartQueueSegment() context.CancelFunc {
- if !enabled {
- return func() {}
- }
- return startDuration(requestSpanDuration.With(prometheus.Labels{"span": "queue"}))
- }
- func StartDownloadingSegment() context.CancelFunc {
- if !enabled {
- return func() {}
- }
- cancel := startDuration(requestSpanDuration.With(prometheus.Labels{"span": "downloading"}))
- cancelLegacy := startDuration(downloadDuration)
- return func() {
- cancel()
- cancelLegacy()
- }
- }
- func StartProcessingSegment() context.CancelFunc {
- if !enabled {
- return func() {}
- }
- cancel := startDuration(requestSpanDuration.With(prometheus.Labels{"span": "processing"}))
- cancelLegacy := startDuration(processingDuration)
- return func() {
- cancel()
- cancelLegacy()
- }
- }
- func StartStreamingSegment() context.CancelFunc {
- if !enabled {
- return func() {}
- }
- return startDuration(requestSpanDuration.With(prometheus.Labels{"span": "streaming"}))
- }
- func startDuration(m prometheus.Observer) context.CancelFunc {
- t := time.Now()
- return func() {
- m.Observe(time.Since(t).Seconds())
- }
- }
- func IncrementErrorsTotal(t string) {
- if enabled {
- errorsTotal.With(prometheus.Labels{"type": t}).Inc()
- }
- }
- func ObserveBufferSize(t string, size int) {
- if enabled {
- bufferSize.With(prometheus.Labels{"type": t}).Observe(float64(size))
- }
- }
- func SetBufferDefaultSize(t string, size int) {
- if enabled {
- bufferDefaultSize.With(prometheus.Labels{"type": t}).Set(float64(size))
- }
- }
- func SetBufferMaxSize(t string, size int) {
- if enabled {
- bufferMaxSize.With(prometheus.Labels{"type": t}).Set(float64(size))
- }
- }
- func AddGaugeFunc(name, help string, f func() float64) {
- if !enabled {
- return
- }
- gauge := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
- Namespace: config.PrometheusNamespace,
- Name: name,
- Help: help,
- }, f)
- prometheus.MustRegister(gauge)
- }
|