mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 04:19:01 +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 {
|
} else {
|
||||||
for _, tag := range groupBy {
|
for _, tag := range groupBy {
|
||||||
filterName := getColumnName(tag, keys)
|
filterName := getColumnName(tag, keys)
|
||||||
selectLabels += fmt.Sprintf(", %s as `%s`", filterName, tag.Key)
|
selectLabels += fmt.Sprintf(" %s as `%s`,", filterName, tag.Key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return selectLabels
|
return selectLabels
|
||||||
@ -235,14 +235,26 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
|||||||
having = " having " + having
|
having = " having " + having
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the aggregate value for interval
|
var queryTmpl string
|
||||||
queryTmpl :=
|
|
||||||
"SELECT toStartOfInterval(timestamp, INTERVAL %d SECOND) AS ts" + selectLabels +
|
if panelType == v3.PanelTypeTable {
|
||||||
", %s as value " +
|
queryTmpl =
|
||||||
"from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_TABLENAME +
|
"SELECT now() as ts," + selectLabels +
|
||||||
" where " + spanIndexTableTimeFilter + "%s " +
|
" %s as value " +
|
||||||
"group by %s%s " +
|
"from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_TABLENAME +
|
||||||
"order by %s"
|
" 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)
|
emptyValuesInGroupByFilter, err := handleEmptyValuesInGroupBy(keys, mq.GroupBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -250,10 +262,15 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
|||||||
}
|
}
|
||||||
filterSubQuery += emptyValuesInGroupByFilter
|
filterSubQuery += emptyValuesInGroupByFilter
|
||||||
|
|
||||||
groupBy := groupByAttributeKeyTags(keys, mq.GroupBy...)
|
groupBy := groupByAttributeKeyTags(panelType, mq.GroupBy...)
|
||||||
|
if groupBy != "" {
|
||||||
|
groupBy = " group by " + groupBy
|
||||||
|
}
|
||||||
enrichedOrderBy := enrichOrderBy(mq.OrderBy, keys)
|
enrichedOrderBy := enrichOrderBy(mq.OrderBy, keys)
|
||||||
orderBy := orderByAttributeKeyTags(panelType, enrichedOrderBy, mq.GroupBy, keys)
|
orderBy := orderByAttributeKeyTags(panelType, enrichedOrderBy, mq.GroupBy, keys)
|
||||||
|
if orderBy != "" {
|
||||||
|
orderBy = " order by " + orderBy
|
||||||
|
}
|
||||||
aggregationKey := ""
|
aggregationKey := ""
|
||||||
if mq.AggregateAttribute.Key != "" {
|
if mq.AggregateAttribute.Key != "" {
|
||||||
aggregationKey = getColumnName(mq.AggregateAttribute, keys)
|
aggregationKey = getColumnName(mq.AggregateAttribute, keys)
|
||||||
@ -266,7 +283,7 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
|||||||
v3.AggregateOperatorRateMin,
|
v3.AggregateOperatorRateMin,
|
||||||
v3.AggregateOperatorRate:
|
v3.AggregateOperatorRate:
|
||||||
op := fmt.Sprintf("%s(%s)/%d", aggregateOperatorToSQLFunc[mq.AggregateOperator], aggregationKey, step)
|
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
|
return query, nil
|
||||||
case
|
case
|
||||||
v3.AggregateOperatorP05,
|
v3.AggregateOperatorP05,
|
||||||
@ -279,11 +296,11 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
|||||||
v3.AggregateOperatorP95,
|
v3.AggregateOperatorP95,
|
||||||
v3.AggregateOperatorP99:
|
v3.AggregateOperatorP99:
|
||||||
op := fmt.Sprintf("quantile(%v)(%s)", aggregateOperatorToPercentile[mq.AggregateOperator], aggregationKey)
|
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
|
return query, nil
|
||||||
case v3.AggregateOperatorAvg, v3.AggregateOperatorSum, v3.AggregateOperatorMin, v3.AggregateOperatorMax:
|
case v3.AggregateOperatorAvg, v3.AggregateOperatorSum, v3.AggregateOperatorMin, v3.AggregateOperatorMax:
|
||||||
op := fmt.Sprintf("%s(%s)", aggregateOperatorToSQLFunc[mq.AggregateOperator], aggregationKey)
|
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
|
return query, nil
|
||||||
case v3.AggregateOperatorCount:
|
case v3.AggregateOperatorCount:
|
||||||
if mq.AggregateAttribute.Key != "" {
|
if mq.AggregateAttribute.Key != "" {
|
||||||
@ -299,11 +316,11 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, tableName str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
op := "toFloat64(count())"
|
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
|
return query, nil
|
||||||
case v3.AggregateOperatorCountDistinct:
|
case v3.AggregateOperatorCountDistinct:
|
||||||
op := fmt.Sprintf("toFloat64(count(distinct(%s)))", aggregationKey)
|
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
|
return query, nil
|
||||||
case v3.AggregateOperatorNoOp:
|
case v3.AggregateOperatorNoOp:
|
||||||
var query string
|
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)
|
return "", fmt.Errorf("select columns cannot be empty for panelType %s", panelType)
|
||||||
}
|
}
|
||||||
selectColumns := getSelectColumns(mq.SelectColumns, keys)
|
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)
|
query = fmt.Sprintf(queryNoOpTmpl, spanIndexTableTimeFilter, filterSubQuery, orderBy)
|
||||||
} else {
|
} else {
|
||||||
return "", fmt.Errorf("unsupported aggregate operator %s for panelType %s", mq.AggregateOperator, panelType)
|
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
|
// groupBy returns a string of comma separated tags for group by clause
|
||||||
// `ts` is always added to the group by clause
|
// `ts` is always added to the group by clause
|
||||||
func groupBy(tags ...string) string {
|
func groupBy(panelType v3.PanelType, tags ...string) string {
|
||||||
tags = append(tags, "ts")
|
if panelType == v3.PanelTypeGraph || panelType == v3.PanelTypeValue {
|
||||||
|
tags = append(tags, "ts")
|
||||||
|
}
|
||||||
return strings.Join(tags, ",")
|
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{}
|
groupTags := []string{}
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
groupTags = append(groupTags, fmt.Sprintf("`%s`", tag.Key))
|
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
|
// 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] {
|
if !addedToOrderBy[item.ColumnName] {
|
||||||
attr := v3.AttributeKey{Key: item.ColumnName, DataType: item.DataType, Type: item.Type, IsColumn: item.IsColumn}
|
attr := v3.AttributeKey{Key: item.ColumnName, DataType: item.DataType, Type: item.Type, IsColumn: item.IsColumn}
|
||||||
name := getColumnName(attr, keys)
|
name := getColumnName(attr, keys)
|
||||||
|
|
||||||
if item.IsColumn {
|
if item.IsColumn {
|
||||||
orderBy = append(orderBy, fmt.Sprintf("`%s` %s", name, item.Order))
|
orderBy = append(orderBy, fmt.Sprintf("`%s` %s", name, item.Order))
|
||||||
} else {
|
} else {
|
||||||
@ -424,7 +443,7 @@ func orderByAttributeKeyTags(panelType v3.PanelType, items []v3.OrderBy, tags []
|
|||||||
|
|
||||||
if panelType == v3.PanelTypeList && len(orderByArray) == 0 {
|
if panelType == v3.PanelTypeList && len(orderByArray) == 0 {
|
||||||
orderByArray = append(orderByArray, constants.TIMESTAMP+" DESC")
|
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")
|
orderByArray = append(orderByArray, "ts")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,13 +216,13 @@ var testGetSelectLabelsData = []struct {
|
|||||||
Name: "select keys for groupBy attribute",
|
Name: "select keys for groupBy attribute",
|
||||||
AggregateOperator: v3.AggregateOperatorCount,
|
AggregateOperator: v3.AggregateOperatorCount,
|
||||||
GroupByTags: []v3.AttributeKey{{Key: "user.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
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",
|
Name: "select keys for groupBy resource",
|
||||||
AggregateOperator: v3.AggregateOperatorCount,
|
AggregateOperator: v3.AggregateOperatorCount,
|
||||||
GroupByTags: []v3.AttributeKey{{Key: "user.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource}},
|
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",
|
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: "user.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource},
|
||||||
{Key: "host", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
|
{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",
|
Name: "select keys for groupBy fixed columns",
|
||||||
AggregateOperator: v3.AggregateOperatorCount,
|
AggregateOperator: v3.AggregateOperatorCount,
|
||||||
GroupByTags: []v3.AttributeKey{{Key: "host", IsColumn: true, DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
|
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",
|
"AND stringTagMap['method'] = 'GET' group by ts having value > 10 order by ts",
|
||||||
PanelType: v3.PanelTypeGraph,
|
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",
|
Name: "Test Noop list view",
|
||||||
Start: 1680066360726210000,
|
Start: 1680066360726210000,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user