Files
grafana/pkg/tsdb/jaeger/client.go
T
Ivana Huckova 59f3ca09ab Jaeger: Run traceID query through backend with feature toggle enabled (#103472)
* Jaeger: Run traceID query through backend with feature toggle enabled

* Reorganize

* Update comments and test

* Fix snapshot test

* Only pass time params if enabled in cofig

* Rename applyVariables to applyTemplateVariables

* Rename transformResponse to transformTraceResponse

* Update comment
2025-04-09 14:13:46 +02:00

161 lines
3.9 KiB
Go

package jaeger
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
)
type JaegerClient struct {
logger log.Logger
url string
httpClient *http.Client
traceIdTimeEnabled bool
}
type ServicesResponse struct {
Data []string `json:"data"`
Errors interface{} `json:"errors"`
Limit int `json:"limit"`
Offset int `json:"offset"`
Total int `json:"total"`
}
func New(url string, hc *http.Client, logger log.Logger, traceIdTimeEnabled bool) (JaegerClient, error) {
client := JaegerClient{
logger: logger,
url: url,
httpClient: hc,
traceIdTimeEnabled: traceIdTimeEnabled,
}
return client, nil
}
func (j *JaegerClient) Services() ([]string, error) {
var response ServicesResponse
services := []string{}
u, err := url.JoinPath(j.url, "/api/services")
if err != nil {
return services, backend.DownstreamError(fmt.Errorf("failed to join url: %w", err))
}
res, err := j.httpClient.Get(u)
if err != nil {
return services, err
}
defer func() {
if err = res.Body.Close(); err != nil {
j.logger.Error("Failed to close response body", "error", err)
}
}()
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return services, err
}
services = response.Data
return services, err
}
func (j *JaegerClient) Operations(s string) ([]string, error) {
var response ServicesResponse
operations := []string{}
u, err := url.JoinPath(j.url, "/api/services/", s, "/operations")
if err != nil {
return operations, backend.DownstreamError(fmt.Errorf("failed to join url: %w", err))
}
res, err := j.httpClient.Get(u)
if err != nil {
return operations, err
}
defer func() {
if err = res.Body.Close(); err != nil {
j.logger.Error("Failed to close response body", "error", err)
}
}()
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return operations, err
}
operations = response.Data
return operations, err
}
func (j *JaegerClient) Trace(ctx context.Context, traceID string, start, end int64) (TraceResponse, error) {
logger := j.logger.FromContext(ctx)
var response TracesResponse
trace := TraceResponse{}
if traceID == "" {
return trace, backend.DownstreamError(fmt.Errorf("traceID is empty"))
}
traceUrl, err := url.JoinPath(j.url, "/api/traces", url.QueryEscape(traceID))
if err != nil {
return trace, backend.DownstreamError(fmt.Errorf("failed to join url: %w", err))
}
// Add time parameters if provided and traceIdTimeEnabled is true
if j.traceIdTimeEnabled {
if start > 0 || end > 0 {
parsedURL, err := url.Parse(traceUrl)
if err != nil {
return trace, backend.DownstreamError(fmt.Errorf("failed to parse url: %w", err))
}
query := parsedURL.Query()
if start > 0 {
query.Set("start", fmt.Sprintf("%d", start))
}
if end > 0 {
query.Set("end", fmt.Sprintf("%d", end))
}
parsedURL.RawQuery = query.Encode()
traceUrl = parsedURL.String()
}
}
res, err := j.httpClient.Get(traceUrl)
if err != nil {
if backend.IsDownstreamHTTPError(err) {
return trace, backend.DownstreamError(err)
}
return trace, err
}
defer func() {
if err = res.Body.Close(); err != nil {
logger.Error("Failed to close response body", "error", err)
}
}()
if res != nil && res.StatusCode/100 != 2 {
err := backend.DownstreamError(fmt.Errorf("request failed: %s", res.Status))
if backend.ErrorSourceFromHTTPStatus(res.StatusCode) == backend.ErrorSourceDownstream {
return trace, backend.DownstreamError(err)
}
return trace, err
}
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return trace, err
}
// We only support one trace at a time
// this is how it was implemented in the frontend before
trace = response.Data[0]
return trace, err
}