1
0
mirror of https://github.com/divan/expvarmon.git synced 2025-04-25 13:48:54 +08:00
expvarmon/var.go

175 lines
3.5 KiB
Go
Raw Permalink Normal View History

2015-05-01 18:48:34 +03:00
package main
2015-05-02 22:29:02 +03:00
import (
"fmt"
"strings"
"time"
"github.com/pyk/byten"
)
2015-05-01 18:48:34 +03:00
// VarName represents variable name.
//
// It has dot-separated format, like "memstats.Alloc",
// but can be used in different forms, hence it's own type.
2015-05-02 10:12:38 +03:00
//
// It also can have optional "kind:" modifier, like "mem:" or "duration:"
2015-05-01 18:48:34 +03:00
type VarName string
2015-05-02 22:29:02 +03:00
// VarKind specifies special kinds of values, affects formatting.
2015-05-02 20:12:53 +03:00
type VarKind int
2015-05-02 10:12:38 +03:00
2015-05-02 22:29:02 +03:00
// VarValue represents arbitrary value for variable.
type VarValue interface{}
2015-05-02 10:12:38 +03:00
const (
2015-05-02 20:12:53 +03:00
KindDefault VarKind = iota
2015-05-02 10:12:38 +03:00
KindMemory
KindDuration
2015-05-02 21:43:32 +03:00
KindString
2015-05-02 10:12:38 +03:00
)
2015-05-01 18:48:34 +03:00
// ToSlice converts "dot-separated" notation into the "slice of strings".
//
// "dot-separated" notation is a human-readable format, passed via args.
// "slice of strings" is used by Jason library.
//
// Example: "memstats.Alloc" => []string{"memstats", "Alloc"}
2015-05-02 10:12:38 +03:00
// Example: "mem:memstats.Alloc" => []string{"memstats", "Alloc"}
2015-05-01 18:48:34 +03:00
func (v VarName) ToSlice() []string {
2015-05-02 10:12:38 +03:00
start := strings.IndexRune(string(v), ':') + 1
slice := DottedFieldsToSliceEscaped(string(v)[start:])
2015-05-02 10:12:38 +03:00
return slice
2015-05-01 18:48:34 +03:00
}
// Short returns short name, which is typically is the last word in the long names.
func (v VarName) Short() string {
if v == "" {
return ""
}
slice := v.ToSlice()
return slice[len(slice)-1]
}
2015-05-02 10:12:38 +03:00
2015-05-02 10:22:49 +03:00
// Long returns long name, without kind: modifier.
func (v VarName) Long() string {
if v == "" {
return ""
}
start := strings.IndexRune(string(v), ':') + 1
return string(v)[start:]
}
2015-05-02 22:29:02 +03:00
// Kind returns kind of variable, based on it's name modifiers ("mem:")
2015-05-02 20:12:53 +03:00
func (v VarName) Kind() VarKind {
2015-05-02 10:12:38 +03:00
start := strings.IndexRune(string(v), ':')
if start == -1 {
return KindDefault
}
switch string(v)[:start] {
case "mem":
return KindMemory
case "duration":
return KindDuration
2015-05-02 21:43:32 +03:00
case "str":
return KindString
2015-05-02 10:12:38 +03:00
}
return KindDefault
}
2015-05-02 22:29:02 +03:00
// Format returns human-readable var value representation.
func Format(v VarValue, kind VarKind) string {
switch kind {
case KindMemory:
if _, ok := v.(int64); !ok {
break
}
return fmt.Sprintf("%s", byten.Size(v.(int64)))
case KindDuration:
if _, ok := v.(int64); !ok {
break
}
2015-05-03 00:00:05 +03:00
return fmt.Sprintf("%s", roundDuration(time.Duration(v.(int64))))
2015-05-02 22:29:02 +03:00
}
if f, ok := v.(float64); ok {
return fmt.Sprintf("%.2f", f)
}
2015-05-02 22:29:02 +03:00
return fmt.Sprintf("%v", v)
}
2015-05-03 00:00:05 +03:00
2015-05-03 19:04:17 +03:00
// roundDuration removes unneeded precision from the String() output for time.Duration.
2015-05-03 00:00:05 +03:00
func roundDuration(d time.Duration) time.Duration {
r := time.Second
if d < time.Second {
r = time.Millisecond
}
if d < time.Millisecond {
r = time.Microsecond
}
if r <= 0 {
return d
}
neg := d < 0
if neg {
d = -d
}
if m := d % r; m+m < r {
d = d - m
} else {
d = d + r - m
}
if neg {
return -d
}
return d
}
func DottedFieldsToSliceEscaped(s string) []string {
rv := make([]string, 0)
lastSlash := false
curr := ""
for _, r := range s {
// base case, dot not after slash
if !lastSlash && r == '.' {
if len(curr) > 0 {
rv = append(rv, curr)
curr = ""
}
continue
} else if !lastSlash {
// any character not after slash
curr += string(r)
if r == '\\' {
lastSlash = true
} else {
lastSlash = false
}
continue
} else if r == '\\' {
// last was slash, and so is this
lastSlash = false // 2 slashes = 0
// we already appended a single slash on first
continue
} else if r == '.' {
// we see \. but already appended \ last time
// replace it with .
curr = curr[:len(curr)-1] + "."
lastSlash = false
} else {
// \ and any other character, ignore
curr += string(r)
lastSlash = false
continue
}
}
if len(curr) > 0 {
rv = append(rv, curr)
}
return rv
}