Gin 接入 OpenTelemetry 和 Prometheus

526 阅读1分钟

Gin 接入 OpenTelemetry 和 Prometheus

项目地址:github.com/xuqil/obser…

接入 OpenTelemetry

package opentelemetry

import (
	"github.com/gin-gonic/gin"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/trace"
)

const instrumentationName = "github.com/xuqil/observability/trace/gin/middlewares/opentelemetry"

type MiddlewareBuilder struct {
	Tracer trace.Tracer
}

func (m MiddlewareBuilder) Build() gin.HandlerFunc {
	if m.Tracer == nil {
		m.Tracer = otel.GetTracerProvider().Tracer(instrumentationName)
	}
	return func(ctx *gin.Context) {
		reqCtx := ctx.Request.Context()
		// 尝试和客户端的 trace 结合在一起
		reqCtx = otel.GetTextMapPropagator().Extract(reqCtx, propagation.HeaderCarrier(ctx.Request.Header))

		reqCtx, span := m.Tracer.Start(reqCtx, "unknown")
		defer span.End()

		span.SetAttributes(attribute.String("http.method", ctx.Request.Method))
		span.SetAttributes(attribute.String("http.url", ctx.Request.URL.String()))
		span.SetAttributes(attribute.String("http.scheme", ctx.Request.URL.Scheme))
		span.SetAttributes(attribute.String("http.host", ctx.Request.Host))

		ctx.Request = ctx.Request.WithContext(reqCtx)

		ctx.Next()

		span.SetName(ctx.FullPath())
		span.SetAttributes(attribute.Int("http.status", ctx.Writer.Status()))
	}
}

接入 Prometheus

package prometheus

import (
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"strconv"
	"time"
)

type MiddlewareBuilder struct {
	Namespace string
	Subsystem string
	Name      string
	Help      string
}

func (m MiddlewareBuilder) Build() gin.HandlerFunc {
	vector := prometheus.NewSummaryVec(prometheus.SummaryOpts{
		Name:      m.Name,
		Subsystem: m.Subsystem,
		Namespace: m.Namespace,
		Help:      m.Help,
		Objectives: map[float64]float64{
			0.5:   0.01,
			0.75:  0.01,
			0.90:  0.01,
			0.99:  0.001,
			0.999: 0.0001,
		},
	}, []string{"pattern", "method", "status"})

	prometheus.MustRegister(vector)
	return func(ctx *gin.Context) {
		startTime := time.Now()
		defer func() {
			duration := time.Now().Sub(startTime).Milliseconds()
			pattern := ctx.FullPath()
			if pattern == "" {
				pattern = "unknown"
			}
			vector.WithLabelValues(pattern, ctx.Request.Method,
				strconv.Itoa(ctx.Writer.Status())).Observe(float64(duration))
		}()
		ctx.Next()
	}
}