1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-30 09:59:13 +08:00

Move metrics to gx

License: MIT
Signed-off-by: Jakub Sztandera <kubuxu@protonmail.ch>
This commit is contained in:
Jakub Sztandera
2016-06-11 16:23:50 +02:00
parent 7910c6e4b7
commit 6bd66cc8c3
15 changed files with 7 additions and 793 deletions

View File

@ -1,9 +0,0 @@
language: go
go:
- 1.3.3
notifications:
# See http://about.travis-ci.org/docs/user/build-configuration/ to learn more
# about configuring notification recipients and more.
email:
recipients:
- coda.hale@gmail.com

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Coda Hale
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,8 +0,0 @@
metrics
=======
[![Build Status](https://travis-ci.org/codahale/metrics.png?branch=master)](https://travis-ci.org/codahale/metrics)
A Go library which provides light-weight instrumentation for your application.
For documentation, check [godoc](http://godoc.org/github.com/codahale/metrics).

View File

@ -1,329 +0,0 @@
// Package metrics provides minimalist instrumentation for your applications in
// the form of counters and gauges.
//
// Counters
//
// A counter is a monotonically-increasing, unsigned, 64-bit integer used to
// represent the number of times an event has occurred. By tracking the deltas
// between measurements of a counter over intervals of time, an aggregation
// layer can derive rates, acceleration, etc.
//
// Gauges
//
// A gauge returns instantaneous measurements of something using signed, 64-bit
// integers. This value does not need to be monotonic.
//
// Histograms
//
// A histogram tracks the distribution of a stream of values (e.g. the number of
// milliseconds it takes to handle requests), adding gauges for the values at
// meaningful quantiles: 50th, 75th, 90th, 95th, 99th, 99.9th.
//
// Reporting
//
// Measurements from counters and gauges are available as expvars. Your service
// should return its expvars from an HTTP endpoint (i.e., /debug/vars) as a JSON
// object.
package metrics
import (
"expvar"
"sync"
"time"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/hdrhistogram"
)
// A Counter is a monotonically increasing unsigned integer.
//
// Use a counter to derive rates (e.g., record total number of requests, derive
// requests per second).
type Counter string
// Add increments the counter by one.
func (c Counter) Add() {
c.AddN(1)
}
// AddN increments the counter by N.
func (c Counter) AddN(delta uint64) {
cm.Lock()
counters[string(c)] += delta
cm.Unlock()
}
// SetFunc sets the counter's value to the lazily-called return value of the
// given function.
func (c Counter) SetFunc(f func() uint64) {
cm.Lock()
defer cm.Unlock()
counterFuncs[string(c)] = f
}
// SetBatchFunc sets the counter's value to the lazily-called return value of
// the given function, with an additional initializer function for a related
// batch of counters, all of which are keyed by an arbitrary value.
func (c Counter) SetBatchFunc(key interface{}, init func(), f func() uint64) {
cm.Lock()
defer cm.Unlock()
gm.Lock()
defer gm.Unlock()
counterFuncs[string(c)] = f
if _, ok := inits[key]; !ok {
inits[key] = init
}
}
// Remove removes the given counter.
func (c Counter) Remove() {
cm.Lock()
defer cm.Unlock()
gm.Lock()
defer gm.Unlock()
delete(counters, string(c))
delete(counterFuncs, string(c))
delete(inits, string(c))
}
// A Gauge is an instantaneous measurement of a value.
//
// Use a gauge to track metrics which increase and decrease (e.g., amount of
// free memory).
type Gauge string
// Set the gauge's value to the given value.
func (g Gauge) Set(value int64) {
gm.Lock()
defer gm.Unlock()
gauges[string(g)] = func() int64 {
return value
}
}
// SetFunc sets the gauge's value to the lazily-called return value of the given
// function.
func (g Gauge) SetFunc(f func() int64) {
gm.Lock()
defer gm.Unlock()
gauges[string(g)] = f
}
// SetBatchFunc sets the gauge's value to the lazily-called return value of the
// given function, with an additional initializer function for a related batch
// of gauges, all of which are keyed by an arbitrary value.
func (g Gauge) SetBatchFunc(key interface{}, init func(), f func() int64) {
gm.Lock()
defer gm.Unlock()
gauges[string(g)] = f
if _, ok := inits[key]; !ok {
inits[key] = init
}
}
// Remove removes the given gauge.
func (g Gauge) Remove() {
gm.Lock()
defer gm.Unlock()
delete(gauges, string(g))
delete(inits, string(g))
}
// Reset removes all existing counters and gauges.
func Reset() {
cm.Lock()
defer cm.Unlock()
gm.Lock()
defer gm.Unlock()
hm.Lock()
defer hm.Unlock()
counters = make(map[string]uint64)
counterFuncs = make(map[string]func() uint64)
gauges = make(map[string]func() int64)
histograms = make(map[string]*Histogram)
inits = make(map[interface{}]func())
}
// Snapshot returns a copy of the values of all registered counters and gauges.
func Snapshot() (c map[string]uint64, g map[string]int64) {
cm.Lock()
defer cm.Unlock()
gm.Lock()
defer gm.Unlock()
hm.Lock()
defer hm.Unlock()
for _, init := range inits {
init()
}
c = make(map[string]uint64, len(counters)+len(counterFuncs))
for n, v := range counters {
c[n] = v
}
for n, f := range counterFuncs {
c[n] = f()
}
g = make(map[string]int64, len(gauges))
for n, f := range gauges {
g[n] = f()
}
return
}
// NewHistogram returns a windowed HDR histogram which drops data older than
// five minutes. The returned histogram is safe to use from multiple goroutines.
//
// Use a histogram to track the distribution of a stream of values (e.g., the
// latency associated with HTTP requests).
func NewHistogram(name string, minValue, maxValue int64, sigfigs int) *Histogram {
hm.Lock()
defer hm.Unlock()
if _, ok := histograms[name]; ok {
panic(name + " already exists")
}
hist := &Histogram{
name: name,
hist: hdrhistogram.NewWindowed(5, minValue, maxValue, sigfigs),
}
histograms[name] = hist
Gauge(name+".P50").SetBatchFunc(hname(name), hist.merge, hist.valueAt(50))
Gauge(name+".P75").SetBatchFunc(hname(name), hist.merge, hist.valueAt(75))
Gauge(name+".P90").SetBatchFunc(hname(name), hist.merge, hist.valueAt(90))
Gauge(name+".P95").SetBatchFunc(hname(name), hist.merge, hist.valueAt(95))
Gauge(name+".P99").SetBatchFunc(hname(name), hist.merge, hist.valueAt(99))
Gauge(name+".P999").SetBatchFunc(hname(name), hist.merge, hist.valueAt(99.9))
return hist
}
// Remove removes the given histogram.
func (h *Histogram) Remove() {
hm.Lock()
defer hm.Unlock()
Gauge(h.name + ".P50").Remove()
Gauge(h.name + ".P75").Remove()
Gauge(h.name + ".P90").Remove()
Gauge(h.name + ".P95").Remove()
Gauge(h.name + ".P99").Remove()
Gauge(h.name + ".P999").Remove()
delete(histograms, h.name)
}
type hname string // unexported to prevent collisions
// A Histogram measures the distribution of a stream of values.
type Histogram struct {
name string
hist *hdrhistogram.WindowedHistogram
m *hdrhistogram.Histogram
rw sync.RWMutex
}
// Name returns the name of the histogram
func (h *Histogram) Name() string {
return h.name
}
// RecordValue records the given value, or returns an error if the value is out
// of range.
// Returned error values are of type Error.
func (h *Histogram) RecordValue(v int64) error {
h.rw.Lock()
defer h.rw.Unlock()
err := h.hist.Current.RecordValue(v)
if err != nil {
return Error{h.name, err}
}
return nil
}
func (h *Histogram) rotate() {
h.rw.Lock()
defer h.rw.Unlock()
h.hist.Rotate()
}
func (h *Histogram) merge() {
h.rw.Lock()
defer h.rw.Unlock()
h.m = h.hist.Merge()
}
func (h *Histogram) valueAt(q float64) func() int64 {
return func() int64 {
h.rw.RLock()
defer h.rw.RUnlock()
if h.m == nil {
return 0
}
return h.m.ValueAtQuantile(q)
}
}
// Error describes an error and the name of the metric where it occurred.
type Error struct {
Metric string
Err error
}
func (e Error) Error() string {
return e.Metric + ": " + e.Err.Error()
}
var (
counters = make(map[string]uint64)
counterFuncs = make(map[string]func() uint64)
gauges = make(map[string]func() int64)
inits = make(map[interface{}]func())
histograms = make(map[string]*Histogram)
cm, gm, hm sync.Mutex
)
func init() {
expvar.Publish("metrics", expvar.Func(func() interface{} {
counters, gauges := Snapshot()
return map[string]interface{}{
"Counters": counters,
"Gauges": gauges,
}
}))
go func() {
for _ = range time.NewTicker(1 * time.Minute).C {
hm.Lock()
for _, h := range histograms {
h.rotate()
}
hm.Unlock()
}
}()
}

View File

@ -1,217 +0,0 @@
package metrics_test
import (
"testing"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
)
func TestCounter(t *testing.T) {
metrics.Reset()
metrics.Counter("whee").Add()
metrics.Counter("whee").AddN(10)
counters, _ := metrics.Snapshot()
if v, want := counters["whee"], uint64(11); v != want {
t.Errorf("Counter was %v, but expected %v", v, want)
}
}
func TestCounterFunc(t *testing.T) {
metrics.Reset()
metrics.Counter("whee").SetFunc(func() uint64 {
return 100
})
counters, _ := metrics.Snapshot()
if v, want := counters["whee"], uint64(100); v != want {
t.Errorf("Counter was %v, but expected %v", v, want)
}
}
func TestCounterBatchFunc(t *testing.T) {
metrics.Reset()
var a, b uint64
metrics.Counter("whee").SetBatchFunc(
"yay",
func() {
a, b = 1, 2
},
func() uint64 {
return a
},
)
metrics.Counter("woo").SetBatchFunc(
"yay",
func() {
a, b = 1, 2
},
func() uint64 {
return b
},
)
counters, _ := metrics.Snapshot()
if v, want := counters["whee"], uint64(1); v != want {
t.Errorf("Counter was %v, but expected %v", v, want)
}
if v, want := counters["woo"], uint64(2); v != want {
t.Errorf("Counter was %v, but expected %v", v, want)
}
}
func TestCounterRemove(t *testing.T) {
metrics.Reset()
metrics.Counter("whee").Add()
metrics.Counter("whee").Remove()
counters, _ := metrics.Snapshot()
if v, ok := counters["whee"]; ok {
t.Errorf("Counter was %v, but expected nothing", v)
}
}
func TestGaugeValue(t *testing.T) {
metrics.Reset()
metrics.Gauge("whee").Set(-100)
_, gauges := metrics.Snapshot()
if v, want := gauges["whee"], int64(-100); v != want {
t.Errorf("Gauge was %v, but expected %v", v, want)
}
}
func TestGaugeFunc(t *testing.T) {
metrics.Reset()
metrics.Gauge("whee").SetFunc(func() int64 {
return -100
})
_, gauges := metrics.Snapshot()
if v, want := gauges["whee"], int64(-100); v != want {
t.Errorf("Gauge was %v, but expected %v", v, want)
}
}
func TestGaugeRemove(t *testing.T) {
metrics.Reset()
metrics.Gauge("whee").Set(1)
metrics.Gauge("whee").Remove()
_, gauges := metrics.Snapshot()
if v, ok := gauges["whee"]; ok {
t.Errorf("Gauge was %v, but expected nothing", v)
}
}
func TestHistogram(t *testing.T) {
metrics.Reset()
h := metrics.NewHistogram("heyo", 1, 1000, 3)
for i := 100; i > 0; i-- {
for j := 0; j < i; j++ {
h.RecordValue(int64(i))
}
}
_, gauges := metrics.Snapshot()
if v, want := gauges["heyo.P50"], int64(71); v != want {
t.Errorf("P50 was %v, but expected %v", v, want)
}
if v, want := gauges["heyo.P75"], int64(87); v != want {
t.Errorf("P75 was %v, but expected %v", v, want)
}
if v, want := gauges["heyo.P90"], int64(95); v != want {
t.Errorf("P90 was %v, but expected %v", v, want)
}
if v, want := gauges["heyo.P95"], int64(98); v != want {
t.Errorf("P95 was %v, but expected %v", v, want)
}
if v, want := gauges["heyo.P99"], int64(100); v != want {
t.Errorf("P99 was %v, but expected %v", v, want)
}
if v, want := gauges["heyo.P999"], int64(100); v != want {
t.Errorf("P999 was %v, but expected %v", v, want)
}
}
func TestHistogramRemove(t *testing.T) {
metrics.Reset()
h := metrics.NewHistogram("heyo", 1, 1000, 3)
h.Remove()
_, gauges := metrics.Snapshot()
if v, ok := gauges["heyo.P50"]; ok {
t.Errorf("Gauge was %v, but expected nothing", v)
}
}
func BenchmarkCounterAdd(b *testing.B) {
metrics.Reset()
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
metrics.Counter("test1").Add()
}
})
}
func BenchmarkCounterAddN(b *testing.B) {
metrics.Reset()
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
metrics.Counter("test2").AddN(100)
}
})
}
func BenchmarkGaugeSet(b *testing.B) {
metrics.Reset()
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
metrics.Gauge("test2").Set(100)
}
})
}
func BenchmarkHistogramRecordValue(b *testing.B) {
metrics.Reset()
h := metrics.NewHistogram("hist", 1, 1000, 3)
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
h.RecordValue(100)
}
})
}

