diff --git a/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice.go b/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice.go index 03eed997c406..b5319f1a2227 100644 --- a/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice.go +++ b/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice.go @@ -51,9 +51,13 @@ func (kv *KeyValues) Swap(i, j int) { kv.keyValues[i], kv.keyValues[j] = kv.keyValues[j], kv.keyValues[i] } func (kv *KeyValues) Less(i, j int) bool { return kv.keyValues[i].Key < kv.keyValues[j].Key } -func (kv *KeyValues) Sort() { sort.Sort(kv) } + +func (kv *KeyValues) Sort() { + sort.Sort(kv) +} func (kv *KeyValues) Replace(key, value string) { + key = sanitize(key) findIndex := sort.Search(len(kv.keyValues), func(index int) bool { return kv.keyValues[index].Key >= key }) @@ -64,14 +68,12 @@ func (kv *KeyValues) Replace(key, value string) { func (kv *KeyValues) AppendMap(mapVal map[string]string) { for key, value := range mapVal { - kv.keyValues = append(kv.keyValues, KeyValue{ - Key: key, - Value: value, - }) + kv.Append(key, value) } } func (kv *KeyValues) Append(key, value string) { + key = sanitize(key) kv.keyValues = append(kv.keyValues, KeyValue{ key, value, diff --git a/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice_test.go b/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice_test.go index 80ab4ede2462..786bb0f8f22f 100644 --- a/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice_test.go +++ b/exporter/alibabacloudlogserviceexporter/metricsdata_to_logservice_test.go @@ -170,3 +170,16 @@ func TestMetricCornerCases(t *testing.T) { }) assert.Equal(t, label.String(), "a#$#b") } + +func TestMetricLabelSanitize(t *testing.T) { + var label KeyValues + label.Append("_test", "key_test") + label.Append("0test", "key_0test") + label.AppendMap(map[string]string{ + "test_normal": "test_normal", + "0test": "key_0test", + }) + assert.Equal(t, label.String(), "key_test#$#key_test|key_0test#$#key_0test|test_normal#$#test_normal|key_0test#$#key_0test") + label.Sort() + assert.Equal(t, label.String(), "key_0test#$#key_0test|key_0test#$#key_0test|key_test#$#key_test|test_normal#$#test_normal") +} diff --git a/exporter/alibabacloudlogserviceexporter/sanitize.go b/exporter/alibabacloudlogserviceexporter/sanitize.go new file mode 100644 index 000000000000..8749513641e0 --- /dev/null +++ b/exporter/alibabacloudlogserviceexporter/sanitize.go @@ -0,0 +1,52 @@ +// Copyright 2020, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package alibabacloudlogserviceexporter + +import ( + "strings" + "unicode" +) + +// The code for sanitize is mostly copied from: +// https://github.com/open-telemetry/opentelemetry-collector/blob/2e84285efc665798d76773b9901727e8836e9d8f/exporter/prometheusexporter/sanitize.go + +// sanitize replaces non-alphanumeric characters with underscores in s. +func sanitize(s string) string { + if len(s) == 0 { + return s + } + + // Note: No length limit for label keys because Prometheus doesn't + // define a length limit, thus we should NOT be truncating label keys. + // See https://github.com/orijtech/prometheus-go-metrics-exporter/issues/4. + + s = strings.Map(sanitizeRune, s) + if unicode.IsDigit(rune(s[0])) { + s = "key_" + s + } + if s[0] == '_' { + s = "key" + s + } + return s +} + +// converts anything that is not a letter or digit to an underscore +func sanitizeRune(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) { + return r + } + // Everything else turns into an underscore + return '_' +} diff --git a/exporter/alibabacloudlogserviceexporter/sanitize_test.go b/exporter/alibabacloudlogserviceexporter/sanitize_test.go new file mode 100644 index 000000000000..1bfdd7c5fbf9 --- /dev/null +++ b/exporter/alibabacloudlogserviceexporter/sanitize_test.go @@ -0,0 +1,32 @@ +// Copyright 2020, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package alibabacloudlogserviceexporter + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// The code for sanitize is mostly copied from: +// https://github.com/open-telemetry/opentelemetry-collector/blob/2e84285efc665798d76773b9901727e8836e9d8f/exporter/prometheusexporter/sanitize_test.go + +func TestSanitize(t *testing.T) { + require.Equal(t, "", sanitize(""), "") + require.Equal(t, "key_test", sanitize("_test")) + require.Equal(t, "key_0test", sanitize("0test")) + require.Equal(t, "test", sanitize("test")) + require.Equal(t, "test__", sanitize("test_/")) +}