mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-29 21:12:03 +08:00
476 lines
11 KiB
Go
476 lines
11 KiB
Go
package tracefunnel
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
tracev4 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v4"
|
|
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
|
"github.com/SigNoz/signoz/pkg/types/tracefunneltypes"
|
|
)
|
|
|
|
// sanitizeClause adds AND prefix to non-empty clauses if not already present
|
|
func sanitizeClause(clause string) string {
|
|
if clause == "" {
|
|
return ""
|
|
}
|
|
// Check if clause already starts with AND
|
|
if strings.HasPrefix(strings.TrimSpace(clause), "AND") {
|
|
return clause
|
|
}
|
|
return "AND " + clause
|
|
}
|
|
|
|
func ValidateTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.ClickHouseQuery, error) {
|
|
var query string
|
|
var err error
|
|
|
|
funnelSteps := funnel.Steps
|
|
containsErrorT1 := 0
|
|
containsErrorT2 := 0
|
|
containsErrorT3 := 0
|
|
|
|
if funnelSteps[0].HasErrors {
|
|
containsErrorT1 = 1
|
|
}
|
|
if funnelSteps[1].HasErrors {
|
|
containsErrorT2 = 1
|
|
}
|
|
if len(funnel.Steps) > 2 && funnelSteps[2].HasErrors {
|
|
containsErrorT3 = 1
|
|
}
|
|
|
|
// Build filter clauses for each step
|
|
clauseStep1, err := tracev4.BuildTracesFilter(funnelSteps[0].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep2, err := tracev4.BuildTracesFilter(funnelSteps[1].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep3 := ""
|
|
if len(funnel.Steps) > 2 {
|
|
clauseStep3, err = tracev4.BuildTracesFilter(funnelSteps[2].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Sanitize clauses
|
|
clauseStep1 = sanitizeClause(clauseStep1)
|
|
clauseStep2 = sanitizeClause(clauseStep2)
|
|
clauseStep3 = sanitizeClause(clauseStep3)
|
|
|
|
if len(funnel.Steps) > 2 {
|
|
query = BuildThreeStepFunnelValidationQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
containsErrorT3,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
funnelSteps[2].ServiceName,
|
|
funnelSteps[2].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
clauseStep3,
|
|
)
|
|
} else {
|
|
query = BuildTwoStepFunnelValidationQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
)
|
|
}
|
|
|
|
return &v3.ClickHouseQuery{
|
|
Query: query,
|
|
}, nil
|
|
}
|
|
|
|
func GetFunnelAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.ClickHouseQuery, error) {
|
|
var query string
|
|
var err error
|
|
|
|
funnelSteps := funnel.Steps
|
|
containsErrorT1 := 0
|
|
containsErrorT2 := 0
|
|
containsErrorT3 := 0
|
|
latencyPointerT1 := funnelSteps[0].LatencyPointer
|
|
latencyPointerT2 := funnelSteps[1].LatencyPointer
|
|
latencyPointerT3 := "start"
|
|
if len(funnel.Steps) > 2 {
|
|
latencyPointerT3 = funnelSteps[2].LatencyPointer
|
|
}
|
|
|
|
if funnelSteps[0].HasErrors {
|
|
containsErrorT1 = 1
|
|
}
|
|
if funnelSteps[1].HasErrors {
|
|
containsErrorT2 = 1
|
|
}
|
|
if len(funnel.Steps) > 2 && funnelSteps[2].HasErrors {
|
|
containsErrorT3 = 1
|
|
}
|
|
|
|
// Build filter clauses for each step
|
|
clauseStep1, err := tracev4.BuildTracesFilter(funnelSteps[0].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep2, err := tracev4.BuildTracesFilter(funnelSteps[1].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep3 := ""
|
|
if len(funnel.Steps) > 2 {
|
|
clauseStep3, err = tracev4.BuildTracesFilter(funnelSteps[2].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Sanitize clauses
|
|
clauseStep1 = sanitizeClause(clauseStep1)
|
|
clauseStep2 = sanitizeClause(clauseStep2)
|
|
clauseStep3 = sanitizeClause(clauseStep3)
|
|
|
|
if len(funnel.Steps) > 2 {
|
|
query = BuildThreeStepFunnelOverviewQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
containsErrorT3,
|
|
latencyPointerT1,
|
|
latencyPointerT2,
|
|
latencyPointerT3,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
funnelSteps[2].ServiceName,
|
|
funnelSteps[2].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
clauseStep3,
|
|
)
|
|
} else {
|
|
query = BuildTwoStepFunnelOverviewQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
latencyPointerT1,
|
|
latencyPointerT2,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
)
|
|
}
|
|
return &v3.ClickHouseQuery{Query: query}, nil
|
|
}
|
|
|
|
func GetFunnelStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.ClickHouseQuery, error) {
|
|
var query string
|
|
var err error
|
|
|
|
funnelSteps := funnel.Steps
|
|
containsErrorT1 := 0
|
|
containsErrorT2 := 0
|
|
containsErrorT3 := 0
|
|
latencyPointerT1 := funnelSteps[0].LatencyPointer
|
|
latencyPointerT2 := funnelSteps[1].LatencyPointer
|
|
latencyPointerT3 := "start"
|
|
if len(funnel.Steps) > 2 {
|
|
latencyPointerT3 = funnelSteps[2].LatencyPointer
|
|
}
|
|
latencyTypeT2 := "0.99"
|
|
latencyTypeT3 := "0.99"
|
|
|
|
if stepStart == stepEnd {
|
|
return nil, fmt.Errorf("step start and end cannot be the same for /step/overview")
|
|
}
|
|
|
|
if funnelSteps[0].HasErrors {
|
|
containsErrorT1 = 1
|
|
}
|
|
if funnelSteps[1].HasErrors {
|
|
containsErrorT2 = 1
|
|
}
|
|
if len(funnel.Steps) > 2 && funnelSteps[2].HasErrors {
|
|
containsErrorT3 = 1
|
|
}
|
|
|
|
if funnelSteps[1].LatencyType != "" {
|
|
latency := strings.ToLower(funnelSteps[1].LatencyType)
|
|
if latency == "p90" {
|
|
latencyTypeT2 = "0.90"
|
|
} else if latency == "p95" {
|
|
latencyTypeT2 = "0.95"
|
|
} else {
|
|
latencyTypeT2 = "0.99"
|
|
}
|
|
}
|
|
if len(funnel.Steps) > 2 && funnelSteps[2].LatencyType != "" {
|
|
latency := strings.ToLower(funnelSteps[2].LatencyType)
|
|
if latency == "p90" {
|
|
latencyTypeT3 = "0.90"
|
|
} else if latency == "p95" {
|
|
latencyTypeT3 = "0.95"
|
|
} else {
|
|
latencyTypeT3 = "0.99"
|
|
}
|
|
}
|
|
|
|
// Build filter clauses for each step
|
|
clauseStep1, err := tracev4.BuildTracesFilter(funnelSteps[0].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep2, err := tracev4.BuildTracesFilter(funnelSteps[1].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep3 := ""
|
|
if len(funnel.Steps) > 2 {
|
|
clauseStep3, err = tracev4.BuildTracesFilter(funnelSteps[2].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Sanitize clauses
|
|
clauseStep1 = sanitizeClause(clauseStep1)
|
|
clauseStep2 = sanitizeClause(clauseStep2)
|
|
clauseStep3 = sanitizeClause(clauseStep3)
|
|
|
|
if len(funnel.Steps) > 2 {
|
|
query = BuildThreeStepFunnelStepOverviewQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
containsErrorT3,
|
|
latencyPointerT1,
|
|
latencyPointerT2,
|
|
latencyPointerT3,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
funnelSteps[2].ServiceName,
|
|
funnelSteps[2].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
clauseStep3,
|
|
stepStart,
|
|
stepEnd,
|
|
latencyTypeT2,
|
|
latencyTypeT3,
|
|
)
|
|
} else {
|
|
query = BuildTwoStepFunnelStepOverviewQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
latencyPointerT1,
|
|
latencyPointerT2,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
latencyTypeT2,
|
|
)
|
|
}
|
|
return &v3.ClickHouseQuery{Query: query}, nil
|
|
}
|
|
|
|
func GetStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.ClickHouseQuery, error) {
|
|
var query string
|
|
|
|
funnelSteps := funnel.Steps
|
|
containsErrorT1 := 0
|
|
containsErrorT2 := 0
|
|
containsErrorT3 := 0
|
|
|
|
if funnelSteps[0].HasErrors {
|
|
containsErrorT1 = 1
|
|
}
|
|
if funnelSteps[1].HasErrors {
|
|
containsErrorT2 = 1
|
|
}
|
|
if len(funnel.Steps) > 2 && funnelSteps[2].HasErrors {
|
|
containsErrorT3 = 1
|
|
}
|
|
|
|
// Build filter clauses for each step
|
|
clauseStep1, err := tracev4.BuildTracesFilter(funnelSteps[0].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep2, err := tracev4.BuildTracesFilter(funnelSteps[1].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep3 := ""
|
|
if len(funnel.Steps) > 2 {
|
|
clauseStep3, err = tracev4.BuildTracesFilter(funnelSteps[2].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Sanitize clauses
|
|
clauseStep1 = sanitizeClause(clauseStep1)
|
|
clauseStep2 = sanitizeClause(clauseStep2)
|
|
clauseStep3 = sanitizeClause(clauseStep3)
|
|
|
|
if len(funnel.Steps) > 2 {
|
|
query = BuildThreeStepFunnelCountQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
containsErrorT3,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
funnelSteps[2].ServiceName,
|
|
funnelSteps[2].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
clauseStep3,
|
|
)
|
|
} else {
|
|
query = BuildTwoStepFunnelCountQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[0].ServiceName,
|
|
funnelSteps[0].SpanName,
|
|
funnelSteps[1].ServiceName,
|
|
funnelSteps[1].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
)
|
|
}
|
|
|
|
return &v3.ClickHouseQuery{
|
|
Query: query,
|
|
}, nil
|
|
}
|
|
|
|
func GetSlowestTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.ClickHouseQuery, error) {
|
|
funnelSteps := funnel.Steps
|
|
containsErrorT1 := 0
|
|
containsErrorT2 := 0
|
|
stepStartOrder := 0
|
|
stepEndOrder := 1
|
|
|
|
if stepStart != stepEnd {
|
|
stepStartOrder = int(stepStart) - 1
|
|
stepEndOrder = int(stepEnd) - 1
|
|
if funnelSteps[stepStartOrder].HasErrors {
|
|
containsErrorT1 = 1
|
|
}
|
|
if funnelSteps[stepEndOrder].HasErrors {
|
|
containsErrorT2 = 1
|
|
}
|
|
}
|
|
|
|
// Build filter clauses for the steps
|
|
clauseStep1, err := tracev4.BuildTracesFilter(funnelSteps[stepStartOrder].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep2, err := tracev4.BuildTracesFilter(funnelSteps[stepEndOrder].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Sanitize clauses
|
|
clauseStep1 = sanitizeClause(clauseStep1)
|
|
clauseStep2 = sanitizeClause(clauseStep2)
|
|
|
|
query := BuildTwoStepFunnelTopSlowTracesQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[stepStartOrder].ServiceName,
|
|
funnelSteps[stepStartOrder].SpanName,
|
|
funnelSteps[stepEndOrder].ServiceName,
|
|
funnelSteps[stepEndOrder].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
)
|
|
return &v3.ClickHouseQuery{Query: query}, nil
|
|
}
|
|
|
|
func GetErroredTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.ClickHouseQuery, error) {
|
|
funnelSteps := funnel.Steps
|
|
containsErrorT1 := 0
|
|
containsErrorT2 := 0
|
|
stepStartOrder := 0
|
|
stepEndOrder := 1
|
|
|
|
if stepStart != stepEnd {
|
|
stepStartOrder = int(stepStart) - 1
|
|
stepEndOrder = int(stepEnd) - 1
|
|
if funnelSteps[stepStartOrder].HasErrors {
|
|
containsErrorT1 = 1
|
|
}
|
|
if funnelSteps[stepEndOrder].HasErrors {
|
|
containsErrorT2 = 1
|
|
}
|
|
}
|
|
|
|
// Build filter clauses for the steps
|
|
clauseStep1, err := tracev4.BuildTracesFilter(funnelSteps[stepStartOrder].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clauseStep2, err := tracev4.BuildTracesFilter(funnelSteps[stepEndOrder].Filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Sanitize clauses
|
|
clauseStep1 = sanitizeClause(clauseStep1)
|
|
clauseStep2 = sanitizeClause(clauseStep2)
|
|
|
|
query := BuildTwoStepFunnelTopSlowErrorTracesQuery(
|
|
containsErrorT1,
|
|
containsErrorT2,
|
|
timeRange.StartTime,
|
|
timeRange.EndTime,
|
|
funnelSteps[stepStartOrder].ServiceName,
|
|
funnelSteps[stepStartOrder].SpanName,
|
|
funnelSteps[stepEndOrder].ServiceName,
|
|
funnelSteps[stepEndOrder].SpanName,
|
|
clauseStep1,
|
|
clauseStep2,
|
|
)
|
|
return &v3.ClickHouseQuery{Query: query}, nil
|
|
}
|