View File

@ -1,18 +0,0 @@
// Package runtime registers gauges and counters for various operationally
// important aspects of the Go runtime.
//
// To use, import this package:
//
// import _ "github.com/codahale/metrics/runtime"
//
// This registers the following gauges:
//
// FileDescriptors.Max
// FileDescriptors.Used
// Mem.NumGC
// Mem.PauseTotalNs
// Mem.LastGC
// Mem.Alloc
// Mem.HeapObjects
// Goroutines.Num
package runtime

View File

@ -1,46 +0,0 @@
// +build !windows
package runtime
import (
"io/ioutil"
"syscall"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
)
func getFDLimit() (uint64, error) {
var rlimit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
return 0, err
}
// rlimit.Cur's type is platform-dependent, so here we widen it as far as Go
// will allow by converting it to a uint64.
return uint64(rlimit.Cur), nil
}
func getFDUsage() (uint64, error) {
fds, err := ioutil.ReadDir("/proc/self/fd")
if err != nil {
return 0, err
}
return uint64(len(fds)), nil
}
func init() {
metrics.Gauge("FileDescriptors.Max").SetFunc(func() int64 {
v, err := getFDLimit()
if err != nil {
return 0
}
return int64(v)
})
metrics.Gauge("FileDescriptors.Used").SetFunc(func() int64 {
v, err := getFDUsage()
if err != nil {
return 0
}
return int64(v)
})
}

