mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-23 06:47:26 +00:00
divide long functions, replace strings.builder with bytes.buffer. (#13)
Co-authored-by: Chris Clayton <[email protected]>
This commit is contained in:
co-authored by
Chris Clayton
parent
de31912d2f
commit
12e4237997
+136
-72
@@ -1,129 +1,193 @@
|
|||||||
package libpack_monitoring
|
package libpack_monitoring
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
|
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ms *MetricsSetup) get_metrics_name(name string, labels map[string]string) (complete_name string) {
|
// Cache for sorted label keys to avoid repeated sorting
|
||||||
|
var sortedLabelKeysCache = struct {
|
||||||
|
sync.RWMutex
|
||||||
|
m map[string][]string
|
||||||
|
}{m: make(map[string][]string)}
|
||||||
|
|
||||||
|
func (ms *MetricsSetup) get_metrics_name(name string, labels map[string]string) string {
|
||||||
const unknownPodName = "unknown"
|
const unknownPodName = "unknown"
|
||||||
var sb strings.Builder
|
var buf bytes.Buffer
|
||||||
|
|
||||||
// Prepare default labels without initializing a new map
|
// Prepare default labels without initializing a new map
|
||||||
podName := unknownPodName
|
podName := getPodName()
|
||||||
if hn, err := os.Hostname(); err == nil {
|
|
||||||
podName = hn
|
|
||||||
}
|
|
||||||
if labels == nil {
|
if labels == nil {
|
||||||
labels = map[string]string{
|
labels = defaultLabels(podName)
|
||||||
"microservice": libpack_config.PKG_NAME,
|
|
||||||
"pod": podName,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if _, exists := labels["microservice"]; !exists {
|
ensureDefaultLabels(&labels, podName)
|
||||||
labels["microservice"] = libpack_config.PKG_NAME
|
|
||||||
}
|
|
||||||
if _, exists := labels["pod"]; !exists {
|
|
||||||
labels["pod"] = podName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefix handling
|
// Prefix handling
|
||||||
if ms.metrics_prefix != "" {
|
if ms.metrics_prefix != "" {
|
||||||
sb.WriteString(ms.metrics_prefix)
|
buf.WriteString(ms.metrics_prefix)
|
||||||
sb.WriteString("_")
|
buf.WriteString("_")
|
||||||
}
|
}
|
||||||
sb.WriteString(name)
|
buf.WriteString(name)
|
||||||
|
|
||||||
// Append labels if any
|
// Append labels if any
|
||||||
if len(labels) > 0 {
|
if len(labels) > 0 {
|
||||||
sb.WriteString("{")
|
buf.WriteString("{")
|
||||||
|
appendSortedLabels(&buf, labels)
|
||||||
|
buf.WriteString("}")
|
||||||
|
}
|
||||||
|
|
||||||
keys := make([]string, 0, len(labels))
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPodName() string {
|
||||||
|
const unknownPodName = "unknown"
|
||||||
|
if hn, err := os.Hostname(); err == nil {
|
||||||
|
return hn
|
||||||
|
}
|
||||||
|
return unknownPodName
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultLabels(podName string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"microservice": libpack_config.PKG_NAME,
|
||||||
|
"pod": podName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureDefaultLabels(labels *map[string]string, podName string) {
|
||||||
|
if *labels == nil {
|
||||||
|
*labels = make(map[string]string)
|
||||||
|
}
|
||||||
|
if _, exists := (*labels)["microservice"]; !exists {
|
||||||
|
(*labels)["microservice"] = libpack_config.PKG_NAME
|
||||||
|
}
|
||||||
|
if _, exists := (*labels)["pod"]; !exists {
|
||||||
|
(*labels)["pod"] = podName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendSortedLabels(buf *bytes.Buffer, labels map[string]string) {
|
||||||
|
keys := getSortedKeys(labels)
|
||||||
|
for i, k := range keys {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(",")
|
||||||
|
}
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteString("=\"")
|
||||||
|
buf.WriteString(labels[k])
|
||||||
|
buf.WriteString("\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
for k := range labels {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
|
||||||
for i, k := range keys {
|
sortedLabelKeysCache.Lock()
|
||||||
if i > 0 {
|
sortedLabelKeysCache.m[labelsKey] = keys
|
||||||
sb.WriteString(",")
|
sortedLabelKeysCache.Unlock()
|
||||||
}
|
|
||||||
sb.WriteString(k)
|
|
||||||
sb.WriteString("=\"")
|
|
||||||
sb.WriteString(labels[k])
|
|
||||||
sb.WriteString("\"")
|
|
||||||
}
|
|
||||||
sb.WriteString("}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func labelsToString(labels map[string]string) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
for k, v := range labels {
|
||||||
|
sb.WriteString(k)
|
||||||
|
sb.WriteString("=")
|
||||||
|
sb.WriteString(v)
|
||||||
|
sb.WriteString(";")
|
||||||
|
}
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate_metrics_name validates the name of the metric to adhere to the Prometheus naming conventions
|
// validate_metrics_name validates the name of the metric to adhere to the Prometheus naming conventions
|
||||||
// https://prometheus.io/docs/practices/naming/
|
// https://prometheus.io/docs/practices/naming/
|
||||||
func validate_metrics_name(name string) error {
|
func validate_metrics_name(name string) error {
|
||||||
var sb strings.Builder // Use strings.Builder for efficient string concatenation
|
cleanedName := clean_metric_name(name)
|
||||||
|
|
||||||
// Track if the last character was an underscore to avoid duplicate underscores
|
|
||||||
lastWasUnderscore := false
|
|
||||||
|
|
||||||
for _, r := range name {
|
|
||||||
// Convert spaces to underscores and skip non-alphanumeric characters except underscores
|
|
||||||
if r == ' ' || (unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_') {
|
|
||||||
if r == ' ' || r == '_' {
|
|
||||||
if lastWasUnderscore {
|
|
||||||
continue // Skip if the previous character was also an underscore
|
|
||||||
}
|
|
||||||
r = '_' // Convert spaces to underscores
|
|
||||||
lastWasUnderscore = true
|
|
||||||
} else {
|
|
||||||
lastWasUnderscore = false
|
|
||||||
}
|
|
||||||
sb.WriteRune(r) // Add valid characters to the builder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Trim leading and trailing underscores
|
// Trim leading and trailing underscores
|
||||||
name_new := strings.Trim(sb.String(), "_")
|
finalName := strings.Trim(cleanedName, "_")
|
||||||
|
|
||||||
// Check if the processed name matches the original input
|
// Check if the processed name matches the original input
|
||||||
if name_new != name {
|
if finalName != name {
|
||||||
return fmt.Errorf("Invalid metric name: %s, expected %s", name, name_new)
|
return fmt.Errorf("Invalid metric name: %s, expected %s", name, finalName)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func compile_metrics_with_labels(name string, labels map[string]string) string {
|
// clean_metric_name processes the metric name according to Prometheus naming conventions
|
||||||
var totalLength int
|
func clean_metric_name(name string) string {
|
||||||
totalLength += len(name)
|
var buf bytes.Buffer
|
||||||
for k, v := range labels {
|
lastWasUnderscore := false
|
||||||
totalLength += len(k) + len(v) + 2
|
|
||||||
|
for _, r := range name {
|
||||||
|
if is_allowed_rune(r) {
|
||||||
|
if is_special_rune(r) {
|
||||||
|
if lastWasUnderscore {
|
||||||
|
continue // Skip if the previous character was also an underscore
|
||||||
|
}
|
||||||
|
r = '_' // Convert spaces and special characters to underscores
|
||||||
|
lastWasUnderscore = true
|
||||||
|
} else {
|
||||||
|
lastWasUnderscore = false
|
||||||
|
}
|
||||||
|
buf.WriteRune(r)
|
||||||
|
} else if !lastWasUnderscore {
|
||||||
|
buf.WriteRune('_')
|
||||||
|
lastWasUnderscore = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sb strings.Builder
|
// Remove trailing underscore
|
||||||
sb.Grow(totalLength + 1)
|
result := buf.String()
|
||||||
|
return strings.Trim(result, "_")
|
||||||
|
}
|
||||||
|
|
||||||
sb.WriteString(name)
|
// 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 == '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
func compile_metrics_with_labels(name string, labels map[string]string) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
buf.WriteString(name)
|
||||||
|
|
||||||
// Collect keys and sort them
|
// Collect keys and sort them
|
||||||
keys := make([]string, 0, len(labels))
|
keys := getSortedKeys(labels)
|
||||||
for k := range labels {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
// Append sorted key-value pairs to the builder
|
// Append sorted key-value pairs to the buffer
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
sb.WriteString("_")
|
buf.WriteString("_")
|
||||||
sb.WriteString(k)
|
buf.WriteString(k)
|
||||||
sb.WriteString("_")
|
buf.WriteString("_")
|
||||||
sb.WriteString(labels[k])
|
buf.WriteString(labels[k])
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package libpack_monitoring
|
package libpack_monitoring
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
|
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
|
||||||
@@ -134,10 +133,96 @@ func TestValidateMetricsName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPodName() string {
|
func TestCleanMetricName(t *testing.T) {
|
||||||
podName, err := os.Hostname()
|
tests := []struct {
|
||||||
if err != nil {
|
input string
|
||||||
return "unknown"
|
expected string
|
||||||
|
}{
|
||||||
|
{"valid metric name", "valid_metric_name"},
|
||||||
|
{"valid@metric#name!", "valid_metric_name"},
|
||||||
|
{"__valid__metric__name__", "valid_metric_name"},
|
||||||
|
{" valid metric name ", "valid_metric_name"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.input, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.expected, clean_metric_name(tt.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultLabels(t *testing.T) {
|
||||||
|
podName := "test-pod"
|
||||||
|
libpack_config.PKG_NAME = "example_microservice"
|
||||||
|
expected := map[string]string{
|
||||||
|
"microservice": "example_microservice",
|
||||||
|
"pod": podName,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, defaultLabels(podName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnsureDefaultLabels(t *testing.T) {
|
||||||
|
podName := "test-pod"
|
||||||
|
libpack_config.PKG_NAME = "example_microservice"
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
inputLabels map[string]string
|
||||||
|
expectedLabels map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Nil labels",
|
||||||
|
inputLabels: nil,
|
||||||
|
expectedLabels: map[string]string{"microservice": "example_microservice", "pod": podName},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty labels",
|
||||||
|
inputLabels: map[string]string{},
|
||||||
|
expectedLabels: map[string]string{"microservice": "example_microservice", "pod": podName},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Partial labels",
|
||||||
|
inputLabels: map[string]string{"microservice": "test_service"},
|
||||||
|
expectedLabels: map[string]string{"microservice": "test_service", "pod": podName},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Complete labels",
|
||||||
|
inputLabels: map[string]string{"microservice": "test_service", "pod": "custom_pod"},
|
||||||
|
expectedLabels: map[string]string{"microservice": "test_service", "pod": "custom_pod"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ensureDefaultLabels(&tt.inputLabels, podName)
|
||||||
|
assert.Equal(t, tt.expectedLabels, tt.inputLabels)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLabelsToString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
labels map[string]string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
labels: map[string]string{"key1": "value1", "key2": "value2"},
|
||||||
|
expected: "key1=value1;key2=value2;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labels: map[string]string{"a": "1", "b": "2"},
|
||||||
|
expected: "a=1;b=2;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labels: map[string]string{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.expected, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.expected, labelsToString(tt.labels))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return podName
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user