mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-07 22:01:50 +08:00

* feat: support for new enrichment logic in traces * fix: default test added * fix: update func name in links * Update pkg/query-service/utils/logs_test.go Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
223 lines
6.3 KiB
Go
223 lines
6.3 KiB
Go
package contextlinks
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"time"
|
|
|
|
tracesV3 "go.signoz.io/signoz/pkg/query-service/app/traces/v3"
|
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
|
)
|
|
|
|
func PrepareLinksToTraces(start, end time.Time, filterItems []v3.FilterItem) string {
|
|
|
|
// Traces list view expects time in nanoseconds
|
|
tr := v3.URLShareableTimeRange{
|
|
Start: start.UnixNano(),
|
|
End: end.UnixNano(),
|
|
PageSize: 100,
|
|
}
|
|
|
|
options := v3.URLShareableOptions{
|
|
MaxLines: 2,
|
|
Format: "list",
|
|
SelectColumns: tracesV3.TracesListViewDefaultSelectedColumns,
|
|
}
|
|
|
|
period, _ := json.Marshal(tr)
|
|
urlEncodedTimeRange := url.QueryEscape(string(period))
|
|
|
|
builderQuery := v3.BuilderQuery{
|
|
DataSource: v3.DataSourceTraces,
|
|
QueryName: "A",
|
|
AggregateOperator: v3.AggregateOperatorNoOp,
|
|
AggregateAttribute: v3.AttributeKey{},
|
|
Filters: &v3.FilterSet{
|
|
Items: filterItems,
|
|
Operator: "AND",
|
|
},
|
|
Expression: "A",
|
|
Disabled: false,
|
|
Having: []v3.Having{},
|
|
StepInterval: 60,
|
|
OrderBy: []v3.OrderBy{
|
|
{
|
|
ColumnName: "timestamp",
|
|
Order: "desc",
|
|
},
|
|
},
|
|
}
|
|
|
|
urlData := v3.URLShareableCompositeQuery{
|
|
QueryType: string(v3.QueryTypeBuilder),
|
|
Builder: v3.URLShareableBuilderQuery{
|
|
QueryData: []v3.BuilderQuery{
|
|
builderQuery,
|
|
},
|
|
QueryFormulas: make([]string, 0),
|
|
},
|
|
}
|
|
|
|
data, _ := json.Marshal(urlData)
|
|
compositeQuery := url.QueryEscape(url.QueryEscape(string(data)))
|
|
|
|
optionsData, _ := json.Marshal(options)
|
|
urlEncodedOptions := url.QueryEscape(string(optionsData))
|
|
|
|
return fmt.Sprintf("compositeQuery=%s&timeRange=%s&startTime=%d&endTime=%d&options=%s", compositeQuery, urlEncodedTimeRange, tr.Start, tr.End, urlEncodedOptions)
|
|
}
|
|
|
|
func PrepareLinksToLogs(start, end time.Time, filterItems []v3.FilterItem) string {
|
|
|
|
// Logs list view expects time in milliseconds
|
|
tr := v3.URLShareableTimeRange{
|
|
Start: start.UnixMilli(),
|
|
End: end.UnixMilli(),
|
|
PageSize: 100,
|
|
}
|
|
|
|
options := v3.URLShareableOptions{
|
|
MaxLines: 2,
|
|
Format: "list",
|
|
SelectColumns: []v3.AttributeKey{},
|
|
}
|
|
|
|
period, _ := json.Marshal(tr)
|
|
urlEncodedTimeRange := url.QueryEscape(string(period))
|
|
|
|
builderQuery := v3.BuilderQuery{
|
|
DataSource: v3.DataSourceLogs,
|
|
QueryName: "A",
|
|
AggregateOperator: v3.AggregateOperatorNoOp,
|
|
AggregateAttribute: v3.AttributeKey{},
|
|
Filters: &v3.FilterSet{
|
|
Items: filterItems,
|
|
Operator: "AND",
|
|
},
|
|
Expression: "A",
|
|
Disabled: false,
|
|
Having: []v3.Having{},
|
|
StepInterval: 60,
|
|
OrderBy: []v3.OrderBy{
|
|
{
|
|
ColumnName: "timestamp",
|
|
Order: "desc",
|
|
},
|
|
},
|
|
}
|
|
|
|
urlData := v3.URLShareableCompositeQuery{
|
|
QueryType: string(v3.QueryTypeBuilder),
|
|
Builder: v3.URLShareableBuilderQuery{
|
|
QueryData: []v3.BuilderQuery{
|
|
builderQuery,
|
|
},
|
|
QueryFormulas: make([]string, 0),
|
|
},
|
|
}
|
|
|
|
data, _ := json.Marshal(urlData)
|
|
compositeQuery := url.QueryEscape(url.QueryEscape(string(data)))
|
|
|
|
optionsData, _ := json.Marshal(options)
|
|
urlEncodedOptions := url.QueryEscape(string(optionsData))
|
|
|
|
return fmt.Sprintf("compositeQuery=%s&timeRange=%s&startTime=%d&endTime=%d&options=%s", compositeQuery, urlEncodedTimeRange, tr.Start, tr.End, urlEncodedOptions)
|
|
}
|
|
|
|
// The following function is used to prepare the where clause for the query
|
|
// `lbls` contains the key value pairs of the labels from the result of the query
|
|
// We iterate over the where clause and replace the labels with the actual values
|
|
// There are two cases:
|
|
// 1. The label is present in the where clause
|
|
// 2. The label is not present in the where clause
|
|
//
|
|
// Example for case 2:
|
|
// Latency by serviceName without any filter
|
|
// In this case, for each service with latency > threshold we send a notification
|
|
// The expectation will be that clicking on the related traces for service A, will
|
|
// take us to the traces page with the filter serviceName=A
|
|
// So for all the missing labels in the where clause, we add them as key = value
|
|
//
|
|
// Example for case 1:
|
|
// Severity text IN (WARN, ERROR)
|
|
// In this case, the Severity text will appear in the `lbls` if it were part of the group
|
|
// by clause, in which case we replace it with the actual value for the notification
|
|
// i.e Severity text = WARN
|
|
// If the Severity text is not part of the group by clause, then we add it as it is
|
|
func PrepareFilters(labels map[string]string, whereClauseItems []v3.FilterItem, groupByItems []v3.AttributeKey, keys map[string]v3.AttributeKey) []v3.FilterItem {
|
|
var filterItems []v3.FilterItem
|
|
|
|
added := make(map[string]struct{})
|
|
|
|
for _, item := range whereClauseItems {
|
|
exists := false
|
|
for key, value := range labels {
|
|
if item.Key.Key == key {
|
|
// if the label is present in the where clause, replace it with key = value
|
|
filterItems = append(filterItems, v3.FilterItem{
|
|
Key: item.Key,
|
|
Operator: v3.FilterOperatorEqual,
|
|
Value: value,
|
|
})
|
|
exists = true
|
|
added[key] = struct{}{}
|
|
break
|
|
}
|
|
}
|
|
|
|
if !exists {
|
|
// if there is no label for the filter item, add it as it is
|
|
filterItems = append(filterItems, item)
|
|
}
|
|
}
|
|
|
|
// if there are labels which are not part of the where clause, but
|
|
// exist in the result, then they could be part of the group by clause
|
|
for key, value := range labels {
|
|
if _, ok := added[key]; !ok {
|
|
// start by taking the attribute key from the keys map, if not present, create a new one
|
|
var attributeKey v3.AttributeKey
|
|
var attrFound bool
|
|
|
|
// as of now this logic will only apply for logs
|
|
for _, tKey := range utils.GenerateEnrichmentKeys(v3.AttributeKey{Key: key}) {
|
|
if val, ok := keys[tKey]; ok {
|
|
attributeKey = val
|
|
attrFound = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// check if the attribute key is directly present, as of now this will always be false for logs
|
|
// as for logs it will be satisfied in the condition above
|
|
if !attrFound {
|
|
attributeKey, attrFound = keys[key]
|
|
}
|
|
|
|
// if the attribute key is not present, create a new one
|
|
if !attrFound {
|
|
attributeKey = v3.AttributeKey{Key: key}
|
|
}
|
|
|
|
// if there is a group by item with the same key, use that instead
|
|
for _, groupByItem := range groupByItems {
|
|
if groupByItem.Key == key {
|
|
attributeKey = groupByItem
|
|
break
|
|
}
|
|
}
|
|
|
|
filterItems = append(filterItems, v3.FilterItem{
|
|
Key: attributeKey,
|
|
Operator: v3.FilterOperatorEqual,
|
|
Value: value,
|
|
})
|
|
}
|
|
}
|
|
|
|
return filterItems
|
|
}
|