github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/distributor/http_admin.go (about)

     1  package distributor
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"html/template"
     7  	"net/http"
     8  	"sort"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/cortexproject/cortex/pkg/util"
    13  )
    14  
    15  const tpl = `
    16  <!DOCTYPE html>
    17  <html>
    18  	<head>
    19  		<meta charset="UTF-8">
    20  		<title>Cortex Ingester Stats</title>
    21  	</head>
    22  	<body>
    23  		<h1>Cortex Ingester Stats</h1>
    24  		<p>Current time: {{ .Now }}</p>
    25  		<p><b>NB stats do not account for replication factor, which is currently set to {{ .ReplicationFactor }}</b></p>
    26  		<form action="" method="POST">
    27  			<input type="hidden" name="csrf_token" value="$__CSRF_TOKEN_PLACEHOLDER__">
    28  			<table border="1">
    29  				<thead>
    30  					<tr>
    31  						<th>User</th>
    32  						<th># Series</th>
    33  						<th>Total Ingest Rate</th>
    34  						<th>API Ingest Rate</th>
    35  						<th>Rule Ingest Rate</th>
    36  					</tr>
    37  				</thead>
    38  				<tbody>
    39  					{{ range .Stats }}
    40  					<tr>
    41  						<td>{{ .UserID }}</td>
    42  						<td align='right'>{{ .UserStats.NumSeries }}</td>
    43  						<td align='right'>{{ printf "%.2f" .UserStats.IngestionRate }}</td>
    44  						<td align='right'>{{ printf "%.2f" .UserStats.APIIngestionRate }}</td>
    45  						<td align='right'>{{ printf "%.2f" .UserStats.RuleIngestionRate }}</td>
    46  					</tr>
    47  					{{ end }}
    48  				</tbody>
    49  			</table>
    50  		</form>
    51  	</body>
    52  </html>`
    53  
    54  var tmpl *template.Template
    55  
    56  func init() {
    57  	tmpl = template.Must(template.New("webpage").Parse(tpl))
    58  }
    59  
    60  type userStatsByTimeseries []UserIDStats
    61  
    62  func (s userStatsByTimeseries) Len() int      { return len(s) }
    63  func (s userStatsByTimeseries) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
    64  
    65  func (s userStatsByTimeseries) Less(i, j int) bool {
    66  	return s[i].NumSeries > s[j].NumSeries ||
    67  		(s[i].NumSeries == s[j].NumSeries && s[i].UserID < s[j].UserID)
    68  }
    69  
    70  // AllUserStatsHandler shows stats for all users.
    71  func (d *Distributor) AllUserStatsHandler(w http.ResponseWriter, r *http.Request) {
    72  	stats, err := d.AllUserStats(r.Context())
    73  	if err != nil {
    74  		http.Error(w, err.Error(), http.StatusInternalServerError)
    75  		return
    76  	}
    77  
    78  	sort.Sort(userStatsByTimeseries(stats))
    79  
    80  	if encodings, found := r.Header["Accept"]; found &&
    81  		len(encodings) > 0 && strings.Contains(encodings[0], "json") {
    82  		if err := json.NewEncoder(w).Encode(stats); err != nil {
    83  			http.Error(w, fmt.Sprintf("Error marshalling response: %v", err), http.StatusInternalServerError)
    84  		}
    85  		return
    86  	}
    87  
    88  	util.RenderHTTPResponse(w, struct {
    89  		Now               time.Time     `json:"now"`
    90  		Stats             []UserIDStats `json:"stats"`
    91  		ReplicationFactor int           `json:"replicationFactor"`
    92  	}{
    93  		Now:               time.Now(),
    94  		Stats:             stats,
    95  		ReplicationFactor: d.ingestersRing.ReplicationFactor(),
    96  	}, tmpl, r)
    97  }