mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 10:37:56 +00:00 
			
		
		
		
	 dc1e0be0e5
			
		
	
	dc1e0be0e5
	
	
	
		
			
			* helper/metricsutil: Update usage of expfmt The previous exposed constants are no longer exposed and we can use expfmt.Format instead. https://github.com/prometheus/common/pull/576 * reodered imports --------- Co-authored-by: Matthias Loibl <mail@matthiasloibl.com>
		
			
				
	
	
		
			178 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: BUSL-1.1
 | |
| 
 | |
| package metricsutil
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/armon/go-metrics"
 | |
| 	"github.com/hashicorp/vault/sdk/logical"
 | |
| 	"github.com/prometheus/client_golang/prometheus"
 | |
| 	"github.com/prometheus/common/expfmt"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	OpenMetricsMIMEType = "application/openmetrics-text"
 | |
| 
 | |
| 	PrometheusSchemaMIMEType = "prometheus/telemetry"
 | |
| 
 | |
| 	// ErrorContentType is the content type returned by an error response.
 | |
| 	ErrorContentType = "text/plain"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	PrometheusMetricFormat = "prometheus"
 | |
| )
 | |
| 
 | |
| // PhysicalTableSizeName is a set of gauge metric keys for physical mount table sizes
 | |
| var PhysicalTableSizeName []string = []string{"core", "mount_table", "size"}
 | |
| 
 | |
| // LogicalTableSizeName is a set of gauge metric keys for logical mount table sizes
 | |
| var LogicalTableSizeName []string = []string{"core", "mount_table", "num_entries"}
 | |
| 
 | |
| type MetricsHelper struct {
 | |
| 	inMemSink         *metrics.InmemSink
 | |
| 	PrometheusEnabled bool
 | |
| 	LoopMetrics       GaugeMetrics
 | |
| }
 | |
| 
 | |
| type GaugeMetrics struct {
 | |
| 	// Metrics is a map from keys concatenated by "." to the metric.
 | |
| 	// It is a map because although we do not care about distinguishing
 | |
| 	// these loop metrics during emission, we must distinguish them
 | |
| 	// when we update a metric.
 | |
| 	Metrics sync.Map
 | |
| }
 | |
| 
 | |
| type GaugeMetric struct {
 | |
| 	Value  float32
 | |
| 	Labels []Label
 | |
| 	Key    []string
 | |
| }
 | |
| 
 | |
| func NewMetricsHelper(inMem *metrics.InmemSink, enablePrometheus bool) *MetricsHelper {
 | |
| 	return &MetricsHelper{inMem, enablePrometheus, GaugeMetrics{Metrics: sync.Map{}}}
 | |
| }
 | |
| 
 | |
