1
0

prometheus.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package prometheus
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/prometheus/client_golang/prometheus"
  8. "github.com/prometheus/client_golang/prometheus/promhttp"
  9. log "github.com/sirupsen/logrus"
  10. "github.com/imgproxy/imgproxy/v3/config"
  11. "github.com/imgproxy/imgproxy/v3/metrics/stats"
  12. "github.com/imgproxy/imgproxy/v3/reuseport"
  13. )
  14. var (
  15. enabled = false
  16. requestsTotal prometheus.Counter
  17. errorsTotal *prometheus.CounterVec
  18. requestDuration prometheus.Histogram
  19. requestSpanDuration *prometheus.HistogramVec
  20. downloadDuration prometheus.Histogram
  21. processingDuration prometheus.Histogram
  22. bufferSize *prometheus.HistogramVec
  23. bufferDefaultSize *prometheus.GaugeVec
  24. bufferMaxSize *prometheus.GaugeVec
  25. requestsInProgress prometheus.GaugeFunc
  26. imagesInProgress prometheus.GaugeFunc
  27. )
  28. func Init() {
  29. if len(config.PrometheusBind) == 0 {
  30. return
  31. }
  32. requestsTotal = prometheus.NewCounter(prometheus.CounterOpts{
  33. Namespace: config.PrometheusNamespace,
  34. Name: "requests_total",
  35. Help: "A counter of the total number of HTTP requests imgproxy processed.",
  36. })
  37. errorsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
  38. Namespace: config.PrometheusNamespace,
  39. Name: "errors_total",
  40. Help: "A counter of the occurred errors separated by type.",
  41. }, []string{"type"})
  42. requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
  43. Namespace: config.PrometheusNamespace,
  44. Name: "request_duration_seconds",
  45. Help: "A histogram of the response latency.",
  46. })
  47. requestSpanDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
  48. Namespace: config.PrometheusNamespace,
  49. Name: "request_span_duration_seconds",
  50. Help: "A histogram of the queue latency.",
  51. }, []string{"span"})
  52. downloadDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
  53. Namespace: config.PrometheusNamespace,
  54. Name: "download_duration_seconds",
  55. Help: "A histogram of the source image downloading latency.",
  56. })
  57. processingDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
  58. Namespace: config.PrometheusNamespace,
  59. Name: "processing_duration_seconds",
  60. Help: "A histogram of the image processing latency.",
  61. })
  62. bufferSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
  63. Namespace: config.PrometheusNamespace,
  64. Name: "buffer_size_bytes",
  65. Help: "A histogram of the buffer size in bytes.",
  66. Buckets: prometheus.ExponentialBuckets(1024, 2, 14),
  67. }, []string{"type"})
  68. bufferDefaultSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  69. Namespace: config.PrometheusNamespace,
  70. Name: "buffer_default_size_bytes",
  71. Help: "A gauge of the buffer default size in bytes.",
  72. }, []string{"type"})
  73. bufferMaxSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  74. Namespace: config.PrometheusNamespace,
  75. Name: "buffer_max_size_bytes",
  76. Help: "A gauge of the buffer max size in bytes.",
  77. }, []string{"type"})
  78. requestsInProgress = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
  79. Namespace: config.PrometheusNamespace,
  80. Name: "requests_in_progress",
  81. Help: "A gauge of the number of requests currently being in progress.",
  82. }, stats.RequestsInProgress)
  83. imagesInProgress = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
  84. Namespace: config.PrometheusNamespace,
  85. Name: "images_in_progress",
  86. Help: "A gauge of the number of images currently being in progress.",
  87. }, stats.ImagesInProgress)
  88. prometheus.MustRegister(
  89. requestsTotal,
  90. errorsTotal,
  91. requestDuration,
  92. requestSpanDuration,
  93. downloadDuration,
  94. processingDuration,
  95. bufferSize,
  96. bufferDefaultSize,
  97. bufferMaxSize,
  98. requestsInProgress,
  99. imagesInProgress,
  100. )
  101. enabled = true
  102. }
  103. func Enabled() bool {
  104. return enabled
  105. }
  106. func StartServer(cancel context.CancelFunc) error {
  107. if !enabled {
  108. return nil
  109. }
  110. s := http.Server{Handler: promhttp.Handler()}
  111. l, err := reuseport.Listen("tcp", config.PrometheusBind)
  112. if err != nil {
  113. return fmt.Errorf("Can't start Prometheus metrics server: %s", err)
  114. }
  115. go func() {
  116. log.Infof("Starting Prometheus server at %s", config.PrometheusBind)
  117. if err := s.Serve(l); err != nil && err != http.ErrServerClosed {
  118. log.Error(err)
  119. }
  120. cancel()
  121. }()
  122. return nil
  123. }
  124. func StartRequest() context.CancelFunc {
  125. if !enabled {
  126. return func() {}
  127. }
  128. requestsTotal.Inc()
  129. return startDuration(requestDuration)
  130. }
  131. func StartQueueSegment() context.CancelFunc {
  132. if !enabled {
  133. return func() {}
  134. }
  135. return startDuration(requestSpanDuration.With(prometheus.Labels{"span": "queue"}))
  136. }
  137. func StartDownloadingSegment() context.CancelFunc {
  138. if !enabled {
  139. return func() {}
  140. }
  141. cancel := startDuration(requestSpanDuration.With(prometheus.Labels{"span": "downloading"}))
  142. cancelLegacy := startDuration(downloadDuration)
  143. return func() {
  144. cancel()
  145. cancelLegacy()
  146. }
  147. }
  148. func StartProcessingSegment() context.CancelFunc {
  149. if !enabled {
  150. return func() {}
  151. }
  152. cancel := startDuration(requestSpanDuration.With(prometheus.Labels{"span": "processing"}))
  153. cancelLegacy := startDuration(processingDuration)
  154. return func() {
  155. cancel()
  156. cancelLegacy()
  157. }
  158. }
  159. func StartStreamingSegment() context.CancelFunc {
  160. if !enabled {
  161. return func() {}
  162. }
  163. return startDuration(requestSpanDuration.With(prometheus.Labels{"span": "streaming"}))
  164. }
  165. func startDuration(m prometheus.Observer) context.CancelFunc {
  166. t := time.Now()
  167. return func() {
  168. m.Observe(time.Since(t).Seconds())
  169. }
  170. }
  171. func IncrementErrorsTotal(t string) {
  172. if enabled {
  173. errorsTotal.With(prometheus.Labels{"type": t}).Inc()
  174. }
  175. }
  176. func ObserveBufferSize(t string, size int) {
  177. if enabled {
  178. bufferSize.With(prometheus.Labels{"type": t}).Observe(float64(size))
  179. }
  180. }
  181. func SetBufferDefaultSize(t string, size int) {
  182. if enabled {
  183. bufferDefaultSize.With(prometheus.Labels{"type": t}).Set(float64(size))
  184. }
  185. }
  186. func SetBufferMaxSize(t string, size int) {
  187. if enabled {
  188. bufferMaxSize.With(prometheus.Labels{"type": t}).Set(float64(size))
  189. }
  190. }
  191. func AddGaugeFunc(name, help string, f func() float64) {
  192. if !enabled {
  193. return
  194. }
  195. gauge := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
  196. Namespace: config.PrometheusNamespace,
  197. Name: name,
  198. Help: help,
  199. }, f)
  200. prometheus.MustRegister(gauge)
  201. }