mirror of
https://github.com/LBRYFoundation/tracker.git
synced 2025-08-23 17:47:29 +00:00
Add crazy struct flattening code
This commit is contained in:
parent
788b349dd7
commit
ab43e6bd97
3 changed files with 126 additions and 39 deletions
|
@ -29,8 +29,15 @@ func (s *Server) check(w http.ResponseWriter, r *http.Request, p httprouter.Para
|
||||||
func (s *Server) stats(w http.ResponseWriter, r *http.Request, p httprouter.Params) (int, error) {
|
func (s *Server) stats(w http.ResponseWriter, r *http.Request, p httprouter.Params) (int, error) {
|
||||||
w.Header().Set("Content-Type", jsonContentType)
|
w.Header().Set("Content-Type", jsonContentType)
|
||||||
|
|
||||||
|
var err error
|
||||||
e := json.NewEncoder(w)
|
e := json.NewEncoder(w)
|
||||||
err := e.Encode(stats.DefaultStats)
|
|
||||||
|
if _, flatten := r.URL.Query()["flatten"]; flatten {
|
||||||
|
err = e.Encode(stats.DefaultStats.Flattened())
|
||||||
|
} else {
|
||||||
|
err = e.Encode(stats.DefaultStats)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,19 +43,18 @@ const (
|
||||||
// line flag.
|
// line flag.
|
||||||
var DefaultStats *Stats
|
var DefaultStats *Stats
|
||||||
|
|
||||||
type PeerStats struct {
|
type PeerClassStats struct {
|
||||||
// Stats for all peers.
|
Current uint64 // Current peer count.
|
||||||
Current uint64 `json:"current"` // Current total peer count.
|
Joined uint64 // Peers that announced.
|
||||||
Joined uint64 `json:"joined"` // Total peers that announced.
|
Left uint64 // Peers that paused or stopped.
|
||||||
Left uint64 `json:"left"` // Total peers that paused or stopped.
|
Reaped uint64 // Peers cleaned up after inactivity.
|
||||||
Reaped uint64 `json:"reaped"` // Total peers cleaned up after inactivity.
|
}
|
||||||
Completed uint64 `json:"completed"` // Number of transitions from leech to seed.
|
|
||||||
|
|
||||||
// Stats for seeds only (subset of total).
|
type PeerStats struct {
|
||||||
SeedsCurrent uint64 `json:"seeds_current"` // Current seed count.
|
PeerClassStats `json:"Peers"`
|
||||||
SeedsJoined uint64 `json:"seeds_joined"` // Seeds that announced (does not included leechers that completed).
|
Seeds PeerClassStats `json:"Seeds"`
|
||||||
SeedsLeft uint64 `json:"seeds_left"` // Seeds that paused or stopped.
|
|
||||||
SeedsReaped uint64 `json:"seeds_reaped"` // Seeds cleaned up after inactivity.
|
Completed uint64 // Number of transitions from leech to seed.
|
||||||
}
|
}
|
||||||
|
|
||||||
type PercentileTimes struct {
|
type PercentileTimes struct {
|
||||||
|
@ -65,40 +64,42 @@ type PercentileTimes struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
Start time.Time `json:"start_time"` // Time at which Chihaya was booted.
|
Started time.Time // Time at which Chihaya was booted.
|
||||||
|
|
||||||
Announces uint64 `json:"announces"` // Total number of announces.
|
Announces uint64 `json:"Tracker.Announces"` // Total number of announces.
|
||||||
Scrapes uint64 `json:"scrapes"` // Total number of scrapes.
|
Scrapes uint64 `json:"Tracker.Scrapes"` // Total number of scrapes.
|
||||||
|
|
||||||
IPv4Peers PeerStats `json:"ipv4_peers"`
|
IPv4Peers PeerStats `json:"Peers.IPv4"`
|
||||||
IPv6Peers PeerStats `json:"ipv6_peers"`
|
IPv6Peers PeerStats `json:"Peers.IPv6"`
|
||||||
|
|
||||||
TorrentsAdded uint64 `json:"torrents_added"`
|
TorrentsAdded uint64 `json:"Torrents.Added"`
|
||||||
TorrentsRemoved uint64 `json:"torrents_removed"`
|
TorrentsRemoved uint64 `json:"Torrents.Removed"`
|
||||||
TorrentsReaped uint64 `json:"torrents_reaped"`
|
TorrentsReaped uint64 `json:"Torrents.Reaped"`
|
||||||
|
|
||||||
OpenConnections uint64 `json:"open_connections"`
|
OpenConnections uint64 `json:"Connections.Open"`
|
||||||
ConnectionsAccepted uint64 `json:"connections_accepted"`
|
ConnectionsAccepted uint64 `json:"Connections.Accepted"`
|
||||||
BytesTransmitted uint64 `json:"bytes_transmitted"`
|
BytesTransmitted uint64 `json:"BytesTransmitted"`
|
||||||
|
|
||||||
RequestsHandled uint64 `json:"requests_handled"`
|
RequestsHandled uint64 `json:"Requests.Handled"`
|
||||||
RequestsErrored uint64 `json:"requests_errored"`
|
RequestsErrored uint64 `json:"Requests.Errored"`
|
||||||
ClientErrors uint64 `json:"client_errors"`
|
ClientErrors uint64 `json:"Requests.Bad"`
|
||||||
|
|
||||||
ResponseTime PercentileTimes `json:"response_time"`
|
ResponseTime PercentileTimes
|
||||||
MemStats *MemStatsWrapper `json:"mem,omitempty"`
|
MemStats *MemStatsWrapper `json:"Memory,omitempty"`
|
||||||
|
|
||||||
events chan int
|
events chan int
|
||||||
ipv4PeerEvents chan int
|
ipv4PeerEvents chan int
|
||||||
ipv6PeerEvents chan int
|
ipv6PeerEvents chan int
|
||||||
responseTimeEvents chan time.Duration
|
responseTimeEvents chan time.Duration
|
||||||
recordMemStats <-chan time.Time
|
recordMemStats <-chan time.Time
|
||||||
|
|
||||||
|
flattened FlatMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg config.StatsConfig) *Stats {
|
func New(cfg config.StatsConfig) *Stats {
|
||||||
s := &Stats{
|
s := &Stats{
|
||||||
Start: time.Now(),
|
Started: time.Now(),
|
||||||
events: make(chan int, cfg.BufferSize),
|
events: make(chan int, cfg.BufferSize),
|
||||||
|
|
||||||
ipv4PeerEvents: make(chan int, cfg.BufferSize),
|
ipv4PeerEvents: make(chan int, cfg.BufferSize),
|
||||||
ipv6PeerEvents: make(chan int, cfg.BufferSize),
|
ipv6PeerEvents: make(chan int, cfg.BufferSize),
|
||||||
|
@ -116,16 +117,21 @@ func New(cfg config.StatsConfig) *Stats {
|
||||||
s.recordMemStats = time.NewTicker(cfg.MemUpdateInterval.Duration).C
|
s.recordMemStats = time.NewTicker(cfg.MemUpdateInterval.Duration).C
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.flattened = Flatten(s)
|
||||||
go s.handleEvents()
|
go s.handleEvents()
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Stats) Flattened() FlatMap {
|
||||||
|
return s.flattened
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Stats) Close() {
|
func (s *Stats) Close() {
|
||||||
close(s.events)
|
close(s.events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) Uptime() time.Duration {
|
func (s *Stats) Uptime() time.Duration {
|
||||||
return time.Since(s.Start)
|
return time.Since(s.Started)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) RecordEvent(event int) {
|
func (s *Stats) RecordEvent(event int) {
|
||||||
|
@ -215,7 +221,7 @@ func (s *Stats) handlePeerEvent(ps *PeerStats, event int) {
|
||||||
switch event {
|
switch event {
|
||||||
case Completed:
|
case Completed:
|
||||||
ps.Completed++
|
ps.Completed++
|
||||||
ps.SeedsCurrent++
|
ps.Seeds.Current++
|
||||||
|
|
||||||
case NewLeech:
|
case NewLeech:
|
||||||
ps.Joined++
|
ps.Joined++
|
||||||
|
@ -230,20 +236,20 @@ func (s *Stats) handlePeerEvent(ps *PeerStats, event int) {
|
||||||
ps.Current--
|
ps.Current--
|
||||||
|
|
||||||
case NewSeed:
|
case NewSeed:
|
||||||
ps.SeedsJoined++
|
ps.Seeds.Joined++
|
||||||
ps.SeedsCurrent++
|
ps.Seeds.Current++
|
||||||
ps.Joined++
|
ps.Joined++
|
||||||
ps.Current++
|
ps.Current++
|
||||||
|
|
||||||
case DeletedSeed:
|
case DeletedSeed:
|
||||||
ps.SeedsLeft++
|
ps.Seeds.Left++
|
||||||
ps.SeedsCurrent--
|
ps.Seeds.Current--
|
||||||
ps.Left++
|
ps.Left++
|
||||||
ps.Current--
|
ps.Current--
|
||||||
|
|
||||||
case ReapedSeed:
|
case ReapedSeed:
|
||||||
ps.SeedsReaped++
|
ps.Seeds.Reaped++
|
||||||
ps.SeedsCurrent--
|
ps.Seeds.Current--
|
||||||
ps.Reaped++
|
ps.Reaped++
|
||||||
ps.Current--
|
ps.Current--
|
||||||
|
|
||||||
|
|
74
stats/struct_flattener.go
Normal file
74
stats/struct_flattener.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlatMap map[string]interface{}
|
||||||
|
|
||||||
|
func isEmptyValue(v reflect.Value) bool {
|
||||||
|
return v.Interface() == reflect.Zero(v.Type()).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyForField(field reflect.StructField, v reflect.Value) string {
|
||||||
|
if tag := field.Tag.Get("json"); tag != "" {
|
||||||
|
tokens := strings.SplitN(tag, ",", 2)
|
||||||
|
name := tokens[0]
|
||||||
|
opts := ""
|
||||||
|
|
||||||
|
if len(tokens) > 1 {
|
||||||
|
opts = tokens[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "-" || strings.Contains(opts, "omitempty") && isEmptyValue(v) {
|
||||||
|
return ""
|
||||||
|
} else if name != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func recursiveFlatten(val reflect.Value, prefix string, output FlatMap) int {
|
||||||
|
valType := val.Type()
|
||||||
|
added := 0
|
||||||
|
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
child := val.Field(i)
|
||||||
|
childType := valType.Field(i)
|
||||||
|
key := prefix + keyForField(childType, child)
|
||||||
|
|
||||||
|
if childType.PkgPath != "" || key == "" {
|
||||||
|
continue
|
||||||
|
} else if child.Kind() == reflect.Struct {
|
||||||
|
if recursiveFlatten(child, key+".", output) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output[key] = child.Addr().Interface()
|
||||||
|
added++
|
||||||
|
}
|
||||||
|
|
||||||
|
return added
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenPointer(val reflect.Value) FlatMap {
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
return flattenPointer(val.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() != reflect.Struct {
|
||||||
|
panic("must be called with a struct type")
|
||||||
|
}
|
||||||
|
|
||||||
|
m := FlatMap{}
|
||||||
|
recursiveFlatten(val, "", m)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten(val interface{}) FlatMap {
|
||||||
|
return flattenPointer(reflect.ValueOf(val))
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue