github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/framework/http/prometheus/middleware.go (about)

     1  package prometheus
     2  
     3  // borrow code from https://github.com/TannerGabriel/learning-go/tree/master/advanced-programs/PrometheusHTTPServer
     4  // Many thanks to TannerGabriel https://github.com/TannerGabriel
     5  // Post link https://gabrieltanner.org/blog/collecting-prometheus-metrics-in-golang written by TannerGabriel
     6  import (
     7  	"github.com/prometheus/client_golang/prometheus"
     8  	"github.com/unionj-cloud/go-doudou/framework/buildinfo"
     9  	"github.com/unionj-cloud/go-doudou/toolkit/constants"
    10  	"github.com/unionj-cloud/go-doudou/toolkit/load"
    11  	"github.com/unionj-cloud/go-doudou/toolkit/process"
    12  	"github.com/unionj-cloud/go-doudou/toolkit/stringutils"
    13  	logger "github.com/unionj-cloud/go-doudou/toolkit/zlogger"
    14  	"net/http"
    15  	"runtime"
    16  	"strconv"
    17  	"sync"
    18  	"time"
    19  )
    20  
    21  type responseWriter struct {
    22  	http.ResponseWriter
    23  	statusCode int
    24  }
    25  
    26  // NewResponseWriter creates new responseWriter
    27  func NewResponseWriter(w http.ResponseWriter) *responseWriter {
    28  	return &responseWriter{w, http.StatusOK}
    29  }
    30  
    31  // WriteHeader set header to code
    32  func (rw *responseWriter) WriteHeader(code int) {
    33  	rw.statusCode = code
    34  	rw.ResponseWriter.WriteHeader(code)
    35  }
    36  
    37  var countRequests = prometheus.NewCounterVec(
    38  	prometheus.CounterOpts{
    39  		Name: "go_doudou_http_request_count",
    40  		Help: "Number of http requests.",
    41  	},
    42  	[]string{"path", "method", "status"},
    43  )
    44  
    45  var httpDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
    46  	Name: "go_doudou_http_response_time_seconds",
    47  	Help: "Duration of HTTP requests.",
    48  }, []string{"path", "method"})
    49  
    50  // PrometheusMiddleware returns http HandlerFunc for prometheus matrix
    51  func PrometheusMiddleware(next http.Handler) http.Handler {
    52  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    53  		path := r.URL.Path
    54  		method := r.Method
    55  		timer := prometheus.NewTimer(httpDuration.WithLabelValues(path, method))
    56  
    57  		rw := NewResponseWriter(w)
    58  		next.ServeHTTP(rw, r)
    59  
    60  		statusCode := rw.statusCode
    61  
    62  		countRequests.WithLabelValues(path, method, strconv.Itoa(statusCode)).Inc()
    63  
    64  		timer.ObserveDuration()
    65  	})
    66  }
    67  
    68  var processPool sync.Pool
    69  
    70  func init() {
    71  	processPool = sync.Pool{
    72  		New: func() interface{} {
    73  			return process.NewCurrentProcess()
    74  		},
    75  	}
    76  	prometheus.Register(countRequests)
    77  	prometheus.Register(httpDuration)
    78  	buildTime := buildinfo.BuildTime
    79  	if stringutils.IsNotEmpty(buildinfo.BuildTime) {
    80  		if t, err := time.Parse(constants.FORMAT15, buildinfo.BuildTime); err == nil {
    81  			buildTime = t.Local().Format(constants.FORMAT8)
    82  		}
    83  	}
    84  	appInfo := prometheus.NewGauge(prometheus.GaugeOpts{
    85  		Name: "go_doudou_app_info",
    86  		Help: "Information about the go-doudou app",
    87  		ConstLabels: prometheus.Labels{
    88  			"goVer":     runtime.Version(),
    89  			"gddVer":    buildinfo.GddVer,
    90  			"buildUser": buildinfo.BuildUser,
    91  			"buildTime": buildTime,
    92  		},
    93  	})
    94  	appInfo.Set(1)
    95  	prometheus.Register(appInfo)
    96  
    97  	prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
    98  		Name:        "go_doudou_gomaxprocs",
    99  		Help:        "The value of gomaxprocs",
   100  		ConstLabels: nil,
   101  	}, func() float64 {
   102  		return float64(runtime.GOMAXPROCS(0))
   103  	}))
   104  
   105  	prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
   106  		Name:        "go_doudou_numcpu",
   107  		Help:        "The value of numcpu",
   108  		ConstLabels: nil,
   109  	}, func() float64 {
   110  		return float64(runtime.NumCPU())
   111  	}))
   112  
   113  	prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
   114  		Name:        "go_doudou_system_cpu_usage",
   115  		Help:        "The \"recent cpu usage\" for the whole system",
   116  		ConstLabels: nil,
   117  	}, func() float64 {
   118  		p := processPool.Get().(*process.Process)
   119  		defer processPool.Put(p)
   120  		if p.Err != nil {
   121  			return 0
   122  		}
   123  		ts, err := p.Times1()
   124  		if err != nil {
   125  			logger.Error().Err(err).Msg("")
   126  			return 0
   127  		}
   128  		return ts.System
   129  	}))
   130  	prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
   131  		Name:        "go_doudou_process_cpu_usage",
   132  		Help:        "The \"recent cpu usage\" for the go-doudou process",
   133  		ConstLabels: nil,
   134  	}, func() float64 {
   135  		p := processPool.Get().(*process.Process)
   136  		defer processPool.Put(p)
   137  		if p.Err != nil {
   138  			return 0
   139  		}
   140  		ts, err := p.Times1()
   141  		if err != nil {
   142  			logger.Error().Err(err).Msg("")
   143  			return 0
   144  		}
   145  		return ts.User
   146  	}))
   147  	prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
   148  		Name:        "go_doudou_process_start_time_millis",
   149  		Help:        "Start time of the process since unix epoch.",
   150  		ConstLabels: nil,
   151  	}, func() float64 {
   152  		p := processPool.Get().(*process.Process)
   153  		defer processPool.Put(p)
   154  		if p.Err != nil {
   155  			return float64(time.Unix(0, 0).UnixMilli())
   156  		}
   157  		start, err := p.CreateTime()
   158  		if err != nil {
   159  			logger.Error().Err(err).Msg("")
   160  			return float64(time.Unix(0, 0).UnixMilli())
   161  		}
   162  		return float64(start)
   163  	}))
   164  	prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
   165  		Name:        "go_doudou_process_uptime_millis",
   166  		Help:        "The uptime of the go-doudou process.",
   167  		ConstLabels: nil,
   168  	}, func() float64 {
   169  		p := processPool.Get().(*process.Process)
   170  		defer processPool.Put(p)
   171  		if p.Err != nil {
   172  			return float64(time.Since(time.Unix(0, 0).Local()).Milliseconds())
   173  		}
   174  		start, err := p.CreateTime()
   175  		if err != nil {
   176  			logger.Error().Err(err).Msg("")
   177  			return float64(time.Since(time.Unix(0, 0).Local()).Milliseconds())
   178  		}
   179  		return float64(time.Since(time.UnixMilli(start).Local()).Milliseconds())
   180  	}))
   181  
   182  	prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
   183  		Name:        "go_doudou_system_load_average_1m",
   184  		Help:        "The sum of the number of runnable entities queued to available processors and the number of runnable entities running on the available processors averaged over a period of time",
   185  		ConstLabels: nil,
   186  	}, func() float64 {
   187  		as, err := load.Avg1()
   188  		if err != nil {
   189  			logger.Error().Err(err).Msg("")
   190  			return 0
   191  		}
   192  		return as.Load1
   193  	}))
   194  }