View File

@ -1,24 +0,0 @@
// +build !windows
package runtime
import (
"testing"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
)
func TestFdStats(t *testing.T) {
_, gauges := metrics.Snapshot()
expected := []string{
"FileDescriptors.Max",
"FileDescriptors.Used",
}
for _, name := range expected {
if _, ok := gauges[name]; !ok {
t.Errorf("Missing gauge %q", name)
}
}
}

View File

@ -1,4 +0,0 @@
package runtime
func init() {
}

View File

@ -1,13 +0,0 @@
package runtime
import (
"runtime"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
)
func init() {
metrics.Gauge("Goroutines.Num").SetFunc(func() int64 {
return int64(runtime.NumGoroutine())
})
}

View File

@ -1,21 +0,0 @@
package runtime
import (
"testing"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
)
func TestGoroutinesStats(t *testing.T) {
_, gauges := metrics.Snapshot()
expected := []string{
"Goroutines.Num",
}
for _, name := range expected {
if _, ok := gauges[name]; !ok {
t.Errorf("Missing gauge %q", name)
}
}
}

View File

@ -1,48 +0,0 @@
package runtime
import (
"runtime"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
)
func init() {
msg := &memStatGauges{}
metrics.Counter("Mem.NumGC").SetBatchFunc(key{}, msg.init, msg.numGC)
metrics.Counter("Mem.PauseTotalNs").SetBatchFunc(key{}, msg.init, msg.totalPause)
metrics.Gauge("Mem.LastGC").SetBatchFunc(key{}, msg.init, msg.lastPause)
metrics.Gauge("Mem.Alloc").SetBatchFunc(key{}, msg.init, msg.alloc)
metrics.Gauge("Mem.HeapObjects").SetBatchFunc(key{}, msg.init, msg.objects)
}
type key struct{} // unexported to prevent collision
type memStatGauges struct {
stats runtime.MemStats
}
func (msg *memStatGauges) init() {
runtime.ReadMemStats(&msg.stats)
}
func (msg *memStatGauges) numGC() uint64 {
return uint64(msg.stats.NumGC)
}
func (msg *memStatGauges) totalPause() uint64 {
return msg.stats.PauseTotalNs
}
func (msg *memStatGauges) lastPause() int64 {
return int64(msg.stats.LastGC)
}
func (msg *memStatGauges) alloc() int64 {
return int64(msg.stats.Alloc)
}
func (msg *memStatGauges) objects() int64 {
return int64(msg.stats.HeapObjects)
}

