mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-30 14:56:15 +00:00
* debrand: signoz/SigNoz/SIGNOZ -> o11y/O11y/O11Y across the tree Rename all SigNoz *branding* to O11y in file contents, file/dir names, package names, env vars, config keys, comments, docs, SDK code, and the ClickHouse schema identifiers the querier reads. Collector dependency: - Repoint github.com/hanzoai/signoz-otel-collector -> github.com/hanzoai/otel-collector and bump v0.144.6 -> v0.144.7 (go.mod + go.sum reconciled via go mod tidy). Internal package paths also debranded upstream: signozschemamigrator -> o11yschemamigrator, signozlogspipelineprocessor -> o11ylogspipelineprocessor. - Fix: v0.144.7 dropped otelconst.DistributedFieldKeysTable from the collector's public constants; pin FieldKeysTable = "distributed_field_keys" locally in pkg/telemetrymetadata/tables.go. Package/dir renames (package decl + all importers): pkg/apiserver/signozapiserver -> o11yapiserver, plus signozalertmanager, signozauthzapi, signozglobal, signozquerier, signozruler, and pkg/signoz -> pkg/o11y. Schema (read plane): signoz_traces/metrics/logs/metadata/analytics/meter and signoz_index_v3/index_v2/error_index_v2/spans (+ distributed_*) -> o11y_*. Data-preserving cutover migration added: deploy/clickhouse/migrations/0001_rename_signoz_to_o11y.sql (metadata-only RENAME DATABASE/TABLE, no drop/recreate). Preserved (attribution / external / wire contracts): - LICENSE + NOTICE verbatim (incl. "software from SigNoz", "Copyright ... SigNoz Inc."). - Upstream github.com/SigNoz/* repo URLs + @SigNoz/* CODEOWNERS teams. - Billing resource-attribute regex "signoz.workspace.*" (external wire contract). go build ./pkg/... ./cmd/... = 0; gofmt clean; renamed+schema pkg tests pass. * o11y: bump collector to v0.144.8 (o11y_* physical schema writer) + lockstep deploy plan Collector v0.144.8's schema-migrator + exporters now CREATE/WRITE the same o11y_* physical databases/tables the querier reads and the RENAME migration (0001_rename_signoz_to_o11y.sql) targets. Cross-checked byte-identical: writer DB set == migration target DB set (6); writer prefixed-table set == migration target table set (8); o11y querier reads and cloud reads are subsets of that set. Zero table-name mismatches between writer, readers, and migration. Collector go.mod unchanged between v0.144.7 and v0.144.8 (identical go.mod hash) — pure source rename, no module-graph change. Adds deploy/clickhouse/DEPLOY_signoz_to_o11y_cutover.md: the lockstep order (collector v0.144.8 -> RENAME migration -> o11y+cloud readers) with a fresh/scratch-ClickHouse pre-flight (create via migrator, emit trace+metric, query back) and rollback. Ships together or telemetry blackholes. go build ./pkg/... ./cmd/community = 0.
1095 lines
29 KiB
Go
1095 lines
29 KiB
Go
package v3
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
|
|
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
)
|
|
|
|
var testEnrichmentRequiredData = []struct {
|
|
Name string
|
|
Params v3.QueryRangeParamsV3
|
|
EnrichmentRequired bool
|
|
}{
|
|
{
|
|
Name: "attribute enrichment not required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "test",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "attribute enrichment required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "test",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "filter enrichment required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "john", Operator: "="},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "filter enrichment required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "filter enrichment required required json",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "body.xyz", IsJSON: true, DataType: v3.AttributeKeyDataTypeString}, Value: "john", Operator: "="},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "groupBy enrichment required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
GroupBy: []v3.AttributeKey{{Key: "userid", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "groupBy enrichment required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
GroupBy: []v3.AttributeKey{{Key: "userid"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "orderBy enrichment not required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
GroupBy: []v3.AttributeKey{{Key: "userid", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
|
OrderBy: []v3.OrderBy{{ColumnName: "userid"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "orderBy enrichment required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
OrderBy: []v3.OrderBy{{ColumnName: "userid"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
{
|
|
Name: "top level key",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
GroupBy: []v3.AttributeKey{{Key: "trace_id", Type: v3.AttributeKeyTypeUnspecified, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: false,
|
|
},
|
|
{
|
|
Name: "orderBy enrichment required",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
// here we have to fallback to trace_id attribute instead of column
|
|
GroupBy: []v3.AttributeKey{{Key: "trace_id", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
|
OrderBy: []v3.OrderBy{{ColumnName: "#O11Y_VALUE", Order: "ASC"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
EnrichmentRequired: true,
|
|
},
|
|
}
|
|
|
|
func TestEnrichmentRquired(t *testing.T) {
|
|
for _, tt := range testEnrichmentRequiredData {
|
|
Convey("testEnrichmentRequiredData", t, func() {
|
|
res := EnrichmentRequired(&tt.Params)
|
|
So(res, ShouldEqual, tt.EnrichmentRequired)
|
|
})
|
|
}
|
|
}
|
|
|
|
var testEnrichParamsData = []struct {
|
|
Name string
|
|
Params v3.QueryRangeParamsV3
|
|
Fields map[string]v3.AttributeKey
|
|
Result v3.QueryRangeParamsV3
|
|
}{
|
|
{
|
|
Name: "Enriching query range v3 params",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "test",
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
|
|
}},
|
|
GroupBy: []v3.AttributeKey{{Key: "trace_id"}},
|
|
OrderBy: []v3.OrderBy{{ColumnName: "response_time"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Fields: map[string]v3.AttributeKey{
|
|
"test##tag##int64": {
|
|
Key: "test",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
},
|
|
"user_name##tag##string": {
|
|
Key: "user_name",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
},
|
|
"response_time##tag##int64": {
|
|
Key: "response_time",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
IsColumn: true,
|
|
},
|
|
},
|
|
Result: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "test",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}, Value: "john", Operator: "="},
|
|
}},
|
|
GroupBy: []v3.AttributeKey{{Key: "trace_id", Type: v3.AttributeKeyTypeUnspecified, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}},
|
|
OrderBy: []v3.OrderBy{{ColumnName: "response_time", Key: "response_time", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeInt64, IsColumn: true}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "Enriching query range v3 params with dot support",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "method.name",
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "service.name"}, Value: "test", Operator: "="},
|
|
}},
|
|
GroupBy: []v3.AttributeKey{{Key: "host.name"}},
|
|
OrderBy: []v3.OrderBy{{ColumnName: "host.name"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Fields: map[string]v3.AttributeKey{
|
|
"method.name##tag##string": {
|
|
Key: "method.name",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
IsColumn: true,
|
|
},
|
|
"service.name##tag##string": {
|
|
Key: "service.name",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
},
|
|
"host.name##tag##string": {
|
|
Key: "host.name",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
},
|
|
},
|
|
Result: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "method.name",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
IsColumn: true,
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "service.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}, Value: "test", Operator: "="},
|
|
}},
|
|
GroupBy: []v3.AttributeKey{{Key: "host.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}},
|
|
OrderBy: []v3.OrderBy{{ColumnName: "host.name", Key: "host.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "Don't enrich if other keys are non empty and not same",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "test",
|
|
Type: v3.AttributeKeyTypeResource,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "test", Type: v3.AttributeKeyTypeTag}, Value: "test", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "test", DataType: v3.AttributeKeyDataTypeString}, Value: "test1", Operator: "="},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Fields: map[string]v3.AttributeKey{
|
|
"test##tag##string": {
|
|
Key: "test",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
IsColumn: true,
|
|
},
|
|
},
|
|
Result: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "test",
|
|
Type: v3.AttributeKeyTypeResource,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "test", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Value: "test", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "test", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Value: "test1", Operator: "="},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "Enrich if an attribute/resource attribute is materialized/dematerialized",
|
|
Params: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "mat_resource",
|
|
Type: v3.AttributeKeyTypeResource,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
IsColumn: true,
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "mat_attr", Type: v3.AttributeKeyTypeTag, IsColumn: true}, Value: "test", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "normal_attr", DataType: v3.AttributeKeyDataTypeString, IsColumn: false}, Value: "test1", Operator: "="},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Fields: map[string]v3.AttributeKey{
|
|
"mat_resource##resource##int64": {
|
|
Key: "mat_resource",
|
|
Type: v3.AttributeKeyTypeResource,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
IsColumn: false,
|
|
},
|
|
"mat_attr##tag##string": {
|
|
Key: "mat_attr",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
IsColumn: false,
|
|
},
|
|
"normal_attr##tag##string": {
|
|
Key: "normal_attr",
|
|
Type: v3.AttributeKeyTypeTag,
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
IsColumn: true,
|
|
},
|
|
},
|
|
Result: v3.QueryRangeParamsV3{
|
|
CompositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "mat_resource",
|
|
Type: v3.AttributeKeyTypeResource,
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
IsColumn: false,
|
|
},
|
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "mat_attr", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: false}, Value: "test", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "normal_attr", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Value: "test1", Operator: "="},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestEnrichParams(t *testing.T) {
|
|
for _, tt := range testEnrichParamsData {
|
|
Convey("testEnrichmentRequiredData", t, func() {
|
|
Enrich(&tt.Params, tt.Fields)
|
|
So(tt.Params, ShouldResemble, tt.Result)
|
|
})
|
|
}
|
|
}
|
|
|
|
var testJSONFilterEnrichData = []struct {
|
|
Name string
|
|
Filter v3.FilterItem
|
|
Result v3.FilterItem
|
|
}{
|
|
{
|
|
Name: "array string",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.requestor_list[*]",
|
|
DataType: v3.AttributeKeyDataTypeUnspecified,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "has",
|
|
Value: "index_service",
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.requestor_list[*]",
|
|
DataType: v3.AttributeKeyDataTypeArrayString,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: true,
|
|
},
|
|
Operator: "has",
|
|
Value: "index_service",
|
|
},
|
|
},
|
|
{
|
|
Name: "int64",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.intx",
|
|
DataType: v3.AttributeKeyDataTypeUnspecified,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.intx",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: true,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
},
|
|
{
|
|
Name: "int64 string",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.intx",
|
|
DataType: v3.AttributeKeyDataTypeUnspecified,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "=",
|
|
Value: "0",
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.intx",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: true,
|
|
},
|
|
Operator: "=",
|
|
Value: int64(0),
|
|
},
|
|
},
|
|
{
|
|
Name: "float64",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.float64[*]",
|
|
DataType: v3.AttributeKeyDataTypeArrayFloat64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "!=",
|
|
Value: 10.0,
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.float64[*]",
|
|
DataType: v3.AttributeKeyDataTypeArrayFloat64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: true,
|
|
},
|
|
Operator: "!=",
|
|
Value: 10.0,
|
|
},
|
|
},
|
|
{
|
|
Name: "float64x",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.float64x",
|
|
DataType: v3.AttributeKeyDataTypeUnspecified,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "!=",
|
|
Value: "10.0",
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.float64x",
|
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: true,
|
|
},
|
|
Operator: "!=",
|
|
Value: 10.0,
|
|
},
|
|
},
|
|
{
|
|
Name: "check IN",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.attr",
|
|
DataType: v3.AttributeKeyDataTypeUnspecified,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "IN",
|
|
Value: []interface{}{"hello", "world"},
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.attr",
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: true,
|
|
},
|
|
Operator: "IN",
|
|
Value: []interface{}{"hello", "world"},
|
|
},
|
|
},
|
|
{
|
|
Name: "check NOT_IN",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.attr",
|
|
DataType: v3.AttributeKeyDataTypeUnspecified,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "NOT_IN",
|
|
Value: []interface{}{10, 20},
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.attr",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: true,
|
|
},
|
|
Operator: "NOT_IN",
|
|
Value: []interface{}{10, 20},
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestJsonEnrich(t *testing.T) {
|
|
for _, tt := range testJSONFilterEnrichData {
|
|
Convey(tt.Name, t, func() {
|
|
res := jsonFilterEnrich(tt.Filter)
|
|
So(res, ShouldResemble, tt.Result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJsonReplaceField(t *testing.T) {
|
|
fields := map[string]v3.AttributeKey{
|
|
"method.name": {
|
|
Key: "method.name",
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
Type: v3.AttributeKeyTypeTag,
|
|
},
|
|
"status": {
|
|
Key: "status",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeTag,
|
|
},
|
|
"data.error": {
|
|
Key: "data.error",
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
Type: v3.AttributeKeyTypeTag,
|
|
IsColumn: true,
|
|
},
|
|
}
|
|
var TestJsonReplaceFieldData = []struct {
|
|
Name string
|
|
Filter v3.FilterItem
|
|
Result v3.FilterItem
|
|
}{
|
|
{
|
|
Name: "key in nested json",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.method.name",
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "has",
|
|
Value: "index_service",
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "method.name",
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
Type: v3.AttributeKeyTypeTag,
|
|
IsJSON: false,
|
|
},
|
|
Operator: "has",
|
|
Value: "index_service",
|
|
},
|
|
},
|
|
{
|
|
Name: "key at top level",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.status",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "status",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeTag,
|
|
IsJSON: false,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
},
|
|
{
|
|
Name: "key not present",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.status.code",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.status.code",
|
|
DataType: v3.AttributeKeyDataTypeInt64,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
IsJSON: false,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
},
|
|
{
|
|
Name: "key materialized",
|
|
Filter: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "body.data.error",
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
Type: v3.AttributeKeyTypeUnspecified,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
Result: v3.FilterItem{
|
|
Key: v3.AttributeKey{
|
|
Key: "data.error",
|
|
DataType: v3.AttributeKeyDataTypeString,
|
|
Type: v3.AttributeKeyTypeTag,
|
|
IsJSON: false,
|
|
IsColumn: true,
|
|
},
|
|
Operator: "=",
|
|
Value: 10,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range TestJsonReplaceFieldData {
|
|
Convey(tt.Name, t, func() {
|
|
res := jsonReplaceField(tt.Filter, fields)
|
|
So(res, ShouldResemble, tt.Result)
|
|
})
|
|
}
|
|
}
|
|
|
|
var testParseStrValueData = []struct {
|
|
Name string
|
|
Operator v3.FilterOperator
|
|
Value interface{}
|
|
ResultType string
|
|
Result interface{}
|
|
}{
|
|
{
|
|
Name: "bool",
|
|
Value: "true",
|
|
Operator: v3.FilterOperatorEqual,
|
|
ResultType: "bool",
|
|
Result: true,
|
|
},
|
|
{
|
|
Name: "int",
|
|
Value: "10",
|
|
Operator: v3.FilterOperatorNotEqual,
|
|
ResultType: "int64",
|
|
Result: 10,
|
|
},
|
|
{
|
|
Name: "float",
|
|
Value: "10.0",
|
|
Operator: v3.FilterOperatorGreaterThan,
|
|
ResultType: "float64",
|
|
Result: 10.0,
|
|
},
|
|
{
|
|
Name: "string",
|
|
Value: "hello",
|
|
Operator: v3.FilterOperatorLessThan,
|
|
ResultType: "string",
|
|
Result: "hello",
|
|
},
|
|
}
|
|
|
|
func TestParseStrValue(t *testing.T) {
|
|
for _, tt := range testParseStrValueData {
|
|
Convey(tt.Name, t, func() {
|
|
vtype, value := parseStrValue(tt.Value.(string), tt.Operator)
|
|
So(vtype, ShouldEqual, tt.ResultType)
|
|
So(value, ShouldEqual, tt.Result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetFieldNames(t *testing.T) {
|
|
type args struct {
|
|
compositeQuery *v3.CompositeQuery
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want []string
|
|
}{
|
|
{
|
|
name: "nil composite query",
|
|
args: args{
|
|
compositeQuery: nil,
|
|
},
|
|
want: []string{},
|
|
},
|
|
{
|
|
name: "empty builder queries",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{},
|
|
},
|
|
},
|
|
want: []string{},
|
|
},
|
|
{
|
|
name: "aggregate attribute only",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "response_time",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"response_time"},
|
|
},
|
|
{
|
|
name: "filters only",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{
|
|
Operator: "AND",
|
|
Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "status"}, Value: "error", Operator: "!="},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"user_name", "status"},
|
|
},
|
|
{
|
|
name: "JSON filters",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{
|
|
Operator: "AND",
|
|
Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "body.user_name"}, Value: "john", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "body.status.code"}, Value: 200, Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "body.requestor_list[*]"}, Value: "index_service", Operator: "has"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// requestor_list[*] is not required though
|
|
want: []string{"user_name", "status.code", "requestor_list[*]"},
|
|
},
|
|
{
|
|
name: "groupBy only",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
GroupBy: []v3.AttributeKey{
|
|
{Key: "service_name"},
|
|
{Key: "host_name"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"service_name", "host_name"},
|
|
},
|
|
{
|
|
name: "orderBy only",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
OrderBy: []v3.OrderBy{
|
|
{ColumnName: "timestamp", Order: "DESC"},
|
|
{ColumnName: "response_time", Order: "ASC"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"timestamp", "response_time"},
|
|
},
|
|
{
|
|
name: "all field types combined",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "response_time",
|
|
},
|
|
Filters: &v3.FilterSet{
|
|
Operator: "AND",
|
|
Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "body.status"}, Value: "error", Operator: "!="},
|
|
},
|
|
},
|
|
GroupBy: []v3.AttributeKey{
|
|
{Key: "service_name"},
|
|
{Key: "user_name"},
|
|
},
|
|
OrderBy: []v3.OrderBy{
|
|
{ColumnName: "timestamp", Order: "DESC"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"response_time", "user_name", "status", "service_name", "timestamp"},
|
|
},
|
|
{
|
|
name: "multiple builder queries",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"query1": {
|
|
QueryName: "query1",
|
|
Expression: "query1",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "field1",
|
|
},
|
|
},
|
|
"query2": {
|
|
QueryName: "query2",
|
|
Expression: "query2",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{
|
|
Operator: "AND",
|
|
Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "field2"}, Value: "value", Operator: "="},
|
|
},
|
|
},
|
|
},
|
|
"query3": {
|
|
QueryName: "query3",
|
|
Expression: "different_expression",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "field3",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"field1", "field2"},
|
|
},
|
|
{
|
|
name: "empty aggregate attribute key and filters",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
AggregateAttribute: v3.AttributeKey{
|
|
Key: "",
|
|
},
|
|
Filters: &v3.FilterSet{
|
|
Operator: "AND",
|
|
Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
|
|
},
|
|
},
|
|
GroupBy: []v3.AttributeKey{
|
|
{Key: "service_name"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"user_name", "service_name"},
|
|
},
|
|
{
|
|
name: "JSON filter with array suffix - no use",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{
|
|
Operator: "AND",
|
|
Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "body.tags[*]"}, Value: "important", Operator: "has"},
|
|
{Key: v3.AttributeKey{Key: "body.nested.array[*]"}, Value: "value", Operator: "has"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"tags[*]", "nested.array[*]"},
|
|
},
|
|
{
|
|
name: "mixed JSON and regular filters",
|
|
args: args{
|
|
compositeQuery: &v3.CompositeQuery{
|
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
|
"test": {
|
|
QueryName: "test",
|
|
Expression: "test",
|
|
DataSource: v3.DataSourceLogs,
|
|
Filters: &v3.FilterSet{
|
|
Operator: "AND",
|
|
Items: []v3.FilterItem{
|
|
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
|
|
{Key: v3.AttributeKey{Key: "body.status.abc"}, Value: "error", Operator: "!="},
|
|
{Key: v3.AttributeKey{Key: "service_name"}, Value: "api", Operator: "="},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []string{"user_name", "status.abc", "service_name"},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := GetFieldNames(tt.args.compositeQuery)
|
|
// Sort both slices since order doesn't matter
|
|
sort.Strings(got)
|
|
sort.Strings(tt.want)
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("GetFieldNames() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|