diff --git a/monitoring/helpers.go b/monitoring/helpers.go index ed1e4cc..4cee6f9 100644 --- a/monitoring/helpers.go +++ b/monitoring/helpers.go @@ -3,57 +3,78 @@ package libpack_monitoring import ( "fmt" "os" - "sort" "strings" + "unicode" libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" ) func (ms *MetricsSetup) get_metrics_name(name string, labels map[string]string) (complete_name string) { - var err error if labels == nil { labels = make(map[string]string) } + + // Adding default labels labels["microservice"] = libpack_config.PKG_NAME - labels["pod"], err = os.Hostname() - if err != nil { + if podName, err := os.Hostname(); err == nil { + labels["pod"] = podName + } else { labels["pod"] = "unknown" } + var sb strings.Builder if ms.metrics_prefix != "" { - complete_name = ms.metrics_prefix + "_" + name - } else { - complete_name = name + sb.WriteString(ms.metrics_prefix) + sb.WriteString("_") } - if labels != nil { - keys := make([]string, 0, len(labels)) - for k := range labels { - keys = append(keys, k) + sb.WriteString(name) + + if len(labels) > 0 { + sb.WriteString("{") + first := true + for k, v := range labels { + if !first { + sb.WriteString(",") + } + sb.WriteString(k) + sb.WriteString("=\"") + sb.WriteString(v) + sb.WriteString("\"") + first = false } - sort.Strings(keys) - complete_name += "{" - for _, k := range keys { - complete_name += k + "=\"" + labels[k] + "\"," - } - complete_name = strings.TrimSuffix(complete_name, ",") - complete_name += "}" + sb.WriteString("}") } - return + + 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 { - // replace all spaces with underscores and remove all other non-alphanumeric characters - name_new := strings.ReplaceAll(name, " ", "_") - name_new = strings.Map(func(r rune) rune { - if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' { - return r + var sb strings.Builder // Use strings.Builder for efficient string concatenation + + // 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 } - return -1 - }, name_new) - name_new = strings.ReplaceAll(name_new, "__", "_") - name_new = strings.Trim(name_new, "_") + } + // Trim leading and trailing underscores + name_new := strings.Trim(sb.String(), "_") + + // Check if the processed name matches the original input if name_new != name { return fmt.Errorf("Invalid metric name: %s, expected %s", name, name_new) }