mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-02-09 13:37:12 +02:00
Make the default label encoding unique (#508)
* Make the default label encoding unique * More tests * Cleanup
This commit is contained in:
parent
ffdbc05665
commit
148c9ce5ba
@ -189,6 +189,29 @@ func TestSDKLabelEncoder(t *testing.T) {
|
|||||||
|
|
||||||
func TestDefaultLabelEncoder(t *testing.T) {
|
func TestDefaultLabelEncoder(t *testing.T) {
|
||||||
encoder := sdk.NewDefaultLabelEncoder()
|
encoder := sdk.NewDefaultLabelEncoder()
|
||||||
|
|
||||||
encoded := encoder.Encode([]core.KeyValue{key.String("A", "B"), key.String("C", "D")})
|
encoded := encoder.Encode([]core.KeyValue{key.String("A", "B"), key.String("C", "D")})
|
||||||
require.Equal(t, `A=B,C=D`, encoded)
|
require.Equal(t, `A=B,C=D`, encoded)
|
||||||
|
|
||||||
|
encoded = encoder.Encode([]core.KeyValue{key.String("A", "B,c=d"), key.String(`C\`, "D")})
|
||||||
|
require.Equal(t, `A=B\,c\=d,C\\=D`, encoded)
|
||||||
|
|
||||||
|
encoded = encoder.Encode([]core.KeyValue{key.String(`\`, `=`), key.String(`,`, `\`)})
|
||||||
|
require.Equal(t, `\\=\=,\,=\\`, encoded)
|
||||||
|
|
||||||
|
// Note: the label encoder does not sort or de-dup values,
|
||||||
|
// that is done in Labels(...).
|
||||||
|
encoded = encoder.Encode([]core.KeyValue{
|
||||||
|
key.Int("I", 1),
|
||||||
|
key.Uint("U", 1),
|
||||||
|
key.Int32("I32", 1),
|
||||||
|
key.Uint32("U32", 1),
|
||||||
|
key.Int64("I64", 1),
|
||||||
|
key.Uint64("U64", 1),
|
||||||
|
key.Float64("F64", 1),
|
||||||
|
key.Float64("F64", 1),
|
||||||
|
key.String("S", "1"),
|
||||||
|
key.Bool("B", true),
|
||||||
|
})
|
||||||
|
require.Equal(t, "I=1,U=1,I32=1,U32=1,I64=1,U64=1,F64=1,F64=1,S=1,B=true", encoded)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,13 @@ import (
|
|||||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// escapeChar is used to ensure uniqueness of the label encoding where
|
||||||
|
// keys or values contain either '=' or ','. Since there is no parser
|
||||||
|
// needed for this encoding and its only requirement is to be unique,
|
||||||
|
// this choice is arbitrary. Users will see these in some exporters
|
||||||
|
// (e.g., stdout), so the backslash ('\') is used a conventional choice.
|
||||||
|
const escapeChar = '\\'
|
||||||
|
|
||||||
type defaultLabelEncoder struct {
|
type defaultLabelEncoder struct {
|
||||||
// pool is a pool of labelset builders. The buffers in this
|
// pool is a pool of labelset builders. The buffers in this
|
||||||
// pool grow to a size that most label encodings will not
|
// pool grow to a size that most label encodings will not
|
||||||
@ -54,9 +61,25 @@ func (d *defaultLabelEncoder) Encode(labels []core.KeyValue) string {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
_, _ = buf.WriteRune(',')
|
_, _ = buf.WriteRune(',')
|
||||||
}
|
}
|
||||||
_, _ = buf.WriteString(string(kv.Key))
|
copyAndEscape(buf, string(kv.Key))
|
||||||
|
|
||||||
_, _ = buf.WriteRune('=')
|
_, _ = buf.WriteRune('=')
|
||||||
_, _ = buf.WriteString(kv.Value.Emit())
|
|
||||||
|
if kv.Value.Type() == core.STRING {
|
||||||
|
copyAndEscape(buf, kv.Value.AsString())
|
||||||
|
} else {
|
||||||
|
_, _ = buf.WriteString(kv.Value.Emit())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyAndEscape(buf *bytes.Buffer, val string) {
|
||||||
|
for _, ch := range val {
|
||||||
|
switch ch {
|
||||||
|
case '=', ',', escapeChar:
|
||||||
|
buf.WriteRune(escapeChar)
|
||||||
|
}
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user