github.com/astaxie/beego@v1.12.3/metric/prometheus.go (about) 1 // Copyright 2020 astaxie 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package metric 16 17 import ( 18 "net/http" 19 "reflect" 20 "strconv" 21 "strings" 22 "time" 23 24 "github.com/prometheus/client_golang/prometheus" 25 26 "github.com/astaxie/beego" 27 "github.com/astaxie/beego/logs" 28 ) 29 30 func PrometheusMiddleWare(next http.Handler) http.Handler { 31 summaryVec := prometheus.NewSummaryVec(prometheus.SummaryOpts{ 32 Name: "beego", 33 Subsystem: "http_request", 34 ConstLabels: map[string]string{ 35 "server": beego.BConfig.ServerName, 36 "env": beego.BConfig.RunMode, 37 "appname": beego.BConfig.AppName, 38 }, 39 Help: "The statics info for http request", 40 }, []string{"pattern", "method", "status", "duration"}) 41 42 prometheus.MustRegister(summaryVec) 43 44 registerBuildInfo() 45 46 return http.HandlerFunc(func(writer http.ResponseWriter, q *http.Request) { 47 start := time.Now() 48 next.ServeHTTP(writer, q) 49 end := time.Now() 50 go report(end.Sub(start), writer, q, summaryVec) 51 }) 52 } 53 54 func registerBuildInfo() { 55 buildInfo := prometheus.NewGaugeVec(prometheus.GaugeOpts{ 56 Name: "beego", 57 Subsystem: "build_info", 58 Help: "The building information", 59 ConstLabels: map[string]string{ 60 "appname": beego.BConfig.AppName, 61 "build_version": beego.BuildVersion, 62 "build_revision": beego.BuildGitRevision, 63 "build_status": beego.BuildStatus, 64 "build_tag": beego.BuildTag, 65 "build_time": strings.Replace(beego.BuildTime, "--", " ", 1), 66 "go_version": beego.GoVersion, 67 "git_branch": beego.GitBranch, 68 "start_time": time.Now().Format("2006-01-02 15:04:05"), 69 }, 70 }, []string{}) 71 72 prometheus.MustRegister(buildInfo) 73 buildInfo.WithLabelValues().Set(1) 74 } 75 76 func report(dur time.Duration, writer http.ResponseWriter, q *http.Request, vec *prometheus.SummaryVec) { 77 ctrl := beego.BeeApp.Handlers 78 ctx := ctrl.GetContext() 79 ctx.Reset(writer, q) 80 defer ctrl.GiveBackContext(ctx) 81 82 // We cannot read the status code from q.Response.StatusCode 83 // since the http server does not set q.Response. So q.Response is nil 84 // Thus, we use reflection to read the status from writer whose concrete type is http.response 85 responseVal := reflect.ValueOf(writer).Elem() 86 field := responseVal.FieldByName("status") 87 status := -1 88 if field.IsValid() && field.Kind() == reflect.Int { 89 status = int(field.Int()) 90 } 91 ptn := "UNKNOWN" 92 if rt, found := ctrl.FindRouter(ctx); found { 93 ptn = rt.GetPattern() 94 } else { 95 logs.Warn("we can not find the router info for this request, so request will be recorded as UNKNOWN: " + q.URL.String()) 96 } 97 ms := dur / time.Millisecond 98 vec.WithLabelValues(ptn, q.Method, strconv.Itoa(status), strconv.Itoa(int(ms))).Observe(float64(ms)) 99 }