多租户更改
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Scraper collects metrics from a tinode server.
|
||||
type Scraper struct {
|
||||
// Target Tinode server address.
|
||||
address string
|
||||
// List of simple numeric metrics to scrape.
|
||||
simpleMetrics []string
|
||||
// List of histogram metrics to scrape.
|
||||
histogramMetrics []string
|
||||
}
|
||||
|
||||
// Histogram struct.
|
||||
type histogram struct {
|
||||
count uint64
|
||||
sum float64
|
||||
buckets map[float64]uint64
|
||||
}
|
||||
|
||||
var errKeyNotFound = errors.New("key not found")
|
||||
var errMalformed = errors.New("input malformed")
|
||||
|
||||
// CollectRaw gathers all metrics from the configured Tinode instance,
|
||||
// and returns them as a map.
|
||||
func (s *Scraper) CollectRaw() (map[string]interface{}, error) {
|
||||
stats, err := s.Scrape()
|
||||
if err != nil {
|
||||
log.Println("Failed to fetch or parse response", err)
|
||||
return nil, err
|
||||
}
|
||||
metrics, err := s.parseStatsRaw(stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metrics["up"] = 1.0
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// Scrape fetches the data from Tinode server using HTTP GET then decodes the response.
|
||||
func (s *Scraper) Scrape() (map[string]interface{}, error) {
|
||||
resp, err := http.Get(s.address)
|
||||
if err != nil {
|
||||
log.Println("Failed to connect to server", err)
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var stats map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&stats)
|
||||
return stats, err
|
||||
}
|
||||
|
||||
func (s *Scraper) parseStatsRaw(stats map[string]interface{}) (map[string]interface{}, error) {
|
||||
metrics := make(map[string]interface{})
|
||||
for _, key := range s.simpleMetrics {
|
||||
if val, err := parseNumeric(stats, key); err == nil {
|
||||
metrics[key] = val
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, key := range s.histogramMetrics {
|
||||
if val, err := parseHisto(stats, key); err == nil {
|
||||
metrics[key] = val
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// Extracts a simple histogram from `stats` and returns a cumulative histogram
|
||||
// corresponding to the simple histogram.
|
||||
// Returns: (count, sum, buckets, error) tuple.
|
||||
func parseHisto(stats map[string]interface{}, key string) (*histogram, error) {
|
||||
// Histogram is presented as a json with the predefined fields: count, sum, count_per_bucket, bounds.
|
||||
count, err := parseNumeric(stats, key+".count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sum, err := parseNumeric(stats, key+".sum")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buckets, err := parseList(stats, key+".count_per_bucket")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bounds, err := parseList(stats, key+".bounds")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := len(buckets)
|
||||
if n != len(bounds)+1 {
|
||||
return nil, errMalformed
|
||||
}
|
||||
result := make(map[float64]uint64)
|
||||
s := uint64(0)
|
||||
for i, v := range bounds {
|
||||
s += uint64(buckets[i])
|
||||
result[v] = s
|
||||
}
|
||||
return &histogram{count: uint64(count), sum: sum, buckets: result}, nil
|
||||
}
|
||||
|
||||
// Extracts a list of numerics from `stats` for the given path.
|
||||
func parseList(stats map[string]interface{}, path string) ([]float64, error) {
|
||||
value, err := parseMetric(stats, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listval, ok := value.([]interface{})
|
||||
if !ok {
|
||||
log.Println("Value at path is not a float64 array:", path, value)
|
||||
return nil, errMalformed
|
||||
}
|
||||
result := []float64{}
|
||||
for _, v := range listval {
|
||||
result = append(result, v.(float64))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Extracts a numeric from `stats` for the given path.
|
||||
func parseNumeric(stats map[string]interface{}, path string) (float64, error) {
|
||||
value, err := parseMetric(stats, path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
floatval, ok := value.(float64)
|
||||
if !ok {
|
||||
log.Println("Value at path is not a float64:", path, value)
|
||||
return 0, errKeyNotFound
|
||||
}
|
||||
return floatval, nil
|
||||
}
|
||||
|
||||
// Extracts a metric from `stats` for the given path.
|
||||
func parseMetric(stats map[string]interface{}, path string) (interface{}, error) {
|
||||
parts := strings.Split(path, ".")
|
||||
var value interface{}
|
||||
var found bool
|
||||
value = stats
|
||||
for i := 0; i < len(parts); i++ {
|
||||
subset, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
log.Println("Invalid key path:", path)
|
||||
return 0, errKeyNotFound
|
||||
}
|
||||
value, found = subset[parts[i]]
|
||||
if !found {
|
||||
log.Println("Invalid key path:", path, "(", parts[i], ")")
|
||||
return 0, errKeyNotFound
|
||||
}
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user