github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/server/exemplars_query.go (about) 1 package server 2 3 import ( 4 "fmt" 5 "net/http" 6 "strconv" 7 8 "github.com/pyroscope-io/pyroscope/pkg/flameql" 9 "github.com/pyroscope-io/pyroscope/pkg/storage" 10 "github.com/pyroscope-io/pyroscope/pkg/structs/flamebearer" 11 ) 12 13 type queryExemplarsParams struct { 14 input storage.QueryExemplarsInput 15 maxNodes int 16 format string 17 } 18 19 type queryExemplarsResponse struct { 20 flamebearer.FlamebearerProfile 21 Metadata queryExemplarsMetadataResponse `json:"metadata"` 22 } 23 24 type queryExemplarsMetadataResponse struct { 25 flamebearer.FlamebearerMetadataV1 26 AppName string `json:"appName"` 27 StartTime int64 `json:"startTime"` 28 EndTime int64 `json:"endTime"` 29 Query string `json:"query"` 30 MaxNodes int `json:"maxNodes"` 31 } 32 33 func (h ExemplarsHandler) QueryExemplars(w http.ResponseWriter, r *http.Request) { 34 var p queryExemplarsParams 35 if err := h.queryExemplarsParamsFromRequest(r, &p); err != nil { 36 h.HTTPUtils.WriteInvalidParameterError(r, w, err) 37 return 38 } 39 40 out, err := h.ExemplarsQuerier.QueryExemplars(r.Context(), p.input) 41 if err != nil { 42 h.HTTPUtils.WriteInternalServerError(r, w, err, "failed to retrieve data") 43 return 44 } 45 46 flame := flamebearer.NewProfile(flamebearer.ProfileConfig{ 47 MaxNodes: p.maxNodes, 48 Metadata: out.Metadata, 49 Tree: out.Tree, 50 Heatmap: h.HeatmapBuilder.BuildFromSketch(out.HeatmapSketch), 51 Telemetry: out.Telemetry, 52 }) 53 54 md := queryExemplarsMetadataResponse{ 55 FlamebearerMetadataV1: flame.Metadata, 56 AppName: p.input.Query.AppName, 57 StartTime: p.input.StartTime.Unix(), 58 EndTime: p.input.EndTime.Unix(), 59 Query: p.input.Query.String(), 60 MaxNodes: p.maxNodes, 61 } 62 63 h.StatsReceiver.StatsInc("exemplars-query") 64 h.HTTPUtils.WriteResponseJSON(r, w, queryExemplarsResponse{ 65 FlamebearerProfile: flame, 66 Metadata: md, 67 }) 68 } 69 70 func (h ExemplarsHandler) queryExemplarsParamsFromRequest(r *http.Request, p *queryExemplarsParams) (err error) { 71 v := r.URL.Query() 72 if p.input.Query, err = flameql.ParseQuery(v.Get("query")); err != nil { 73 return fmt.Errorf("query: %w", err) 74 } 75 76 p.input.StartTime = parseTimeFallback(v.Get("startTime"), v.Get("from")) 77 p.input.EndTime = parseTimeFallback(v.Get("endTime"), v.Get("until")) 78 79 p.input.HeatmapParams.StartTime = p.input.StartTime 80 p.input.HeatmapParams.EndTime = p.input.EndTime 81 if p.input.HeatmapParams.MinValue, err = parseNumber(v.Get("minValue"), false); err != nil { 82 return fmt.Errorf("can't parse minValue: %w", err) 83 } 84 if p.input.HeatmapParams.MaxValue, err = parseNumber(v.Get("maxValue"), true); err != nil { 85 return fmt.Errorf("can't parse maxValue: %w", err) 86 } 87 if heatmapTimeBuckets := v.Get("heatmapTimeBuckets"); heatmapTimeBuckets != "" { 88 if p.input.HeatmapParams.TimeBuckets, err = strconv.ParseInt(heatmapTimeBuckets, 10, 64); err != nil { 89 return fmt.Errorf("can't parse heatmapTimeBuckets: %w", err) 90 } 91 } 92 if heatmapValueBuckets := v.Get("heatmapValueBuckets"); heatmapValueBuckets != "" { 93 if p.input.HeatmapParams.ValueBuckets, err = strconv.ParseInt(heatmapValueBuckets, 10, 64); err != nil { 94 return fmt.Errorf("can't parse heatmapValueBuckets: %w", err) 95 } 96 } 97 98 p.input.ExemplarsSelection.StartTime = parseTime(v.Get("selectionStartTime")) 99 p.input.ExemplarsSelection.EndTime = parseTime(v.Get("selectionEndTime")) 100 if p.input.ExemplarsSelection.MinValue, err = parseNumber(v.Get("selectionMinValue"), false); err != nil { 101 return fmt.Errorf("can't parse selectionMinValue: %w", err) 102 } 103 if p.input.ExemplarsSelection.MaxValue, err = parseNumber(v.Get("selectionMaxValue"), true); err != nil { 104 return fmt.Errorf("can't parse selectionMaxValue: %w", err) 105 } 106 107 if p.input.HeatmapParams.TimeBuckets == 0 && p.input.HeatmapParams.ValueBuckets == 0 { 108 p.input.StartTime = p.input.ExemplarsSelection.StartTime 109 p.input.EndTime = p.input.ExemplarsSelection.EndTime 110 } 111 112 p.maxNodes = h.MaxNodesDefault 113 if newMaxNodes, ok := MaxNodesFromContext(r.Context()); ok { 114 p.maxNodes = newMaxNodes 115 } 116 var x int 117 if x, err = strconv.Atoi(v.Get("max-nodes")); err == nil && x != 0 { 118 p.maxNodes = x 119 } 120 if x, err = strconv.Atoi(v.Get("maxNodes")); err == nil && x != 0 { 121 p.maxNodes = x 122 } 123 124 return nil 125 }