github.com/kiali/kiali@v1.84.0/tracing/jaeger/grpc_client.go (about) 1 package jaeger 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "strings" 8 "time" 9 10 "google.golang.org/grpc" 11 "google.golang.org/grpc/codes" 12 "google.golang.org/grpc/status" 13 "google.golang.org/protobuf/types/known/durationpb" 14 "google.golang.org/protobuf/types/known/timestamppb" 15 16 "github.com/kiali/kiali/config" 17 "github.com/kiali/kiali/log" 18 "github.com/kiali/kiali/models" 19 "github.com/kiali/kiali/tracing/jaeger/model" 20 jsonConv "github.com/kiali/kiali/tracing/jaeger/model/converter/json" 21 jsonModel "github.com/kiali/kiali/tracing/jaeger/model/json" 22 "github.com/kiali/kiali/util" 23 ) 24 25 type JaegerGRPCClient struct { 26 JaegergRPCClient model.QueryServiceClient 27 IgnoreCluster bool 28 } 29 30 func NewGRPCJaegerClient(cc model.QueryServiceClient) (jaegerClient *JaegerGRPCClient, err error) { 31 32 conf := config.Get() 33 ctx := context.Background() 34 35 var jaegerService string 36 var ignoreCluster bool 37 38 if err == nil { 39 var services *model.GetServicesResponse 40 services, err = cc.GetServices(ctx, &model.GetServicesRequest{}) 41 if err != nil { 42 log.Errorf("Error getting services") 43 } else { 44 for _, service := range services.Services { 45 if !strings.Contains(service, "istio") && !strings.Contains(service, "jaeger") { 46 jaegerService = service 47 break 48 } 49 } 50 end := time.Now() 51 tags := map[string]string{ 52 "cluster": conf.KubernetesConfig.ClusterName, 53 } 54 findTracesRQ := &model.FindTracesRequest{ 55 Query: &model.TraceQueryParameters{ 56 ServiceName: jaegerService, 57 StartTimeMin: timestamppb.New(end.Add(-10 * time.Minute)), 58 StartTimeMax: timestamppb.New(end), 59 Tags: tags, 60 DurationMin: durationpb.New(0), 61 SearchDepth: int32(10), 62 }, 63 } 64 stream, err := cc.FindTraces(context.TODO(), findTracesRQ) 65 if err != nil { 66 err = fmt.Errorf("GetAppTraces, Tracing GRPC client error: %v", err) 67 return nil, err 68 } 69 70 tracesMap, err := readSpansStream(stream) 71 if tracesMap != nil && err == nil && len(tracesMap) == 0 || err != nil { 72 log.Debugf("Error getting query for tracing. cluster tags will be disabled.") 73 ignoreCluster = true 74 } else { 75 ignoreCluster = false 76 } 77 return &JaegerGRPCClient{JaegergRPCClient: cc, IgnoreCluster: ignoreCluster}, nil 78 } 79 } 80 return nil, err 81 } 82 83 // FindTraces 84 func (jc JaegerGRPCClient) FindTraces(ctx context.Context, serviceName string, q models.TracingQuery) (response *model.TracingResponse, err error) { 85 jaegerServiceName := serviceName 86 r := model.TracingResponse{ 87 Data: []jsonModel.Trace{}, 88 TracingServiceName: jaegerServiceName, 89 } 90 91 var tags = util.CopyStringMap(q.Tags) 92 if jc.IgnoreCluster { 93 delete(tags, "cluster") 94 } 95 96 findTracesRQ := &model.FindTracesRequest{ 97 Query: &model.TraceQueryParameters{ 98 ServiceName: jaegerServiceName, 99 StartTimeMin: timestamppb.New(q.Start), 100 StartTimeMax: timestamppb.New(q.End), 101 Tags: tags, 102 DurationMin: durationpb.New(q.MinDuration), 103 SearchDepth: int32(q.Limit), 104 }, 105 } 106 107 tracesMap, err := jc.queryTraces(ctx, findTracesRQ) 108 if jc.IgnoreCluster { 109 r.FromAllClusters = true 110 } 111 112 if err != nil { 113 return nil, err 114 } 115 116 for _, t := range tracesMap { 117 converted := jsonConv.FromDomain(t) 118 r.Data = append(r.Data, *converted) 119 } 120 121 return &r, nil 122 } 123 124 func (jc JaegerGRPCClient) GetTrace(ctx context.Context, strTraceID string) (*model.TracingSingleTrace, error) { 125 126 traceID, err := model.TraceIDFromString(strTraceID) 127 if err != nil { 128 return nil, fmt.Errorf("GetTraceDetail, invalid trace ID: %v", err) 129 } 130 bTraceId := make([]byte, 16) 131 _, err = traceID.MarshalTo(bTraceId) 132 if err != nil { 133 return nil, fmt.Errorf("GetTraceDetail, invalid marshall: %v", err) 134 } 135 getTraceRQ := &model.GetTraceRequest{TraceId: bTraceId} 136 137 ctx, cancel := context.WithTimeout(ctx, 4*time.Second) 138 defer cancel() 139 140 stream, err := jc.JaegergRPCClient.GetTrace(ctx, getTraceRQ) 141 if err != nil { 142 return nil, fmt.Errorf("GetTraceDetail, Tracing GRPC client error: %v", err) 143 } 144 tracesMap, err := readSpansStream(stream) 145 if err != nil { 146 return nil, err 147 } 148 if trace, ok := tracesMap[traceID]; ok { 149 converted := jsonConv.FromDomain(trace) 150 return &model.TracingSingleTrace{Data: *converted}, nil 151 } 152 // Not found 153 return nil, nil 154 } 155 156 // GetServices 157 func (jc JaegerGRPCClient) GetServices(ctxSrv context.Context) (bool, error) { 158 ctx, cancel := context.WithTimeout(ctxSrv, 4*time.Second) 159 defer cancel() 160 161 _, err := jc.JaegergRPCClient.GetServices(ctx, &model.GetServicesRequest{}) 162 return err == nil, err 163 } 164 165 // query traces 166 func (jc JaegerGRPCClient) queryTraces(ctx context.Context, findTracesRQ *model.FindTracesRequest) (map[model.TraceID]*model.Trace, error) { 167 ctx, cancel := context.WithTimeout(ctx, time.Duration(config.Get().ExternalServices.Tracing.QueryTimeout)*time.Second) 168 defer cancel() 169 170 stream, err := jc.JaegergRPCClient.FindTraces(ctx, findTracesRQ) 171 if err != nil { 172 err = fmt.Errorf("GetAppTraces, Tracing GRPC client error: %v", err) 173 log.Error(err.Error()) 174 return nil, err 175 } 176 177 tracesMap, err := readSpansStream(stream) 178 179 return tracesMap, err 180 } 181 182 type SpansStreamer interface { 183 Recv() (*model.SpansResponseChunk, error) 184 grpc.ClientStream 185 } 186 187 func readSpansStream(stream SpansStreamer) (map[model.TraceID]*model.Trace, error) { 188 tracesMap := make(map[model.TraceID]*model.Trace) 189 for received, err := stream.Recv(); err != io.EOF; received, err = stream.Recv() { 190 if err != nil { 191 if status.Code(err) == codes.DeadlineExceeded { 192 log.Trace("Tracing GRPC client timeout") 193 break 194 } 195 log.Errorf("jaeger GRPC client, stream error: %v", err) 196 return nil, fmt.Errorf("Tracing GRPC client, stream error: %v", err) 197 } 198 for i, span := range received.Spans { 199 traceId := model.TraceID{} 200 err := traceId.Unmarshal(span.TraceId) 201 if err != nil { 202 log.Errorf("Tracing TraceId unmarshall error: %v", err) 203 continue 204 } 205 if trace, ok := tracesMap[traceId]; ok { 206 trace.Spans = append(trace.Spans, received.Spans[i]) 207 } else { 208 tracesMap[traceId] = &model.Trace{ 209 Spans: []*model.Span{received.Spans[i]}, 210 } 211 } 212 } 213 } 214 return tracesMap, nil 215 }