* Jaeger: Migrate Services and Operations to the gRPC Jaeger endpoint (#112384) * add grpc feature toggle * move types into types.go * creates grpc client functions for services and operations * Call grpc services function when feature flag is enabled for health check * remove unnecessary double encoding * check for successful status code before decoding response and return nil in case of successful response * remove duplicate code * use variable * fix error type in testsz * Jaeger: Migrate search and Trace Search calls to use gRPC endpoint (#112610) * move all types into types package except for JagerClient * move all helper functions into utils package * change return type of search function to be frames and add grpc search functionality * fix tests * fix types and the way we check error response from grpc * change trace name and duration unit conversion * fix types and add tests * support queryAttributes * quick limit implementation in post processing * add todo for attributes / tags * make trace functionality ready to support grpc flow * add functions to process search response for a specific trace and create the Trace frame * tests for helper funtions * remove grpc querying for now! * change logic to be able to process and support multiple resource spans * remove logic for gRPC from grpc_client.go * add equivalent fields for logs and references * add tests for grpcTraceResponse function * fix types after merge with main * fix status code checks and return nil for error on successful responses * enable reading through config flag for trace search * create sigle key value type since they are similar for OTLP and non OTLP based formats * reference right type * convert events and links into references and logs * add status code, status message and kind to data frame * fix tests to accomodate new format * remove unused function and add more tests * remove edit flag for jsonc golden test files * add clarifying comment * fix tests and linting * fix golden files for testing * fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * add clarifying comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * remove unnecessary logging statement * fix downstream errors --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * use downstreamerrorf where applicable and add missing downstream eror sources. * tests --------- Co-authored-by: ismail simsek <ismailsimsek09@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
214 lines
5.3 KiB
Go
214 lines
5.3 KiB
Go
package utils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
"github.com/grafana/grafana/pkg/tsdb/jaeger/types"
|
|
)
|
|
|
|
func TransformSearchResponse(response []types.TraceResponse, dsUID string, dsName string) *data.Frame {
|
|
// Create a frame for the traces
|
|
frame := data.NewFrame("traces",
|
|
data.NewField("traceID", nil, []string{}).SetConfig(&data.FieldConfig{
|
|
DisplayName: "Trace ID",
|
|
Links: []data.DataLink{
|
|
{
|
|
Title: "Trace: ${__value.raw}",
|
|
URL: "",
|
|
Internal: &data.InternalDataLink{
|
|
DatasourceUID: dsUID,
|
|
DatasourceName: dsName,
|
|
Query: map[string]interface{}{
|
|
"query": "${__value.raw}",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
data.NewField("traceName", nil, []string{}).SetConfig(&data.FieldConfig{
|
|
DisplayName: "Trace name",
|
|
}),
|
|
data.NewField("startTime", nil, []time.Time{}).SetConfig(&data.FieldConfig{
|
|
DisplayName: "Start time",
|
|
}),
|
|
data.NewField("duration", nil, []int64{}).SetConfig(&data.FieldConfig{
|
|
DisplayName: "Duration",
|
|
Unit: "µs",
|
|
}),
|
|
)
|
|
|
|
// Set the visualization type to table
|
|
frame.Meta = &data.FrameMeta{
|
|
PreferredVisualization: "table",
|
|
}
|
|
|
|
// Sort traces by start time in descending order (newest first)
|
|
sort.Slice(response, func(i, j int) bool {
|
|
rootSpanI := response[i].Spans[0]
|
|
rootSpanJ := response[j].Spans[0]
|
|
|
|
for _, span := range response[i].Spans {
|
|
if span.StartTime < rootSpanI.StartTime {
|
|
rootSpanI = span
|
|
}
|
|
}
|
|
|
|
for _, span := range response[j].Spans {
|
|
if span.StartTime < rootSpanJ.StartTime {
|
|
rootSpanJ = span
|
|
}
|
|
}
|
|
|
|
return rootSpanI.StartTime > rootSpanJ.StartTime
|
|
})
|
|
|
|
// Process each trace
|
|
for _, trace := range response {
|
|
if len(trace.Spans) == 0 {
|
|
continue
|
|
}
|
|
|
|
// Get the root span
|
|
rootSpan := trace.Spans[0]
|
|
for _, span := range trace.Spans {
|
|
if span.StartTime < rootSpan.StartTime {
|
|
rootSpan = span
|
|
}
|
|
}
|
|
|
|
// Get the service name for the trace
|
|
serviceName := ""
|
|
if process, ok := trace.Processes[rootSpan.ProcessID]; ok {
|
|
serviceName = process.ServiceName
|
|
}
|
|
|
|
// Get the trace name and start time
|
|
traceName := fmt.Sprintf("%s: %s", serviceName, rootSpan.OperationName)
|
|
startTime := time.Unix(0, rootSpan.StartTime*1000)
|
|
|
|
// Append the row to the frame
|
|
frame.AppendRow(
|
|
trace.TraceID,
|
|
traceName,
|
|
startTime,
|
|
rootSpan.Duration,
|
|
)
|
|
}
|
|
|
|
return frame
|
|
}
|
|
|
|
func TransformTraceResponse(trace types.TraceResponse, refID string) *data.Frame {
|
|
frame := data.NewFrame(refID,
|
|
data.NewField("traceID", nil, []string{}),
|
|
data.NewField("spanID", nil, []string{}),
|
|
data.NewField("parentSpanID", nil, []*string{}),
|
|
data.NewField("operationName", nil, []string{}),
|
|
data.NewField("serviceName", nil, []string{}),
|
|
data.NewField("serviceTags", nil, []json.RawMessage{}),
|
|
data.NewField("startTime", nil, []float64{}),
|
|
data.NewField("duration", nil, []float64{}),
|
|
data.NewField("logs", nil, []json.RawMessage{}),
|
|
data.NewField("references", nil, []json.RawMessage{}),
|
|
data.NewField("tags", nil, []json.RawMessage{}),
|
|
data.NewField("warnings", nil, []json.RawMessage{}),
|
|
data.NewField("stackTraces", nil, []json.RawMessage{}),
|
|
)
|
|
|
|
// Set metadata for trace visualization
|
|
frame.Meta = &data.FrameMeta{
|
|
PreferredVisualization: "trace",
|
|
Custom: map[string]interface{}{
|
|
"traceFormat": "jaeger",
|
|
},
|
|
}
|
|
|
|
// Process each span in the trace
|
|
for _, span := range trace.Spans {
|
|
// Find parent span ID
|
|
var parentSpanID *string
|
|
for _, ref := range span.References {
|
|
if ref.RefType == "CHILD_OF" {
|
|
s := ref.SpanID
|
|
parentSpanID = &s
|
|
break
|
|
}
|
|
}
|
|
|
|
// Get service name and tags
|
|
serviceName := ""
|
|
serviceTags := json.RawMessage{}
|
|
if process, ok := trace.Processes[span.ProcessID]; ok {
|
|
serviceName = process.ServiceName
|
|
tagsMarshaled, err := json.Marshal(process.Tags)
|
|
if err == nil {
|
|
serviceTags = json.RawMessage(tagsMarshaled)
|
|
}
|
|
}
|
|
|
|
// Convert logs
|
|
logs := json.RawMessage{}
|
|
logsMarshaled, err := json.Marshal(span.Logs)
|
|
if err == nil {
|
|
logs = json.RawMessage(logsMarshaled)
|
|
}
|
|
|
|
// Convert references (excluding parent)
|
|
references := json.RawMessage{}
|
|
filteredRefs := []types.TraceSpanReference{}
|
|
for _, ref := range span.References {
|
|
if parentSpanID == nil || ref.SpanID != *parentSpanID {
|
|
filteredRefs = append(filteredRefs, ref)
|
|
}
|
|
}
|
|
refsMarshaled, err := json.Marshal(filteredRefs)
|
|
if err == nil {
|
|
references = json.RawMessage(refsMarshaled)
|
|
}
|
|
|
|
// Convert tags
|
|
tags := json.RawMessage{}
|
|
tagsMarshaled, err := json.Marshal(span.Tags)
|
|
if err == nil {
|
|
tags = json.RawMessage(tagsMarshaled)
|
|
}
|
|
|
|
// Convert warnings
|
|
warnings := json.RawMessage{}
|
|
warningsMarshaled, err := json.Marshal(span.Warnings)
|
|
if err == nil {
|
|
warnings = json.RawMessage(warningsMarshaled)
|
|
}
|
|
|
|
// Convert stack traces
|
|
stackTraces := json.RawMessage{}
|
|
stackTracesMarshaled, err := json.Marshal(span.StackTraces)
|
|
if err == nil {
|
|
stackTraces = json.RawMessage(stackTracesMarshaled)
|
|
}
|
|
|
|
// Add span to frame
|
|
frame.AppendRow(
|
|
span.TraceID,
|
|
span.SpanID,
|
|
parentSpanID,
|
|
span.OperationName,
|
|
serviceName,
|
|
serviceTags,
|
|
float64(span.StartTime)/1000, // Convert microseconds to milliseconds
|
|
float64(span.Duration)/1000, // Convert microseconds to milliseconds
|
|
logs,
|
|
references,
|
|
tags,
|
|
warnings,
|
|
stackTraces,
|
|
)
|
|
}
|
|
|
|
return frame
|
|
}
|