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))
 | 
						|
}
 |