Skip to content

Commit 5ba1b8c

Browse files
committed
core/runmet: m fmt metrics
1 parent 2fad2a1 commit 5ba1b8c

2 files changed

Lines changed: 112 additions & 16 deletions

File tree

intra/core/fmtstr.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ func FmtSecs(s int64) string {
6868
return FmtPeriod(time.Duration(s) * time.Second)
6969
}
7070

71+
func FmtSecsFloat(s float64) string {
72+
return FmtPeriod(time.Duration(s * float64(time.Second)))
73+
}
74+
7175
func FmtMillis(ms int64) string {
7276
return FmtPeriod(time.Duration(ms) * time.Millisecond)
7377
}

intra/core/runmet.go

Lines changed: 108 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"time"
1515
)
1616

17+
// from pkg.go.dev/runtime/metrics#Read
18+
1719
const (
1820
// ex: /cgo/go-to-c-calls:calls
1921
MetCgo = "/cgo"
@@ -33,18 +35,24 @@ const (
3335
memoizationThreshold = 10 * time.Second
3436
)
3537

36-
var (
37-
// Get descriptions for all supported metrics.
38-
descs = metrics.All()
38+
type metricUnit = int
3939

40-
// Create a sample for each metric.
41-
allsamples = make([]metrics.Sample, len(descs))
42-
43-
sb strings.Builder
40+
const (
41+
unitUnknown = metricUnit(iota)
42+
unitSeconds
43+
unitCount
44+
unitPercent
45+
unitBytes
46+
unitGcTime
47+
)
4448

45-
mu sync.Mutex // protects allsamples, last, sb
49+
var (
50+
descs = metrics.All()
51+
allsamples = make([]metrics.Sample, len(descs))
4652

53+
sb strings.Builder
4754
lastcall time.Time
55+
mu sync.Mutex // protects allsamples, last, sb
4856
)
4957

5058
func init() {
@@ -54,7 +62,6 @@ func init() {
5462
sb.Grow(len(allsamples) * 100)
5563
}
5664

57-
// from pkg.go.dev/runtime/metrics#Read
5865
func Metrics() string {
5966
mu.Lock()
6067
defer mu.Unlock()
@@ -78,17 +85,45 @@ func Metrics() string {
7885
continue
7986
}
8087

88+
unit := ""
89+
u := unitUnknown
90+
namesplit := strings.Split(name, ":")
91+
if len(namesplit) >= 2 {
92+
name = namesplit[0]
93+
unit = namesplit[1]
94+
}
95+
96+
if unit == "cpu-seconds" || unit == "seconds" {
97+
u = unitSeconds
98+
} else if unit == "count" ||
99+
unit == "cleanups" ||
100+
unit == "calls" ||
101+
unit == "gc-cycles" ||
102+
unit == "finalizers" ||
103+
unit == "objects" ||
104+
unit == "events" ||
105+
unit == "threads" ||
106+
unit == "goroutines" {
107+
u = unitCount
108+
} else if unit == "percent" {
109+
u = unitPercent
110+
} else if unit == "bytes" {
111+
u = unitBytes
112+
} else if unit == "gc-cycle" {
113+
u = unitGcTime
114+
}
115+
81116
switch value.Kind() {
82117
case metrics.KindUint64:
83-
s := fmt.Sprintf("%s: %d\n", name, value.Uint64())
118+
s := fmt.Sprintf("%s: %s\n", name, unit4int(value.Uint64(), u))
84119
sb.WriteString(s)
85120
case metrics.KindFloat64:
86-
s := fmt.Sprintf("%s: %f\n", name, value.Float64())
121+
s := fmt.Sprintf("%s: %s\n", name, unit4float(value.Float64(), u))
87122
sb.WriteString(s)
88123
case metrics.KindFloat64Histogram:
89124
// The histogram may be quite large, so let's just pull out
90125
// a crude estimate for the median.
91-
s := fmt.Sprintf("%s: hist(%s)\n", name, histoCsv(value.Float64Histogram()))
126+
s := fmt.Sprintf("%s: hist(%s)\n", name, histoCsv(value.Float64Histogram(), u))
92127
sb.WriteString(s)
93128
case metrics.KindBad:
94129
fallthrough
@@ -101,15 +136,72 @@ func Metrics() string {
101136
return sb.String()
102137
}
103138

104-
func histoCsv(h *metrics.Float64Histogram) string {
139+
func histoCsv(h *metrics.Float64Histogram, u metricUnit) string {
105140
var sb strings.Builder
106141
sb.Grow(20 * len(h.Buckets))
107142
for i, b := range h.Buckets {
108-
s := fmt.Sprintf("%f:%d", b, h.Counts[i])
109-
sb.WriteString(s)
110-
if i < len(h.Buckets)-1 {
143+
if i >= len(h.Buckets)-1 {
144+
break
145+
}
146+
if i > 0 {
111147
sb.WriteString(",")
112148
}
149+
s := fmt.Sprintf("%s:%s", unit4float(b, u), unit4int(h.Counts[i], u))
150+
sb.WriteString(s)
113151
}
114152
return sb.String()
115153
}
154+
155+
func unit4int(v uint64, u metricUnit) string {
156+
switch u {
157+
case unitSeconds:
158+
return FmtSecs(int64(v)) // may wrap?
159+
case unitPercent:
160+
return fmt.Sprintf("%d%", v)
161+
case unitBytes:
162+
return FmtBytes(v)
163+
case unitGcTime:
164+
return fmt.Sprintf("%d", v)
165+
case unitCount:
166+
return FmtWithCommas(v)
167+
default:
168+
return fmt.Sprintf("%d", v)
169+
}
170+
}
171+
172+
// FmtWithCommas formats a uint64 with comma separators every 3 digits (e.g. 1234567 -> "1,234,567").
173+
func FmtWithCommas(v uint64) string {
174+
s := fmt.Sprintf("%d", v)
175+
n := len(s)
176+
if n <= 3 {
177+
return s
178+
}
179+
// pre-allocate exact size: n digits + (n-1)/3 commas
180+
b := make([]byte, n+(n-1)/3)
181+
for i, j, k := n-1, len(b)-1, 0; i >= 0; i, k = i-1, k+1 {
182+
if k > 0 && k%3 == 0 {
183+
b[j] = ','
184+
j--
185+
}
186+
b[j] = s[i]
187+
j--
188+
}
189+
return string(b)
190+
}
191+
192+
func unit4float(v float64, u metricUnit) string {
193+
switch u {
194+
case unitSeconds:
195+
return FmtSecsFloat(v)
196+
case unitPercent:
197+
return fmt.Sprintf("%.2f%%", v)
198+
case unitBytes:
199+
return FmtBytes(uint64(v))
200+
case unitGcTime:
201+
return fmt.Sprintf("%.2f", v)
202+
case unitCount:
203+
return FmtWithCommas(uint64(v))
204+
default:
205+
return fmt.Sprintf("%.3g", v)
206+
}
207+
}

0 commit comments

Comments
 (0)