mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 03:29:02 +08:00
feat: add support for table view in traces (#3047)
* feat: add support for table view in traces * fix: alignment issue * feat: handle table view in traces * fix: value type panel * fix: order by in table view * chore: remove obsolete code * fix: use now() as ts in query to support formula * test: update tests
This commit is contained in:
parent
149fdebfaa
commit
39c6410bbe
@ -102,7 +102,7 @@ func getSelectLabels(aggregatorOperator v3.AggregateOperator, groupBy []v3.Attri
|
||||
} else {
|
||||
for _, tag := range groupBy {
|
||||
filterName := getColumnName(tag, keys)
|
||||
selectLabels += fmt.Sprintf(", %s as `%s`", filterName, tag.Key)
|
||||
selectLabels += fmt.Sprintf(" %s as `%s`,", filterName, tag.Key)
|
||||
}
|
||||
}
|
||||
return selectLabels
|
||||
@ -235,14 +235,26 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
||||
having = " having " + having
|
||||
}
|
||||
|
||||
// Select the aggregate value for interval
|
||||
queryTmpl :=
|
||||
"SELECT toStartOfInterval(timestamp, INTERVAL %d SECOND) AS ts" + selectLabels +
|
||||
", %s as value " +
|
||||
"from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_TABLENAME +
|
||||
" where " + spanIndexTableTimeFilter + "%s " +
|
||||
"group by %s%s " +
|
||||
"order by %s"
|
||||
var queryTmpl string
|
||||
|
||||
if panelType == v3.PanelTypeTable {
|
||||
queryTmpl =
|
||||
"SELECT now() as ts," + selectLabels +
|
||||
" %s as value " +
|
||||
"from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_TABLENAME +
|
||||
" where " + spanIndexTableTimeFilter + "%s" +
|
||||
"%s%s" +
|
||||
"%s"
|
||||
} else if panelType == v3.PanelTypeGraph || panelType == v3.PanelTypeValue {
|
||||
// Select the aggregate value for interval
|
||||
queryTmpl =
|
||||
fmt.Sprintf("SELECT toStartOfInterval(timestamp, INTERVAL %d SECOND) AS ts,", step) + selectLabels +
|
||||
" %s as value " +
|
||||
"from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_TABLENAME +
|
||||
" where " + spanIndexTableTimeFilter + "%s" +
|
||||
"%s%s" +
|
||||
"%s"
|
||||
}
|
||||
|
||||
emptyValuesInGroupByFilter, err := handleEmptyValuesInGroupBy(keys, mq.GroupBy)
|
||||
if err != nil {
|
||||
@ -250,10 +262,15 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
||||
}
|
||||
filterSubQuery += emptyValuesInGroupByFilter
|
||||
|
||||
groupBy := groupByAttributeKeyTags(keys, mq.GroupBy...)
|
||||
groupBy := groupByAttributeKeyTags(panelType, mq.GroupBy...)
|
||||
if groupBy != "" {
|
||||
groupBy = " group by " + groupBy
|
||||
}
|
||||
enrichedOrderBy := enrichOrderBy(mq.OrderBy, keys)
|
||||
orderBy := orderByAttributeKeyTags(panelType, enrichedOrderBy, mq.GroupBy, keys)
|
||||
|
||||
if orderBy != "" {
|
||||
orderBy = " order by " + orderBy
|
||||
}
|
||||
aggregationKey := ""
|
||||
if mq.AggregateAttribute.Key != "" {
|
||||
aggregationKey = getColumnName(mq.AggregateAttribute, keys)
|
||||
@ -266,7 +283,7 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
||||
v3.AggregateOperatorRateMin,
|
||||
v3.AggregateOperatorRate:
|
||||
op := fmt.Sprintf("%s(%s)/%d", aggregateOperatorToSQLFunc[mq.AggregateOperator], aggregationKey, step)
|
||||
query := fmt.Sprintf(queryTmpl, step, op, filterSubQuery, groupBy, having, orderBy)
|
||||
query := fmt.Sprintf(queryTmpl, op, filterSubQuery, groupBy, having, orderBy)
|
||||
return query, nil
|
||||
case
|
||||
v3.AggregateOperatorP05,
|
||||
@ -279,11 +296,11 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
||||
v3.AggregateOperatorP95,
|
||||
v3.AggregateOperatorP99:
|
||||
op := fmt.Sprintf("quantile(%v)(%s)", aggregateOperatorToPercentile[mq.AggregateOperator], aggregationKey)
|
||||
query := fmt.Sprintf(queryTmpl, step, op, filterSubQuery, groupBy, having, orderBy)
|
||||
query := fmt.Sprintf(queryTmpl, op, filterSubQuery, groupBy, having, orderBy)
|
||||
return query, nil
|
||||
case v3.AggregateOperatorAvg, v3.AggregateOperatorSum, v3.AggregateOperatorMin, v3.AggregateOperatorMax:
|
||||
op := fmt.Sprintf("%s(%s)", aggregateOperatorToSQLFunc[mq.AggregateOperator], aggregationKey)
|
||||
query := fmt.Sprintf(queryTmpl, step, op, filterSubQuery, groupBy, having, orderBy)
|
||||
query := fmt.Sprintf(queryTmpl, op, filterSubQuery, groupBy, having, orderBy)
|
||||
return query, nil
|
||||
case v3.AggregateOperatorCount:
|
||||
if mq.AggregateAttribute.Key != "" {
|
||||
@ -299,11 +316,11 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
||||
}
|
||||
}
|
||||
op := "toFloat64(count())"
|
||||
query := fmt.Sprintf(queryTmpl, step, op, filterSubQuery, groupBy, having, orderBy)
|
||||
query := fmt.Sprintf(queryTmpl, op, filterSubQuery, groupBy, having, orderBy)
|
||||
return query, nil
|
||||
case v3.AggregateOperatorCountDistinct:
|
||||
op := fmt.Sprintf("toFloat64(count(distinct(%s)))", aggregationKey)
|
||||
query := fmt.Sprintf(queryTmpl, step, op, filterSubQuery, groupBy, having, orderBy)
|
||||
query := fmt.Sprintf(queryTmpl, op, filterSubQuery, groupBy, having, orderBy)
|
||||
return query, nil
|
||||
case v3.AggregateOperatorNoOp:
|
||||
var query string
|
||||
@ -319,7 +336,7 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
||||
return "", fmt.Errorf("select columns cannot be empty for panelType %s", panelType)
|
||||
}
|
||||
selectColumns := getSelectColumns(mq.SelectColumns, keys)
|
||||
queryNoOpTmpl := fmt.Sprintf("SELECT timestamp as timestamp_datetime, spanID, traceID, "+"%s ", selectColumns) + "from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_TABLENAME + " where %s %s" + " order by %s"
|
||||
queryNoOpTmpl := fmt.Sprintf("SELECT timestamp as timestamp_datetime, spanID, traceID, "+"%s ", selectColumns) + "from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_TABLENAME + " where %s %s" + "%s"
|
||||
query = fmt.Sprintf(queryNoOpTmpl, spanIndexTableTimeFilter, filterSubQuery, orderBy)
|
||||
} else {
|
||||
return "", fmt.Errorf("unsupported aggregate operator %s for panelType %s", mq.AggregateOperator, panelType)
|
||||
@ -350,17 +367,19 @@ func enrichOrderBy(items []v3.OrderBy, keys map[string]v3.AttributeKey) []v3.Ord
|
||||
|
||||
// groupBy returns a string of comma separated tags for group by clause
|
||||
// `ts` is always added to the group by clause
|
||||
func groupBy(tags ...string) string {
|
||||
tags = append(tags, "ts")
|
||||
func groupBy(panelType v3.PanelType, tags ...string) string {
|
||||
if panelType == v3.PanelTypeGraph || panelType == v3.PanelTypeValue {
|
||||
tags = append(tags, "ts")
|
||||
}
|
||||
return strings.Join(tags, ",")
|
||||
}
|
||||
|
||||
func groupByAttributeKeyTags(keys map[string]v3.AttributeKey, tags ...v3.AttributeKey) string {
|
||||
func groupByAttributeKeyTags(panelType v3.PanelType, tags ...v3.AttributeKey) string {
|
||||
groupTags := []string{}
|
||||
for _, tag := range tags {
|
||||
groupTags = append(groupTags, fmt.Sprintf("`%s`", tag.Key))
|
||||
}
|
||||
return groupBy(groupTags...)
|
||||
return groupBy(panelType, groupTags...)
|
||||
}
|
||||
|
||||
// orderBy returns a string of comma separated tags for order by clause
|
||||
@ -403,7 +422,7 @@ func orderBy(panelType v3.PanelType, items []v3.OrderBy, tags []string, keys map
|
||||
if !addedToOrderBy[item.ColumnName] {
|
||||
attr := v3.AttributeKey{Key: item.ColumnName, DataType: item.DataType, Type: item.Type, IsColumn: item.IsColumn}
|
||||
name := getColumnName(attr, keys)
|
||||
|
||||
|
||||
if item.IsColumn {
|
||||
orderBy = append(orderBy, fmt.Sprintf("`%s` %s", name, item.Order))
|
||||
} else {
|
||||
@ -424,7 +443,7 @@ func orderByAttributeKeyTags(panelType v3.PanelType, items []v3.OrderBy, tags []
|
||||
|
||||
if panelType == v3.PanelTypeList && len(orderByArray) == 0 {
|
||||
orderByArray = append(orderByArray, constants.TIMESTAMP+" DESC")
|
||||
} else if panelType == v3.PanelTypeGraph || panelType == v3.PanelTypeTable {
|
||||
} else if panelType == v3.PanelTypeGraph || panelType == v3.PanelTypeValue {
|
||||
orderByArray = append(orderByArray, "ts")
|
||||
}
|
||||
|
||||
|
@ -216,13 +216,13 @@ var testGetSelectLabelsData = []struct {
|
||||
Name: "select keys for groupBy attribute",
|
||||
AggregateOperator: v3.AggregateOperatorCount,
|
||||
GroupByTags: []v3.AttributeKey{{Key: "user.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
||||
SelectLabels: ", stringTagMap['user.name'] as `user.name`",
|
||||
SelectLabels: " stringTagMap['user.name'] as `user.name`,",
|
||||
},
|
||||
{
|
||||
Name: "select keys for groupBy resource",
|
||||
AggregateOperator: v3.AggregateOperatorCount,
|
||||
GroupByTags: []v3.AttributeKey{{Key: "user.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource}},
|
||||
SelectLabels: ", resourceTagsMap['user.name'] as `user.name`",
|
||||
SelectLabels: " resourceTagsMap['user.name'] as `user.name`,",
|
||||
},
|
||||
{
|
||||
Name: "select keys for groupBy attribute and resource",
|
||||
@ -231,13 +231,13 @@ var testGetSelectLabelsData = []struct {
|
||||
{Key: "user.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource},
|
||||
{Key: "host", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
|
||||
},
|
||||
SelectLabels: ", resourceTagsMap['user.name'] as `user.name`, stringTagMap['host'] as `host`",
|
||||
SelectLabels: " resourceTagsMap['user.name'] as `user.name`, stringTagMap['host'] as `host`,",
|
||||
},
|
||||
{
|
||||
Name: "select keys for groupBy fixed columns",
|
||||
AggregateOperator: v3.AggregateOperatorCount,
|
||||
GroupByTags: []v3.AttributeKey{{Key: "host", IsColumn: true, DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
||||
SelectLabels: ", host as `host`",
|
||||
SelectLabels: " host as `host`,",
|
||||
},
|
||||
}
|
||||
|
||||
@ -956,6 +956,77 @@ var testBuildTracesQueryData = []struct {
|
||||
"AND stringTagMap['method'] = 'GET' group by ts having value > 10 order by ts",
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
},
|
||||
{
|
||||
Name: "Test count with having clause and filters",
|
||||
Start: 1680066360726210000,
|
||||
End: 1680066458000000000,
|
||||
Step: 60,
|
||||
BuilderQuery: &v3.BuilderQuery{
|
||||
QueryName: "A",
|
||||
AggregateAttribute: v3.AttributeKey{Key: "name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
|
||||
AggregateOperator: v3.AggregateOperatorCount,
|
||||
Expression: "A",
|
||||
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||||
{Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "GET", Operator: "="},
|
||||
},
|
||||
},
|
||||
Having: []v3.Having{
|
||||
{
|
||||
ColumnName: "name",
|
||||
Operator: ">",
|
||||
Value: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
TableName: "signoz_traces.distributed_signoz_index_v2",
|
||||
ExpectedQuery: "SELECT toStartOfInterval(timestamp, INTERVAL 60 SECOND) AS ts, toFloat64(count()) as value" +
|
||||
" from signoz_traces.distributed_signoz_index_v2 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
|
||||
"AND stringTagMap['method'] = 'GET' AND has(stringTagMap, 'name') group by ts having value > 10 order by ts",
|
||||
PanelType: v3.PanelTypeValue,
|
||||
},
|
||||
{
|
||||
Name: "Test aggregate PXX",
|
||||
Start: 1680066360726210000,
|
||||
End: 1680066458000000000,
|
||||
Step: 60,
|
||||
BuilderQuery: &v3.BuilderQuery{
|
||||
QueryName: "A",
|
||||
AggregateAttribute: v3.AttributeKey{Key: "durationNano", IsColumn: true, DataType: v3.AttributeKeyDataTypeFloat64, Type: v3.AttributeKeyTypeTag},
|
||||
AggregateOperator: v3.AggregateOperatorP05,
|
||||
Expression: "A",
|
||||
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
|
||||
GroupBy: []v3.AttributeKey{{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
||||
OrderBy: []v3.OrderBy{{ColumnName: "method", Order: "ASC"}},
|
||||
},
|
||||
TableName: "signoz_traces.distributed_signoz_index_v2",
|
||||
ExpectedQuery: "SELECT now() as ts, stringTagMap['method'] as `method`, " +
|
||||
"quantile(0.05)(durationNano) as value " +
|
||||
"from signoz_traces.distributed_signoz_index_v2 " +
|
||||
"where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
|
||||
"AND has(stringTagMap, 'method') group by `method` " +
|
||||
"order by `method` ASC",
|
||||
PanelType: v3.PanelTypeTable,
|
||||
},
|
||||
{
|
||||
Name: "Test aggregate PXX",
|
||||
Start: 1680066360726210000,
|
||||
End: 1680066458000000000,
|
||||
Step: 60,
|
||||
BuilderQuery: &v3.BuilderQuery{
|
||||
QueryName: "A",
|
||||
AggregateAttribute: v3.AttributeKey{Key: "durationNano", IsColumn: true, DataType: v3.AttributeKeyDataTypeFloat64, Type: v3.AttributeKeyTypeTag},
|
||||
AggregateOperator: v3.AggregateOperatorP05,
|
||||
Expression: "A",
|
||||
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
|
||||
GroupBy: []v3.AttributeKey{},
|
||||
OrderBy: []v3.OrderBy{},
|
||||
},
|
||||
TableName: "signoz_traces.distributed_signoz_index_v2",
|
||||
ExpectedQuery: "SELECT now() as ts, quantile(0.05)(durationNano) as value " +
|
||||
"from signoz_traces.distributed_signoz_index_v2 " +
|
||||
"where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000')",
|
||||
PanelType: v3.PanelTypeTable,
|
||||
},
|
||||
{
|
||||
Name: "Test Noop list view",
|
||||
Start: 1680066360726210000,
|
||||
|
Loading…
x
Reference in New Issue
Block a user