| func FormatFromRequest(req *logical.Request) string {
 | |
| 	acceptHeaders := req.Headers["Accept"]
 | |
| 	if len(acceptHeaders) > 0 {
 | |
| 		acceptHeader := acceptHeaders[0]
 | |
| 		if strings.HasPrefix(acceptHeader, OpenMetricsMIMEType) {
 | |
| 			return PrometheusMetricFormat
 | |
| 		}
 | |
| 
 | |
| 		// Look for prometheus accept header
 | |
| 		for _, header := range acceptHeaders {
 | |
| 			if strings.Contains(header, PrometheusSchemaMIMEType) {
 | |
| 				return PrometheusMetricFormat
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| func (m *MetricsHelper) AddGaugeLoopMetric(key []string, val float32, labels []Label) {
 | |
| 	mapKey := m.CreateMetricsCacheKeyName(key, val, labels)
 | |
| 	m.LoopMetrics.Metrics.Store(mapKey,
 | |
| 		GaugeMetric{
 | |
| 			Key:    key,
 | |
| 			Value:  val,
 | |
| 			Labels: labels,
 | |
| 		})
 | |
| }
 | |
| 
 | |
| func (m *MetricsHelper) CreateMetricsCacheKeyName(key []string, val float32, labels []Label) string {
 | |
| 	var keyJoin string = strings.Join(key, ".")
 | |
| 	labelJoinStr := ""
 | |
| 	for _, label := range labels {
 | |
| 		labelJoinStr = labelJoinStr + label.Name + "|" + label.Value + "||"
 | |
| 	}
 | |
| 	keyJoin = keyJoin + "." + labelJoinStr
 | |
| 	return keyJoin
 | |
| }
 | |
| 
 | |
| func (m *MetricsHelper) ResponseForFormat(format string) *logical.Response {
 | |
| 	switch format {
 | |
| 	case PrometheusMetricFormat:
 | |
| 		return m.PrometheusResponse()
 | |
| 	case "":
 | |
| 		return m.GenericResponse()
 | |
| 	default:
 | |
| 		return &logical.Response{
 | |
| 			Data: map[string]interface{}{
 | |
| 				logical.HTTPContentType: ErrorContentType,
 | |
| 				logical.HTTPRawBody:     fmt.Sprintf("metric response format %q unknown", format),
 | |
| 				logical.HTTPStatusCode:  http.StatusBadRequest,
 | |
| 			},
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (m *MetricsHelper) PrometheusResponse() *logical.Response {
 | |
| 	resp := &logical.Response{
 | |
| 		Data: map[string]interface{}{
 | |
| 			logical.HTTPContentType: ErrorContentType,
 | |
| 			logical.HTTPStatusCode:  http.StatusBadRequest,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	if !m.PrometheusEnabled {
 | |
| 		resp.Data[logical.HTTPRawBody] = "prometheus is not enabled"
 | |
| 		return resp
 | |
| 	}
 | |
| 	metricsFamilies, err := prometheus.DefaultGatherer.Gather()
 | |
| 	if err != nil && len(metricsFamilies) == 0 {
 | |
| 		resp.Data[logical.HTTPRawBody] = fmt.Sprintf("no prometheus metrics could be decoded: %s", err)
 | |
| 		return resp
 | |
| 	}
 | |
| 
 | |
| 	// Initialize a byte buffer.
 | |
| 	buf := &bytes.Buffer{}
 | |
| 	defer buf.Reset()
 | |
| 
 | |
| 	format := expfmt.NewFormat(expfmt.TypeTextPlain)
 | |
| 	e := expfmt.NewEncoder(buf, format)
 | |
| 	for _, mf := range metricsFamilies {
 | |
| 		err := e.Encode(mf)
 | |
| 		if err != nil {
 | |
| 			resp.Data[logical.HTTPRawBody] = fmt.Sprintf("error during the encoding of metrics: %s", err)
 | |
| 			return resp
 | |
| 		}
 | |
| 	}
 | |
| 	resp.Data[logical.HTTPContentType] = string(format)
 | |
| 	resp.Data[logical.HTTPRawBody] = buf.Bytes()
 | |
| 	resp.Data[logical.HTTPStatusCode] = http.StatusOK
 | |
| 	return resp
 | |
| }
 | |
| 
 | |
| func (m *MetricsHelper) GenericResponse() *logical.Response {
 | |
| 	resp := &logical.Response{
 | |
| 		Data: map[string]interface{}{
 | |
| 			logical.HTTPContentType: ErrorContentType,
 | |
| 			logical.HTTPStatusCode:  http.StatusBadRequest,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	summary, err := m.inMemSink.DisplayMetrics(nil, nil)
 | |
| 	if err != nil {
 | |
| 		resp.Data[logical.HTTPRawBody] = fmt.Sprintf("error while fetching the in-memory metrics: %s", err)
 | |
| 		return resp
 | |
| 	}
 | |
| 	content, err := json.Marshal(summary)
 | |
| 	if err != nil {
 | |
| 		resp.Data[logical.HTTPRawBody] = fmt.Sprintf("error while marshalling the in-memory metrics: %s", err)
 | |
| 		return resp
 | |
| 	}
 | |
| 	resp.Data[logical.HTTPContentType] = "application/json"
 | |
| 	resp.Data[logical.HTTPRawBody] = content
 | |
| 	resp.Data[logical.HTTPStatusCode] = http.StatusOK
 | |
| 	return resp
 | |
| }
 |