2019-02-14 20:46:59 +00:00
|
|
|
package metricsutil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"github.com/armon/go-metrics"
|
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/common/expfmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
OpenMetricsMIMEType = "application/openmetrics-text"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
PrometheusMetricFormat = "prometheus"
|
|
|
|
)
|
|
|
|
|
|
|
|
type MetricsHelper struct {
|
2019-02-20 20:12:21 +00:00
|
|
|
inMemSink *metrics.InmemSink
|
2019-02-14 20:46:59 +00:00
|
|
|
PrometheusEnabled bool
|
|
|
|
}
|
|
|
|
|
2019-02-20 20:12:21 +00:00
|
|
|
func NewMetricsHelper(inMem *metrics.InmemSink, enablePrometheus bool) *MetricsHelper {
|
2019-02-14 20:46:59 +00:00
|
|
|
return &MetricsHelper{inMem, enablePrometheus}
|
|
|
|
}
|
|
|
|
|
2019-02-20 20:12:21 +00:00
|
|
|
func FormatFromRequest(req *logical.Request) string {
|
2019-02-14 20:46:59 +00:00
|
|
|
acceptHeaders := req.Headers["Accept"]
|
|
|
|
if len(acceptHeaders) > 0 {
|
|
|
|
acceptHeader := acceptHeaders[0]
|
|
|
|
if strings.HasPrefix(acceptHeader, OpenMetricsMIMEType) {
|
|
|
|
return "prometheus"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MetricsHelper) ResponseForFormat(format string) (*logical.Response, error) {
|
|
|
|
switch format {
|
|
|
|
case PrometheusMetricFormat:
|
|
|
|
return m.PrometheusResponse()
|
|
|
|
case "":
|
|
|
|
return m.GenericResponse()
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("metric response format \"%s\" unknown", format)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MetricsHelper) PrometheusResponse() (*logical.Response, error) {
|
|
|
|
if !m.PrometheusEnabled {
|
|
|
|
return &logical.Response{
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
logical.HTTPContentType: "text/plain",
|
|
|
|
logical.HTTPRawBody: "prometheus is not enabled",
|
|
|
|
logical.HTTPStatusCode: 400,
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
metricsFamilies, err := prometheus.DefaultGatherer.Gather()
|
|
|
|
if err != nil && len(metricsFamilies) == 0 {
|
|
|
|
return nil, fmt.Errorf("no prometheus metrics could be decoded: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize a byte buffer.
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
defer buf.Reset()
|
|
|
|
|
|
|
|
e := expfmt.NewEncoder(buf, expfmt.FmtText)
|
|
|
|
for _, mf := range metricsFamilies {
|
|
|
|
err := e.Encode(mf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error during the encoding of metrics: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &logical.Response{
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
logical.HTTPContentType: string(expfmt.FmtText),
|
|
|
|
logical.HTTPRawBody: buf.Bytes(),
|
|
|
|
logical.HTTPStatusCode: 200,
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MetricsHelper) GenericResponse() (*logical.Response, error) {
|
2019-02-20 20:12:21 +00:00
|
|
|
summary, err := m.inMemSink.DisplayMetrics(nil, nil)
|
2019-02-14 20:46:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error while fetching the in-memory metrics: %s", err)
|
|
|
|
}
|
|
|
|
content, err := json.Marshal(summary)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error while marshalling the in-memory metrics: %s", err)
|
|
|
|
}
|
|
|
|
return &logical.Response{
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
logical.HTTPContentType: "application/json",
|
|
|
|
logical.HTTPRawBody: content,
|
|
|
|
logical.HTTPStatusCode: 200,
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|