mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-12 05:32:36 +00:00
+27
-47
@@ -12,17 +12,14 @@ import (
|
||||
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
|
||||
)
|
||||
|
||||
// Cache for sorted label keys to avoid repeated sorting
|
||||
var sortedLabelKeysCache = struct {
|
||||
m map[string][]string
|
||||
sync.RWMutex
|
||||
}{m: make(map[string][]string)}
|
||||
m sync.Map
|
||||
}{}
|
||||
|
||||
func (ms *MetricsSetup) get_metrics_name(name string, labels map[string]string) string {
|
||||
const unknownPodName = "unknown"
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Prepare default labels without initializing a new map
|
||||
podName := getPodName()
|
||||
if labels == nil {
|
||||
labels = defaultLabels(podName)
|
||||
@@ -30,18 +27,16 @@ func (ms *MetricsSetup) get_metrics_name(name string, labels map[string]string)
|
||||
ensureDefaultLabels(&labels, podName)
|
||||
}
|
||||
|
||||
// Prefix handling
|
||||
if ms.metrics_prefix != "" {
|
||||
buf.WriteString(ms.metrics_prefix)
|
||||
buf.WriteString("_")
|
||||
buf.WriteByte('_')
|
||||
}
|
||||
buf.WriteString(name)
|
||||
|
||||
// Append labels if any
|
||||
if len(labels) > 0 {
|
||||
buf.WriteString("{")
|
||||
buf.WriteByte('{')
|
||||
appendSortedLabels(&buf, labels)
|
||||
buf.WriteString("}")
|
||||
buf.WriteByte('}')
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
@@ -78,34 +73,30 @@ func appendSortedLabels(buf *bytes.Buffer, labels map[string]string) {
|
||||
keys := getSortedKeys(labels)
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
buf.WriteString(",")
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
buf.WriteString(k)
|
||||
buf.WriteString("=\"")
|
||||
buf.WriteString(`="`)
|
||||
buf.WriteString(labels[k])
|
||||
buf.WriteString("\"")
|
||||
buf.WriteByte('"')
|
||||
}
|
||||
}
|
||||
|
||||
func getSortedKeys(labels map[string]string) []string {
|
||||
labelsKey := labelsToString(labels)
|
||||
|
||||
sortedLabelKeysCache.RLock()
|
||||
keys, exists := sortedLabelKeysCache.m[labelsKey]
|
||||
sortedLabelKeysCache.RUnlock()
|
||||
|
||||
if !exists {
|
||||
keys = make([]string, 0, len(labels))
|
||||
for k := range labels {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
sortedLabelKeysCache.Lock()
|
||||
sortedLabelKeysCache.m[labelsKey] = keys
|
||||
sortedLabelKeysCache.Unlock()
|
||||
if keys, ok := sortedLabelKeysCache.m.Load(labelsKey); ok {
|
||||
return keys.([]string)
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(labels))
|
||||
for k := range labels {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
sortedLabelKeysCache.m.Store(labelsKey, keys)
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
@@ -113,29 +104,24 @@ func labelsToString(labels map[string]string) string {
|
||||
var sb strings.Builder
|
||||
for k, v := range labels {
|
||||
sb.WriteString(k)
|
||||
sb.WriteString("=")
|
||||
sb.WriteByte('=')
|
||||
sb.WriteString(v)
|
||||
sb.WriteString(";")
|
||||
sb.WriteByte(';')
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// validate_metrics_name validates the name of the metric to adhere to the Prometheus naming conventions
|
||||
// https://prometheus.io/docs/practices/naming/
|
||||
func validate_metrics_name(name string) error {
|
||||
cleanedName := clean_metric_name(name)
|
||||
|
||||
// Trim leading and trailing underscores
|
||||
finalName := strings.Trim(cleanedName, "_")
|
||||
|
||||
// Check if the processed name matches the original input
|
||||
if finalName != name {
|
||||
return fmt.Errorf("Invalid metric name: %s, expected %s", name, finalName)
|
||||
return fmt.Errorf("invalid metric name: %s, expected %s", name, finalName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// clean_metric_name processes the metric name according to Prometheus naming conventions
|
||||
func clean_metric_name(name string) string {
|
||||
var buf bytes.Buffer
|
||||
lastWasUnderscore := false
|
||||
@@ -144,31 +130,27 @@ func clean_metric_name(name string) string {
|
||||
if is_allowed_rune(r) {
|
||||
if is_special_rune(r) {
|
||||
if lastWasUnderscore {
|
||||
continue // Skip if the previous character was also an underscore
|
||||
continue
|
||||
}
|
||||
r = '_' // Convert spaces and special characters to underscores
|
||||
r = '_'
|
||||
lastWasUnderscore = true
|
||||
} else {
|
||||
lastWasUnderscore = false
|
||||
}
|
||||
buf.WriteRune(r)
|
||||
} else if !lastWasUnderscore {
|
||||
buf.WriteRune('_')
|
||||
buf.WriteByte('_')
|
||||
lastWasUnderscore = true
|
||||
}
|
||||
}
|
||||
|
||||
// Remove trailing underscore
|
||||
result := buf.String()
|
||||
return strings.Trim(result, "_")
|
||||
return strings.Trim(buf.String(), "_")
|
||||
}
|
||||
|
||||
// is_allowed_rune checks if the rune is allowed in the metric name
|
||||
func is_allowed_rune(r rune) bool {
|
||||
return unicode.IsLetter(r) || unicode.IsDigit(r) || r == ' ' || r == '_'
|
||||
}
|
||||
|
||||
// is_special_rune checks if the rune is a space or an underscore
|
||||
func is_special_rune(r rune) bool {
|
||||
return r == ' ' || r == '_'
|
||||
}
|
||||
@@ -178,14 +160,12 @@ func compile_metrics_with_labels(name string, labels map[string]string) string {
|
||||
|
||||
buf.WriteString(name)
|
||||
|
||||
// Collect keys and sort them
|
||||
keys := getSortedKeys(labels)
|
||||
|
||||
// Append sorted key-value pairs to the buffer
|
||||
for _, k := range keys {
|
||||
buf.WriteString("_")
|
||||
buf.WriteByte('_')
|
||||
buf.WriteString(k)
|
||||
buf.WriteString("_")
|
||||
buf.WriteByte('_')
|
||||
buf.WriteString(labels[k])
|
||||
}
|
||||
|
||||
|
||||
+16
-20
@@ -1,6 +1,3 @@
|
||||
// Package `libpack_monitoring` provides and easy way to add prometheus metrics to your application.
|
||||
// It also provides a way to add custom metrics to the already started prometheus registry.
|
||||
|
||||
package libpack_monitoring
|
||||
|
||||
import (
|
||||
@@ -22,9 +19,7 @@ type MetricsSetup struct {
|
||||
metrics_prefix string
|
||||
}
|
||||
|
||||
var (
|
||||
log *libpack_logger.Logger
|
||||
)
|
||||
var log = libpack_logger.New().SetMinLogLevel(libpack_logger.LEVEL_INFO)
|
||||
|
||||
type InitConfig struct {
|
||||
PurgeOnCrawl bool
|
||||
@@ -32,11 +27,11 @@ type InitConfig struct {
|
||||
}
|
||||
|
||||
func NewMonitoring(ic *InitConfig) *MetricsSetup {
|
||||
log = libpack_logger.New().SetMinLogLevel(libpack_logger.LEVEL_INFO)
|
||||
ms := &MetricsSetup{ic: ic}
|
||||
ms.metrics_set = metrics.NewSet()
|
||||
ms.metrics_set_custom = metrics.NewSet()
|
||||
// if not testing, start the prometheus endpoint
|
||||
ms := &MetricsSetup{
|
||||
ic: ic,
|
||||
metrics_set: metrics.NewSet(),
|
||||
metrics_set_custom: metrics.NewSet(),
|
||||
}
|
||||
|
||||
if flag.Lookup("test.v") == nil {
|
||||
go ms.startPrometheusEndpoint()
|
||||
@@ -60,9 +55,11 @@ func (ms *MetricsSetup) startPrometheusEndpoint() {
|
||||
AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION),
|
||||
})
|
||||
app.Get("/metrics", ms.metricsEndpoint)
|
||||
err := app.Listen(fmt.Sprintf(":%d", envutil.GetInt("MONITORING_PORT", 9393)))
|
||||
if err != nil {
|
||||
fmt.Println("Can't start the service: ", err)
|
||||
if err := app.Listen(fmt.Sprintf(":%d", envutil.GetInt("MONITORING_PORT", 9393))); err != nil {
|
||||
log.Critical(&libpack_logger.LogMessage{
|
||||
Message: "Can't start the service",
|
||||
Pairs: map[string]interface{}{"error": err},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +82,7 @@ func (ms *MetricsSetup) ListActiveMetrics() []string {
|
||||
}
|
||||
|
||||
func (ms *MetricsSetup) RegisterMetricsGauge(metric_name string, labels map[string]string, val float64) *metrics.Gauge {
|
||||
if validate_metrics_name(metric_name) != nil {
|
||||
if err := validate_metrics_name(metric_name); err != nil {
|
||||
log.Critical(&libpack_logger.LogMessage{
|
||||
Message: "RegisterMetricsGauge() error",
|
||||
Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name},
|
||||
@@ -93,13 +90,12 @@ func (ms *MetricsSetup) RegisterMetricsGauge(metric_name string, labels map[stri
|
||||
return nil
|
||||
}
|
||||
return ms.metrics_set_custom.GetOrCreateGauge(ms.get_metrics_name(metric_name, labels), func() float64 {
|
||||
// get current value of the gauge and add val to it
|
||||
return val
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MetricsSetup) RegisterMetricsCounter(metric_name string, labels map[string]string) *metrics.Counter {
|
||||
if validate_metrics_name(metric_name) != nil {
|
||||
if err := validate_metrics_name(metric_name); err != nil {
|
||||
log.Critical(&libpack_logger.LogMessage{
|
||||
Message: "RegisterMetricsCounter() error",
|
||||
Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name},
|
||||
@@ -113,7 +109,7 @@ func (ms *MetricsSetup) RegisterMetricsCounter(metric_name string, labels map[st
|
||||
}
|
||||
|
||||
func (ms *MetricsSetup) RegisterFloatCounter(metric_name string, labels map[string]string) *metrics.FloatCounter {
|
||||
if validate_metrics_name(metric_name) != nil {
|
||||
if err := validate_metrics_name(metric_name); err != nil {
|
||||
log.Critical(&libpack_logger.LogMessage{
|
||||
Message: "RegisterFloatCounter() error",
|
||||
Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name},
|
||||
@@ -124,7 +120,7 @@ func (ms *MetricsSetup) RegisterFloatCounter(metric_name string, labels map[stri
|
||||
}
|
||||
|
||||
func (ms *MetricsSetup) RegisterMetricsSummary(metric_name string, labels map[string]string) *metrics.Summary {
|
||||
if validate_metrics_name(metric_name) != nil {
|
||||
if err := validate_metrics_name(metric_name); err != nil {
|
||||
log.Critical(&libpack_logger.LogMessage{
|
||||
Message: "RegisterMetricsSummary() error",
|
||||
Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name},
|
||||
@@ -135,7 +131,7 @@ func (ms *MetricsSetup) RegisterMetricsSummary(metric_name string, labels map[st
|
||||
}
|
||||
|
||||
func (ms *MetricsSetup) RegisterMetricsHistogram(metric_name string, labels map[string]string) *metrics.Histogram {
|
||||
if validate_metrics_name(metric_name) != nil {
|
||||
if err := validate_metrics_name(metric_name); err != nil {
|
||||
log.Critical(&libpack_logger.LogMessage{
|
||||
Message: "RegisterMetricsHistogram() error",
|
||||
Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name},
|
||||
|
||||
Reference in New Issue
Block a user