View File

@ -1,34 +0,0 @@
package runtime
import (
"testing"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
)
func TestMemStats(t *testing.T) {
counters, gauges := metrics.Snapshot()
expectedCounters := []string{
"Mem.NumGC",
"Mem.PauseTotalNs",
}
expectedGauges := []string{
"Mem.LastGC",
"Mem.Alloc",
"Mem.HeapObjects",
}
for _, name := range expectedCounters {
if _, ok := counters[name]; !ok {
t.Errorf("Missing counters %q", name)
}
}
for _, name := range expectedGauges {
if _, ok := gauges[name]; !ok {
t.Errorf("Missing gauge %q", name)
}
}
}

View File

@ -11,8 +11,8 @@ import (
"strings"
"sync"
_ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics/runtime"
"gx/ipfs/QmPpRcbNUXauP3zWZ1NJMLWpe4QnmEHrd2ba2D3yqWznw7/go-multiaddr-net"
_ "gx/ipfs/QmV3NSS3A1kX5s28r7yLczhDsXzkgo65cqRgKFXYunWZmD/metrics/runtime"
ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr"

View File

@ -147,6 +147,12 @@
"hash": "QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN",
"name": "go-datastore",
"version": "0.0.1"
},
{
"author": "codahale",
"hash": "QmV3NSS3A1kX5s28r7yLczhDsXzkgo65cqRgKFXYunWZmD",
"name": "metrics",
"version": "0.0.0"
}
],
"gxVersion": "0.4.0",