github.com/grafana/pyroscope@v1.18.0/pkg/storegateway/gateway_blocks_http.go (about) 1 // SPDX-License-Identifier: AGPL-3.0-only 2 3 package storegateway 4 5 import ( 6 _ "embed" // Used to embed html template 7 "fmt" 8 "html/template" 9 "net/http" 10 "path/filepath" 11 "strconv" 12 "time" 13 14 "github.com/dustin/go-humanize" 15 "github.com/gorilla/mux" 16 "github.com/prometheus/prometheus/model/labels" 17 18 "github.com/grafana/pyroscope/pkg/phlaredb/block" 19 "github.com/grafana/pyroscope/pkg/util" 20 ) 21 22 //go:embed blocks.gohtml 23 var blocksPageHTML string 24 var blocksPageTemplate = template.Must(template.New("webpage").Parse(blocksPageHTML)) 25 26 type blocksPageContents struct { 27 Now time.Time `json:"now"` 28 Tenant string `json:"tenant,omitempty"` 29 RichMetas []richMeta `json:"metas"` 30 FormattedBlocks []formattedBlockData `json:"-"` 31 ShowDeleted bool `json:"-"` 32 ShowSources bool `json:"-"` 33 ShowParents bool `json:"-"` 34 SplitCount int `json:"-"` 35 } 36 37 type formattedBlockData struct { 38 ULID string 39 ULIDTime string 40 SplitID *uint32 41 MinTime string 42 MaxTime string 43 Duration string 44 DeletedTime string 45 CompactionLevel int 46 BlockSize string 47 Labels string 48 Sources []string 49 Parents []string 50 Stats block.BlockStats 51 } 52 53 type richMeta struct { 54 *block.Meta 55 // DeletedTime *int64 `json:"deletedTime,omitempty"` 56 SplitID *uint32 `json:"splitId,omitempty"` 57 } 58 59 func (s *StoreGateway) BlocksHandler(w http.ResponseWriter, req *http.Request) { 60 vars := mux.Vars(req) 61 tenantID := vars["tenant"] 62 if tenantID == "" { 63 util.WriteTextResponse(w, "Tenant ID can't be empty") 64 return 65 } 66 67 if err := req.ParseForm(); err != nil { 68 util.WriteTextResponse(w, fmt.Sprintf("Can't parse form: %s", err)) 69 return 70 } 71 72 showDeleted := req.Form.Get("show_deleted") == "on" 73 showSources := req.Form.Get("show_sources") == "on" 74 showParents := req.Form.Get("show_parents") == "on" 75 var splitCount int 76 if sc := req.Form.Get("split_count"); sc != "" { 77 splitCount, _ = strconv.Atoi(sc) 78 if splitCount < 0 { 79 splitCount = 0 80 } 81 } 82 83 metasMap, err := block.ListBlocks(filepath.Join(s.gatewayCfg.BucketStoreConfig.SyncDir, tenantID), time.Time{}) 84 if err != nil { 85 util.WriteTextResponse(w, fmt.Sprintf("Failed to read block metadata: %s", err)) 86 return 87 } 88 metas := block.SortBlocks(metasMap) 89 90 formattedBlocks := make([]formattedBlockData, 0, len(metas)) 91 richMetas := make([]richMeta, 0, len(metas)) 92 93 for _, m := range metas { 94 // if !showDeleted && !deletedTimes[m.ULID].IsZero() { 95 // continue 96 // } 97 var parents []string 98 for _, pb := range m.Compaction.Parents { 99 parents = append(parents, pb.ULID.String()) 100 } 101 var sources []string 102 for _, pb := range m.Compaction.Sources { 103 sources = append(sources, pb.String()) 104 } 105 var blockSplitID *uint32 106 if splitCount > 0 { 107 bsc := block.HashBlockID(m.ULID) % uint32(splitCount) 108 blockSplitID = &bsc 109 } 110 111 var blockSize uint64 112 for _, f := range m.Files { 113 blockSize += f.SizeBytes 114 } 115 116 formattedBlocks = append(formattedBlocks, formattedBlockData{ 117 ULID: m.ULID.String(), 118 ULIDTime: util.TimeFromMillis(int64(m.ULID.Time())).UTC().Format(time.RFC3339), 119 SplitID: blockSplitID, 120 MinTime: util.TimeFromMillis(int64(m.MinTime)).UTC().Format(time.RFC3339), 121 MaxTime: util.TimeFromMillis(int64(m.MaxTime)).UTC().Format(time.RFC3339), 122 Duration: util.TimeFromMillis(int64(m.MaxTime)).Sub(util.TimeFromMillis(int64(m.MinTime))).String(), 123 CompactionLevel: m.Compaction.Level, 124 BlockSize: humanize.Bytes(blockSize), 125 Stats: m.Stats, 126 Sources: sources, 127 Parents: parents, 128 Labels: labels.FromMap(m.Labels).String(), 129 }) 130 131 richMetas = append(richMetas, richMeta{ 132 Meta: m, 133 SplitID: blockSplitID, 134 }) 135 } 136 137 util.RenderHTTPResponse(w, blocksPageContents{ 138 Now: time.Now(), 139 Tenant: tenantID, 140 RichMetas: richMetas, 141 FormattedBlocks: formattedBlocks, 142 143 SplitCount: splitCount, 144 ShowDeleted: showDeleted, 145 ShowSources: showSources, 146 ShowParents: showParents, 147 }, blocksPageTemplate, req) 148 } 149 150 // func formatTimeIfNotZero(t time.Time, format string) string { 151 // if t.IsZero() { 152 // return "" 153 // } 154 155 // return t.Format(format) 156 // }