100 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			100 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2020 astaxie
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| // http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package metric
 | |
| 
 | |
| import (
 | |
| 	"net/http"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/prometheus/client_golang/prometheus"
 | |
| 
 | |
| 	"github.com/astaxie/beego"
 | |
| 	"github.com/astaxie/beego/logs"
 | |
| )
 | |
| 
 | |
| func PrometheusMiddleWare(next http.Handler) http.Handler {
 | |
| 	summaryVec := prometheus.NewSummaryVec(prometheus.SummaryOpts{
 | |
| 		Name:      "beego",
 | |
| 		Subsystem: "http_request",
 | |
| 		ConstLabels: map[string]string{
 | |
| 			"server":  beego.BConfig.ServerName,
 | |
| 			"env":     beego.BConfig.RunMode,
 | |
| 			"appname": beego.BConfig.AppName,
 | |
| 		},
 | |
| 		Help: "The statics info for http request",
 | |
| 	}, []string{"pattern", "method", "status", "duration"})
 | |
| 
 | |
| 	prometheus.MustRegister(summaryVec)
 | |
| 
 | |
| 	registerBuildInfo()
 | |
| 
 | |
| 	return http.HandlerFunc(func(writer http.ResponseWriter, q *http.Request) {
 | |
| 		start := time.Now()
 | |
| 		next.ServeHTTP(writer, q)
 | |
| 		end := time.Now()
 | |
| 		go report(end.Sub(start), writer, q, summaryVec)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func registerBuildInfo() {
 | |
| 	buildInfo := prometheus.NewGaugeVec(prometheus.GaugeOpts{
 | |
| 		Name:      "beego",
 | |
| 		Subsystem: "build_info",
 | |
| 		Help:      "The building information",
 | |
| 		ConstLabels: map[string]string{
 | |
| 			"appname": beego.BConfig.AppName,
 | |
| 			"build_version":  beego.BuildVersion,
 | |
| 			"build_revision": beego.BuildGitRevision,
 | |
| 			"build_status":   beego.BuildStatus,
 | |
| 			"build_tag":      beego.BuildTag,
 | |
| 			"build_time": strings.Replace(beego.BuildTime, "--", " ", 1),
 | |
| 			"go_version":     beego.GoVersion,
 | |
| 			"git_branch":     beego.GitBranch,
 | |
| 			"start_time": time.Now().Format("2006-01-02 15:04:05"),
 | |
| 		},
 | |
| 	}, []string{})
 | |
| 
 | |
| 	prometheus.MustRegister(buildInfo)
 | |
| 	buildInfo.WithLabelValues().Set(1)
 | |
| }
 | |
| 
 | |
| func report(dur time.Duration, writer http.ResponseWriter, q *http.Request, vec *prometheus.SummaryVec) {
 | |
| 	ctrl := beego.BeeApp.Handlers
 | |
| 	ctx := ctrl.GetContext()
 | |
| 	ctx.Reset(writer, q)
 | |
| 	defer ctrl.GiveBackContext(ctx)
 | |
| 
 | |
| 	// We cannot read the status code from q.Response.StatusCode
 | |
| 	// since the http server does not set q.Response. So q.Response is nil
 | |
| 	// Thus, we use reflection to read the status from writer whose concrete type is http.response
 | |
| 	responseVal := reflect.ValueOf(writer).Elem()
 | |
| 	field := responseVal.FieldByName("status")
 | |
| 	status := -1
 | |
| 	if field.IsValid() && field.Kind() == reflect.Int {
 | |
| 		status = int(field.Int())
 | |
| 	}
 | |
| 	ptn := "UNKNOWN"
 | |
| 	if rt, found := ctrl.FindRouter(ctx); found {
 | |
| 		ptn = rt.GetPattern()
 | |
| 	} else {
 | |
| 		logs.Warn("we can not find the router info for this request, so request will be recorded as UNKNOWN: " + q.URL.String())
 | |
| 	}
 | |
| 	ms := dur / time.Millisecond
 | |
| 	vec.WithLabelValues(ptn, q.Method, strconv.Itoa(status), strconv.Itoa(int(ms))).Observe(float64(ms))
 | |
| }
 |