mirror of
https://github.com/SigNoz/signoz.git
synced 2026-11-05 16:00:41 +00:00
Compare commits
2 Commits
main
...
tvats-fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
219e6ac54c | ||
|
|
a04f65a4f2 |
@@ -15826,13 +15826,22 @@ paths:
|
||||
where it is case-sensitive and % and _ act as wildcards. For keys, a context
|
||||
prefix such as resource.service.name sets fieldContext when it is empty.'
|
||||
type: string
|
||||
- description: The key whose values to return, such as service.name. A context
|
||||
prefix such as resource.service.name sets fieldContext when it is empty.
|
||||
- description: 'The key whose values to return, such as service.name. With fieldContext
|
||||
set, it is the stored name as is. Otherwise a context prefix such as resource.service.name
|
||||
sets the context and a type suffix such as service.name:string sets fieldDataType
|
||||
when that is empty; as in filter expressions, a prefixed name also matches
|
||||
a key stored under the full name, whose values come first: log.level returns
|
||||
values of a key named log.level, then of the log field level.'
|
||||
in: query
|
||||
name: name
|
||||
schema:
|
||||
description: The key whose values to return, such as service.name. A context
|
||||
prefix such as resource.service.name sets fieldContext when it is empty.
|
||||
description: 'The key whose values to return, such as service.name. With
|
||||
fieldContext set, it is the stored name as is. Otherwise a context prefix
|
||||
such as resource.service.name sets the context and a type suffix such
|
||||
as service.name:string sets fieldDataType when that is empty; as in filter
|
||||
expressions, a prefixed name also matches a key stored under the full
|
||||
name, whose values come first: log.level returns values of a key named
|
||||
log.level, then of the log field level.'
|
||||
type: string
|
||||
- description: Filter expression already applied in the explorer. When set,
|
||||
relatedValues lists the values seen on data matching it.
|
||||
@@ -28411,13 +28420,22 @@ paths:
|
||||
where it is case-sensitive and % and _ act as wildcards. For keys, a context
|
||||
prefix such as resource.service.name sets fieldContext when it is empty.'
|
||||
type: string
|
||||
- description: The key whose values to return, such as service.name. A context
|
||||
prefix such as resource.service.name sets fieldContext when it is empty.
|
||||
- description: 'The key whose values to return, such as service.name. With fieldContext
|
||||
set, it is the stored name as is. Otherwise a context prefix such as resource.service.name
|
||||
sets the context and a type suffix such as service.name:string sets fieldDataType
|
||||
when that is empty; as in filter expressions, a prefixed name also matches
|
||||
a key stored under the full name, whose values come first: log.level returns
|
||||
values of a key named log.level, then of the log field level.'
|
||||
in: query
|
||||
name: name
|
||||
schema:
|
||||
description: The key whose values to return, such as service.name. A context
|
||||
prefix such as resource.service.name sets fieldContext when it is empty.
|
||||
description: 'The key whose values to return, such as service.name. With
|
||||
fieldContext set, it is the stored name as is. Otherwise a context prefix
|
||||
such as resource.service.name sets the context and a type suffix such
|
||||
as service.name:string sets fieldDataType when that is empty; as in filter
|
||||
expressions, a prefixed name also matches a key stored under the full
|
||||
name, whose values come first: log.level returns values of a key named
|
||||
log.level, then of the log field level.'
|
||||
type: string
|
||||
- description: Filter expression already applied in the explorer. When set,
|
||||
relatedValues lists the values seen on data matching it.
|
||||
|
||||
50
pkg/telemetrymetadata/context_prefixed_values_test.go
Normal file
50
pkg/telemetrymetadata/context_prefixed_values_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package telemetrymetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
cmock "github.com/SigNoz/clickhouse-go-mock"
|
||||
"github.com/SigNoz/signoz/pkg/flagger/flaggertest"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore/telemetrystoretest"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetAllValuesExactNameFirst(t *testing.T) {
|
||||
mockTelemetryStore := telemetrystoretest.New(telemetrystore.Config{}, ®exMatcher{})
|
||||
mock := mockTelemetryStore.Mock()
|
||||
metadata := NewTelemetryMetaStore(instrumentationtest.New().ToProviderSettings(), mockTelemetryStore, flaggertest.New(t))
|
||||
|
||||
boolColumns := []cmock.ColumnType{{Name: "1", Type: "UInt8"}}
|
||||
valueColumns := []cmock.ColumnType{{Name: "string_value", Type: "String"}, {Name: "number_value", Type: "Float64"}}
|
||||
|
||||
mock.ExpectQuery(`SELECT 1 FROM .*tag_key = \? AND tag_data_type = \? LIMIT`).
|
||||
WithArgs("log.level", "bool", 1).
|
||||
WillReturnRows(cmock.NewRows(boolColumns, [][]any{}))
|
||||
mock.ExpectQuery(`SELECT DISTINCT string_value, number_value FROM .*tag_key = \? AND tag_data_type <> \? LIMIT`).
|
||||
WithArgs("log.level", "bool", 3).
|
||||
WillReturnRows(cmock.NewRows(valueColumns, [][]any{{"warn", float64(0)}, {"info", float64(0)}}))
|
||||
mock.ExpectQuery(`SELECT 1 FROM .*tag_key = \? AND tag_data_type = \? AND tag_type = \?`).
|
||||
WithArgs("level", "bool", "logfield", 1).
|
||||
WillReturnRows(cmock.NewRows(boolColumns, [][]any{}))
|
||||
mock.ExpectQuery(`SELECT DISTINCT string_value, number_value FROM .*tag_key = \? AND tag_data_type <> \? AND tag_type = \?`).
|
||||
WithArgs("level", "bool", "logfield", 3).
|
||||
WillReturnRows(cmock.NewRows(valueColumns, [][]any{{"info", float64(0)}, {"debug", float64(0)}}))
|
||||
|
||||
selector := telemetrytypes.NewFieldValueSelectorFromPostableFieldValueParams(telemetrytypes.PostableFieldValueParams{
|
||||
Name: "log.level",
|
||||
PostableFieldKeysParams: telemetrytypes.PostableFieldKeysParams{Signal: telemetrytypes.SignalLogs, Limit: 2},
|
||||
})
|
||||
|
||||
values, complete, err := metadata.GetAllValues(context.Background(), valuer.GenerateUUID(), selector)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, []string{"warn", "info"}, values.StringValues)
|
||||
assert.False(t, complete)
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
}
|
||||
@@ -1517,7 +1517,24 @@ func (t *telemetryMetaStore) getRelatedValues(ctx context.Context, orgID valuer.
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) GetRelatedValues(ctx context.Context, orgID valuer.UUID, fieldValueSelector *telemetrytypes.FieldValueSelector) ([]string, bool, error) {
|
||||
return t.getRelatedValues(ctx, orgID, fieldValueSelector)
|
||||
selectors := fieldValueSelector.NameMatchSelectors()
|
||||
if len(selectors) == 1 {
|
||||
return t.getRelatedValues(ctx, orgID, fieldValueSelector)
|
||||
}
|
||||
|
||||
all := make([]*telemetrytypes.TelemetryFieldValues, 0, len(selectors))
|
||||
complete := true
|
||||
for _, selector := range selectors {
|
||||
values, selectorComplete, err := t.getRelatedValues(ctx, orgID, selector)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
all = append(all, &telemetrytypes.TelemetryFieldValues{RelatedValues: values})
|
||||
complete = complete && selectorComplete
|
||||
}
|
||||
|
||||
merged, mergeComplete := mergeFieldValues(fieldValueSelector.Limit, all...)
|
||||
return merged.RelatedValues, complete && mergeComplete, nil
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getSpanFieldValues(ctx context.Context, orgID valuer.UUID, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
@@ -1826,7 +1843,7 @@ func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, orgID val
|
||||
}
|
||||
|
||||
if fieldValueSelector.FieldContext != telemetrytypes.FieldContextUnspecified {
|
||||
sb.Where(sb.E("attr_type", fieldValueSelector.FieldContext.TagType()))
|
||||
sb.Where(sb.E("attr_type", fieldValueSelector.FieldContext.MetricAttrType()))
|
||||
}
|
||||
|
||||
if fieldValueSelector.FieldDataType != telemetrytypes.FieldDataTypeUnspecified {
|
||||
@@ -2092,6 +2109,23 @@ func (t *telemetryMetaStore) getMeterSourceMetricFieldValues(ctx context.Context
|
||||
return values, complete, nil
|
||||
}
|
||||
|
||||
// mergeFieldValues dedupes the values into one result capped at limit, and reports
|
||||
// false when the cap dropped any.
|
||||
func mergeFieldValues(limit int, values ...*telemetrytypes.TelemetryFieldValues) (*telemetrytypes.TelemetryFieldValues, bool) {
|
||||
if limit == 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
merged := &telemetrytypes.TelemetryFieldValues{}
|
||||
mapOfValues := make(map[any]bool)
|
||||
mapOfRelatedValues := make(map[any]bool)
|
||||
complete := true
|
||||
for _, v := range values {
|
||||
complete = populateAllUnspecifiedValues(merged, mapOfValues, mapOfRelatedValues, v, limit) && complete
|
||||
}
|
||||
return merged, complete
|
||||
}
|
||||
|
||||
func populateAllUnspecifiedValues(allUnspecifiedValues *telemetrytypes.TelemetryFieldValues, mapOfValues map[any]bool, mapOfRelatedValues map[any]bool, values *telemetrytypes.TelemetryFieldValues, limit int) bool {
|
||||
complete := true
|
||||
totalCount := len(mapOfValues) + len(mapOfRelatedValues)
|
||||
@@ -2149,6 +2183,27 @@ func populateAllUnspecifiedValues(allUnspecifiedValues *telemetrytypes.Telemetry
|
||||
|
||||
// GetAllValues returns all values and whether the result is complete.
|
||||
func (t *telemetryMetaStore) GetAllValues(ctx context.Context, orgID valuer.UUID, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
selectors := fieldValueSelector.NameMatchSelectors()
|
||||
if len(selectors) == 1 {
|
||||
return t.getAllValues(ctx, orgID, fieldValueSelector)
|
||||
}
|
||||
|
||||
all := make([]*telemetrytypes.TelemetryFieldValues, 0, len(selectors))
|
||||
complete := true
|
||||
for _, selector := range selectors {
|
||||
values, selectorComplete, err := t.getAllValues(ctx, orgID, selector)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
all = append(all, values)
|
||||
complete = complete && selectorComplete
|
||||
}
|
||||
|
||||
merged, mergeComplete := mergeFieldValues(fieldValueSelector.Limit, all...)
|
||||
return merged, complete && mergeComplete, nil
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getAllValues(ctx context.Context, orgID valuer.UUID, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
values := &telemetrytypes.TelemetryFieldValues{}
|
||||
var complete = true
|
||||
var err error
|
||||
|
||||
@@ -147,7 +147,11 @@ func (f *TelemetryFieldKey) Equal(key *TelemetryFieldKey) bool {
|
||||
// key.Normalize()
|
||||
// // Result: Name: "service.name", FieldContext: FieldContextResource, FieldDataType: FieldDataTypeString
|
||||
func (f *TelemetryFieldKey) Normalize() {
|
||||
f.normalizeDataType()
|
||||
f.normalizeContext()
|
||||
}
|
||||
|
||||
func (f *TelemetryFieldKey) normalizeDataType() {
|
||||
// Step 1: Parse data type from the right (after the last ":") if not already specified
|
||||
if f.FieldDataType == FieldDataTypeUnspecified {
|
||||
if colonIdx := strings.LastIndex(f.Name, ":"); colonIdx != -1 {
|
||||
@@ -158,7 +162,9 @@ func (f *TelemetryFieldKey) Normalize() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TelemetryFieldKey) normalizeContext() {
|
||||
// Step 2: Parse field context from the left if not already specified
|
||||
if f.FieldContext == FieldContextUnspecified {
|
||||
if dotIdx := strings.Index(f.Name, "."); dotIdx != -1 {
|
||||
@@ -279,6 +285,9 @@ type FieldValueSelector struct {
|
||||
ExistingQuery string `json:"existingQuery" description:"Filter expression already applied in the explorer. Used to compute relatedValues; empty disables them."`
|
||||
Value string `json:"value" description:"Substring the returned values must contain, case-insensitive except for the meter source, where it is case-sensitive and % and _ act as wildcards. Empty returns any value."`
|
||||
Limit int `json:"limit" description:"Maximum number of values to return."`
|
||||
|
||||
// nameAsWritten is set when FieldContext was parsed from the name's prefix.
|
||||
nameAsWritten string
|
||||
}
|
||||
|
||||
type GettableFieldKeys struct {
|
||||
@@ -306,7 +315,7 @@ type GettableFieldValues struct {
|
||||
|
||||
type PostableFieldValueParams struct {
|
||||
PostableFieldKeysParams
|
||||
Name string `query:"name" description:"The key whose values to return, such as service.name. A context prefix such as resource.service.name sets fieldContext when it is empty."`
|
||||
Name string `query:"name" description:"The key whose values to return, such as service.name. With fieldContext set, it is the stored name as is. Otherwise a context prefix such as resource.service.name sets the context and a type suffix such as service.name:string sets fieldDataType when that is empty; as in filter expressions, a prefixed name also matches a key stored under the full name, whose values come first: log.level returns values of a key named log.level, then of the log field level."`
|
||||
ExistingQuery string `query:"existingQuery" description:"Filter expression already applied in the explorer. When set, relatedValues lists the values seen on data matching it."`
|
||||
}
|
||||
|
||||
@@ -358,26 +367,35 @@ func NewFieldKeySelectorFromPostableFieldKeysParams(params PostableFieldKeysPara
|
||||
}
|
||||
|
||||
func NewFieldValueSelectorFromPostableFieldValueParams(params PostableFieldValueParams) *FieldValueSelector {
|
||||
|
||||
keySelector := NewFieldKeySelectorFromPostableFieldKeysParams(params.PostableFieldKeysParams)
|
||||
// SearchText filters values here, so it must not be parsed as a key.
|
||||
keyParams := params.PostableFieldKeysParams
|
||||
keyParams.SearchText = ""
|
||||
keySelector := NewFieldKeySelectorFromPostableFieldKeysParams(keyParams)
|
||||
|
||||
fieldValueSelector := &FieldValueSelector{
|
||||
FieldKeySelector: keySelector,
|
||||
}
|
||||
|
||||
fieldValueSelector.Name = params.Name
|
||||
|
||||
if params.Name != "" && fieldValueSelector.FieldContext == FieldContextUnspecified {
|
||||
parsedFieldKey := GetFieldKeyFromKeyText(params.Name)
|
||||
if parsedFieldKey.FieldContext != FieldContextUnspecified {
|
||||
// Only apply inferred context if it is valid for the current signal
|
||||
if isContextValidForSignal(parsedFieldKey.FieldContext, fieldValueSelector.Signal) {
|
||||
fieldValueSelector.Name = parsedFieldKey.Name
|
||||
fieldValueSelector.FieldContext = parsedFieldKey.FieldContext
|
||||
// An explicit context means the name is the stored name as is.
|
||||
key := TelemetryFieldKey{Name: params.Name, FieldContext: keySelector.FieldContext, FieldDataType: keySelector.FieldDataType}
|
||||
if keySelector.FieldContext == FieldContextUnspecified {
|
||||
key.normalizeDataType()
|
||||
nameWithContext := key.Name
|
||||
key.normalizeContext()
|
||||
// Only apply inferred context if it is valid for the current signal
|
||||
if isContextValidForSignal(key.FieldContext, keySelector.Signal) {
|
||||
if key.FieldContext != FieldContextUnspecified {
|
||||
fieldValueSelector.nameAsWritten = nameWithContext
|
||||
}
|
||||
} else {
|
||||
key.Name = nameWithContext
|
||||
key.FieldContext = FieldContextUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
fieldValueSelector.Name = key.Name
|
||||
fieldValueSelector.FieldContext = key.FieldContext
|
||||
fieldValueSelector.FieldDataType = key.FieldDataType
|
||||
fieldValueSelector.ExistingQuery = params.ExistingQuery
|
||||
fieldValueSelector.Value = params.SearchText
|
||||
|
||||
@@ -390,6 +408,29 @@ func NewFieldValueSelectorFromPostableFieldValueParams(params PostableFieldValue
|
||||
return fieldValueSelector
|
||||
}
|
||||
|
||||
// NameMatchSelectors returns the lookups for the requested key, highest priority first.
|
||||
// A context parsed from the name may instead be part of the stored name (attribute.key
|
||||
// may be the attribute named attribute.key), so like the query builder both readings are
|
||||
// looked up: the name as written, in any context, before the name with its prefix stripped.
|
||||
func (s *FieldValueSelector) NameMatchSelectors() []*FieldValueSelector {
|
||||
if s.nameAsWritten != "" {
|
||||
return []*FieldValueSelector{s.withName(s.nameAsWritten), s}
|
||||
}
|
||||
return []*FieldValueSelector{s}
|
||||
}
|
||||
|
||||
// withName returns a copy looking up name in any context.
|
||||
func (s *FieldValueSelector) withName(name string) *FieldValueSelector {
|
||||
keySelector := *s.FieldKeySelector
|
||||
keySelector.Name = name
|
||||
keySelector.FieldContext = FieldContextUnspecified
|
||||
|
||||
selector := *s
|
||||
selector.FieldKeySelector = &keySelector
|
||||
selector.nameAsWritten = ""
|
||||
return &selector
|
||||
}
|
||||
|
||||
func NewTelemetryFieldKey(name string, fieldContext FieldContext, fieldDataType FieldDataType) *TelemetryFieldKey {
|
||||
return &TelemetryFieldKey{
|
||||
Name: name,
|
||||
|
||||
@@ -164,6 +164,15 @@ func (f FieldContext) TagType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// MetricAttrType returns the attr_type the metrics metadata table stores for the context;
|
||||
// metric attributes are stored as point.
|
||||
func (f FieldContext) MetricAttrType() string {
|
||||
if f == FieldContextAttribute {
|
||||
return "point"
|
||||
}
|
||||
return f.TagType()
|
||||
}
|
||||
|
||||
func isContextValidForSignal(ctx FieldContext, signal Signal) bool {
|
||||
if ctx == FieldContextResource ||
|
||||
ctx == FieldContextAttribute ||
|
||||
|
||||
25
pkg/types/telemetrytypes/field_context_test.go
Normal file
25
pkg/types/telemetrytypes/field_context_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package telemetrytypes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFieldContextMetricAttrType(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
context FieldContext
|
||||
expected string
|
||||
}{
|
||||
{name: "Attribute_Point", context: FieldContextAttribute, expected: "point"},
|
||||
{name: "Resource_Resource", context: FieldContextResource, expected: "resource"},
|
||||
{name: "Scope_Scope", context: FieldContextScope, expected: "scope"},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
assert.Equal(t, testCase.expected, testCase.context.MetricAttrType())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package telemetrytypes
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetFieldKeyFromKeyText(t *testing.T) {
|
||||
@@ -422,3 +425,168 @@ func TestNewFieldKeySelectorFromPostableFieldKeysParamsMetricNamespace(t *testin
|
||||
t.Fatalf("expected metric name to remain empty, got %q", selector.MetricContext.MetricName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFieldValueSelectorFromPostableFieldValueParams(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
params PostableFieldValueParams
|
||||
expectedName string
|
||||
expectedContext FieldContext
|
||||
expectedDataType FieldDataType
|
||||
expectedValue string
|
||||
expectedLookups []TelemetryFieldKey
|
||||
}{
|
||||
{
|
||||
name: "ResourcePrefix_Parsed",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "resource.service.name"},
|
||||
expectedName: "service.name",
|
||||
expectedContext: FieldContextResource,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "resource.service.name", FieldContext: FieldContextUnspecified}, {Name: "service.name", FieldContext: FieldContextResource}},
|
||||
},
|
||||
{
|
||||
name: "AttributePrefix_Parsed",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "attribute.key"},
|
||||
expectedName: "key",
|
||||
expectedContext: FieldContextAttribute,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "attribute.key", FieldContext: FieldContextUnspecified}, {Name: "key", FieldContext: FieldContextAttribute}},
|
||||
},
|
||||
{
|
||||
name: "DoubleAttributePrefix_OnlyFirstStripped",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "attribute.attribute.key"},
|
||||
expectedName: "attribute.key",
|
||||
expectedContext: FieldContextAttribute,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "attribute.attribute.key", FieldContext: FieldContextUnspecified}, {Name: "attribute.key", FieldContext: FieldContextAttribute}},
|
||||
},
|
||||
{
|
||||
name: "TagAlias_ExactMatchAsWritten",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "tag.region"},
|
||||
expectedName: "region",
|
||||
expectedContext: FieldContextAttribute,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "tag.region", FieldContext: FieldContextUnspecified}, {Name: "region", FieldContext: FieldContextAttribute}},
|
||||
},
|
||||
{
|
||||
name: "LogPrefix_Parsed",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "log.level"},
|
||||
expectedName: "level",
|
||||
expectedContext: FieldContextLog,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "log.level", FieldContext: FieldContextUnspecified}, {Name: "level", FieldContext: FieldContextLog}},
|
||||
},
|
||||
{
|
||||
name: "SpanPrefix_Parsed",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalTraces}, Name: "span.kind"},
|
||||
expectedName: "kind",
|
||||
expectedContext: FieldContextSpan,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "span.kind", FieldContext: FieldContextUnspecified}, {Name: "kind", FieldContext: FieldContextSpan}},
|
||||
},
|
||||
{
|
||||
name: "ContextInvalidForSignal_KeptAsWritten",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "span.kind"},
|
||||
expectedName: "span.kind",
|
||||
expectedContext: FieldContextUnspecified,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "span.kind", FieldContext: FieldContextUnspecified}},
|
||||
},
|
||||
{
|
||||
name: "ExplicitContext_NameNotParsed",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, FieldContext: FieldContextAttribute}, Name: "log.level"},
|
||||
expectedName: "log.level",
|
||||
expectedContext: FieldContextAttribute,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "log.level", FieldContext: FieldContextAttribute}},
|
||||
},
|
||||
{
|
||||
name: "ExplicitBodyContext_SingleLookup",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, FieldContext: FieldContextBody}, Name: "key"},
|
||||
expectedName: "key",
|
||||
expectedContext: FieldContextBody,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "key", FieldContext: FieldContextBody}},
|
||||
},
|
||||
{
|
||||
name: "ExplicitAttributeContext_SingleLookup",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, FieldContext: FieldContextAttribute}, Name: "key"},
|
||||
expectedName: "key",
|
||||
expectedContext: FieldContextAttribute,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "key", FieldContext: FieldContextAttribute}},
|
||||
},
|
||||
{
|
||||
name: "ExplicitAttributeContext_PrefixedStoredName",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, FieldContext: FieldContextAttribute}, Name: "attribute.key"},
|
||||
expectedName: "attribute.key",
|
||||
expectedContext: FieldContextAttribute,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "attribute.key", FieldContext: FieldContextAttribute}},
|
||||
},
|
||||
{
|
||||
name: "ExplicitContext_TypeSuffixNotParsed",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, FieldContext: FieldContextAttribute}, Name: "http.method:string"},
|
||||
expectedName: "http.method:string",
|
||||
expectedContext: FieldContextAttribute,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "http.method:string", FieldContext: FieldContextAttribute}},
|
||||
},
|
||||
{
|
||||
name: "DataTypeSuffix_WithoutContext",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "service.name:string"},
|
||||
expectedName: "service.name",
|
||||
expectedContext: FieldContextUnspecified,
|
||||
expectedDataType: FieldDataTypeString,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "service.name", FieldContext: FieldContextUnspecified}},
|
||||
},
|
||||
{
|
||||
name: "DataTypeSuffix_WithContext",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "resource.host.name:string"},
|
||||
expectedName: "host.name",
|
||||
expectedContext: FieldContextResource,
|
||||
expectedDataType: FieldDataTypeString,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "resource.host.name", FieldContext: FieldContextUnspecified}, {Name: "host.name", FieldContext: FieldContextResource}},
|
||||
},
|
||||
{
|
||||
name: "DataTypeSuffix_WithInvalidContext",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs}, Name: "span.kind:int64"},
|
||||
expectedName: "span.kind",
|
||||
expectedContext: FieldContextUnspecified,
|
||||
expectedDataType: FieldDataTypeInt64,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "span.kind", FieldContext: FieldContextUnspecified}},
|
||||
},
|
||||
{
|
||||
name: "ExplicitDataType_SuffixKept",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, FieldDataType: FieldDataTypeBool}, Name: "http.method:string"},
|
||||
expectedName: "http.method:string",
|
||||
expectedContext: FieldContextUnspecified,
|
||||
expectedDataType: FieldDataTypeBool,
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "http.method:string", FieldContext: FieldContextUnspecified}},
|
||||
},
|
||||
{
|
||||
name: "SearchTextWithContextPrefix_NotParsedAsKey",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, SearchText: "log.x"}, Name: "service.name"},
|
||||
expectedName: "service.name",
|
||||
expectedContext: FieldContextUnspecified,
|
||||
expectedValue: "log.x",
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "service.name", FieldContext: FieldContextUnspecified}},
|
||||
},
|
||||
{
|
||||
name: "SearchTextWithContextPrefix_NameStillParsed",
|
||||
params: PostableFieldValueParams{PostableFieldKeysParams: PostableFieldKeysParams{Signal: SignalLogs, SearchText: "attribute.x"}, Name: "resource.k8s.pod.name"},
|
||||
expectedName: "k8s.pod.name",
|
||||
expectedContext: FieldContextResource,
|
||||
expectedValue: "attribute.x",
|
||||
expectedLookups: []TelemetryFieldKey{{Name: "resource.k8s.pod.name", FieldContext: FieldContextUnspecified}, {Name: "k8s.pod.name", FieldContext: FieldContextResource}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
selector := NewFieldValueSelectorFromPostableFieldValueParams(testCase.params)
|
||||
|
||||
assert.Equal(t, testCase.expectedName, selector.Name)
|
||||
assert.Equal(t, testCase.expectedContext, selector.FieldContext)
|
||||
assert.Equal(t, testCase.expectedDataType, selector.FieldDataType)
|
||||
assert.Equal(t, testCase.expectedValue, selector.Value)
|
||||
|
||||
lookups := selector.NameMatchSelectors()
|
||||
require.Len(t, lookups, len(testCase.expectedLookups))
|
||||
for i, expected := range testCase.expectedLookups {
|
||||
assert.Equal(t, expected.Name, lookups[i].Name)
|
||||
assert.Equal(t, expected.FieldContext, lookups[i].FieldContext)
|
||||
assert.Equal(t, selector.FieldDataType, lookups[i].FieldDataType)
|
||||
}
|
||||
assert.Equal(t, testCase.expectedName, selector.Name, "lookups must not mutate the selector")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user