Add Prometheus Metrics
at the first iteration, only the following metrics are collected: - HTTP metrics of each API endpoint - cache counter for request/hit/miss - histogram of storage actions, including: GetContent, PutContent, Stat, List, Move, and Delete Signed-off-by: tifayuki <tifayuki@gmail.com>
This commit is contained in:
parent
3800056b88
commit
e3c37a46e2
118 changed files with 17182 additions and 1550 deletions
|
@ -31,7 +31,10 @@ storage:
|
||||||
http:
|
http:
|
||||||
addr: :5000
|
addr: :5000
|
||||||
debug:
|
debug:
|
||||||
addr: localhost:5001
|
addr: :5001
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
path: /metrics
|
||||||
headers:
|
headers:
|
||||||
X-Content-Type-Options: [nosniff]
|
X-Content-Type-Options: [nosniff]
|
||||||
redis:
|
redis:
|
||||||
|
|
|
@ -129,6 +129,11 @@ type Configuration struct {
|
||||||
Debug struct {
|
Debug struct {
|
||||||
// Addr specifies the bind address for the debug server.
|
// Addr specifies the bind address for the debug server.
|
||||||
Addr string `yaml:"addr,omitempty"`
|
Addr string `yaml:"addr,omitempty"`
|
||||||
|
// Prometheus configures the Prometheus telemetry endpoint.
|
||||||
|
Prometheus struct {
|
||||||
|
Enabled bool `yaml:"enabled,omitempty"`
|
||||||
|
Path string `yaml:"path,omitempty"`
|
||||||
|
} `yaml:"prometheus,omitempty"`
|
||||||
} `yaml:"debug,omitempty"`
|
} `yaml:"debug,omitempty"`
|
||||||
|
|
||||||
// HTTP2 configuration options
|
// HTTP2 configuration options
|
||||||
|
|
|
@ -84,7 +84,11 @@ var configStruct = Configuration{
|
||||||
} `yaml:"tls,omitempty"`
|
} `yaml:"tls,omitempty"`
|
||||||
Headers http.Header `yaml:"headers,omitempty"`
|
Headers http.Header `yaml:"headers,omitempty"`
|
||||||
Debug struct {
|
Debug struct {
|
||||||
Addr string `yaml:"addr,omitempty"`
|
Addr string `yaml:"addr,omitempty"`
|
||||||
|
Prometheus struct {
|
||||||
|
Enabled bool `yaml:"enabled,omitempty"`
|
||||||
|
Path string `yaml:"path,omitempty"`
|
||||||
|
} `yaml:"prometheus,omitempty"`
|
||||||
} `yaml:"debug,omitempty"`
|
} `yaml:"debug,omitempty"`
|
||||||
HTTP2 struct {
|
HTTP2 struct {
|
||||||
Disabled bool `yaml:"disabled,omitempty"`
|
Disabled bool `yaml:"disabled,omitempty"`
|
||||||
|
|
|
@ -213,6 +213,9 @@ http:
|
||||||
email: emailused@letsencrypt.com
|
email: emailused@letsencrypt.com
|
||||||
debug:
|
debug:
|
||||||
addr: localhost:5001
|
addr: localhost:5001
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
path: /metrics
|
||||||
headers:
|
headers:
|
||||||
X-Content-Type-Options: [nosniff]
|
X-Content-Type-Options: [nosniff]
|
||||||
http2:
|
http2:
|
||||||
|
@ -784,6 +787,19 @@ access to the debug endpoint is locked down in a production environment.
|
||||||
The `debug` section takes a single required `addr` parameter, which specifies
|
The `debug` section takes a single required `addr` parameter, which specifies
|
||||||
the `HOST:PORT` on which the debug server should accept connections.
|
the `HOST:PORT` on which the debug server should accept connections.
|
||||||
|
|
||||||
|
## `prometheus`
|
||||||
|
|
||||||
|
The `prometheus` option defines whether the prometheus metrics is enable, as well
|
||||||
|
as the path to access the metrics.
|
||||||
|
|
||||||
|
| Parameter | Required | Description |
|
||||||
|
|-----------|----------|-------------------------------------------------------|
|
||||||
|
| `enabled` | no | Set `true` to enable the prometheus server |
|
||||||
|
| `path` | no | The path to access the metrics, `/metrics` by default |
|
||||||
|
|
||||||
|
The url to access the metrics is `HOST:PORT/path`, where `HOST:PORT` is defined
|
||||||
|
in `addr` under `debug`.
|
||||||
|
|
||||||
### `headers`
|
### `headers`
|
||||||
|
|
||||||
The `headers` option is **optional** . Use it to specify headers that the HTTP
|
The `headers` option is **optional** . Use it to specify headers that the HTTP
|
||||||
|
|
13
metrics/prometheus.go
Normal file
13
metrics/prometheus.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import "github.com/docker/go-metrics"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NamespacePrefix is the namespace of prometheus metrics
|
||||||
|
NamespacePrefix = "registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// StorageNamespace is the prometheus namespace of blob/cache related operations
|
||||||
|
StorageNamespace = metrics.NewNamespace(NamespacePrefix, "storage", nil)
|
||||||
|
)
|
|
@ -20,6 +20,7 @@ import (
|
||||||
dcontext "github.com/docker/distribution/context"
|
dcontext "github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/health"
|
"github.com/docker/distribution/health"
|
||||||
"github.com/docker/distribution/health/checks"
|
"github.com/docker/distribution/health/checks"
|
||||||
|
prometheus "github.com/docker/distribution/metrics"
|
||||||
"github.com/docker/distribution/notifications"
|
"github.com/docker/distribution/notifications"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
|
@ -35,6 +36,7 @@ import (
|
||||||
"github.com/docker/distribution/registry/storage/driver/factory"
|
"github.com/docker/distribution/registry/storage/driver/factory"
|
||||||
storagemiddleware "github.com/docker/distribution/registry/storage/driver/middleware"
|
storagemiddleware "github.com/docker/distribution/registry/storage/driver/middleware"
|
||||||
"github.com/docker/distribution/version"
|
"github.com/docker/distribution/version"
|
||||||
|
"github.com/docker/go-metrics"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/garyburd/redigo/redis"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -412,6 +414,15 @@ func (app *App) RegisterHealthChecks(healthRegistries ...*health.Registry) {
|
||||||
// passed through the application filters and context will be constructed at
|
// passed through the application filters and context will be constructed at
|
||||||
// request time.
|
// request time.
|
||||||
func (app *App) register(routeName string, dispatch dispatchFunc) {
|
func (app *App) register(routeName string, dispatch dispatchFunc) {
|
||||||
|
handler := app.dispatcher(dispatch)
|
||||||
|
|
||||||
|
// Chain the handler with prometheus instrumented handler
|
||||||
|
if app.Config.HTTP.Debug.Prometheus.Enabled {
|
||||||
|
namespace := metrics.NewNamespace(prometheus.NamespacePrefix, "http", nil)
|
||||||
|
httpMetrics := namespace.NewDefaultHttpMetrics(strings.Replace(routeName, "-", "_", -1))
|
||||||
|
metrics.Register(namespace)
|
||||||
|
handler = metrics.InstrumentHandler(httpMetrics, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(stevvooe): This odd dispatcher/route registration is by-product of
|
// TODO(stevvooe): This odd dispatcher/route registration is by-product of
|
||||||
// some limitations in the gorilla/mux router. We are using it to keep
|
// some limitations in the gorilla/mux router. We are using it to keep
|
||||||
|
@ -419,7 +430,7 @@ func (app *App) register(routeName string, dispatch dispatchFunc) {
|
||||||
// replace it with manual routing and structure-based dispatch for better
|
// replace it with manual routing and structure-based dispatch for better
|
||||||
// control over the request execution.
|
// control over the request execution.
|
||||||
|
|
||||||
app.router.GetRoute(routeName).Handler(app.dispatcher(dispatch))
|
app.router.GetRoute(routeName).Handler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// configureEvents prepares the event sink for action.
|
// configureEvents prepares the event sink for action.
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/docker/distribution/registry/listener"
|
"github.com/docker/distribution/registry/listener"
|
||||||
"github.com/docker/distribution/uuid"
|
"github.com/docker/distribution/uuid"
|
||||||
"github.com/docker/distribution/version"
|
"github.com/docker/distribution/version"
|
||||||
|
"github.com/docker/go-metrics"
|
||||||
gorhandlers "github.com/gorilla/handlers"
|
gorhandlers "github.com/gorilla/handlers"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -58,6 +59,15 @@ var ServeCmd = &cobra.Command{
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.HTTP.Debug.Prometheus.Enabled {
|
||||||
|
path := config.HTTP.Debug.Prometheus.Path
|
||||||
|
if path == "" {
|
||||||
|
path = "/metrics"
|
||||||
|
}
|
||||||
|
log.Info("providing prometheus metrics on ", path)
|
||||||
|
http.Handle(path, metrics.Handler())
|
||||||
|
}
|
||||||
|
|
||||||
if err = registry.ListenAndServe(); err != nil {
|
if err = registry.ListenAndServe(); err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
|
prometheus "github.com/docker/distribution/metrics"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,6 +39,11 @@ type cachedBlobStatter struct {
|
||||||
tracker MetricsTracker
|
tracker MetricsTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// cacheCount is the number of total cache request received/hits/misses
|
||||||
|
cacheCount = prometheus.StorageNamespace.NewLabeledCounter("cache", "The number of cache request received", "type")
|
||||||
|
)
|
||||||
|
|
||||||
// NewCachedBlobStatter creates a new statter which prefers a cache and
|
// NewCachedBlobStatter creates a new statter which prefers a cache and
|
||||||
// falls back to a backend.
|
// falls back to a backend.
|
||||||
func NewCachedBlobStatter(cache distribution.BlobDescriptorService, backend distribution.BlobDescriptorService) distribution.BlobDescriptorService {
|
func NewCachedBlobStatter(cache distribution.BlobDescriptorService, backend distribution.BlobDescriptorService) distribution.BlobDescriptorService {
|
||||||
|
@ -58,6 +64,7 @@ func NewCachedBlobStatterWithMetrics(cache distribution.BlobDescriptorService, b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cbds *cachedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
func (cbds *cachedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
cacheCount.WithValues("Request").Inc(1)
|
||||||
desc, err := cbds.cache.Stat(ctx, dgst)
|
desc, err := cbds.cache.Stat(ctx, dgst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != distribution.ErrBlobUnknown {
|
if err != distribution.ErrBlobUnknown {
|
||||||
|
@ -66,12 +73,13 @@ func (cbds *cachedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (di
|
||||||
|
|
||||||
goto fallback
|
goto fallback
|
||||||
}
|
}
|
||||||
|
cacheCount.WithValues("Hit").Inc(1)
|
||||||
if cbds.tracker != nil {
|
if cbds.tracker != nil {
|
||||||
cbds.tracker.Hit()
|
cbds.tracker.Hit()
|
||||||
}
|
}
|
||||||
return desc, nil
|
return desc, nil
|
||||||
fallback:
|
fallback:
|
||||||
|
cacheCount.WithValues("Miss").Inc(1)
|
||||||
if cbds.tracker != nil {
|
if cbds.tracker != nil {
|
||||||
cbds.tracker.Miss()
|
cbds.tracker.Miss()
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,9 +42,21 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
dcontext "github.com/docker/distribution/context"
|
dcontext "github.com/docker/distribution/context"
|
||||||
|
prometheus "github.com/docker/distribution/metrics"
|
||||||
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
||||||
|
"github.com/docker/go-metrics"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// storageAction is the metrics of blob related operations
|
||||||
|
storageAction = prometheus.StorageNamespace.NewLabeledTimer("action", "The number of seconds that the storage action takes", "driver", "action")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
metrics.Register(prometheus.StorageNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
// Base provides a wrapper around a storagedriver implementation that provides
|
// Base provides a wrapper around a storagedriver implementation that provides
|
||||||
// common path and bounds checking.
|
// common path and bounds checking.
|
||||||
type Base struct {
|
type Base struct {
|
||||||
|
@ -87,7 +99,9 @@ func (base *Base) GetContent(ctx context.Context, path string) ([]byte, error) {
|
||||||
return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
b, e := base.StorageDriver.GetContent(ctx, path)
|
b, e := base.StorageDriver.GetContent(ctx, path)
|
||||||
|
storageAction.WithValues(base.Name(), "GetContent").UpdateSince(start)
|
||||||
return b, base.setDriverName(e)
|
return b, base.setDriverName(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +114,10 @@ func (base *Base) PutContent(ctx context.Context, path string, content []byte) e
|
||||||
return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.setDriverName(base.StorageDriver.PutContent(ctx, path, content))
|
start := time.Now()
|
||||||
|
err := base.setDriverName(base.StorageDriver.PutContent(ctx, path, content))
|
||||||
|
storageAction.WithValues(base.Name(), "PutContent").UpdateSince(start)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reader wraps Reader of underlying storage driver.
|
// Reader wraps Reader of underlying storage driver.
|
||||||
|
@ -142,7 +159,9 @@ func (base *Base) Stat(ctx context.Context, path string) (storagedriver.FileInfo
|
||||||
return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
fi, e := base.StorageDriver.Stat(ctx, path)
|
fi, e := base.StorageDriver.Stat(ctx, path)
|
||||||
|
storageAction.WithValues(base.Name(), "Stat").UpdateSince(start)
|
||||||
return fi, base.setDriverName(e)
|
return fi, base.setDriverName(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +174,9 @@ func (base *Base) List(ctx context.Context, path string) ([]string, error) {
|
||||||
return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
return nil, storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
str, e := base.StorageDriver.List(ctx, path)
|
str, e := base.StorageDriver.List(ctx, path)
|
||||||
|
storageAction.WithValues(base.Name(), "List").UpdateSince(start)
|
||||||
return str, base.setDriverName(e)
|
return str, base.setDriverName(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +191,10 @@ func (base *Base) Move(ctx context.Context, sourcePath string, destPath string)
|
||||||
return storagedriver.InvalidPathError{Path: destPath, DriverName: base.StorageDriver.Name()}
|
return storagedriver.InvalidPathError{Path: destPath, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.setDriverName(base.StorageDriver.Move(ctx, sourcePath, destPath))
|
start := time.Now()
|
||||||
|
err := base.setDriverName(base.StorageDriver.Move(ctx, sourcePath, destPath))
|
||||||
|
storageAction.WithValues(base.Name(), "Move").UpdateSince(start)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete wraps Delete of underlying storage driver.
|
// Delete wraps Delete of underlying storage driver.
|
||||||
|
@ -182,7 +206,10 @@ func (base *Base) Delete(ctx context.Context, path string) error {
|
||||||
return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.setDriverName(base.StorageDriver.Delete(ctx, path))
|
start := time.Now()
|
||||||
|
err := base.setDriverName(base.StorageDriver.Delete(ctx, path))
|
||||||
|
storageAction.WithValues(base.Name(), "Delete").UpdateSince(start)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// URLFor wraps URLFor of underlying storage driver.
|
// URLFor wraps URLFor of underlying storage driver.
|
||||||
|
@ -194,6 +221,8 @@ func (base *Base) URLFor(ctx context.Context, path string, options map[string]in
|
||||||
return "", storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
return "", storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
str, e := base.StorageDriver.URLFor(ctx, path, options)
|
str, e := base.StorageDriver.URLFor(ctx, path, options)
|
||||||
|
storageAction.WithValues(base.Name(), "URLFor").UpdateSince(start)
|
||||||
return str, base.setDriverName(e)
|
return str, base.setDriverName(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@ github.com/Azure/go-autorest ec5f4903f77ed9927ac95b19ab8e44ada64c1356
|
||||||
github.com/sirupsen/logrus 3d4380f53a34dcdc95f0c1db702615992b38d9a4
|
github.com/sirupsen/logrus 3d4380f53a34dcdc95f0c1db702615992b38d9a4
|
||||||
github.com/aws/aws-sdk-go c6fc52983ea2375810aa38ddb5370e9cdf611716
|
github.com/aws/aws-sdk-go c6fc52983ea2375810aa38ddb5370e9cdf611716
|
||||||
github.com/bshuster-repo/logrus-logstash-hook d2c0ecc1836d91814e15e23bb5dc309c3ef51f4a
|
github.com/bshuster-repo/logrus-logstash-hook d2c0ecc1836d91814e15e23bb5dc309c3ef51f4a
|
||||||
|
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
||||||
github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274
|
github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274
|
||||||
github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702
|
github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702
|
||||||
github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782
|
github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782
|
||||||
github.com/denverdino/aliyungo afedced274aa9a7fcdd47ac97018f0f8db4e5de2
|
github.com/denverdino/aliyungo afedced274aa9a7fcdd47ac97018f0f8db4e5de2
|
||||||
github.com/dgrijalva/jwt-go a601269ab70c205d26370c16f7c81e9017c14e04
|
github.com/dgrijalva/jwt-go a601269ab70c205d26370c16f7c81e9017c14e04
|
||||||
|
github.com/docker/go-metrics 399ea8c73916000c64c2c76e8da00ca82f8387ab
|
||||||
github.com/docker/goamz f0a21f5b2e12f83a505ecf79b633bb2035cf6f85
|
github.com/docker/goamz f0a21f5b2e12f83a505ecf79b633bb2035cf6f85
|
||||||
github.com/docker/libtrust fa567046d9b14f6aa788882a950d69651d230b21
|
github.com/docker/libtrust fa567046d9b14f6aa788882a950d69651d230b21
|
||||||
github.com/garyburd/redigo 535138d7bcd717d6531c701ef5933d98b1866257
|
github.com/garyburd/redigo 535138d7bcd717d6531c701ef5933d98b1866257
|
||||||
|
@ -18,9 +20,14 @@ github.com/gorilla/handlers 60c7bfde3e33c201519a200a4507a158cc03a17b
|
||||||
github.com/gorilla/mux 599cba5e7b6137d46ddf58fb1765f5d928e69604
|
github.com/gorilla/mux 599cba5e7b6137d46ddf58fb1765f5d928e69604
|
||||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||||
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||||
|
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c
|
||||||
github.com/miekg/dns 271c58e0c14f552178ea321a545ff9af38930f39
|
github.com/miekg/dns 271c58e0c14f552178ea321a545ff9af38930f39
|
||||||
github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef
|
github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef
|
||||||
github.com/ncw/swift b964f2ca856aac39885e258ad25aec08d5f64ee6
|
github.com/ncw/swift b964f2ca856aac39885e258ad25aec08d5f64ee6
|
||||||
|
github.com/prometheus/client_golang c332b6f63c0658a65eca15c0e5247ded801cf564
|
||||||
|
github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
|
||||||
|
github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
|
||||||
|
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
|
||||||
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064
|
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064
|
||||||
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842
|
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842
|
||||||
github.com/stevvooe/resumable 2aaf90b2ceea5072cb503ef2a620b08ff3119870
|
github.com/stevvooe/resumable 2aaf90b2ceea5072cb503ef2a620b08ff3119870
|
||||||
|
@ -40,4 +47,4 @@ gopkg.in/check.v1 64131543e7896d5bcc6bd5a76287eb75ea96c673
|
||||||
gopkg.in/square/go-jose.v1 40d457b439244b546f023d056628e5184136899b
|
gopkg.in/square/go-jose.v1 40d457b439244b546f023d056628e5184136899b
|
||||||
gopkg.in/yaml.v2 bef53efd0c76e49e6de55ead051f886bea7e9420
|
gopkg.in/yaml.v2 bef53efd0c76e49e6de55ead051f886bea7e9420
|
||||||
rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git
|
rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git
|
||||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
20
vendor/github.com/beorn7/perks/LICENSE
generated
vendored
Normal file
20
vendor/github.com/beorn7/perks/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (C) 2013 Blake Mizerany
|
||||||
|
|
||||||
|
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.
|
31
vendor/github.com/beorn7/perks/README.md
generated
vendored
Normal file
31
vendor/github.com/beorn7/perks/README.md
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Perks for Go (golang.org)
|
||||||
|
|
||||||
|
Perks contains the Go package quantile that computes approximate quantiles over
|
||||||
|
an unbounded data stream within low memory and CPU bounds.
|
||||||
|
|
||||||
|
For more information and examples, see:
|
||||||
|
http://godoc.org/github.com/bmizerany/perks
|
||||||
|
|
||||||
|
A very special thank you and shout out to Graham Cormode (Rutgers University),
|
||||||
|
Flip Korn (AT&T Labs–Research), S. Muthukrishnan (Rutgers University), and
|
||||||
|
Divesh Srivastava (AT&T Labs–Research) for their research and publication of
|
||||||
|
[Effective Computation of Biased Quantiles over Data Streams](http://www.cs.rutgers.edu/~muthu/bquant.pdf)
|
||||||
|
|
||||||
|
Thank you, also:
|
||||||
|
* Armon Dadgar (@armon)
|
||||||
|
* Andrew Gerrand (@nf)
|
||||||
|
* Brad Fitzpatrick (@bradfitz)
|
||||||
|
* Keith Rarick (@kr)
|
||||||
|
|
||||||
|
FAQ:
|
||||||
|
|
||||||
|
Q: Why not move the quantile package into the project root?
|
||||||
|
A: I want to add more packages to perks later.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Blake Mizerany
|
||||||
|
|
||||||
|
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.
|
292
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
292
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
// Package quantile computes approximate quantiles over an unbounded data
|
||||||
|
// stream within low memory and CPU bounds.
|
||||||
|
//
|
||||||
|
// A small amount of accuracy is traded to achieve the above properties.
|
||||||
|
//
|
||||||
|
// Multiple streams can be merged before calling Query to generate a single set
|
||||||
|
// of results. This is meaningful when the streams represent the same type of
|
||||||
|
// data. See Merge and Samples.
|
||||||
|
//
|
||||||
|
// For more detailed information about the algorithm used, see:
|
||||||
|
//
|
||||||
|
// Effective Computation of Biased Quantiles over Data Streams
|
||||||
|
//
|
||||||
|
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
|
||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sample holds an observed value and meta information for compression. JSON
|
||||||
|
// tags have been added for convenience.
|
||||||
|
type Sample struct {
|
||||||
|
Value float64 `json:",string"`
|
||||||
|
Width float64 `json:",string"`
|
||||||
|
Delta float64 `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples represents a slice of samples. It implements sort.Interface.
|
||||||
|
type Samples []Sample
|
||||||
|
|
||||||
|
func (a Samples) Len() int { return len(a) }
|
||||||
|
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||||
|
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
type invariant func(s *stream, r float64) float64
|
||||||
|
|
||||||
|
// NewLowBiased returns an initialized Stream for low-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the lower ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewLowBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * r
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHighBiased returns an initialized Stream for high-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the higher ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewHighBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * (s.n - r)
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTargeted returns an initialized Stream concerned with a particular set of
|
||||||
|
// quantile values that are supplied a priori. Knowing these a priori reduces
|
||||||
|
// space and computation time. The targets map maps the desired quantiles to
|
||||||
|
// their absolute errors, i.e. the true quantile of a value returned by a query
|
||||||
|
// is guaranteed to be within (Quantile±Epsilon).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
||||||
|
func NewTargeted(targets map[float64]float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
var m = math.MaxFloat64
|
||||||
|
var f float64
|
||||||
|
for quantile, epsilon := range targets {
|
||||||
|
if quantile*s.n <= r {
|
||||||
|
f = (2 * epsilon * r) / quantile
|
||||||
|
} else {
|
||||||
|
f = (2 * epsilon * (s.n - r)) / (1 - quantile)
|
||||||
|
}
|
||||||
|
if f < m {
|
||||||
|
m = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|
||||||
|
// design. Take care when using across multiple goroutines.
|
||||||
|
type Stream struct {
|
||||||
|
*stream
|
||||||
|
b Samples
|
||||||
|
sorted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStream(ƒ invariant) *Stream {
|
||||||
|
x := &stream{ƒ: ƒ}
|
||||||
|
return &Stream{x, make(Samples, 0, 500), true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts v into the stream.
|
||||||
|
func (s *Stream) Insert(v float64) {
|
||||||
|
s.insert(Sample{Value: v, Width: 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) insert(sample Sample) {
|
||||||
|
s.b = append(s.b, sample)
|
||||||
|
s.sorted = false
|
||||||
|
if len(s.b) == cap(s.b) {
|
||||||
|
s.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns the computed qth percentiles value. If s was created with
|
||||||
|
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
|
||||||
|
// will return an unspecified result.
|
||||||
|
func (s *Stream) Query(q float64) float64 {
|
||||||
|
if !s.flushed() {
|
||||||
|
// Fast path when there hasn't been enough data for a flush;
|
||||||
|
// this also yields better accuracy for small sets of data.
|
||||||
|
l := len(s.b)
|
||||||
|
if l == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i := int(math.Ceil(float64(l) * q))
|
||||||
|
if i > 0 {
|
||||||
|
i -= 1
|
||||||
|
}
|
||||||
|
s.maybeSort()
|
||||||
|
return s.b[i].Value
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.query(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges samples into the underlying streams samples. This is handy when
|
||||||
|
// merging multiple streams from separate threads, database shards, etc.
|
||||||
|
//
|
||||||
|
// ATTENTION: This method is broken and does not yield correct results. The
|
||||||
|
// underlying algorithm is not capable of merging streams correctly.
|
||||||
|
func (s *Stream) Merge(samples Samples) {
|
||||||
|
sort.Sort(samples)
|
||||||
|
s.stream.merge(samples)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset reinitializes and clears the list reusing the samples buffer memory.
|
||||||
|
func (s *Stream) Reset() {
|
||||||
|
s.stream.reset()
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples returns stream samples held by s.
|
||||||
|
func (s *Stream) Samples() Samples {
|
||||||
|
if !s.flushed() {
|
||||||
|
return s.b
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.samples()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the total number of samples observed in the stream
|
||||||
|
// since initialization.
|
||||||
|
func (s *Stream) Count() int {
|
||||||
|
return len(s.b) + s.stream.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flush() {
|
||||||
|
s.maybeSort()
|
||||||
|
s.stream.merge(s.b)
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) maybeSort() {
|
||||||
|
if !s.sorted {
|
||||||
|
s.sorted = true
|
||||||
|
sort.Sort(s.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flushed() bool {
|
||||||
|
return len(s.stream.l) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type stream struct {
|
||||||
|
n float64
|
||||||
|
l []Sample
|
||||||
|
ƒ invariant
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) reset() {
|
||||||
|
s.l = s.l[:0]
|
||||||
|
s.n = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) insert(v float64) {
|
||||||
|
s.merge(Samples{{v, 1, 0}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) merge(samples Samples) {
|
||||||
|
// TODO(beorn7): This tries to merge not only individual samples, but
|
||||||
|
// whole summaries. The paper doesn't mention merging summaries at
|
||||||
|
// all. Unittests show that the merging is inaccurate. Find out how to
|
||||||
|
// do merges properly.
|
||||||
|
var r float64
|
||||||
|
i := 0
|
||||||
|
for _, sample := range samples {
|
||||||
|
for ; i < len(s.l); i++ {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Value > sample.Value {
|
||||||
|
// Insert at position i.
|
||||||
|
s.l = append(s.l, Sample{})
|
||||||
|
copy(s.l[i+1:], s.l[i:])
|
||||||
|
s.l[i] = Sample{
|
||||||
|
sample.Value,
|
||||||
|
sample.Width,
|
||||||
|
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
|
||||||
|
// TODO(beorn7): How to calculate delta correctly?
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
goto inserted
|
||||||
|
}
|
||||||
|
r += c.Width
|
||||||
|
}
|
||||||
|
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
|
||||||
|
i++
|
||||||
|
inserted:
|
||||||
|
s.n += sample.Width
|
||||||
|
r += sample.Width
|
||||||
|
}
|
||||||
|
s.compress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) count() int {
|
||||||
|
return int(s.n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) query(q float64) float64 {
|
||||||
|
t := math.Ceil(q * s.n)
|
||||||
|
t += math.Ceil(s.ƒ(s, t) / 2)
|
||||||
|
p := s.l[0]
|
||||||
|
var r float64
|
||||||
|
for _, c := range s.l[1:] {
|
||||||
|
r += p.Width
|
||||||
|
if r+c.Width+c.Delta > t {
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
p = c
|
||||||
|
}
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) compress() {
|
||||||
|
if len(s.l) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x := s.l[len(s.l)-1]
|
||||||
|
xi := len(s.l) - 1
|
||||||
|
r := s.n - 1 - x.Width
|
||||||
|
|
||||||
|
for i := len(s.l) - 2; i >= 0; i-- {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
|
||||||
|
x.Width += c.Width
|
||||||
|
s.l[xi] = x
|
||||||
|
// Remove element at i.
|
||||||
|
copy(s.l[i:], s.l[i+1:])
|
||||||
|
s.l = s.l[:len(s.l)-1]
|
||||||
|
xi -= 1
|
||||||
|
} else {
|
||||||
|
x = c
|
||||||
|
xi = i
|
||||||
|
}
|
||||||
|
r -= c.Width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) samples() Samples {
|
||||||
|
samples := make(Samples, len(s.l))
|
||||||
|
copy(samples, s.l)
|
||||||
|
return samples
|
||||||
|
}
|
191
vendor/github.com/docker/go-metrics/LICENSE.code
generated
vendored
Normal file
191
vendor/github.com/docker/go-metrics/LICENSE.code
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2013-2016 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
425
vendor/github.com/docker/go-metrics/LICENSE.docs
generated
vendored
Normal file
425
vendor/github.com/docker/go-metrics/LICENSE.docs
generated
vendored
Normal file
|
@ -0,0 +1,425 @@
|
||||||
|
Attribution-ShareAlike 4.0 International
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||||
|
does not provide legal services or legal advice. Distribution of
|
||||||
|
Creative Commons public licenses does not create a lawyer-client or
|
||||||
|
other relationship. Creative Commons makes its licenses and related
|
||||||
|
information available on an "as-is" basis. Creative Commons gives no
|
||||||
|
warranties regarding its licenses, any material licensed under their
|
||||||
|
terms and conditions, or any related information. Creative Commons
|
||||||
|
disclaims all liability for damages resulting from their use to the
|
||||||
|
fullest extent possible.
|
||||||
|
|
||||||
|
Using Creative Commons Public Licenses
|
||||||
|
|
||||||
|
Creative Commons public licenses provide a standard set of terms and
|
||||||
|
conditions that creators and other rights holders may use to share
|
||||||
|
original works of authorship and other material subject to copyright
|
||||||
|
and certain other rights specified in the public license below. The
|
||||||
|
following considerations are for informational purposes only, are not
|
||||||
|
exhaustive, and do not form part of our licenses.
|
||||||
|
|
||||||
|
Considerations for licensors: Our public licenses are
|
||||||
|
intended for use by those authorized to give the public
|
||||||
|
permission to use material in ways otherwise restricted by
|
||||||
|
copyright and certain other rights. Our licenses are
|
||||||
|
irrevocable. Licensors should read and understand the terms
|
||||||
|
and conditions of the license they choose before applying it.
|
||||||
|
Licensors should also secure all rights necessary before
|
||||||
|
applying our licenses so that the public can reuse the
|
||||||
|
material as expected. Licensors should clearly mark any
|
||||||
|
material not subject to the license. This includes other CC-
|
||||||
|
licensed material, or material used under an exception or
|
||||||
|
limitation to copyright. More considerations for licensors:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensors
|
||||||
|
|
||||||
|
Considerations for the public: By using one of our public
|
||||||
|
licenses, a licensor grants the public permission to use the
|
||||||
|
licensed material under specified terms and conditions. If
|
||||||
|
the licensor's permission is not necessary for any reason--for
|
||||||
|
example, because of any applicable exception or limitation to
|
||||||
|
copyright--then that use is not regulated by the license. Our
|
||||||
|
licenses grant only permissions under copyright and certain
|
||||||
|
other rights that a licensor has authority to grant. Use of
|
||||||
|
the licensed material may still be restricted for other
|
||||||
|
reasons, including because others have copyright or other
|
||||||
|
rights in the material. A licensor may make special requests,
|
||||||
|
such as asking that all changes be marked or described.
|
||||||
|
Although not required by our licenses, you are encouraged to
|
||||||
|
respect those requests where reasonable. More_considerations
|
||||||
|
for the public:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensees
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Attribution-ShareAlike 4.0 International Public
|
||||||
|
License
|
||||||
|
|
||||||
|
By exercising the Licensed Rights (defined below), You accept and agree
|
||||||
|
to be bound by the terms and conditions of this Creative Commons
|
||||||
|
Attribution-ShareAlike 4.0 International Public License ("Public
|
||||||
|
License"). To the extent this Public License may be interpreted as a
|
||||||
|
contract, You are granted the Licensed Rights in consideration of Your
|
||||||
|
acceptance of these terms and conditions, and the Licensor grants You
|
||||||
|
such rights in consideration of benefits the Licensor receives from
|
||||||
|
making the Licensed Material available under these terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
|
||||||
|
Section 1 -- Definitions.
|
||||||
|
|
||||||
|
a. Adapted Material means material subject to Copyright and Similar
|
||||||
|
Rights that is derived from or based upon the Licensed Material
|
||||||
|
and in which the Licensed Material is translated, altered,
|
||||||
|
arranged, transformed, or otherwise modified in a manner requiring
|
||||||
|
permission under the Copyright and Similar Rights held by the
|
||||||
|
Licensor. For purposes of this Public License, where the Licensed
|
||||||
|
Material is a musical work, performance, or sound recording,
|
||||||
|
Adapted Material is always produced where the Licensed Material is
|
||||||
|
synched in timed relation with a moving image.
|
||||||
|
|
||||||
|
b. Adapter's License means the license You apply to Your Copyright
|
||||||
|
and Similar Rights in Your contributions to Adapted Material in
|
||||||
|
accordance with the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
c. BY-SA Compatible License means a license listed at
|
||||||
|
creativecommons.org/compatiblelicenses, approved by Creative
|
||||||
|
Commons as essentially the equivalent of this Public License.
|
||||||
|
|
||||||
|
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||||
|
closely related to copyright including, without limitation,
|
||||||
|
performance, broadcast, sound recording, and Sui Generis Database
|
||||||
|
Rights, without regard to how the rights are labeled or
|
||||||
|
categorized. For purposes of this Public License, the rights
|
||||||
|
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||||
|
Rights.
|
||||||
|
|
||||||
|
e. Effective Technological Measures means those measures that, in the
|
||||||
|
absence of proper authority, may not be circumvented under laws
|
||||||
|
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||||
|
Treaty adopted on December 20, 1996, and/or similar international
|
||||||
|
agreements.
|
||||||
|
|
||||||
|
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||||
|
any other exception or limitation to Copyright and Similar Rights
|
||||||
|
that applies to Your use of the Licensed Material.
|
||||||
|
|
||||||
|
g. License Elements means the license attributes listed in the name
|
||||||
|
of a Creative Commons Public License. The License Elements of this
|
||||||
|
Public License are Attribution and ShareAlike.
|
||||||
|
|
||||||
|
h. Licensed Material means the artistic or literary work, database,
|
||||||
|
or other material to which the Licensor applied this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
i. Licensed Rights means the rights granted to You subject to the
|
||||||
|
terms and conditions of this Public License, which are limited to
|
||||||
|
all Copyright and Similar Rights that apply to Your use of the
|
||||||
|
Licensed Material and that the Licensor has authority to license.
|
||||||
|
|
||||||
|
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||||
|
under this Public License.
|
||||||
|
|
||||||
|
k. Share means to provide material to the public by any means or
|
||||||
|
process that requires permission under the Licensed Rights, such
|
||||||
|
as reproduction, public display, public performance, distribution,
|
||||||
|
dissemination, communication, or importation, and to make material
|
||||||
|
available to the public including in ways that members of the
|
||||||
|
public may access the material from a place and at a time
|
||||||
|
individually chosen by them.
|
||||||
|
|
||||||
|
l. Sui Generis Database Rights means rights other than copyright
|
||||||
|
resulting from Directive 96/9/EC of the European Parliament and of
|
||||||
|
the Council of 11 March 1996 on the legal protection of databases,
|
||||||
|
as amended and/or succeeded, as well as other essentially
|
||||||
|
equivalent rights anywhere in the world.
|
||||||
|
|
||||||
|
m. You means the individual or entity exercising the Licensed Rights
|
||||||
|
under this Public License. Your has a corresponding meaning.
|
||||||
|
|
||||||
|
|
||||||
|
Section 2 -- Scope.
|
||||||
|
|
||||||
|
a. License grant.
|
||||||
|
|
||||||
|
1. Subject to the terms and conditions of this Public License,
|
||||||
|
the Licensor hereby grants You a worldwide, royalty-free,
|
||||||
|
non-sublicensable, non-exclusive, irrevocable license to
|
||||||
|
exercise the Licensed Rights in the Licensed Material to:
|
||||||
|
|
||||||
|
a. reproduce and Share the Licensed Material, in whole or
|
||||||
|
in part; and
|
||||||
|
|
||||||
|
b. produce, reproduce, and Share Adapted Material.
|
||||||
|
|
||||||
|
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||||
|
Exceptions and Limitations apply to Your use, this Public
|
||||||
|
License does not apply, and You do not need to comply with
|
||||||
|
its terms and conditions.
|
||||||
|
|
||||||
|
3. Term. The term of this Public License is specified in Section
|
||||||
|
6(a).
|
||||||
|
|
||||||
|
4. Media and formats; technical modifications allowed. The
|
||||||
|
Licensor authorizes You to exercise the Licensed Rights in
|
||||||
|
all media and formats whether now known or hereafter created,
|
||||||
|
and to make technical modifications necessary to do so. The
|
||||||
|
Licensor waives and/or agrees not to assert any right or
|
||||||
|
authority to forbid You from making technical modifications
|
||||||
|
necessary to exercise the Licensed Rights, including
|
||||||
|
technical modifications necessary to circumvent Effective
|
||||||
|
Technological Measures. For purposes of this Public License,
|
||||||
|
simply making modifications authorized by this Section 2(a)
|
||||||
|
(4) never produces Adapted Material.
|
||||||
|
|
||||||
|
5. Downstream recipients.
|
||||||
|
|
||||||
|
a. Offer from the Licensor -- Licensed Material. Every
|
||||||
|
recipient of the Licensed Material automatically
|
||||||
|
receives an offer from the Licensor to exercise the
|
||||||
|
Licensed Rights under the terms and conditions of this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
b. Additional offer from the Licensor -- Adapted Material.
|
||||||
|
Every recipient of Adapted Material from You
|
||||||
|
automatically receives an offer from the Licensor to
|
||||||
|
exercise the Licensed Rights in the Adapted Material
|
||||||
|
under the conditions of the Adapter's License You apply.
|
||||||
|
|
||||||
|
c. No downstream restrictions. You may not offer or impose
|
||||||
|
any additional or different terms or conditions on, or
|
||||||
|
apply any Effective Technological Measures to, the
|
||||||
|
Licensed Material if doing so restricts exercise of the
|
||||||
|
Licensed Rights by any recipient of the Licensed
|
||||||
|
Material.
|
||||||
|
|
||||||
|
6. No endorsement. Nothing in this Public License constitutes or
|
||||||
|
may be construed as permission to assert or imply that You
|
||||||
|
are, or that Your use of the Licensed Material is, connected
|
||||||
|
with, or sponsored, endorsed, or granted official status by,
|
||||||
|
the Licensor or others designated to receive attribution as
|
||||||
|
provided in Section 3(a)(1)(A)(i).
|
||||||
|
|
||||||
|
b. Other rights.
|
||||||
|
|
||||||
|
1. Moral rights, such as the right of integrity, are not
|
||||||
|
licensed under this Public License, nor are publicity,
|
||||||
|
privacy, and/or other similar personality rights; however, to
|
||||||
|
the extent possible, the Licensor waives and/or agrees not to
|
||||||
|
assert any such rights held by the Licensor to the limited
|
||||||
|
extent necessary to allow You to exercise the Licensed
|
||||||
|
Rights, but not otherwise.
|
||||||
|
|
||||||
|
2. Patent and trademark rights are not licensed under this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
3. To the extent possible, the Licensor waives any right to
|
||||||
|
collect royalties from You for the exercise of the Licensed
|
||||||
|
Rights, whether directly or through a collecting society
|
||||||
|
under any voluntary or waivable statutory or compulsory
|
||||||
|
licensing scheme. In all other cases the Licensor expressly
|
||||||
|
reserves any right to collect such royalties.
|
||||||
|
|
||||||
|
|
||||||
|
Section 3 -- License Conditions.
|
||||||
|
|
||||||
|
Your exercise of the Licensed Rights is expressly made subject to the
|
||||||
|
following conditions.
|
||||||
|
|
||||||
|
a. Attribution.
|
||||||
|
|
||||||
|
1. If You Share the Licensed Material (including in modified
|
||||||
|
form), You must:
|
||||||
|
|
||||||
|
a. retain the following if it is supplied by the Licensor
|
||||||
|
with the Licensed Material:
|
||||||
|
|
||||||
|
i. identification of the creator(s) of the Licensed
|
||||||
|
Material and any others designated to receive
|
||||||
|
attribution, in any reasonable manner requested by
|
||||||
|
the Licensor (including by pseudonym if
|
||||||
|
designated);
|
||||||
|
|
||||||
|
ii. a copyright notice;
|
||||||
|
|
||||||
|
iii. a notice that refers to this Public License;
|
||||||
|
|
||||||
|
iv. a notice that refers to the disclaimer of
|
||||||
|
warranties;
|
||||||
|
|
||||||
|
v. a URI or hyperlink to the Licensed Material to the
|
||||||
|
extent reasonably practicable;
|
||||||
|
|
||||||
|
b. indicate if You modified the Licensed Material and
|
||||||
|
retain an indication of any previous modifications; and
|
||||||
|
|
||||||
|
c. indicate the Licensed Material is licensed under this
|
||||||
|
Public License, and include the text of, or the URI or
|
||||||
|
hyperlink to, this Public License.
|
||||||
|
|
||||||
|
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||||
|
reasonable manner based on the medium, means, and context in
|
||||||
|
which You Share the Licensed Material. For example, it may be
|
||||||
|
reasonable to satisfy the conditions by providing a URI or
|
||||||
|
hyperlink to a resource that includes the required
|
||||||
|
information.
|
||||||
|
|
||||||
|
3. If requested by the Licensor, You must remove any of the
|
||||||
|
information required by Section 3(a)(1)(A) to the extent
|
||||||
|
reasonably practicable.
|
||||||
|
|
||||||
|
b. ShareAlike.
|
||||||
|
|
||||||
|
In addition to the conditions in Section 3(a), if You Share
|
||||||
|
Adapted Material You produce, the following conditions also apply.
|
||||||
|
|
||||||
|
1. The Adapter's License You apply must be a Creative Commons
|
||||||
|
license with the same License Elements, this version or
|
||||||
|
later, or a BY-SA Compatible License.
|
||||||
|
|
||||||
|
2. You must include the text of, or the URI or hyperlink to, the
|
||||||
|
Adapter's License You apply. You may satisfy this condition
|
||||||
|
in any reasonable manner based on the medium, means, and
|
||||||
|
context in which You Share Adapted Material.
|
||||||
|
|
||||||
|
3. You may not offer or impose any additional or different terms
|
||||||
|
or conditions on, or apply any Effective Technological
|
||||||
|
Measures to, Adapted Material that restrict exercise of the
|
||||||
|
rights granted under the Adapter's License You apply.
|
||||||
|
|
||||||
|
|
||||||
|
Section 4 -- Sui Generis Database Rights.
|
||||||
|
|
||||||
|
Where the Licensed Rights include Sui Generis Database Rights that
|
||||||
|
apply to Your use of the Licensed Material:
|
||||||
|
|
||||||
|
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||||
|
to extract, reuse, reproduce, and Share all or a substantial
|
||||||
|
portion of the contents of the database;
|
||||||
|
|
||||||
|
b. if You include all or a substantial portion of the database
|
||||||
|
contents in a database in which You have Sui Generis Database
|
||||||
|
Rights, then the database in which You have Sui Generis Database
|
||||||
|
Rights (but not its individual contents) is Adapted Material,
|
||||||
|
|
||||||
|
including for purposes of Section 3(b); and
|
||||||
|
c. You must comply with the conditions in Section 3(a) if You Share
|
||||||
|
all or a substantial portion of the contents of the database.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 4 supplements and does not
|
||||||
|
replace Your obligations under this Public License where the Licensed
|
||||||
|
Rights include other Copyright and Similar Rights.
|
||||||
|
|
||||||
|
|
||||||
|
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||||
|
|
||||||
|
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||||
|
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||||
|
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||||
|
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||||
|
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||||
|
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||||
|
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||||
|
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||||
|
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||||
|
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||||
|
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||||
|
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||||
|
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||||
|
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||||
|
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||||
|
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
c. The disclaimer of warranties and limitation of liability provided
|
||||||
|
above shall be interpreted in a manner that, to the extent
|
||||||
|
possible, most closely approximates an absolute disclaimer and
|
||||||
|
waiver of all liability.
|
||||||
|
|
||||||
|
|
||||||
|
Section 6 -- Term and Termination.
|
||||||
|
|
||||||
|
a. This Public License applies for the term of the Copyright and
|
||||||
|
Similar Rights licensed here. However, if You fail to comply with
|
||||||
|
this Public License, then Your rights under this Public License
|
||||||
|
terminate automatically.
|
||||||
|
|
||||||
|
b. Where Your right to use the Licensed Material has terminated under
|
||||||
|
Section 6(a), it reinstates:
|
||||||
|
|
||||||
|
1. automatically as of the date the violation is cured, provided
|
||||||
|
it is cured within 30 days of Your discovery of the
|
||||||
|
violation; or
|
||||||
|
|
||||||
|
2. upon express reinstatement by the Licensor.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||||
|
right the Licensor may have to seek remedies for Your violations
|
||||||
|
of this Public License.
|
||||||
|
|
||||||
|
c. For the avoidance of doubt, the Licensor may also offer the
|
||||||
|
Licensed Material under separate terms or conditions or stop
|
||||||
|
distributing the Licensed Material at any time; however, doing so
|
||||||
|
will not terminate this Public License.
|
||||||
|
|
||||||
|
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 7 -- Other Terms and Conditions.
|
||||||
|
|
||||||
|
a. The Licensor shall not be bound by any additional or different
|
||||||
|
terms or conditions communicated by You unless expressly agreed.
|
||||||
|
|
||||||
|
b. Any arrangements, understandings, or agreements regarding the
|
||||||
|
Licensed Material not stated herein are separate from and
|
||||||
|
independent of the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 8 -- Interpretation.
|
||||||
|
|
||||||
|
a. For the avoidance of doubt, this Public License does not, and
|
||||||
|
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||||
|
conditions on any use of the Licensed Material that could lawfully
|
||||||
|
be made without permission under this Public License.
|
||||||
|
|
||||||
|
b. To the extent possible, if any provision of this Public License is
|
||||||
|
deemed unenforceable, it shall be automatically reformed to the
|
||||||
|
minimum extent necessary to make it enforceable. If the provision
|
||||||
|
cannot be reformed, it shall be severed from this Public License
|
||||||
|
without affecting the enforceability of the remaining terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
c. No term or condition of this Public License will be waived and no
|
||||||
|
failure to comply consented to unless expressly agreed to by the
|
||||||
|
Licensor.
|
||||||
|
|
||||||
|
d. Nothing in this Public License constitutes or may be interpreted
|
||||||
|
as a limitation upon, or waiver of, any privileges and immunities
|
||||||
|
that apply to the Licensor or You, including from the legal
|
||||||
|
processes of any jurisdiction or authority.
|
||||||
|
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons is not a party to its public licenses.
|
||||||
|
Notwithstanding, Creative Commons may elect to apply one of its public
|
||||||
|
licenses to material it publishes and in those instances will be
|
||||||
|
considered the "Licensor." Except for the limited purpose of indicating
|
||||||
|
that material is shared under a Creative Commons public license or as
|
||||||
|
otherwise permitted by the Creative Commons policies published at
|
||||||
|
creativecommons.org/policies, Creative Commons does not authorize the
|
||||||
|
use of the trademark "Creative Commons" or any other trademark or logo
|
||||||
|
of Creative Commons without its prior written consent including,
|
||||||
|
without limitation, in connection with any unauthorized modifications
|
||||||
|
to any of its public licenses or any other arrangements,
|
||||||
|
understandings, or agreements concerning use of licensed material. For
|
||||||
|
the avoidance of doubt, this paragraph does not form part of the public
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
Creative Commons may be contacted at creativecommons.org.
|
16
vendor/github.com/docker/go-metrics/NOTICE
generated
vendored
Normal file
16
vendor/github.com/docker/go-metrics/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
Docker
|
||||||
|
Copyright 2012-2015 Docker, Inc.
|
||||||
|
|
||||||
|
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||||
|
|
||||||
|
The following is courtesy of our legal counsel:
|
||||||
|
|
||||||
|
|
||||||
|
Use and transfer of Docker may be subject to certain restrictions by the
|
||||||
|
United States and other governments.
|
||||||
|
It is your responsibility to ensure that your use and/or transfer does not
|
||||||
|
violate applicable laws.
|
||||||
|
|
||||||
|
For more information, please see https://www.bis.doc.gov
|
||||||
|
|
||||||
|
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
91
vendor/github.com/docker/go-metrics/README.md
generated
vendored
Normal file
91
vendor/github.com/docker/go-metrics/README.md
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
# go-metrics [![GoDoc](https://godoc.org/github.com/docker/go-metrics?status.svg)](https://godoc.org/github.com/docker/go-metrics) ![Badge Badge](http://doyouevenbadge.com/github.com/docker/go-metrics)
|
||||||
|
|
||||||
|
This package is small wrapper around the prometheus go client to help enforce convention and best practices for metrics collection in Docker projects.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
This packages is meant to be used for collecting metrics in Docker projects.
|
||||||
|
It is not meant to be used as a replacement for the prometheus client but to help enforce consistent naming across metrics collected.
|
||||||
|
If you have not already read the prometheus best practices around naming and labels you can read the page [here](https://prometheus.io/docs/practices/naming/).
|
||||||
|
|
||||||
|
The following are a few Docker specific rules that will help you name and work with metrics in your project.
|
||||||
|
|
||||||
|
1. Namespace and Subsystem
|
||||||
|
|
||||||
|
This package provides you with a namespace type that allows you to specify the same namespace and subsystem for your metrics.
|
||||||
|
|
||||||
|
```go
|
||||||
|
ns := metrics.NewNamespace("engine", "daemon", metrics.Labels{
|
||||||
|
"version": dockerversion.Version,
|
||||||
|
"commit": dockerversion.GitCommit,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above we are creating metrics for the Docker engine's daemon package.
|
||||||
|
`engine` would be the namespace in this example where `daemon` is the subsystem or package where we are collecting the metrics.
|
||||||
|
|
||||||
|
A namespace also allows you to attach constant labels to the metrics such as the git commit and version that it is collecting.
|
||||||
|
|
||||||
|
2. Declaring your Metrics
|
||||||
|
|
||||||
|
Try to keep all your metric declarations in one file.
|
||||||
|
This makes it easy for others to see what constant labels are defined on the namespace and what labels are defined on the metrics when they are created.
|
||||||
|
|
||||||
|
3. Use labels instead of multiple metrics
|
||||||
|
|
||||||
|
Labels allow you to define one metric such as the time it takes to perform a certain action on an object.
|
||||||
|
If we wanted to collect timings on various container actions such as create, start, and delete then we can define one metric called `container_actions` and use labels to specify the type of action.
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
containerActions = ns.NewLabeledTimer("container_actions", "The number of milliseconds it takes to process each container action", "action")
|
||||||
|
```
|
||||||
|
|
||||||
|
The last parameter is the label name or key.
|
||||||
|
When adding a data point to the metric you will use the `WithValues` function to specify the `action` that you are collecting for.
|
||||||
|
|
||||||
|
```go
|
||||||
|
containerActions.WithValues("create").UpdateSince(start)
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Always use a unit
|
||||||
|
|
||||||
|
The metric name should describe what you are measuring but you also need to provide the unit that it is being measured with.
|
||||||
|
For a timer, the standard unit is seconds and a counter's standard unit is a total.
|
||||||
|
For gauges you must provide the unit.
|
||||||
|
This package provides a standard set of units for use within the Docker projects.
|
||||||
|
|
||||||
|
```go
|
||||||
|
Nanoseconds Unit = "nanoseconds"
|
||||||
|
Seconds Unit = "seconds"
|
||||||
|
Bytes Unit = "bytes"
|
||||||
|
Total Unit = "total"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to use a unit but it is not defined in the package please open a PR to add it but first try to see if one of the already created units will work for your metric, i.e. seconds or nanoseconds vs adding milliseconds.
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
Package documentation can be found [here](https://godoc.org/github.com/docker/go-metrics).
|
||||||
|
|
||||||
|
## HTTP Metrics
|
||||||
|
|
||||||
|
To instrument a http handler, you can wrap the code like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
namespace := metrics.NewNamespace("docker_distribution", "http", metrics.Labels{"handler": "your_http_handler_name"})
|
||||||
|
httpMetrics := namespace.NewDefaultHttpMetrics()
|
||||||
|
metrics.Register(namespace)
|
||||||
|
instrumentedHandler = metrics.InstrumentHandler(httpMetrics, unInstrumentedHandler)
|
||||||
|
```
|
||||||
|
Note: The `handler` label must be provided when a new namespace is created.
|
||||||
|
|
||||||
|
## Additional Metrics
|
||||||
|
|
||||||
|
Additional metrics are also defined here that are not available in the prometheus client.
|
||||||
|
If you need a custom metrics and it is generic enough to be used by multiple projects, define it here.
|
||||||
|
|
||||||
|
|
||||||
|
## Copyright and license
|
||||||
|
|
||||||
|
Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/.
|
52
vendor/github.com/docker/go-metrics/counter.go
generated
vendored
Normal file
52
vendor/github.com/docker/go-metrics/counter.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import "github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
// Counter is a metrics that can only increment its current count
|
||||||
|
type Counter interface {
|
||||||
|
// Inc adds Sum(vs) to the counter. Sum(vs) must be positive.
|
||||||
|
//
|
||||||
|
// If len(vs) == 0, increments the counter by 1.
|
||||||
|
Inc(vs ...float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabeledCounter is counter that must have labels populated before use.
|
||||||
|
type LabeledCounter interface {
|
||||||
|
WithValues(vs ...string) Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
type labeledCounter struct {
|
||||||
|
pc *prometheus.CounterVec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *labeledCounter) WithValues(vs ...string) Counter {
|
||||||
|
return &counter{pc: lc.pc.WithLabelValues(vs...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *labeledCounter) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
lc.pc.Describe(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *labeledCounter) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
lc.pc.Collect(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
type counter struct {
|
||||||
|
pc prometheus.Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Inc(vs ...float64) {
|
||||||
|
if len(vs) == 0 {
|
||||||
|
c.pc.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
c.pc.Add(sumFloat64(vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
c.pc.Describe(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
c.pc.Collect(ch)
|
||||||
|
}
|
3
vendor/github.com/docker/go-metrics/docs.go
generated
vendored
Normal file
3
vendor/github.com/docker/go-metrics/docs.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// This package is small wrapper around the prometheus go client to help enforce convention and best practices for metrics collection in Docker projects.
|
||||||
|
|
||||||
|
package metrics
|
72
vendor/github.com/docker/go-metrics/gauge.go
generated
vendored
Normal file
72
vendor/github.com/docker/go-metrics/gauge.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import "github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
// Gauge is a metric that allows incrementing and decrementing a value
|
||||||
|
type Gauge interface {
|
||||||
|
Inc(...float64)
|
||||||
|
Dec(...float64)
|
||||||
|
|
||||||
|
// Add adds the provided value to the gauge's current value
|
||||||
|
Add(float64)
|
||||||
|
|
||||||
|
// Set replaces the gauge's current value with the provided value
|
||||||
|
Set(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabeledGauge describes a gauge the must have values populated before use.
|
||||||
|
type LabeledGauge interface {
|
||||||
|
WithValues(labels ...string) Gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
type labeledGauge struct {
|
||||||
|
pg *prometheus.GaugeVec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lg *labeledGauge) WithValues(labels ...string) Gauge {
|
||||||
|
return &gauge{pg: lg.pg.WithLabelValues(labels...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lg *labeledGauge) Describe(c chan<- *prometheus.Desc) {
|
||||||
|
lg.pg.Describe(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lg *labeledGauge) Collect(c chan<- prometheus.Metric) {
|
||||||
|
lg.pg.Collect(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type gauge struct {
|
||||||
|
pg prometheus.Gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Inc(vs ...float64) {
|
||||||
|
if len(vs) == 0 {
|
||||||
|
g.pg.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Add(sumFloat64(vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Dec(vs ...float64) {
|
||||||
|
if len(vs) == 0 {
|
||||||
|
g.pg.Dec()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Add(-sumFloat64(vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Add(v float64) {
|
||||||
|
g.pg.Add(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Set(v float64) {
|
||||||
|
g.pg.Set(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Describe(c chan<- *prometheus.Desc) {
|
||||||
|
g.pg.Describe(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Collect(c chan<- prometheus.Metric) {
|
||||||
|
g.pg.Collect(c)
|
||||||
|
}
|
74
vendor/github.com/docker/go-metrics/handler.go
generated
vendored
Normal file
74
vendor/github.com/docker/go-metrics/handler.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPHandlerOpts describes a set of configurable options of http metrics
|
||||||
|
type HTTPHandlerOpts struct {
|
||||||
|
DurationBuckets []float64
|
||||||
|
RequestSizeBuckets []float64
|
||||||
|
ResponseSizeBuckets []float64
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
InstrumentHandlerResponseSize = iota
|
||||||
|
InstrumentHandlerRequestSize
|
||||||
|
InstrumentHandlerDuration
|
||||||
|
InstrumentHandlerCounter
|
||||||
|
InstrumentHandlerInFlight
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPMetric struct {
|
||||||
|
prometheus.Collector
|
||||||
|
handlerType int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultDurationBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 25, 60}
|
||||||
|
defaultRequestSizeBuckets = prometheus.ExponentialBuckets(1024, 2, 22) //1K to 4G
|
||||||
|
defaultResponseSizeBuckets = defaultRequestSizeBuckets
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler returns the global http.Handler that provides the prometheus
|
||||||
|
// metrics format on GET requests. This handler is no longer instrumented.
|
||||||
|
func Handler() http.Handler {
|
||||||
|
return promhttp.Handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
func InstrumentHandler(metrics []*HTTPMetric, handler http.Handler) http.HandlerFunc {
|
||||||
|
return InstrumentHandlerFunc(metrics, handler.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InstrumentHandlerFunc(metrics []*HTTPMetric, handlerFunc http.HandlerFunc) http.HandlerFunc {
|
||||||
|
var handler http.Handler
|
||||||
|
handler = http.HandlerFunc(handlerFunc)
|
||||||
|
for _, metric := range metrics {
|
||||||
|
switch metric.handlerType {
|
||||||
|
case InstrumentHandlerResponseSize:
|
||||||
|
if collector, ok := metric.Collector.(prometheus.ObserverVec); ok {
|
||||||
|
handler = promhttp.InstrumentHandlerResponseSize(collector, handler)
|
||||||
|
}
|
||||||
|
case InstrumentHandlerRequestSize:
|
||||||
|
if collector, ok := metric.Collector.(prometheus.ObserverVec); ok {
|
||||||
|
handler = promhttp.InstrumentHandlerRequestSize(collector, handler)
|
||||||
|
}
|
||||||
|
case InstrumentHandlerDuration:
|
||||||
|
if collector, ok := metric.Collector.(prometheus.ObserverVec); ok {
|
||||||
|
handler = promhttp.InstrumentHandlerDuration(collector, handler)
|
||||||
|
}
|
||||||
|
case InstrumentHandlerCounter:
|
||||||
|
if collector, ok := metric.Collector.(*prometheus.CounterVec); ok {
|
||||||
|
handler = promhttp.InstrumentHandlerCounter(collector, handler)
|
||||||
|
}
|
||||||
|
case InstrumentHandlerInFlight:
|
||||||
|
if collector, ok := metric.Collector.(prometheus.Gauge); ok {
|
||||||
|
handler = promhttp.InstrumentHandlerInFlight(collector, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler.ServeHTTP
|
||||||
|
}
|
10
vendor/github.com/docker/go-metrics/helpers.go
generated
vendored
Normal file
10
vendor/github.com/docker/go-metrics/helpers.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
func sumFloat64(vs ...float64) float64 {
|
||||||
|
var sum float64
|
||||||
|
for _, v := range vs {
|
||||||
|
sum += v
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum
|
||||||
|
}
|
315
vendor/github.com/docker/go-metrics/namespace.go
generated
vendored
Normal file
315
vendor/github.com/docker/go-metrics/namespace.go
generated
vendored
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Labels map[string]string
|
||||||
|
|
||||||
|
// NewNamespace returns a namespaces that is responsible for managing a collection of
|
||||||
|
// metrics for a particual namespace and subsystem
|
||||||
|
//
|
||||||
|
// labels allows const labels to be added to all metrics created in this namespace
|
||||||
|
// and are commonly used for data like application version and git commit
|
||||||
|
func NewNamespace(name, subsystem string, labels Labels) *Namespace {
|
||||||
|
if labels == nil {
|
||||||
|
labels = make(map[string]string)
|
||||||
|
}
|
||||||
|
return &Namespace{
|
||||||
|
name: name,
|
||||||
|
subsystem: subsystem,
|
||||||
|
labels: labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace describes a set of metrics that share a namespace and subsystem.
|
||||||
|
type Namespace struct {
|
||||||
|
name string
|
||||||
|
subsystem string
|
||||||
|
labels Labels
|
||||||
|
mu sync.Mutex
|
||||||
|
metrics []prometheus.Collector
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithConstLabels returns a namespace with the provided set of labels merged
|
||||||
|
// with the existing constant labels on the namespace.
|
||||||
|
//
|
||||||
|
// Only metrics created with the returned namespace will get the new constant
|
||||||
|
// labels. The returned namespace must be registered separately.
|
||||||
|
func (n *Namespace) WithConstLabels(labels Labels) *Namespace {
|
||||||
|
n.mu.Lock()
|
||||||
|
ns := &Namespace{
|
||||||
|
name: n.name,
|
||||||
|
subsystem: n.subsystem,
|
||||||
|
labels: mergeLabels(n.labels, labels),
|
||||||
|
}
|
||||||
|
n.mu.Unlock()
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewCounter(name, help string) Counter {
|
||||||
|
c := &counter{pc: prometheus.NewCounter(n.newCounterOpts(name, help))}
|
||||||
|
n.Add(c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewLabeledCounter(name, help string, labels ...string) LabeledCounter {
|
||||||
|
c := &labeledCounter{pc: prometheus.NewCounterVec(n.newCounterOpts(name, help), labels)}
|
||||||
|
n.Add(c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) newCounterOpts(name, help string) prometheus.CounterOpts {
|
||||||
|
return prometheus.CounterOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: makeName(name, Total),
|
||||||
|
Help: help,
|
||||||
|
ConstLabels: prometheus.Labels(n.labels),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewTimer(name, help string) Timer {
|
||||||
|
t := &timer{
|
||||||
|
m: prometheus.NewHistogram(n.newTimerOpts(name, help)),
|
||||||
|
}
|
||||||
|
n.Add(t)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewLabeledTimer(name, help string, labels ...string) LabeledTimer {
|
||||||
|
t := &labeledTimer{
|
||||||
|
m: prometheus.NewHistogramVec(n.newTimerOpts(name, help), labels),
|
||||||
|
}
|
||||||
|
n.Add(t)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) newTimerOpts(name, help string) prometheus.HistogramOpts {
|
||||||
|
return prometheus.HistogramOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: makeName(name, Seconds),
|
||||||
|
Help: help,
|
||||||
|
ConstLabels: prometheus.Labels(n.labels),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewGauge(name, help string, unit Unit) Gauge {
|
||||||
|
g := &gauge{
|
||||||
|
pg: prometheus.NewGauge(n.newGaugeOpts(name, help, unit)),
|
||||||
|
}
|
||||||
|
n.Add(g)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewLabeledGauge(name, help string, unit Unit, labels ...string) LabeledGauge {
|
||||||
|
g := &labeledGauge{
|
||||||
|
pg: prometheus.NewGaugeVec(n.newGaugeOpts(name, help, unit), labels),
|
||||||
|
}
|
||||||
|
n.Add(g)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) newGaugeOpts(name, help string, unit Unit) prometheus.GaugeOpts {
|
||||||
|
return prometheus.GaugeOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: makeName(name, unit),
|
||||||
|
Help: help,
|
||||||
|
ConstLabels: prometheus.Labels(n.labels),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
n.mu.Lock()
|
||||||
|
defer n.mu.Unlock()
|
||||||
|
|
||||||
|
for _, metric := range n.metrics {
|
||||||
|
metric.Describe(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
n.mu.Lock()
|
||||||
|
defer n.mu.Unlock()
|
||||||
|
|
||||||
|
for _, metric := range n.metrics {
|
||||||
|
metric.Collect(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) Add(collector prometheus.Collector) {
|
||||||
|
n.mu.Lock()
|
||||||
|
n.metrics = append(n.metrics, collector)
|
||||||
|
n.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewDesc(name, help string, unit Unit, labels ...string) *prometheus.Desc {
|
||||||
|
name = makeName(name, unit)
|
||||||
|
namespace := n.name
|
||||||
|
if n.subsystem != "" {
|
||||||
|
namespace = fmt.Sprintf("%s_%s", namespace, n.subsystem)
|
||||||
|
}
|
||||||
|
name = fmt.Sprintf("%s_%s", namespace, name)
|
||||||
|
return prometheus.NewDesc(name, help, labels, prometheus.Labels(n.labels))
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeLabels merges two or more labels objects into a single map, favoring
|
||||||
|
// the later labels.
|
||||||
|
func mergeLabels(lbs ...Labels) Labels {
|
||||||
|
merged := make(Labels)
|
||||||
|
|
||||||
|
for _, target := range lbs {
|
||||||
|
for k, v := range target {
|
||||||
|
merged[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeName(name string, unit Unit) string {
|
||||||
|
if unit == "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s_%s", name, unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewDefaultHttpMetrics(handlerName string) []*HTTPMetric {
|
||||||
|
return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{
|
||||||
|
DurationBuckets: defaultDurationBuckets,
|
||||||
|
RequestSizeBuckets: defaultResponseSizeBuckets,
|
||||||
|
ResponseSizeBuckets: defaultResponseSizeBuckets,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewHttpMetrics(handlerName string, durationBuckets, requestSizeBuckets, responseSizeBuckets []float64) []*HTTPMetric {
|
||||||
|
return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{
|
||||||
|
DurationBuckets: durationBuckets,
|
||||||
|
RequestSizeBuckets: requestSizeBuckets,
|
||||||
|
ResponseSizeBuckets: responseSizeBuckets,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewHttpMetricsWithOpts(handlerName string, opts HTTPHandlerOpts) []*HTTPMetric {
|
||||||
|
var httpMetrics []*HTTPMetric
|
||||||
|
inFlightMetric := n.NewInFlightGaugeMetric(handlerName)
|
||||||
|
requestTotalMetric := n.NewRequestTotalMetric(handlerName)
|
||||||
|
requestDurationMetric := n.NewRequestDurationMetric(handlerName, opts.DurationBuckets)
|
||||||
|
requestSizeMetric := n.NewRequestSizeMetric(handlerName, opts.RequestSizeBuckets)
|
||||||
|
responseSizeMetric := n.NewResponseSizeMetric(handlerName, opts.ResponseSizeBuckets)
|
||||||
|
httpMetrics = append(httpMetrics, inFlightMetric, requestDurationMetric, requestTotalMetric, requestSizeMetric, responseSizeMetric)
|
||||||
|
return httpMetrics
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewInFlightGaugeMetric(handlerName string) *HTTPMetric {
|
||||||
|
labels := prometheus.Labels(n.labels)
|
||||||
|
labels["handler"] = handlerName
|
||||||
|
metric := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: "in_flight_requests",
|
||||||
|
Help: "The in-flight HTTP requests",
|
||||||
|
ConstLabels: prometheus.Labels(labels),
|
||||||
|
})
|
||||||
|
httpMetric := &HTTPMetric{
|
||||||
|
Collector: metric,
|
||||||
|
handlerType: InstrumentHandlerInFlight,
|
||||||
|
}
|
||||||
|
n.Add(httpMetric)
|
||||||
|
return httpMetric
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewRequestTotalMetric(handlerName string) *HTTPMetric {
|
||||||
|
labels := prometheus.Labels(n.labels)
|
||||||
|
labels["handler"] = handlerName
|
||||||
|
metric := prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: "requests_total",
|
||||||
|
Help: "Total number of HTTP requests made.",
|
||||||
|
ConstLabels: prometheus.Labels(labels),
|
||||||
|
},
|
||||||
|
[]string{"code", "method"},
|
||||||
|
)
|
||||||
|
httpMetric := &HTTPMetric{
|
||||||
|
Collector: metric,
|
||||||
|
handlerType: InstrumentHandlerCounter,
|
||||||
|
}
|
||||||
|
n.Add(httpMetric)
|
||||||
|
return httpMetric
|
||||||
|
}
|
||||||
|
func (n *Namespace) NewRequestDurationMetric(handlerName string, buckets []float64) *HTTPMetric {
|
||||||
|
if len(buckets) == 0 {
|
||||||
|
panic("DurationBuckets must be provided")
|
||||||
|
}
|
||||||
|
labels := prometheus.Labels(n.labels)
|
||||||
|
labels["handler"] = handlerName
|
||||||
|
opts := prometheus.HistogramOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: "request_duration_seconds",
|
||||||
|
Help: "The HTTP request latencies in seconds.",
|
||||||
|
Buckets: buckets,
|
||||||
|
ConstLabels: prometheus.Labels(labels),
|
||||||
|
}
|
||||||
|
metric := prometheus.NewHistogramVec(opts, []string{"method"})
|
||||||
|
httpMetric := &HTTPMetric{
|
||||||
|
Collector: metric,
|
||||||
|
handlerType: InstrumentHandlerDuration,
|
||||||
|
}
|
||||||
|
n.Add(httpMetric)
|
||||||
|
return httpMetric
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewRequestSizeMetric(handlerName string, buckets []float64) *HTTPMetric {
|
||||||
|
if len(buckets) == 0 {
|
||||||
|
panic("RequestSizeBuckets must be provided")
|
||||||
|
}
|
||||||
|
labels := prometheus.Labels(n.labels)
|
||||||
|
labels["handler"] = handlerName
|
||||||
|
opts := prometheus.HistogramOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: "request_size_bytes",
|
||||||
|
Help: "The HTTP request sizes in bytes.",
|
||||||
|
Buckets: buckets,
|
||||||
|
ConstLabels: prometheus.Labels(labels),
|
||||||
|
}
|
||||||
|
metric := prometheus.NewHistogramVec(opts, []string{})
|
||||||
|
httpMetric := &HTTPMetric{
|
||||||
|
Collector: metric,
|
||||||
|
handlerType: InstrumentHandlerRequestSize,
|
||||||
|
}
|
||||||
|
n.Add(httpMetric)
|
||||||
|
return httpMetric
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) NewResponseSizeMetric(handlerName string, buckets []float64) *HTTPMetric {
|
||||||
|
if len(buckets) == 0 {
|
||||||
|
panic("ResponseSizeBuckets must be provided")
|
||||||
|
}
|
||||||
|
labels := prometheus.Labels(n.labels)
|
||||||
|
labels["handler"] = handlerName
|
||||||
|
opts := prometheus.HistogramOpts{
|
||||||
|
Namespace: n.name,
|
||||||
|
Subsystem: n.subsystem,
|
||||||
|
Name: "response_size_bytes",
|
||||||
|
Help: "The HTTP response sizes in bytes.",
|
||||||
|
Buckets: buckets,
|
||||||
|
ConstLabels: prometheus.Labels(labels),
|
||||||
|
}
|
||||||
|
metrics := prometheus.NewHistogramVec(opts, []string{})
|
||||||
|
httpMetric := &HTTPMetric{
|
||||||
|
Collector: metrics,
|
||||||
|
handlerType: InstrumentHandlerResponseSize,
|
||||||
|
}
|
||||||
|
n.Add(httpMetric)
|
||||||
|
return httpMetric
|
||||||
|
}
|
15
vendor/github.com/docker/go-metrics/register.go
generated
vendored
Normal file
15
vendor/github.com/docker/go-metrics/register.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import "github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
// Register adds all the metrics in the provided namespace to the global
|
||||||
|
// metrics registry
|
||||||
|
func Register(n *Namespace) {
|
||||||
|
prometheus.MustRegister(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deregister removes all the metrics in the provided namespace from the
|
||||||
|
// global metrics registry
|
||||||
|
func Deregister(n *Namespace) {
|
||||||
|
prometheus.Unregister(n)
|
||||||
|
}
|
85
vendor/github.com/docker/go-metrics/timer.go
generated
vendored
Normal file
85
vendor/github.com/docker/go-metrics/timer.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartTimer begins a timer observation at the callsite. When the target
|
||||||
|
// operation is completed, the caller should call the return done func().
|
||||||
|
func StartTimer(timer Timer) (done func()) {
|
||||||
|
start := time.Now()
|
||||||
|
return func() {
|
||||||
|
timer.Update(time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer is a metric that allows collecting the duration of an action in seconds
|
||||||
|
type Timer interface {
|
||||||
|
// Update records an observation, duration, and converts to the target
|
||||||
|
// units.
|
||||||
|
Update(duration time.Duration)
|
||||||
|
|
||||||
|
// UpdateSince will add the duration from the provided starting time to the
|
||||||
|
// timer's summary with the precisions that was used in creation of the timer
|
||||||
|
UpdateSince(time.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabeledTimer is a timer that must have label values populated before use.
|
||||||
|
type LabeledTimer interface {
|
||||||
|
WithValues(labels ...string) *labeledTimerObserver
|
||||||
|
}
|
||||||
|
|
||||||
|
type labeledTimer struct {
|
||||||
|
m *prometheus.HistogramVec
|
||||||
|
}
|
||||||
|
|
||||||
|
type labeledTimerObserver struct {
|
||||||
|
m prometheus.Observer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lbo *labeledTimerObserver) Update(duration time.Duration) {
|
||||||
|
lbo.m.Observe(duration.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lbo *labeledTimerObserver) UpdateSince(since time.Time) {
|
||||||
|
lbo.m.Observe(time.Since(since).Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *labeledTimer) WithValues(labels ...string) *labeledTimerObserver {
|
||||||
|
return &labeledTimerObserver{m: lt.m.WithLabelValues(labels...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *labeledTimer) Describe(c chan<- *prometheus.Desc) {
|
||||||
|
lt.m.Describe(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *labeledTimer) Collect(c chan<- prometheus.Metric) {
|
||||||
|
lt.m.Collect(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type timer struct {
|
||||||
|
m prometheus.Observer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) Update(duration time.Duration) {
|
||||||
|
t.m.Observe(duration.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) UpdateSince(since time.Time) {
|
||||||
|
t.m.Observe(time.Since(since).Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) Describe(c chan<- *prometheus.Desc) {
|
||||||
|
c <- t.m.(prometheus.Metric).Desc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) Collect(c chan<- prometheus.Metric) {
|
||||||
|
// Are there any observers that don't implement Collector? It is really
|
||||||
|
// unclear what the point of the upstream change was, but we'll let this
|
||||||
|
// panic if we get an observer that doesn't implement collector. In this
|
||||||
|
// case, we should almost always see metricVec objects, so this should
|
||||||
|
// never panic.
|
||||||
|
t.m.(prometheus.Collector).Collect(c)
|
||||||
|
}
|
12
vendor/github.com/docker/go-metrics/unit.go
generated
vendored
Normal file
12
vendor/github.com/docker/go-metrics/unit.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
// Unit represents the type or precision of a metric that is appended to
|
||||||
|
// the metrics fully qualified name
|
||||||
|
type Unit string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Nanoseconds Unit = "nanoseconds"
|
||||||
|
Seconds Unit = "seconds"
|
||||||
|
Bytes Unit = "bytes"
|
||||||
|
Total Unit = "total"
|
||||||
|
)
|
201
vendor/github.com/matttproud/golang_protobuf_extensions/LICENSE
generated
vendored
Normal file
201
vendor/github.com/matttproud/golang_protobuf_extensions/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
1
vendor/github.com/matttproud/golang_protobuf_extensions/NOTICE
generated
vendored
Normal file
1
vendor/github.com/matttproud/golang_protobuf_extensions/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Copyright 2012 Matt T. Proud (matt.proud@gmail.com)
|
20
vendor/github.com/matttproud/golang_protobuf_extensions/README.md
generated
vendored
Normal file
20
vendor/github.com/matttproud/golang_protobuf_extensions/README.md
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Overview
|
||||||
|
This repository provides various Protocol Buffer extensions for the Go
|
||||||
|
language (golang), namely support for record length-delimited message
|
||||||
|
streaming.
|
||||||
|
|
||||||
|
| Java | Go |
|
||||||
|
| ------------------------------ | --------------------- |
|
||||||
|
| MessageLite#parseDelimitedFrom | pbutil.ReadDelimited |
|
||||||
|
| MessageLite#writeDelimitedTo | pbutil.WriteDelimited |
|
||||||
|
|
||||||
|
Because [Code Review 9102043](https://codereview.appspot.com/9102043/) is
|
||||||
|
destined to never be merged into mainline (i.e., never be promoted to formal
|
||||||
|
[goprotobuf features](https://github.com/golang/protobuf)), this repository
|
||||||
|
will live here in the wild.
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
We have [generated Go Doc documentation](http://godoc.org/github.com/matttproud/golang_protobuf_extensions/pbutil) here.
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
[![Build Status](https://travis-ci.org/matttproud/golang_protobuf_extensions.png?branch=master)](https://travis-ci.org/matttproud/golang_protobuf_extensions)
|
75
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go
generated
vendored
Normal file
75
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2013 Matt T. Proud
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package pbutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidVarint = errors.New("invalid varint32 encountered")
|
||||||
|
|
||||||
|
// ReadDelimited decodes a message from the provided length-delimited stream,
|
||||||
|
// where the length is encoded as 32-bit varint prefix to the message body.
|
||||||
|
// It returns the total number of bytes read and any applicable error. This is
|
||||||
|
// roughly equivalent to the companion Java API's
|
||||||
|
// MessageLite#parseDelimitedFrom. As per the reader contract, this function
|
||||||
|
// calls r.Read repeatedly as required until exactly one message including its
|
||||||
|
// prefix is read and decoded (or an error has occurred). The function never
|
||||||
|
// reads more bytes from the stream than required. The function never returns
|
||||||
|
// an error if a message has been read and decoded correctly, even if the end
|
||||||
|
// of the stream has been reached in doing so. In that case, any subsequent
|
||||||
|
// calls return (0, io.EOF).
|
||||||
|
func ReadDelimited(r io.Reader, m proto.Message) (n int, err error) {
|
||||||
|
// Per AbstractParser#parsePartialDelimitedFrom with
|
||||||
|
// CodedInputStream#readRawVarint32.
|
||||||
|
var headerBuf [binary.MaxVarintLen32]byte
|
||||||
|
var bytesRead, varIntBytes int
|
||||||
|
var messageLength uint64
|
||||||
|
for varIntBytes == 0 { // i.e. no varint has been decoded yet.
|
||||||
|
if bytesRead >= len(headerBuf) {
|
||||||
|
return bytesRead, errInvalidVarint
|
||||||
|
}
|
||||||
|
// We have to read byte by byte here to avoid reading more bytes
|
||||||
|
// than required. Each read byte is appended to what we have
|
||||||
|
// read before.
|
||||||
|
newBytesRead, err := r.Read(headerBuf[bytesRead : bytesRead+1])
|
||||||
|
if newBytesRead == 0 {
|
||||||
|
if err != nil {
|
||||||
|
return bytesRead, err
|
||||||
|
}
|
||||||
|
// A Reader should not return (0, nil), but if it does,
|
||||||
|
// it should be treated as no-op (according to the
|
||||||
|
// Reader contract). So let's go on...
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bytesRead += newBytesRead
|
||||||
|
// Now present everything read so far to the varint decoder and
|
||||||
|
// see if a varint can be decoded already.
|
||||||
|
messageLength, varIntBytes = proto.DecodeVarint(headerBuf[:bytesRead])
|
||||||
|
}
|
||||||
|
|
||||||
|
messageBuf := make([]byte, messageLength)
|
||||||
|
newBytesRead, err := io.ReadFull(r, messageBuf)
|
||||||
|
bytesRead += newBytesRead
|
||||||
|
if err != nil {
|
||||||
|
return bytesRead, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesRead, proto.Unmarshal(messageBuf, m)
|
||||||
|
}
|
16
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/doc.go
generated
vendored
Normal file
16
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2013 Matt T. Proud
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package pbutil provides record length-delimited Protocol Buffer streaming.
|
||||||
|
package pbutil
|
46
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go
generated
vendored
Normal file
46
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright 2013 Matt T. Proud
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package pbutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteDelimited encodes and dumps a message to the provided writer prefixed
|
||||||
|
// with a 32-bit varint indicating the length of the encoded message, producing
|
||||||
|
// a length-delimited record stream, which can be used to chain together
|
||||||
|
// encoded messages of the same type together in a file. It returns the total
|
||||||
|
// number of bytes written and any applicable error. This is roughly
|
||||||
|
// equivalent to the companion Java API's MessageLite#writeDelimitedTo.
|
||||||
|
func WriteDelimited(w io.Writer, m proto.Message) (n int, err error) {
|
||||||
|
buffer, err := proto.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [binary.MaxVarintLen32]byte
|
||||||
|
encodedLength := binary.PutUvarint(buf[:], uint64(len(buffer)))
|
||||||
|
|
||||||
|
sync, err := w.Write(buf[:encodedLength])
|
||||||
|
if err != nil {
|
||||||
|
return sync, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = w.Write(buffer)
|
||||||
|
return n + sync, err
|
||||||
|
}
|
340
vendor/github.com/miekg/dns/msg_generate.go
generated
vendored
340
vendor/github.com/miekg/dns/msg_generate.go
generated
vendored
|
@ -1,340 +0,0 @@
|
||||||
//+build ignore
|
|
||||||
|
|
||||||
// msg_generate.go is meant to run with go generate. It will use
|
|
||||||
// go/{importer,types} to track down all the RR struct types. Then for each type
|
|
||||||
// it will generate pack/unpack methods based on the struct tags. The generated source is
|
|
||||||
// written to zmsg.go, and is meant to be checked into git.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"go/importer"
|
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var packageHdr = `
|
|
||||||
// *** DO NOT MODIFY ***
|
|
||||||
// AUTOGENERATED BY go generate from msg_generate.go
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
// getTypeStruct will take a type and the package scope, and return the
|
|
||||||
// (innermost) struct if the type is considered a RR type (currently defined as
|
|
||||||
// those structs beginning with a RR_Header, could be redefined as implementing
|
|
||||||
// the RR interface). The bool return value indicates if embedded structs were
|
|
||||||
// resolved.
|
|
||||||
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
|
|
||||||
st, ok := t.Underlying().(*types.Struct)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
|
|
||||||
return st, false
|
|
||||||
}
|
|
||||||
if st.Field(0).Anonymous() {
|
|
||||||
st, _ := getTypeStruct(st.Field(0).Type(), scope)
|
|
||||||
return st, true
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Import and type-check the package
|
|
||||||
pkg, err := importer.Default().Import("github.com/miekg/dns")
|
|
||||||
fatalIfErr(err)
|
|
||||||
scope := pkg.Scope()
|
|
||||||
|
|
||||||
// Collect actual types (*X)
|
|
||||||
var namedTypes []string
|
|
||||||
for _, name := range scope.Names() {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
if o == nil || !o.Exported() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if st, _ := getTypeStruct(o.Type(), scope); st == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if name == "PrivateRR" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if corresponding TypeX exists
|
|
||||||
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
|
|
||||||
log.Fatalf("Constant Type%s does not exist.", o.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
namedTypes = append(namedTypes, o.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
b.WriteString(packageHdr)
|
|
||||||
|
|
||||||
fmt.Fprint(b, "// pack*() functions\n\n")
|
|
||||||
for _, name := range namedTypes {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
st, _ := getTypeStruct(o.Type(), scope)
|
|
||||||
|
|
||||||
fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {\n", name)
|
|
||||||
fmt.Fprint(b, `off, err := rr.Hdr.pack(msg, off, compression, compress)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
headerEnd := off
|
|
||||||
`)
|
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
|
||||||
o := func(s string) {
|
|
||||||
fmt.Fprintf(b, s, st.Field(i).Name())
|
|
||||||
fmt.Fprint(b, `if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
|
||||||
switch st.Tag(i) {
|
|
||||||
case `dns:"-"`: // ignored
|
|
||||||
case `dns:"txt"`:
|
|
||||||
o("off, err = packStringTxt(rr.%s, msg, off)\n")
|
|
||||||
case `dns:"opt"`:
|
|
||||||
o("off, err = packDataOpt(rr.%s, msg, off)\n")
|
|
||||||
case `dns:"nsec"`:
|
|
||||||
o("off, err = packDataNsec(rr.%s, msg, off)\n")
|
|
||||||
case `dns:"domain-name"`:
|
|
||||||
o("off, err = packDataDomainNames(rr.%s, msg, off, compression, compress)\n")
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case st.Tag(i) == `dns:"-"`: // ignored
|
|
||||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
|
||||||
fallthrough
|
|
||||||
case st.Tag(i) == `dns:"domain-name"`:
|
|
||||||
o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n")
|
|
||||||
case st.Tag(i) == `dns:"a"`:
|
|
||||||
o("off, err = packDataA(rr.%s, msg, off)\n")
|
|
||||||
case st.Tag(i) == `dns:"aaaa"`:
|
|
||||||
o("off, err = packDataAAAA(rr.%s, msg, off)\n")
|
|
||||||
case st.Tag(i) == `dns:"uint48"`:
|
|
||||||
o("off, err = packUint48(rr.%s, msg, off)\n")
|
|
||||||
case st.Tag(i) == `dns:"txt"`:
|
|
||||||
o("off, err = packString(rr.%s, msg, off)\n")
|
|
||||||
|
|
||||||
case strings.HasPrefix(st.Tag(i), `dns:"size-base32`): // size-base32 can be packed just like base32
|
|
||||||
fallthrough
|
|
||||||
case st.Tag(i) == `dns:"base32"`:
|
|
||||||
o("off, err = packStringBase32(rr.%s, msg, off)\n")
|
|
||||||
|
|
||||||
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): // size-base64 can be packed just like base64
|
|
||||||
fallthrough
|
|
||||||
case st.Tag(i) == `dns:"base64"`:
|
|
||||||
o("off, err = packStringBase64(rr.%s, msg, off)\n")
|
|
||||||
|
|
||||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex:SaltLength`): // Hack to fix empty salt length for NSEC3
|
|
||||||
o("if rr.%s == \"-\" { /* do nothing, empty salt */ }\n")
|
|
||||||
continue
|
|
||||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`): // size-hex can be packed just like hex
|
|
||||||
fallthrough
|
|
||||||
case st.Tag(i) == `dns:"hex"`:
|
|
||||||
o("off, err = packStringHex(rr.%s, msg, off)\n")
|
|
||||||
|
|
||||||
case st.Tag(i) == `dns:"octet"`:
|
|
||||||
o("off, err = packStringOctet(rr.%s, msg, off)\n")
|
|
||||||
case st.Tag(i) == "":
|
|
||||||
switch st.Field(i).Type().(*types.Basic).Kind() {
|
|
||||||
case types.Uint8:
|
|
||||||
o("off, err = packUint8(rr.%s, msg, off)\n")
|
|
||||||
case types.Uint16:
|
|
||||||
o("off, err = packUint16(rr.%s, msg, off)\n")
|
|
||||||
case types.Uint32:
|
|
||||||
o("off, err = packUint32(rr.%s, msg, off)\n")
|
|
||||||
case types.Uint64:
|
|
||||||
o("off, err = packUint64(rr.%s, msg, off)\n")
|
|
||||||
case types.String:
|
|
||||||
o("off, err = packString(rr.%s, msg, off)\n")
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We have packed everything, only now we know the rdlength of this RR
|
|
||||||
fmt.Fprintln(b, "rr.Header().Rdlength = uint16(off-headerEnd)")
|
|
||||||
fmt.Fprintln(b, "return off, nil }\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(b, "// unpack*() functions\n\n")
|
|
||||||
for _, name := range namedTypes {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
st, _ := getTypeStruct(o.Type(), scope)
|
|
||||||
|
|
||||||
fmt.Fprintf(b, "func unpack%s(h RR_Header, msg []byte, off int) (RR, int, error) {\n", name)
|
|
||||||
fmt.Fprintf(b, "rr := new(%s)\n", name)
|
|
||||||
fmt.Fprint(b, "rr.Hdr = h\n")
|
|
||||||
fmt.Fprint(b, `if noRdata(h) {
|
|
||||||
return rr, off, nil
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
rdStart := off
|
|
||||||
_ = rdStart
|
|
||||||
|
|
||||||
`)
|
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
|
||||||
o := func(s string) {
|
|
||||||
fmt.Fprintf(b, s, st.Field(i).Name())
|
|
||||||
fmt.Fprint(b, `if err != nil {
|
|
||||||
return rr, off, err
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// size-* are special, because they reference a struct member we should use for the length.
|
|
||||||
if strings.HasPrefix(st.Tag(i), `dns:"size-`) {
|
|
||||||
structMember := structMember(st.Tag(i))
|
|
||||||
structTag := structTag(st.Tag(i))
|
|
||||||
switch structTag {
|
|
||||||
case "hex":
|
|
||||||
fmt.Fprintf(b, "rr.%s, off, err = unpackStringHex(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
|
|
||||||
case "base32":
|
|
||||||
fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase32(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
|
|
||||||
case "base64":
|
|
||||||
fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase64(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
|
||||||
}
|
|
||||||
fmt.Fprint(b, `if err != nil {
|
|
||||||
return rr, off, err
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
|
||||||
switch st.Tag(i) {
|
|
||||||
case `dns:"-"`: // ignored
|
|
||||||
case `dns:"txt"`:
|
|
||||||
o("rr.%s, off, err = unpackStringTxt(msg, off)\n")
|
|
||||||
case `dns:"opt"`:
|
|
||||||
o("rr.%s, off, err = unpackDataOpt(msg, off)\n")
|
|
||||||
case `dns:"nsec"`:
|
|
||||||
o("rr.%s, off, err = unpackDataNsec(msg, off)\n")
|
|
||||||
case `dns:"domain-name"`:
|
|
||||||
o("rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch st.Tag(i) {
|
|
||||||
case `dns:"-"`: // ignored
|
|
||||||
case `dns:"cdomain-name"`:
|
|
||||||
fallthrough
|
|
||||||
case `dns:"domain-name"`:
|
|
||||||
o("rr.%s, off, err = UnpackDomainName(msg, off)\n")
|
|
||||||
case `dns:"a"`:
|
|
||||||
o("rr.%s, off, err = unpackDataA(msg, off)\n")
|
|
||||||
case `dns:"aaaa"`:
|
|
||||||
o("rr.%s, off, err = unpackDataAAAA(msg, off)\n")
|
|
||||||
case `dns:"uint48"`:
|
|
||||||
o("rr.%s, off, err = unpackUint48(msg, off)\n")
|
|
||||||
case `dns:"txt"`:
|
|
||||||
o("rr.%s, off, err = unpackString(msg, off)\n")
|
|
||||||
case `dns:"base32"`:
|
|
||||||
o("rr.%s, off, err = unpackStringBase32(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
|
||||||
case `dns:"base64"`:
|
|
||||||
o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
|
||||||
case `dns:"hex"`:
|
|
||||||
o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
|
||||||
case `dns:"octet"`:
|
|
||||||
o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
|
|
||||||
case "":
|
|
||||||
switch st.Field(i).Type().(*types.Basic).Kind() {
|
|
||||||
case types.Uint8:
|
|
||||||
o("rr.%s, off, err = unpackUint8(msg, off)\n")
|
|
||||||
case types.Uint16:
|
|
||||||
o("rr.%s, off, err = unpackUint16(msg, off)\n")
|
|
||||||
case types.Uint32:
|
|
||||||
o("rr.%s, off, err = unpackUint32(msg, off)\n")
|
|
||||||
case types.Uint64:
|
|
||||||
o("rr.%s, off, err = unpackUint64(msg, off)\n")
|
|
||||||
case types.String:
|
|
||||||
o("rr.%s, off, err = unpackString(msg, off)\n")
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
|
||||||
}
|
|
||||||
// If we've hit len(msg) we return without error.
|
|
||||||
if i < st.NumFields()-1 {
|
|
||||||
fmt.Fprintf(b, `if off == len(msg) {
|
|
||||||
return rr, off, nil
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "return rr, off, err }\n\n")
|
|
||||||
}
|
|
||||||
// Generate typeToUnpack map
|
|
||||||
fmt.Fprintln(b, "var typeToUnpack = map[uint16]func(RR_Header, []byte, int) (RR, int, error){")
|
|
||||||
for _, name := range namedTypes {
|
|
||||||
if name == "RFC3597" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "Type%s: unpack%s,\n", name, name)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(b, "}\n")
|
|
||||||
|
|
||||||
// gofmt
|
|
||||||
res, err := format.Source(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
b.WriteTo(os.Stderr)
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// write result
|
|
||||||
f, err := os.Create("zmsg.go")
|
|
||||||
fatalIfErr(err)
|
|
||||||
defer f.Close()
|
|
||||||
f.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// structMember will take a tag like dns:"size-base32:SaltLength" and return the last part of this string.
|
|
||||||
func structMember(s string) string {
|
|
||||||
fields := strings.Split(s, ":")
|
|
||||||
if len(fields) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
f := fields[len(fields)-1]
|
|
||||||
// f should have a closing "
|
|
||||||
if len(f) > 1 {
|
|
||||||
return f[:len(f)-1]
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// structTag will take a tag like dns:"size-base32:SaltLength" and return base32.
|
|
||||||
func structTag(s string) string {
|
|
||||||
fields := strings.Split(s, ":")
|
|
||||||
if len(fields) < 2 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return fields[1][len("\"size-"):]
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatalIfErr(err error) {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
271
vendor/github.com/miekg/dns/types_generate.go
generated
vendored
271
vendor/github.com/miekg/dns/types_generate.go
generated
vendored
|
@ -1,271 +0,0 @@
|
||||||
//+build ignore
|
|
||||||
|
|
||||||
// types_generate.go is meant to run with go generate. It will use
|
|
||||||
// go/{importer,types} to track down all the RR struct types. Then for each type
|
|
||||||
// it will generate conversion tables (TypeToRR and TypeToString) and banal
|
|
||||||
// methods (len, Header, copy) based on the struct tags. The generated source is
|
|
||||||
// written to ztypes.go, and is meant to be checked into git.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"go/importer"
|
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
var skipLen = map[string]struct{}{
|
|
||||||
"NSEC": {},
|
|
||||||
"NSEC3": {},
|
|
||||||
"OPT": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
var packageHdr = `
|
|
||||||
// *** DO NOT MODIFY ***
|
|
||||||
// AUTOGENERATED BY go generate from type_generate.go
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
var TypeToRR = template.Must(template.New("TypeToRR").Parse(`
|
|
||||||
// TypeToRR is a map of constructors for each RR type.
|
|
||||||
var TypeToRR = map[uint16]func() RR{
|
|
||||||
{{range .}}{{if ne . "RFC3597"}} Type{{.}}: func() RR { return new({{.}}) },
|
|
||||||
{{end}}{{end}} }
|
|
||||||
|
|
||||||
`))
|
|
||||||
|
|
||||||
var typeToString = template.Must(template.New("typeToString").Parse(`
|
|
||||||
// TypeToString is a map of strings for each RR type.
|
|
||||||
var TypeToString = map[uint16]string{
|
|
||||||
{{range .}}{{if ne . "NSAPPTR"}} Type{{.}}: "{{.}}",
|
|
||||||
{{end}}{{end}} TypeNSAPPTR: "NSAP-PTR",
|
|
||||||
}
|
|
||||||
|
|
||||||
`))
|
|
||||||
|
|
||||||
var headerFunc = template.Must(template.New("headerFunc").Parse(`
|
|
||||||
// Header() functions
|
|
||||||
{{range .}} func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
`))
|
|
||||||
|
|
||||||
// getTypeStruct will take a type and the package scope, and return the
|
|
||||||
// (innermost) struct if the type is considered a RR type (currently defined as
|
|
||||||
// those structs beginning with a RR_Header, could be redefined as implementing
|
|
||||||
// the RR interface). The bool return value indicates if embedded structs were
|
|
||||||
// resolved.
|
|
||||||
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
|
|
||||||
st, ok := t.Underlying().(*types.Struct)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
|
|
||||||
return st, false
|
|
||||||
}
|
|
||||||
if st.Field(0).Anonymous() {
|
|
||||||
st, _ := getTypeStruct(st.Field(0).Type(), scope)
|
|
||||||
return st, true
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Import and type-check the package
|
|
||||||
pkg, err := importer.Default().Import("github.com/miekg/dns")
|
|
||||||
fatalIfErr(err)
|
|
||||||
scope := pkg.Scope()
|
|
||||||
|
|
||||||
// Collect constants like TypeX
|
|
||||||
var numberedTypes []string
|
|
||||||
for _, name := range scope.Names() {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
if o == nil || !o.Exported() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
b, ok := o.Type().(*types.Basic)
|
|
||||||
if !ok || b.Kind() != types.Uint16 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(o.Name(), "Type") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := strings.TrimPrefix(o.Name(), "Type")
|
|
||||||
if name == "PrivateRR" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
numberedTypes = append(numberedTypes, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect actual types (*X)
|
|
||||||
var namedTypes []string
|
|
||||||
for _, name := range scope.Names() {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
if o == nil || !o.Exported() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if st, _ := getTypeStruct(o.Type(), scope); st == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if name == "PrivateRR" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if corresponding TypeX exists
|
|
||||||
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
|
|
||||||
log.Fatalf("Constant Type%s does not exist.", o.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
namedTypes = append(namedTypes, o.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
b.WriteString(packageHdr)
|
|
||||||
|
|
||||||
// Generate TypeToRR
|
|
||||||
fatalIfErr(TypeToRR.Execute(b, namedTypes))
|
|
||||||
|
|
||||||
// Generate typeToString
|
|
||||||
fatalIfErr(typeToString.Execute(b, numberedTypes))
|
|
||||||
|
|
||||||
// Generate headerFunc
|
|
||||||
fatalIfErr(headerFunc.Execute(b, namedTypes))
|
|
||||||
|
|
||||||
// Generate len()
|
|
||||||
fmt.Fprint(b, "// len() functions\n")
|
|
||||||
for _, name := range namedTypes {
|
|
||||||
if _, ok := skipLen[name]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
st, isEmbedded := getTypeStruct(o.Type(), scope)
|
|
||||||
if isEmbedded {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "func (rr *%s) len() int {\n", name)
|
|
||||||
fmt.Fprintf(b, "l := rr.Hdr.len()\n")
|
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
|
||||||
o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }
|
|
||||||
|
|
||||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
|
||||||
switch st.Tag(i) {
|
|
||||||
case `dns:"-"`:
|
|
||||||
// ignored
|
|
||||||
case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`:
|
|
||||||
o("for _, x := range rr.%s { l += len(x) + 1 }\n")
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case st.Tag(i) == `dns:"-"`:
|
|
||||||
// ignored
|
|
||||||
case st.Tag(i) == `dns:"cdomain-name"`, st.Tag(i) == `dns:"domain-name"`:
|
|
||||||
o("l += len(rr.%s) + 1\n")
|
|
||||||
case st.Tag(i) == `dns:"octet"`:
|
|
||||||
o("l += len(rr.%s)\n")
|
|
||||||
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`):
|
|
||||||
fallthrough
|
|
||||||
case st.Tag(i) == `dns:"base64"`:
|
|
||||||
o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
|
|
||||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`):
|
|
||||||
fallthrough
|
|
||||||
case st.Tag(i) == `dns:"hex"`:
|
|
||||||
o("l += len(rr.%s)/2 + 1\n")
|
|
||||||
case st.Tag(i) == `dns:"a"`:
|
|
||||||
o("l += net.IPv4len // %s\n")
|
|
||||||
case st.Tag(i) == `dns:"aaaa"`:
|
|
||||||
o("l += net.IPv6len // %s\n")
|
|
||||||
case st.Tag(i) == `dns:"txt"`:
|
|
||||||
o("for _, t := range rr.%s { l += len(t) + 1 }\n")
|
|
||||||
case st.Tag(i) == `dns:"uint48"`:
|
|
||||||
o("l += 6 // %s\n")
|
|
||||||
case st.Tag(i) == "":
|
|
||||||
switch st.Field(i).Type().(*types.Basic).Kind() {
|
|
||||||
case types.Uint8:
|
|
||||||
o("l += 1 // %s\n")
|
|
||||||
case types.Uint16:
|
|
||||||
o("l += 2 // %s\n")
|
|
||||||
case types.Uint32:
|
|
||||||
o("l += 4 // %s\n")
|
|
||||||
case types.Uint64:
|
|
||||||
o("l += 8 // %s\n")
|
|
||||||
case types.String:
|
|
||||||
o("l += len(rr.%s) + 1\n")
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "return l }\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate copy()
|
|
||||||
fmt.Fprint(b, "// copy() functions\n")
|
|
||||||
for _, name := range namedTypes {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
st, isEmbedded := getTypeStruct(o.Type(), scope)
|
|
||||||
if isEmbedded {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name)
|
|
||||||
fields := []string{"*rr.Hdr.copyHeader()"}
|
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
|
||||||
f := st.Field(i).Name()
|
|
||||||
if sl, ok := st.Field(i).Type().(*types.Slice); ok {
|
|
||||||
t := sl.Underlying().String()
|
|
||||||
t = strings.TrimPrefix(t, "[]")
|
|
||||||
if strings.Contains(t, ".") {
|
|
||||||
splits := strings.Split(t, ".")
|
|
||||||
t = splits[len(splits)-1]
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
|
|
||||||
f, t, f, f, f)
|
|
||||||
fields = append(fields, f)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if st.Field(i).Type().String() == "net.IP" {
|
|
||||||
fields = append(fields, "copyIP(rr."+f+")")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields = append(fields, "rr."+f)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "return &%s{%s}\n", name, strings.Join(fields, ","))
|
|
||||||
fmt.Fprintf(b, "}\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// gofmt
|
|
||||||
res, err := format.Source(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
b.WriteTo(os.Stderr)
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// write result
|
|
||||||
f, err := os.Create("ztypes.go")
|
|
||||||
fatalIfErr(err)
|
|
||||||
defer f.Close()
|
|
||||||
f.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatalIfErr(err error) {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
201
vendor/github.com/prometheus/client_golang/LICENSE
generated
vendored
Normal file
201
vendor/github.com/prometheus/client_golang/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
23
vendor/github.com/prometheus/client_golang/NOTICE
generated
vendored
Normal file
23
vendor/github.com/prometheus/client_golang/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Prometheus instrumentation library for Go applications
|
||||||
|
Copyright 2012-2015 The Prometheus Authors
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
SoundCloud Ltd. (http://soundcloud.com/).
|
||||||
|
|
||||||
|
|
||||||
|
The following components are included in this product:
|
||||||
|
|
||||||
|
perks - a fork of https://github.com/bmizerany/perks
|
||||||
|
https://github.com/beorn7/perks
|
||||||
|
Copyright 2013-2015 Blake Mizerany, Björn Rabenstein
|
||||||
|
See https://github.com/beorn7/perks/blob/master/README.md for license details.
|
||||||
|
|
||||||
|
Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
http://github.com/golang/protobuf/
|
||||||
|
Copyright 2010 The Go Authors
|
||||||
|
See source code for license details.
|
||||||
|
|
||||||
|
Support for streaming Protocol Buffer messages for the Go language (golang).
|
||||||
|
https://github.com/matttproud/golang_protobuf_extensions
|
||||||
|
Copyright 2013 Matt T. Proud
|
||||||
|
Licensed under the Apache License, Version 2.0
|
93
vendor/github.com/prometheus/client_golang/README.md
generated
vendored
Normal file
93
vendor/github.com/prometheus/client_golang/README.md
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
# Prometheus Go client library
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/prometheus/client_golang.svg?branch=master)](https://travis-ci.org/prometheus/client_golang)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/prometheus/client_golang)](https://goreportcard.com/report/github.com/prometheus/client_golang)
|
||||||
|
|
||||||
|
This is the [Go](http://golang.org) client library for
|
||||||
|
[Prometheus](http://prometheus.io). It has two separate parts, one for
|
||||||
|
instrumenting application code, and one for creating clients that talk to the
|
||||||
|
Prometheus HTTP API.
|
||||||
|
|
||||||
|
__This library requires Go1.7 or later.__
|
||||||
|
|
||||||
|
## Important note about releases, versioning, tagging, stability, and your favorite Go dependency management tool
|
||||||
|
|
||||||
|
While our goal is to follow [Semantic Versioning](https://semver.org/), this
|
||||||
|
repository is still pre-1.0.0. To quote the
|
||||||
|
[Semantic Versioning spec](https://semver.org/#spec-item-4): “Anything may
|
||||||
|
change at any time. The public API should not be considered stable.” We know
|
||||||
|
that this is at odds with the widespread use of this library. However, just
|
||||||
|
declaring something 1.0.0 doesn't make it 1.0.0. Instead, we are working
|
||||||
|
towards a 1.0.0 release that actually deserves its major version number.
|
||||||
|
|
||||||
|
Having said that, we aim for always keeping the tip of master in a workable
|
||||||
|
state and for only introducing ”mildly” breaking changes up to and including
|
||||||
|
[v0.9.0](https://github.com/prometheus/client_golang/milestone/1). After that,
|
||||||
|
a number of ”hard” breaking changes are planned, see the
|
||||||
|
[v0.10.0 milestone](https://github.com/prometheus/client_golang/milestone/2),
|
||||||
|
which should get the library much closer to 1.0.0 state.
|
||||||
|
|
||||||
|
Dependency management in Go projects is still in flux, and there are many tools
|
||||||
|
floating around. While [dep](https://golang.github.io/dep/) might develop into
|
||||||
|
the de-facto standard tool, it is still officially experimental. The roadmap
|
||||||
|
for this library has been laid out with a lot of sometimes painful experience
|
||||||
|
in mind. We really cannot adjust it every other month to the needs of the
|
||||||
|
currently most popular or most promising Go dependency management tool. The
|
||||||
|
recommended course of action with dependency management tools is the following:
|
||||||
|
|
||||||
|
- Do not expect strict post-1.0.0 semver semantics prior to the 1.0.0
|
||||||
|
release. If your dependency management tool expects strict post-1.0.0 semver
|
||||||
|
semantics, you have to wait. Sorry.
|
||||||
|
- If you want absolute certainty, please lock to a specific commit. You can
|
||||||
|
also lock to tags, but please don't ask for more tagging. This would suggest
|
||||||
|
some release or stability testing procedure that simply is not in place. As
|
||||||
|
said above, we are aiming for stability of the tip of master, but if we
|
||||||
|
tagged every single commit, locking to tags would be the same as locking to
|
||||||
|
commits.
|
||||||
|
- If you want to get the newer features and improvements and are willing to
|
||||||
|
take the minor risk of newly introduced bugs and “mild” breakage, just always
|
||||||
|
update to the tip of master (which is essentially the original idea of Go
|
||||||
|
dependency management). We recommend to not use features marked as
|
||||||
|
_deprecated_ in this case.
|
||||||
|
- Once [v0.9.0](https://github.com/prometheus/client_golang/milestone/1) is
|
||||||
|
out, you could lock to v0.9.x to get bugfixes (and perhaps minor new
|
||||||
|
features) while avoiding the “hard” breakage that will come with post-0.9
|
||||||
|
features.
|
||||||
|
|
||||||
|
## Instrumenting applications
|
||||||
|
|
||||||
|
[![code-coverage](http://gocover.io/_badge/github.com/prometheus/client_golang/prometheus)](http://gocover.io/github.com/prometheus/client_golang/prometheus) [![go-doc](https://godoc.org/github.com/prometheus/client_golang/prometheus?status.svg)](https://godoc.org/github.com/prometheus/client_golang/prometheus)
|
||||||
|
|
||||||
|
The
|
||||||
|
[`prometheus` directory](https://github.com/prometheus/client_golang/tree/master/prometheus)
|
||||||
|
contains the instrumentation library. See the
|
||||||
|
[best practices section](http://prometheus.io/docs/practices/naming/) of the
|
||||||
|
Prometheus documentation to learn more about instrumenting applications.
|
||||||
|
|
||||||
|
The
|
||||||
|
[`examples` directory](https://github.com/prometheus/client_golang/tree/master/examples)
|
||||||
|
contains simple examples of instrumented code.
|
||||||
|
|
||||||
|
## Client for the Prometheus HTTP API
|
||||||
|
|
||||||
|
[![code-coverage](http://gocover.io/_badge/github.com/prometheus/client_golang/api/prometheus/v1)](http://gocover.io/github.com/prometheus/client_golang/api/prometheus/v1) [![go-doc](https://godoc.org/github.com/prometheus/client_golang/api/prometheus?status.svg)](https://godoc.org/github.com/prometheus/client_golang/api)
|
||||||
|
|
||||||
|
The
|
||||||
|
[`api/prometheus` directory](https://github.com/prometheus/client_golang/tree/master/api/prometheus)
|
||||||
|
contains the client for the
|
||||||
|
[Prometheus HTTP API](http://prometheus.io/docs/querying/api/). It allows you
|
||||||
|
to write Go applications that query time series data from a Prometheus
|
||||||
|
server. It is still in alpha stage.
|
||||||
|
|
||||||
|
## Where is `model`, `extraction`, and `text`?
|
||||||
|
|
||||||
|
The `model` packages has been moved to
|
||||||
|
[`prometheus/common/model`](https://github.com/prometheus/common/tree/master/model).
|
||||||
|
|
||||||
|
The `extraction` and `text` packages are now contained in
|
||||||
|
[`prometheus/common/expfmt`](https://github.com/prometheus/common/tree/master/expfmt).
|
||||||
|
|
||||||
|
## Contributing and community
|
||||||
|
|
||||||
|
See the [contributing guidelines](CONTRIBUTING.md) and the
|
||||||
|
[Community section](http://prometheus.io/community/) of the homepage.
|
1
vendor/github.com/prometheus/client_golang/prometheus/README.md
generated
vendored
Normal file
1
vendor/github.com/prometheus/client_golang/prometheus/README.md
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
See [![go-doc](https://godoc.org/github.com/prometheus/client_golang/prometheus?status.svg)](https://godoc.org/github.com/prometheus/client_golang/prometheus).
|
75
vendor/github.com/prometheus/client_golang/prometheus/collector.go
generated
vendored
Normal file
75
vendor/github.com/prometheus/client_golang/prometheus/collector.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
// Collector is the interface implemented by anything that can be used by
|
||||||
|
// Prometheus to collect metrics. A Collector has to be registered for
|
||||||
|
// collection. See Registerer.Register.
|
||||||
|
//
|
||||||
|
// The stock metrics provided by this package (Gauge, Counter, Summary,
|
||||||
|
// Histogram, Untyped) are also Collectors (which only ever collect one metric,
|
||||||
|
// namely itself). An implementer of Collector may, however, collect multiple
|
||||||
|
// metrics in a coordinated fashion and/or create metrics on the fly. Examples
|
||||||
|
// for collectors already implemented in this library are the metric vectors
|
||||||
|
// (i.e. collection of multiple instances of the same Metric but with different
|
||||||
|
// label values) like GaugeVec or SummaryVec, and the ExpvarCollector.
|
||||||
|
type Collector interface {
|
||||||
|
// Describe sends the super-set of all possible descriptors of metrics
|
||||||
|
// collected by this Collector to the provided channel and returns once
|
||||||
|
// the last descriptor has been sent. The sent descriptors fulfill the
|
||||||
|
// consistency and uniqueness requirements described in the Desc
|
||||||
|
// documentation. (It is valid if one and the same Collector sends
|
||||||
|
// duplicate descriptors. Those duplicates are simply ignored. However,
|
||||||
|
// two different Collectors must not send duplicate descriptors.) This
|
||||||
|
// method idempotently sends the same descriptors throughout the
|
||||||
|
// lifetime of the Collector. If a Collector encounters an error while
|
||||||
|
// executing this method, it must send an invalid descriptor (created
|
||||||
|
// with NewInvalidDesc) to signal the error to the registry.
|
||||||
|
Describe(chan<- *Desc)
|
||||||
|
// Collect is called by the Prometheus registry when collecting
|
||||||
|
// metrics. The implementation sends each collected metric via the
|
||||||
|
// provided channel and returns once the last metric has been sent. The
|
||||||
|
// descriptor of each sent metric is one of those returned by
|
||||||
|
// Describe. Returned metrics that share the same descriptor must differ
|
||||||
|
// in their variable label values. This method may be called
|
||||||
|
// concurrently and must therefore be implemented in a concurrency safe
|
||||||
|
// way. Blocking occurs at the expense of total performance of rendering
|
||||||
|
// all registered metrics. Ideally, Collector implementations support
|
||||||
|
// concurrent readers.
|
||||||
|
Collect(chan<- Metric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// selfCollector implements Collector for a single Metric so that the Metric
|
||||||
|
// collects itself. Add it as an anonymous field to a struct that implements
|
||||||
|
// Metric, and call init with the Metric itself as an argument.
|
||||||
|
type selfCollector struct {
|
||||||
|
self Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// init provides the selfCollector with a reference to the metric it is supposed
|
||||||
|
// to collect. It is usually called within the factory function to create a
|
||||||
|
// metric. See example.
|
||||||
|
func (c *selfCollector) init(self Metric) {
|
||||||
|
c.self = self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe implements Collector.
|
||||||
|
func (c *selfCollector) Describe(ch chan<- *Desc) {
|
||||||
|
ch <- c.self.Desc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect implements Collector.
|
||||||
|
func (c *selfCollector) Collect(ch chan<- Metric) {
|
||||||
|
ch <- c.self
|
||||||
|
}
|
277
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
Normal file
277
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Counter is a Metric that represents a single numerical value that only ever
|
||||||
|
// goes up. That implies that it cannot be used to count items whose number can
|
||||||
|
// also go down, e.g. the number of currently running goroutines. Those
|
||||||
|
// "counters" are represented by Gauges.
|
||||||
|
//
|
||||||
|
// A Counter is typically used to count requests served, tasks completed, errors
|
||||||
|
// occurred, etc.
|
||||||
|
//
|
||||||
|
// To create Counter instances, use NewCounter.
|
||||||
|
type Counter interface {
|
||||||
|
Metric
|
||||||
|
Collector
|
||||||
|
|
||||||
|
// Inc increments the counter by 1. Use Add to increment it by arbitrary
|
||||||
|
// non-negative values.
|
||||||
|
Inc()
|
||||||
|
// Add adds the given value to the counter. It panics if the value is <
|
||||||
|
// 0.
|
||||||
|
Add(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CounterOpts is an alias for Opts. See there for doc comments.
|
||||||
|
type CounterOpts Opts
|
||||||
|
|
||||||
|
// NewCounter creates a new Counter based on the provided CounterOpts.
|
||||||
|
//
|
||||||
|
// The returned implementation tracks the counter value in two separate
|
||||||
|
// variables, a float64 and a uint64. The latter is used to track calls of the
|
||||||
|
// Inc method and calls of the Add method with a value that can be represented
|
||||||
|
// as a uint64. This allows atomic increments of the counter with optimal
|
||||||
|
// performance. (It is common to have an Inc call in very hot execution paths.)
|
||||||
|
// Both internal tracking values are added up in the Write method. This has to
|
||||||
|
// be taken into account when it comes to precision and overflow behavior.
|
||||||
|
func NewCounter(opts CounterOpts) Counter {
|
||||||
|
desc := NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
nil,
|
||||||
|
opts.ConstLabels,
|
||||||
|
)
|
||||||
|
result := &counter{desc: desc, labelPairs: desc.constLabelPairs}
|
||||||
|
result.init(result) // Init self-collection.
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type counter struct {
|
||||||
|
// valBits contains the bits of the represented float64 value, while
|
||||||
|
// valInt stores values that are exact integers. Both have to go first
|
||||||
|
// in the struct to guarantee alignment for atomic operations.
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
valBits uint64
|
||||||
|
valInt uint64
|
||||||
|
|
||||||
|
selfCollector
|
||||||
|
desc *Desc
|
||||||
|
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Desc() *Desc {
|
||||||
|
return c.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Add(v float64) {
|
||||||
|
if v < 0 {
|
||||||
|
panic(errors.New("counter cannot decrease in value"))
|
||||||
|
}
|
||||||
|
ival := uint64(v)
|
||||||
|
if float64(ival) == v {
|
||||||
|
atomic.AddUint64(&c.valInt, ival)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&c.valBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
|
||||||
|
if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Inc() {
|
||||||
|
atomic.AddUint64(&c.valInt, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Write(out *dto.Metric) error {
|
||||||
|
fval := math.Float64frombits(atomic.LoadUint64(&c.valBits))
|
||||||
|
ival := atomic.LoadUint64(&c.valInt)
|
||||||
|
val := fval + float64(ival)
|
||||||
|
|
||||||
|
return populateMetric(CounterValue, val, c.labelPairs, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CounterVec is a Collector that bundles a set of Counters that all share the
|
||||||
|
// same Desc, but have different values for their variable labels. This is used
|
||||||
|
// if you want to count the same thing partitioned by various dimensions
|
||||||
|
// (e.g. number of HTTP requests, partitioned by response code and
|
||||||
|
// method). Create instances with NewCounterVec.
|
||||||
|
type CounterVec struct {
|
||||||
|
*metricVec
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
|
||||||
|
// partitioned by the given label names.
|
||||||
|
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||||
|
desc := NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
labelNames,
|
||||||
|
opts.ConstLabels,
|
||||||
|
)
|
||||||
|
return &CounterVec{
|
||||||
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
|
if len(lvs) != len(desc.variableLabels) {
|
||||||
|
panic(errInconsistentCardinality)
|
||||||
|
}
|
||||||
|
result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
|
||||||
|
result.init(result) // Init self-collection.
|
||||||
|
return result
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWithLabelValues returns the Counter for the given slice of label
|
||||||
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
|
// label values is accessed for the first time, a new Counter is created.
|
||||||
|
//
|
||||||
|
// It is possible to call this method without using the returned Counter to only
|
||||||
|
// create the new Counter but leave it at its starting value 0. See also the
|
||||||
|
// SummaryVec example.
|
||||||
|
//
|
||||||
|
// Keeping the Counter for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Counter from the CounterVec. In that case,
|
||||||
|
// the Counter will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Counter with the same label values is created later.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the GaugeVec example.
|
||||||
|
func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Counter), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWith returns the Counter for the given Labels map (the label names
|
||||||
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
|
// accessed for the first time, a new Counter is created. Implications of
|
||||||
|
// creating a Counter without using it and keeping the Counter for later use are
|
||||||
|
// the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Counter), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
|
// error allows shortcuts like
|
||||||
|
// myVec.WithLabelValues("404", "GET").Add(42)
|
||||||
|
func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
|
||||||
|
c, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||||
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
|
||||||
|
func (v *CounterVec) With(labels Labels) Counter {
|
||||||
|
c, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the CounterVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &CounterVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
|
}
|
||||||
|
|
||||||
|
// CounterFunc is a Counter whose value is determined at collect time by calling a
|
||||||
|
// provided function.
|
||||||
|
//
|
||||||
|
// To create CounterFunc instances, use NewCounterFunc.
|
||||||
|
type CounterFunc interface {
|
||||||
|
Metric
|
||||||
|
Collector
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCounterFunc creates a new CounterFunc based on the provided
|
||||||
|
// CounterOpts. The value reported is determined by calling the given function
|
||||||
|
// from within the Write method. Take into account that metric collection may
|
||||||
|
// happen concurrently. If that results in concurrent calls to Write, like in
|
||||||
|
// the case where a CounterFunc is directly registered with Prometheus, the
|
||||||
|
// provided function must be concurrency-safe. The function should also honor
|
||||||
|
// the contract for a Counter (values only go up, not down), but compliance will
|
||||||
|
// not be checked.
|
||||||
|
func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc {
|
||||||
|
return newValueFunc(NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
nil,
|
||||||
|
opts.ConstLabels,
|
||||||
|
), CounterValue, function)
|
||||||
|
}
|
188
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
Normal file
188
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
// Copyright 2016 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Desc is the descriptor used by every Prometheus Metric. It is essentially
|
||||||
|
// the immutable meta-data of a Metric. The normal Metric implementations
|
||||||
|
// included in this package manage their Desc under the hood. Users only have to
|
||||||
|
// deal with Desc if they use advanced features like the ExpvarCollector or
|
||||||
|
// custom Collectors and Metrics.
|
||||||
|
//
|
||||||
|
// Descriptors registered with the same registry have to fulfill certain
|
||||||
|
// consistency and uniqueness criteria if they share the same fully-qualified
|
||||||
|
// name: They must have the same help string and the same label names (aka label
|
||||||
|
// dimensions) in each, constLabels and variableLabels, but they must differ in
|
||||||
|
// the values of the constLabels.
|
||||||
|
//
|
||||||
|
// Descriptors that share the same fully-qualified names and the same label
|
||||||
|
// values of their constLabels are considered equal.
|
||||||
|
//
|
||||||
|
// Use NewDesc to create new Desc instances.
|
||||||
|
type Desc struct {
|
||||||
|
// fqName has been built from Namespace, Subsystem, and Name.
|
||||||
|
fqName string
|
||||||
|
// help provides some helpful information about this metric.
|
||||||
|
help string
|
||||||
|
// constLabelPairs contains precalculated DTO label pairs based on
|
||||||
|
// the constant labels.
|
||||||
|
constLabelPairs []*dto.LabelPair
|
||||||
|
// VariableLabels contains names of labels for which the metric
|
||||||
|
// maintains variable values.
|
||||||
|
variableLabels []string
|
||||||
|
// id is a hash of the values of the ConstLabels and fqName. This
|
||||||
|
// must be unique among all registered descriptors and can therefore be
|
||||||
|
// used as an identifier of the descriptor.
|
||||||
|
id uint64
|
||||||
|
// dimHash is a hash of the label names (preset and variable) and the
|
||||||
|
// Help string. Each Desc with the same fqName must have the same
|
||||||
|
// dimHash.
|
||||||
|
dimHash uint64
|
||||||
|
// err is an error that occurred during construction. It is reported on
|
||||||
|
// registration time.
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
|
||||||
|
// and will be reported on registration time. variableLabels and constLabels can
|
||||||
|
// be nil if no such labels should be set. fqName and help must not be empty.
|
||||||
|
//
|
||||||
|
// variableLabels only contain the label names. Their label values are variable
|
||||||
|
// and therefore not part of the Desc. (They are managed within the Metric.)
|
||||||
|
//
|
||||||
|
// For constLabels, the label values are constant. Therefore, they are fully
|
||||||
|
// specified in the Desc. See the Collector example for a usage pattern.
|
||||||
|
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
|
||||||
|
d := &Desc{
|
||||||
|
fqName: fqName,
|
||||||
|
help: help,
|
||||||
|
variableLabels: variableLabels,
|
||||||
|
}
|
||||||
|
if help == "" {
|
||||||
|
d.err = errors.New("empty help string")
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
||||||
|
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
// labelValues contains the label values of const labels (in order of
|
||||||
|
// their sorted label names) plus the fqName (at position 0).
|
||||||
|
labelValues := make([]string, 1, len(constLabels)+1)
|
||||||
|
labelValues[0] = fqName
|
||||||
|
labelNames := make([]string, 0, len(constLabels)+len(variableLabels))
|
||||||
|
labelNameSet := map[string]struct{}{}
|
||||||
|
// First add only the const label names and sort them...
|
||||||
|
for labelName := range constLabels {
|
||||||
|
if !checkLabelName(labelName) {
|
||||||
|
d.err = fmt.Errorf("%q is not a valid label name", labelName)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
labelNames = append(labelNames, labelName)
|
||||||
|
labelNameSet[labelName] = struct{}{}
|
||||||
|
}
|
||||||
|
sort.Strings(labelNames)
|
||||||
|
// ... so that we can now add const label values in the order of their names.
|
||||||
|
for _, labelName := range labelNames {
|
||||||
|
labelValues = append(labelValues, constLabels[labelName])
|
||||||
|
}
|
||||||
|
// Validate the const label values. They can't have a wrong cardinality, so
|
||||||
|
// use in len(labelValues) as expectedNumberOfValues.
|
||||||
|
if err := validateLabelValues(labelValues, len(labelValues)); err != nil {
|
||||||
|
d.err = err
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
// Now add the variable label names, but prefix them with something that
|
||||||
|
// cannot be in a regular label name. That prevents matching the label
|
||||||
|
// dimension with a different mix between preset and variable labels.
|
||||||
|
for _, labelName := range variableLabels {
|
||||||
|
if !checkLabelName(labelName) {
|
||||||
|
d.err = fmt.Errorf("%q is not a valid label name", labelName)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
labelNames = append(labelNames, "$"+labelName)
|
||||||
|
labelNameSet[labelName] = struct{}{}
|
||||||
|
}
|
||||||
|
if len(labelNames) != len(labelNameSet) {
|
||||||
|
d.err = errors.New("duplicate label names")
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
vh := hashNew()
|
||||||
|
for _, val := range labelValues {
|
||||||
|
vh = hashAdd(vh, val)
|
||||||
|
vh = hashAddByte(vh, separatorByte)
|
||||||
|
}
|
||||||
|
d.id = vh
|
||||||
|
// Sort labelNames so that order doesn't matter for the hash.
|
||||||
|
sort.Strings(labelNames)
|
||||||
|
// Now hash together (in this order) the help string and the sorted
|
||||||
|
// label names.
|
||||||
|
lh := hashNew()
|
||||||
|
lh = hashAdd(lh, help)
|
||||||
|
lh = hashAddByte(lh, separatorByte)
|
||||||
|
for _, labelName := range labelNames {
|
||||||
|
lh = hashAdd(lh, labelName)
|
||||||
|
lh = hashAddByte(lh, separatorByte)
|
||||||
|
}
|
||||||
|
d.dimHash = lh
|
||||||
|
|
||||||
|
d.constLabelPairs = make([]*dto.LabelPair, 0, len(constLabels))
|
||||||
|
for n, v := range constLabels {
|
||||||
|
d.constLabelPairs = append(d.constLabelPairs, &dto.LabelPair{
|
||||||
|
Name: proto.String(n),
|
||||||
|
Value: proto.String(v),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Sort(LabelPairSorter(d.constLabelPairs))
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInvalidDesc returns an invalid descriptor, i.e. a descriptor with the
|
||||||
|
// provided error set. If a collector returning such a descriptor is registered,
|
||||||
|
// registration will fail with the provided error. NewInvalidDesc can be used by
|
||||||
|
// a Collector to signal inability to describe itself.
|
||||||
|
func NewInvalidDesc(err error) *Desc {
|
||||||
|
return &Desc{
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Desc) String() string {
|
||||||
|
lpStrings := make([]string, 0, len(d.constLabelPairs))
|
||||||
|
for _, lp := range d.constLabelPairs {
|
||||||
|
lpStrings = append(
|
||||||
|
lpStrings,
|
||||||
|
fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
|
||||||
|
d.fqName,
|
||||||
|
d.help,
|
||||||
|
strings.Join(lpStrings, ","),
|
||||||
|
d.variableLabels,
|
||||||
|
)
|
||||||
|
}
|
186
vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
Normal file
186
vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package prometheus provides metrics primitives to instrument code for
|
||||||
|
// monitoring. It also offers a registry for metrics. Sub-packages allow to
|
||||||
|
// expose the registered metrics via HTTP (package promhttp) or push them to a
|
||||||
|
// Pushgateway (package push).
|
||||||
|
//
|
||||||
|
// All exported functions and methods are safe to be used concurrently unless
|
||||||
|
// specified otherwise.
|
||||||
|
//
|
||||||
|
// A Basic Example
|
||||||
|
//
|
||||||
|
// As a starting point, a very basic usage example:
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "log"
|
||||||
|
// "net/http"
|
||||||
|
//
|
||||||
|
// "github.com/prometheus/client_golang/prometheus"
|
||||||
|
// "github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// var (
|
||||||
|
// cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
// Name: "cpu_temperature_celsius",
|
||||||
|
// Help: "Current temperature of the CPU.",
|
||||||
|
// })
|
||||||
|
// hdFailures = prometheus.NewCounterVec(
|
||||||
|
// prometheus.CounterOpts{
|
||||||
|
// Name: "hd_errors_total",
|
||||||
|
// Help: "Number of hard-disk errors.",
|
||||||
|
// },
|
||||||
|
// []string{"device"},
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func init() {
|
||||||
|
// // Metrics have to be registered to be exposed:
|
||||||
|
// prometheus.MustRegister(cpuTemp)
|
||||||
|
// prometheus.MustRegister(hdFailures)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// cpuTemp.Set(65.3)
|
||||||
|
// hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
|
||||||
|
//
|
||||||
|
// // The Handler function provides a default handler to expose metrics
|
||||||
|
// // via an HTTP server. "/metrics" is the usual endpoint for that.
|
||||||
|
// http.Handle("/metrics", promhttp.Handler())
|
||||||
|
// log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This is a complete program that exports two metrics, a Gauge and a Counter,
|
||||||
|
// the latter with a label attached to turn it into a (one-dimensional) vector.
|
||||||
|
//
|
||||||
|
// Metrics
|
||||||
|
//
|
||||||
|
// The number of exported identifiers in this package might appear a bit
|
||||||
|
// overwhelming. However, in addition to the basic plumbing shown in the example
|
||||||
|
// above, you only need to understand the different metric types and their
|
||||||
|
// vector versions for basic usage.
|
||||||
|
//
|
||||||
|
// Above, you have already touched the Counter and the Gauge. There are two more
|
||||||
|
// advanced metric types: the Summary and Histogram. A more thorough description
|
||||||
|
// of those four metric types can be found in the Prometheus docs:
|
||||||
|
// https://prometheus.io/docs/concepts/metric_types/
|
||||||
|
//
|
||||||
|
// A fifth "type" of metric is Untyped. It behaves like a Gauge, but signals the
|
||||||
|
// Prometheus server not to assume anything about its type.
|
||||||
|
//
|
||||||
|
// In addition to the fundamental metric types Gauge, Counter, Summary,
|
||||||
|
// Histogram, and Untyped, a very important part of the Prometheus data model is
|
||||||
|
// the partitioning of samples along dimensions called labels, which results in
|
||||||
|
// metric vectors. The fundamental types are GaugeVec, CounterVec, SummaryVec,
|
||||||
|
// HistogramVec, and UntypedVec.
|
||||||
|
//
|
||||||
|
// While only the fundamental metric types implement the Metric interface, both
|
||||||
|
// the metrics and their vector versions implement the Collector interface. A
|
||||||
|
// Collector manages the collection of a number of Metrics, but for convenience,
|
||||||
|
// a Metric can also “collect itself”. Note that Gauge, Counter, Summary,
|
||||||
|
// Histogram, and Untyped are interfaces themselves while GaugeVec, CounterVec,
|
||||||
|
// SummaryVec, HistogramVec, and UntypedVec are not.
|
||||||
|
//
|
||||||
|
// To create instances of Metrics and their vector versions, you need a suitable
|
||||||
|
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, HistogramOpts, or
|
||||||
|
// UntypedOpts.
|
||||||
|
//
|
||||||
|
// Custom Collectors and constant Metrics
|
||||||
|
//
|
||||||
|
// While you could create your own implementations of Metric, most likely you
|
||||||
|
// will only ever implement the Collector interface on your own. At a first
|
||||||
|
// glance, a custom Collector seems handy to bundle Metrics for common
|
||||||
|
// registration (with the prime example of the different metric vectors above,
|
||||||
|
// which bundle all the metrics of the same name but with different labels).
|
||||||
|
//
|
||||||
|
// There is a more involved use case, too: If you already have metrics
|
||||||
|
// available, created outside of the Prometheus context, you don't need the
|
||||||
|
// interface of the various Metric types. You essentially want to mirror the
|
||||||
|
// existing numbers into Prometheus Metrics during collection. An own
|
||||||
|
// implementation of the Collector interface is perfect for that. You can create
|
||||||
|
// Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and
|
||||||
|
// NewConstSummary (and their respective Must… versions). That will happen in
|
||||||
|
// the Collect method. The Describe method has to return separate Desc
|
||||||
|
// instances, representative of the “throw-away” metrics to be created later.
|
||||||
|
// NewDesc comes in handy to create those Desc instances.
|
||||||
|
//
|
||||||
|
// The Collector example illustrates the use case. You can also look at the
|
||||||
|
// source code of the processCollector (mirroring process metrics), the
|
||||||
|
// goCollector (mirroring Go metrics), or the expvarCollector (mirroring expvar
|
||||||
|
// metrics) as examples that are used in this package itself.
|
||||||
|
//
|
||||||
|
// If you just need to call a function to get a single float value to collect as
|
||||||
|
// a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting
|
||||||
|
// shortcuts.
|
||||||
|
//
|
||||||
|
// Advanced Uses of the Registry
|
||||||
|
//
|
||||||
|
// While MustRegister is the by far most common way of registering a Collector,
|
||||||
|
// sometimes you might want to handle the errors the registration might cause.
|
||||||
|
// As suggested by the name, MustRegister panics if an error occurs. With the
|
||||||
|
// Register function, the error is returned and can be handled.
|
||||||
|
//
|
||||||
|
// An error is returned if the registered Collector is incompatible or
|
||||||
|
// inconsistent with already registered metrics. The registry aims for
|
||||||
|
// consistency of the collected metrics according to the Prometheus data model.
|
||||||
|
// Inconsistencies are ideally detected at registration time, not at collect
|
||||||
|
// time. The former will usually be detected at start-up time of a program,
|
||||||
|
// while the latter will only happen at scrape time, possibly not even on the
|
||||||
|
// first scrape if the inconsistency only becomes relevant later. That is the
|
||||||
|
// main reason why a Collector and a Metric have to describe themselves to the
|
||||||
|
// registry.
|
||||||
|
//
|
||||||
|
// So far, everything we did operated on the so-called default registry, as it
|
||||||
|
// can be found in the global DefaultRegisterer variable. With NewRegistry, you
|
||||||
|
// can create a custom registry, or you can even implement the Registerer or
|
||||||
|
// Gatherer interfaces yourself. The methods Register and Unregister work in the
|
||||||
|
// same way on a custom registry as the global functions Register and Unregister
|
||||||
|
// on the default registry.
|
||||||
|
//
|
||||||
|
// There are a number of uses for custom registries: You can use registries with
|
||||||
|
// special properties, see NewPedanticRegistry. You can avoid global state, as
|
||||||
|
// it is imposed by the DefaultRegisterer. You can use multiple registries at
|
||||||
|
// the same time to expose different metrics in different ways. You can use
|
||||||
|
// separate registries for testing purposes.
|
||||||
|
//
|
||||||
|
// Also note that the DefaultRegisterer comes registered with a Collector for Go
|
||||||
|
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
|
||||||
|
// NewProcessCollector). With a custom registry, you are in control and decide
|
||||||
|
// yourself about the Collectors to register.
|
||||||
|
//
|
||||||
|
// HTTP Exposition
|
||||||
|
//
|
||||||
|
// The Registry implements the Gatherer interface. The caller of the Gather
|
||||||
|
// method can then expose the gathered metrics in some way. Usually, the metrics
|
||||||
|
// are served via HTTP on the /metrics endpoint. That's happening in the example
|
||||||
|
// above. The tools to expose metrics via HTTP are in the promhttp sub-package.
|
||||||
|
// (The top-level functions in the prometheus package are deprecated.)
|
||||||
|
//
|
||||||
|
// Pushing to the Pushgateway
|
||||||
|
//
|
||||||
|
// Function for pushing to the Pushgateway can be found in the push sub-package.
|
||||||
|
//
|
||||||
|
// Graphite Bridge
|
||||||
|
//
|
||||||
|
// Functions and examples to push metrics from a Gatherer to Graphite can be
|
||||||
|
// found in the graphite sub-package.
|
||||||
|
//
|
||||||
|
// Other Means of Exposition
|
||||||
|
//
|
||||||
|
// More ways of exposing metrics can easily be added by following the approaches
|
||||||
|
// of the existing implementations.
|
||||||
|
package prometheus
|
119
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
Normal file
119
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"expvar"
|
||||||
|
)
|
||||||
|
|
||||||
|
type expvarCollector struct {
|
||||||
|
exports map[string]*Desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExpvarCollector returns a newly allocated expvar Collector that still has
|
||||||
|
// to be registered with a Prometheus registry.
|
||||||
|
//
|
||||||
|
// An expvar Collector collects metrics from the expvar interface. It provides a
|
||||||
|
// quick way to expose numeric values that are already exported via expvar as
|
||||||
|
// Prometheus metrics. Note that the data models of expvar and Prometheus are
|
||||||
|
// fundamentally different, and that the expvar Collector is inherently slower
|
||||||
|
// than native Prometheus metrics. Thus, the expvar Collector is probably great
|
||||||
|
// for experiments and prototying, but you should seriously consider a more
|
||||||
|
// direct implementation of Prometheus metrics for monitoring production
|
||||||
|
// systems.
|
||||||
|
//
|
||||||
|
// The exports map has the following meaning:
|
||||||
|
//
|
||||||
|
// The keys in the map correspond to expvar keys, i.e. for every expvar key you
|
||||||
|
// want to export as Prometheus metric, you need an entry in the exports
|
||||||
|
// map. The descriptor mapped to each key describes how to export the expvar
|
||||||
|
// value. It defines the name and the help string of the Prometheus metric
|
||||||
|
// proxying the expvar value. The type will always be Untyped.
|
||||||
|
//
|
||||||
|
// For descriptors without variable labels, the expvar value must be a number or
|
||||||
|
// a bool. The number is then directly exported as the Prometheus sample
|
||||||
|
// value. (For a bool, 'false' translates to 0 and 'true' to 1). Expvar values
|
||||||
|
// that are not numbers or bools are silently ignored.
|
||||||
|
//
|
||||||
|
// If the descriptor has one variable label, the expvar value must be an expvar
|
||||||
|
// map. The keys in the expvar map become the various values of the one
|
||||||
|
// Prometheus label. The values in the expvar map must be numbers or bools again
|
||||||
|
// as above.
|
||||||
|
//
|
||||||
|
// For descriptors with more than one variable label, the expvar must be a
|
||||||
|
// nested expvar map, i.e. where the values of the topmost map are maps again
|
||||||
|
// etc. until a depth is reached that corresponds to the number of labels. The
|
||||||
|
// leaves of that structure must be numbers or bools as above to serve as the
|
||||||
|
// sample values.
|
||||||
|
//
|
||||||
|
// Anything that does not fit into the scheme above is silently ignored.
|
||||||
|
func NewExpvarCollector(exports map[string]*Desc) Collector {
|
||||||
|
return &expvarCollector{
|
||||||
|
exports: exports,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe implements Collector.
|
||||||
|
func (e *expvarCollector) Describe(ch chan<- *Desc) {
|
||||||
|
for _, desc := range e.exports {
|
||||||
|
ch <- desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect implements Collector.
|
||||||
|
func (e *expvarCollector) Collect(ch chan<- Metric) {
|
||||||
|
for name, desc := range e.exports {
|
||||||
|
var m Metric
|
||||||
|
expVar := expvar.Get(name)
|
||||||
|
if expVar == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var v interface{}
|
||||||
|
labels := make([]string, len(desc.variableLabels))
|
||||||
|
if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
|
||||||
|
ch <- NewInvalidMetric(desc, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var processValue func(v interface{}, i int)
|
||||||
|
processValue = func(v interface{}, i int) {
|
||||||
|
if i >= len(labels) {
|
||||||
|
copiedLabels := append(make([]string, 0, len(labels)), labels...)
|
||||||
|
switch v := v.(type) {
|
||||||
|
case float64:
|
||||||
|
m = MustNewConstMetric(desc, UntypedValue, v, copiedLabels...)
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
m = MustNewConstMetric(desc, UntypedValue, 1, copiedLabels...)
|
||||||
|
} else {
|
||||||
|
m = MustNewConstMetric(desc, UntypedValue, 0, copiedLabels...)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch <- m
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vm, ok := v.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for lv, val := range vm {
|
||||||
|
labels[i] = lv
|
||||||
|
processValue(val, i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processValue(v, 0)
|
||||||
|
}
|
||||||
|
}
|
29
vendor/github.com/prometheus/client_golang/prometheus/fnv.go
generated
vendored
Normal file
29
vendor/github.com/prometheus/client_golang/prometheus/fnv.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
// Inline and byte-free variant of hash/fnv's fnv64a.
|
||||||
|
|
||||||
|
const (
|
||||||
|
offset64 = 14695981039346656037
|
||||||
|
prime64 = 1099511628211
|
||||||
|
)
|
||||||
|
|
||||||
|
// hashNew initializies a new fnv64a hash value.
|
||||||
|
func hashNew() uint64 {
|
||||||
|
return offset64
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashAdd adds a string to a fnv64a hash value, returning the updated hash.
|
||||||
|
func hashAdd(h uint64, s string) uint64 {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
h ^= uint64(s[i])
|
||||||
|
h *= prime64
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash.
|
||||||
|
func hashAddByte(h uint64, b byte) uint64 {
|
||||||
|
h ^= uint64(b)
|
||||||
|
h *= prime64
|
||||||
|
return h
|
||||||
|
}
|
286
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
Normal file
286
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Gauge is a Metric that represents a single numerical value that can
|
||||||
|
// arbitrarily go up and down.
|
||||||
|
//
|
||||||
|
// A Gauge is typically used for measured values like temperatures or current
|
||||||
|
// memory usage, but also "counts" that can go up and down, like the number of
|
||||||
|
// running goroutines.
|
||||||
|
//
|
||||||
|
// To create Gauge instances, use NewGauge.
|
||||||
|
type Gauge interface {
|
||||||
|
Metric
|
||||||
|
Collector
|
||||||
|
|
||||||
|
// Set sets the Gauge to an arbitrary value.
|
||||||
|
Set(float64)
|
||||||
|
// Inc increments the Gauge by 1. Use Add to increment it by arbitrary
|
||||||
|
// values.
|
||||||
|
Inc()
|
||||||
|
// Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary
|
||||||
|
// values.
|
||||||
|
Dec()
|
||||||
|
// Add adds the given value to the Gauge. (The value can be negative,
|
||||||
|
// resulting in a decrease of the Gauge.)
|
||||||
|
Add(float64)
|
||||||
|
// Sub subtracts the given value from the Gauge. (The value can be
|
||||||
|
// negative, resulting in an increase of the Gauge.)
|
||||||
|
Sub(float64)
|
||||||
|
|
||||||
|
// SetToCurrentTime sets the Gauge to the current Unix time in seconds.
|
||||||
|
SetToCurrentTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaugeOpts is an alias for Opts. See there for doc comments.
|
||||||
|
type GaugeOpts Opts
|
||||||
|
|
||||||
|
// NewGauge creates a new Gauge based on the provided GaugeOpts.
|
||||||
|
//
|
||||||
|
// The returned implementation is optimized for a fast Set method. If you have a
|
||||||
|
// choice for managing the value of a Gauge via Set vs. Inc/Dec/Add/Sub, pick
|
||||||
|
// the former. For example, the Inc method of the returned Gauge is slower than
|
||||||
|
// the Inc method of a Counter returned by NewCounter. This matches the typical
|
||||||
|
// scenarios for Gauges and Counters, where the former tends to be Set-heavy and
|
||||||
|
// the latter Inc-heavy.
|
||||||
|
func NewGauge(opts GaugeOpts) Gauge {
|
||||||
|
desc := NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
nil,
|
||||||
|
opts.ConstLabels,
|
||||||
|
)
|
||||||
|
result := &gauge{desc: desc, labelPairs: desc.constLabelPairs}
|
||||||
|
result.init(result) // Init self-collection.
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type gauge struct {
|
||||||
|
// valBits contains the bits of the represented float64 value. It has
|
||||||
|
// to go first in the struct to guarantee alignment for atomic
|
||||||
|
// operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
valBits uint64
|
||||||
|
|
||||||
|
selfCollector
|
||||||
|
|
||||||
|
desc *Desc
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Desc() *Desc {
|
||||||
|
return g.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Set(val float64) {
|
||||||
|
atomic.StoreUint64(&g.valBits, math.Float64bits(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) SetToCurrentTime() {
|
||||||
|
g.Set(float64(time.Now().UnixNano()) / 1e9)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Inc() {
|
||||||
|
g.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Dec() {
|
||||||
|
g.Add(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Add(val float64) {
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&g.valBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
|
||||||
|
if atomic.CompareAndSwapUint64(&g.valBits, oldBits, newBits) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Sub(val float64) {
|
||||||
|
g.Add(val * -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Write(out *dto.Metric) error {
|
||||||
|
val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
|
||||||
|
return populateMetric(GaugeValue, val, g.labelPairs, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
|
||||||
|
// Desc, but have different values for their variable labels. This is used if
|
||||||
|
// you want to count the same thing partitioned by various dimensions
|
||||||
|
// (e.g. number of operations queued, partitioned by user and operation
|
||||||
|
// type). Create instances with NewGaugeVec.
|
||||||
|
type GaugeVec struct {
|
||||||
|
*metricVec
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
|
||||||
|
// partitioned by the given label names.
|
||||||
|
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||||
|
desc := NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
labelNames,
|
||||||
|
opts.ConstLabels,
|
||||||
|
)
|
||||||
|
return &GaugeVec{
|
||||||
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
|
if len(lvs) != len(desc.variableLabels) {
|
||||||
|
panic(errInconsistentCardinality)
|
||||||
|
}
|
||||||
|
result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
|
||||||
|
result.init(result) // Init self-collection.
|
||||||
|
return result
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWithLabelValues returns the Gauge for the given slice of label
|
||||||
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
|
// label values is accessed for the first time, a new Gauge is created.
|
||||||
|
//
|
||||||
|
// It is possible to call this method without using the returned Gauge to only
|
||||||
|
// create the new Gauge but leave it at its starting value 0. See also the
|
||||||
|
// SummaryVec example.
|
||||||
|
//
|
||||||
|
// Keeping the Gauge for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Gauge from the GaugeVec. In that case, the
|
||||||
|
// Gauge will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Gauge with the same label values is created later. See also the CounterVec
|
||||||
|
// example.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Gauge), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWith returns the Gauge for the given Labels map (the label names
|
||||||
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
|
// accessed for the first time, a new Gauge is created. Implications of
|
||||||
|
// creating a Gauge without using it and keeping the Gauge for later use are
|
||||||
|
// the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Gauge), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
|
// error allows shortcuts like
|
||||||
|
// myVec.WithLabelValues("404", "GET").Add(42)
|
||||||
|
func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge {
|
||||||
|
g, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||||
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
|
||||||
|
func (v *GaugeVec) With(labels Labels) Gauge {
|
||||||
|
g, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the GaugeVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &GaugeVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaugeFunc is a Gauge whose value is determined at collect time by calling a
|
||||||
|
// provided function.
|
||||||
|
//
|
||||||
|
// To create GaugeFunc instances, use NewGaugeFunc.
|
||||||
|
type GaugeFunc interface {
|
||||||
|
Metric
|
||||||
|
Collector
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGaugeFunc creates a new GaugeFunc based on the provided GaugeOpts. The
|
||||||
|
// value reported is determined by calling the given function from within the
|
||||||
|
// Write method. Take into account that metric collection may happen
|
||||||
|
// concurrently. If that results in concurrent calls to Write, like in the case
|
||||||
|
// where a GaugeFunc is directly registered with Prometheus, the provided
|
||||||
|
// function must be concurrency-safe.
|
||||||
|
func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc {
|
||||||
|
return newValueFunc(NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
nil,
|
||||||
|
opts.ConstLabels,
|
||||||
|
), GaugeValue, function)
|
||||||
|
}
|
284
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
Normal file
284
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type goCollector struct {
|
||||||
|
goroutinesDesc *Desc
|
||||||
|
threadsDesc *Desc
|
||||||
|
gcDesc *Desc
|
||||||
|
goInfoDesc *Desc
|
||||||
|
|
||||||
|
// metrics to describe and collect
|
||||||
|
metrics memStatsMetrics
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGoCollector returns a collector which exports metrics about the current
|
||||||
|
// go process.
|
||||||
|
func NewGoCollector() Collector {
|
||||||
|
return &goCollector{
|
||||||
|
goroutinesDesc: NewDesc(
|
||||||
|
"go_goroutines",
|
||||||
|
"Number of goroutines that currently exist.",
|
||||||
|
nil, nil),
|
||||||
|
threadsDesc: NewDesc(
|
||||||
|
"go_threads",
|
||||||
|
"Number of OS threads created.",
|
||||||
|
nil, nil),
|
||||||
|
gcDesc: NewDesc(
|
||||||
|
"go_gc_duration_seconds",
|
||||||
|
"A summary of the GC invocation durations.",
|
||||||
|
nil, nil),
|
||||||
|
goInfoDesc: NewDesc(
|
||||||
|
"go_info",
|
||||||
|
"Information about the Go environment.",
|
||||||
|
nil, Labels{"version": runtime.Version()}),
|
||||||
|
metrics: memStatsMetrics{
|
||||||
|
{
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("alloc_bytes"),
|
||||||
|
"Number of bytes allocated and still in use.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("alloc_bytes_total"),
|
||||||
|
"Total number of bytes allocated, even if freed.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
|
||||||
|
valType: CounterValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("sys_bytes"),
|
||||||
|
"Number of bytes obtained from system.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("lookups_total"),
|
||||||
|
"Total number of pointer lookups.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
|
||||||
|
valType: CounterValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("mallocs_total"),
|
||||||
|
"Total number of mallocs.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
|
||||||
|
valType: CounterValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("frees_total"),
|
||||||
|
"Total number of frees.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
|
||||||
|
valType: CounterValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("heap_alloc_bytes"),
|
||||||
|
"Number of heap bytes allocated and still in use.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("heap_sys_bytes"),
|
||||||
|
"Number of heap bytes obtained from system.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("heap_idle_bytes"),
|
||||||
|
"Number of heap bytes waiting to be used.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("heap_inuse_bytes"),
|
||||||
|
"Number of heap bytes that are in use.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("heap_released_bytes"),
|
||||||
|
"Number of heap bytes released to OS.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("heap_objects"),
|
||||||
|
"Number of allocated objects.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("stack_inuse_bytes"),
|
||||||
|
"Number of bytes in use by the stack allocator.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("stack_sys_bytes"),
|
||||||
|
"Number of bytes obtained from system for stack allocator.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("mspan_inuse_bytes"),
|
||||||
|
"Number of bytes in use by mspan structures.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("mspan_sys_bytes"),
|
||||||
|
"Number of bytes used for mspan structures obtained from system.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("mcache_inuse_bytes"),
|
||||||
|
"Number of bytes in use by mcache structures.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("mcache_sys_bytes"),
|
||||||
|
"Number of bytes used for mcache structures obtained from system.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("buck_hash_sys_bytes"),
|
||||||
|
"Number of bytes used by the profiling bucket hash table.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("gc_sys_bytes"),
|
||||||
|
"Number of bytes used for garbage collection system metadata.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("other_sys_bytes"),
|
||||||
|
"Number of bytes used for other system allocations.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("next_gc_bytes"),
|
||||||
|
"Number of heap bytes when next garbage collection will take place.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("last_gc_time_seconds"),
|
||||||
|
"Number of seconds since 1970 of last garbage collection.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return float64(ms.LastGC) / 1e9 },
|
||||||
|
valType: GaugeValue,
|
||||||
|
}, {
|
||||||
|
desc: NewDesc(
|
||||||
|
memstatNamespace("gc_cpu_fraction"),
|
||||||
|
"The fraction of this program's available CPU time used by the GC since the program started.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction },
|
||||||
|
valType: GaugeValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func memstatNamespace(s string) string {
|
||||||
|
return fmt.Sprintf("go_memstats_%s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe returns all descriptions of the collector.
|
||||||
|
func (c *goCollector) Describe(ch chan<- *Desc) {
|
||||||
|
ch <- c.goroutinesDesc
|
||||||
|
ch <- c.threadsDesc
|
||||||
|
ch <- c.gcDesc
|
||||||
|
ch <- c.goInfoDesc
|
||||||
|
for _, i := range c.metrics {
|
||||||
|
ch <- i.desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect returns the current state of all metrics of the collector.
|
||||||
|
func (c *goCollector) Collect(ch chan<- Metric) {
|
||||||
|
ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
|
||||||
|
n, _ := runtime.ThreadCreateProfile(nil)
|
||||||
|
ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
|
||||||
|
|
||||||
|
var stats debug.GCStats
|
||||||
|
stats.PauseQuantiles = make([]time.Duration, 5)
|
||||||
|
debug.ReadGCStats(&stats)
|
||||||
|
|
||||||
|
quantiles := make(map[float64]float64)
|
||||||
|
for idx, pq := range stats.PauseQuantiles[1:] {
|
||||||
|
quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
|
||||||
|
}
|
||||||
|
quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
|
||||||
|
ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles)
|
||||||
|
|
||||||
|
ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1)
|
||||||
|
|
||||||
|
ms := &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(ms)
|
||||||
|
for _, i := range c.metrics {
|
||||||
|
ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// memStatsMetrics provide description, value, and value type for memstat metrics.
|
||||||
|
type memStatsMetrics []struct {
|
||||||
|
desc *Desc
|
||||||
|
eval func(*runtime.MemStats) float64
|
||||||
|
valType ValueType
|
||||||
|
}
|
505
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
Normal file
505
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
Normal file
|
@ -0,0 +1,505 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Histogram counts individual observations from an event or sample stream in
|
||||||
|
// configurable buckets. Similar to a summary, it also provides a sum of
|
||||||
|
// observations and an observation count.
|
||||||
|
//
|
||||||
|
// On the Prometheus server, quantiles can be calculated from a Histogram using
|
||||||
|
// the histogram_quantile function in the query language.
|
||||||
|
//
|
||||||
|
// Note that Histograms, in contrast to Summaries, can be aggregated with the
|
||||||
|
// Prometheus query language (see the documentation for detailed
|
||||||
|
// procedures). However, Histograms require the user to pre-define suitable
|
||||||
|
// buckets, and they are in general less accurate. The Observe method of a
|
||||||
|
// Histogram has a very low performance overhead in comparison with the Observe
|
||||||
|
// method of a Summary.
|
||||||
|
//
|
||||||
|
// To create Histogram instances, use NewHistogram.
|
||||||
|
type Histogram interface {
|
||||||
|
Metric
|
||||||
|
Collector
|
||||||
|
|
||||||
|
// Observe adds a single observation to the histogram.
|
||||||
|
Observe(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bucketLabel is used for the label that defines the upper bound of a
|
||||||
|
// bucket of a histogram ("le" -> "less or equal").
|
||||||
|
const bucketLabel = "le"
|
||||||
|
|
||||||
|
// DefBuckets are the default Histogram buckets. The default buckets are
|
||||||
|
// tailored to broadly measure the response time (in seconds) of a network
|
||||||
|
// service. Most likely, however, you will be required to define buckets
|
||||||
|
// customized to your use case.
|
||||||
|
var (
|
||||||
|
DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
|
||||||
|
|
||||||
|
errBucketLabelNotAllowed = fmt.Errorf(
|
||||||
|
"%q is not allowed as label name in histograms", bucketLabel,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinearBuckets creates 'count' buckets, each 'width' wide, where the lowest
|
||||||
|
// bucket has an upper bound of 'start'. The final +Inf bucket is not counted
|
||||||
|
// and not included in the returned slice. The returned slice is meant to be
|
||||||
|
// used for the Buckets field of HistogramOpts.
|
||||||
|
//
|
||||||
|
// The function panics if 'count' is zero or negative.
|
||||||
|
func LinearBuckets(start, width float64, count int) []float64 {
|
||||||
|
if count < 1 {
|
||||||
|
panic("LinearBuckets needs a positive count")
|
||||||
|
}
|
||||||
|
buckets := make([]float64, count)
|
||||||
|
for i := range buckets {
|
||||||
|
buckets[i] = start
|
||||||
|
start += width
|
||||||
|
}
|
||||||
|
return buckets
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExponentialBuckets creates 'count' buckets, where the lowest bucket has an
|
||||||
|
// upper bound of 'start' and each following bucket's upper bound is 'factor'
|
||||||
|
// times the previous bucket's upper bound. The final +Inf bucket is not counted
|
||||||
|
// and not included in the returned slice. The returned slice is meant to be
|
||||||
|
// used for the Buckets field of HistogramOpts.
|
||||||
|
//
|
||||||
|
// The function panics if 'count' is 0 or negative, if 'start' is 0 or negative,
|
||||||
|
// or if 'factor' is less than or equal 1.
|
||||||
|
func ExponentialBuckets(start, factor float64, count int) []float64 {
|
||||||
|
if count < 1 {
|
||||||
|
panic("ExponentialBuckets needs a positive count")
|
||||||
|
}
|
||||||
|
if start <= 0 {
|
||||||
|
panic("ExponentialBuckets needs a positive start value")
|
||||||
|
}
|
||||||
|
if factor <= 1 {
|
||||||
|
panic("ExponentialBuckets needs a factor greater than 1")
|
||||||
|
}
|
||||||
|
buckets := make([]float64, count)
|
||||||
|
for i := range buckets {
|
||||||
|
buckets[i] = start
|
||||||
|
start *= factor
|
||||||
|
}
|
||||||
|
return buckets
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistogramOpts bundles the options for creating a Histogram metric. It is
|
||||||
|
// mandatory to set Name and Help to a non-empty string. All other fields are
|
||||||
|
// optional and can safely be left at their zero value.
|
||||||
|
type HistogramOpts struct {
|
||||||
|
// Namespace, Subsystem, and Name are components of the fully-qualified
|
||||||
|
// name of the Histogram (created by joining these components with
|
||||||
|
// "_"). Only Name is mandatory, the others merely help structuring the
|
||||||
|
// name. Note that the fully-qualified name of the Histogram must be a
|
||||||
|
// valid Prometheus metric name.
|
||||||
|
Namespace string
|
||||||
|
Subsystem string
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Help provides information about this Histogram. Mandatory!
|
||||||
|
//
|
||||||
|
// Metrics with the same fully-qualified name must have the same Help
|
||||||
|
// string.
|
||||||
|
Help string
|
||||||
|
|
||||||
|
// ConstLabels are used to attach fixed labels to this metric. Metrics
|
||||||
|
// with the same fully-qualified name must have the same label names in
|
||||||
|
// their ConstLabels.
|
||||||
|
//
|
||||||
|
// ConstLabels are only used rarely. In particular, do not use them to
|
||||||
|
// attach the same labels to all your metrics. Those use cases are
|
||||||
|
// better covered by target labels set by the scraping Prometheus
|
||||||
|
// server, or by one specific metric (e.g. a build_info or a
|
||||||
|
// machine_role metric). See also
|
||||||
|
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||||
|
ConstLabels Labels
|
||||||
|
|
||||||
|
// Buckets defines the buckets into which observations are counted. Each
|
||||||
|
// element in the slice is the upper inclusive bound of a bucket. The
|
||||||
|
// values must be sorted in strictly increasing order. There is no need
|
||||||
|
// to add a highest bucket with +Inf bound, it will be added
|
||||||
|
// implicitly. The default value is DefBuckets.
|
||||||
|
Buckets []float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHistogram creates a new Histogram based on the provided HistogramOpts. It
|
||||||
|
// panics if the buckets in HistogramOpts are not in strictly increasing order.
|
||||||
|
func NewHistogram(opts HistogramOpts) Histogram {
|
||||||
|
return newHistogram(
|
||||||
|
NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
nil,
|
||||||
|
opts.ConstLabels,
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
|
||||||
|
if len(desc.variableLabels) != len(labelValues) {
|
||||||
|
panic(errInconsistentCardinality)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range desc.variableLabels {
|
||||||
|
if n == bucketLabel {
|
||||||
|
panic(errBucketLabelNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, lp := range desc.constLabelPairs {
|
||||||
|
if lp.GetName() == bucketLabel {
|
||||||
|
panic(errBucketLabelNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Buckets) == 0 {
|
||||||
|
opts.Buckets = DefBuckets
|
||||||
|
}
|
||||||
|
|
||||||
|
h := &histogram{
|
||||||
|
desc: desc,
|
||||||
|
upperBounds: opts.Buckets,
|
||||||
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
|
}
|
||||||
|
for i, upperBound := range h.upperBounds {
|
||||||
|
if i < len(h.upperBounds)-1 {
|
||||||
|
if upperBound >= h.upperBounds[i+1] {
|
||||||
|
panic(fmt.Errorf(
|
||||||
|
"histogram buckets must be in increasing order: %f >= %f",
|
||||||
|
upperBound, h.upperBounds[i+1],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if math.IsInf(upperBound, +1) {
|
||||||
|
// The +Inf bucket is implicit. Remove it here.
|
||||||
|
h.upperBounds = h.upperBounds[:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finally we know the final length of h.upperBounds and can make counts.
|
||||||
|
h.counts = make([]uint64, len(h.upperBounds))
|
||||||
|
|
||||||
|
h.init(h) // Init self-collection.
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
type histogram struct {
|
||||||
|
// sumBits contains the bits of the float64 representing the sum of all
|
||||||
|
// observations. sumBits and count have to go first in the struct to
|
||||||
|
// guarantee alignment for atomic operations.
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
sumBits uint64
|
||||||
|
count uint64
|
||||||
|
|
||||||
|
selfCollector
|
||||||
|
// Note that there is no mutex required.
|
||||||
|
|
||||||
|
desc *Desc
|
||||||
|
|
||||||
|
upperBounds []float64
|
||||||
|
counts []uint64
|
||||||
|
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *histogram) Desc() *Desc {
|
||||||
|
return h.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *histogram) Observe(v float64) {
|
||||||
|
// TODO(beorn7): For small numbers of buckets (<30), a linear search is
|
||||||
|
// slightly faster than the binary search. If we really care, we could
|
||||||
|
// switch from one search strategy to the other depending on the number
|
||||||
|
// of buckets.
|
||||||
|
//
|
||||||
|
// Microbenchmarks (BenchmarkHistogramNoLabels):
|
||||||
|
// 11 buckets: 38.3 ns/op linear - binary 48.7 ns/op
|
||||||
|
// 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
|
||||||
|
// 300 buckets: 154 ns/op linear - binary 61.6 ns/op
|
||||||
|
i := sort.SearchFloat64s(h.upperBounds, v)
|
||||||
|
if i < len(h.counts) {
|
||||||
|
atomic.AddUint64(&h.counts[i], 1)
|
||||||
|
}
|
||||||
|
atomic.AddUint64(&h.count, 1)
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&h.sumBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
|
||||||
|
if atomic.CompareAndSwapUint64(&h.sumBits, oldBits, newBits) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *histogram) Write(out *dto.Metric) error {
|
||||||
|
his := &dto.Histogram{}
|
||||||
|
buckets := make([]*dto.Bucket, len(h.upperBounds))
|
||||||
|
|
||||||
|
his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&h.sumBits)))
|
||||||
|
his.SampleCount = proto.Uint64(atomic.LoadUint64(&h.count))
|
||||||
|
var count uint64
|
||||||
|
for i, upperBound := range h.upperBounds {
|
||||||
|
count += atomic.LoadUint64(&h.counts[i])
|
||||||
|
buckets[i] = &dto.Bucket{
|
||||||
|
CumulativeCount: proto.Uint64(count),
|
||||||
|
UpperBound: proto.Float64(upperBound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
his.Bucket = buckets
|
||||||
|
out.Histogram = his
|
||||||
|
out.Label = h.labelPairs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistogramVec is a Collector that bundles a set of Histograms that all share the
|
||||||
|
// same Desc, but have different values for their variable labels. This is used
|
||||||
|
// if you want to count the same thing partitioned by various dimensions
|
||||||
|
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||||
|
// instances with NewHistogramVec.
|
||||||
|
type HistogramVec struct {
|
||||||
|
*metricVec
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
|
||||||
|
// partitioned by the given label names.
|
||||||
|
func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||||
|
desc := NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
labelNames,
|
||||||
|
opts.ConstLabels,
|
||||||
|
)
|
||||||
|
return &HistogramVec{
|
||||||
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
|
return newHistogram(desc, opts, lvs...)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWithLabelValues returns the Histogram for the given slice of label
|
||||||
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
|
// label values is accessed for the first time, a new Histogram is created.
|
||||||
|
//
|
||||||
|
// It is possible to call this method without using the returned Histogram to only
|
||||||
|
// create the new Histogram but leave it at its starting value, a Histogram without
|
||||||
|
// any observations.
|
||||||
|
//
|
||||||
|
// Keeping the Histogram for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Histogram from the HistogramVec. In that case, the
|
||||||
|
// Histogram will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Histogram with the same label values is created later. See also the CounterVec
|
||||||
|
// example.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the GaugeVec example.
|
||||||
|
func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Observer), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWith returns the Histogram for the given Labels map (the label names
|
||||||
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
|
// accessed for the first time, a new Histogram is created. Implications of
|
||||||
|
// creating a Histogram without using it and keeping the Histogram for later use
|
||||||
|
// are the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Observer), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
|
// error allows shortcuts like
|
||||||
|
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
||||||
|
func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
|
||||||
|
h, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// With works as GetMetricWith but panics where GetMetricWithLabels would have
|
||||||
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
||||||
|
func (v *HistogramVec) With(labels Labels) Observer {
|
||||||
|
h, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the HistogramVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &HistogramVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *HistogramVec) MustCurryWith(labels Labels) ObserverVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
|
}
|
||||||
|
|
||||||
|
type constHistogram struct {
|
||||||
|
desc *Desc
|
||||||
|
count uint64
|
||||||
|
sum float64
|
||||||
|
buckets map[float64]uint64
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *constHistogram) Desc() *Desc {
|
||||||
|
return h.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *constHistogram) Write(out *dto.Metric) error {
|
||||||
|
his := &dto.Histogram{}
|
||||||
|
buckets := make([]*dto.Bucket, 0, len(h.buckets))
|
||||||
|
|
||||||
|
his.SampleCount = proto.Uint64(h.count)
|
||||||
|
his.SampleSum = proto.Float64(h.sum)
|
||||||
|
|
||||||
|
for upperBound, count := range h.buckets {
|
||||||
|
buckets = append(buckets, &dto.Bucket{
|
||||||
|
CumulativeCount: proto.Uint64(count),
|
||||||
|
UpperBound: proto.Float64(upperBound),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buckets) > 0 {
|
||||||
|
sort.Sort(buckSort(buckets))
|
||||||
|
}
|
||||||
|
his.Bucket = buckets
|
||||||
|
|
||||||
|
out.Histogram = his
|
||||||
|
out.Label = h.labelPairs
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConstHistogram returns a metric representing a Prometheus histogram with
|
||||||
|
// fixed values for the count, sum, and bucket counts. As those parameters
|
||||||
|
// cannot be changed, the returned value does not implement the Histogram
|
||||||
|
// interface (but only the Metric interface). Users of this package will not
|
||||||
|
// have much use for it in regular operations. However, when implementing custom
|
||||||
|
// Collectors, it is useful as a throw-away metric that is generated on the fly
|
||||||
|
// to send it to Prometheus in the Collect method.
|
||||||
|
//
|
||||||
|
// buckets is a map of upper bounds to cumulative counts, excluding the +Inf
|
||||||
|
// bucket.
|
||||||
|
//
|
||||||
|
// NewConstHistogram returns an error if the length of labelValues is not
|
||||||
|
// consistent with the variable labels in Desc.
|
||||||
|
func NewConstHistogram(
|
||||||
|
desc *Desc,
|
||||||
|
count uint64,
|
||||||
|
sum float64,
|
||||||
|
buckets map[float64]uint64,
|
||||||
|
labelValues ...string,
|
||||||
|
) (Metric, error) {
|
||||||
|
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &constHistogram{
|
||||||
|
desc: desc,
|
||||||
|
count: count,
|
||||||
|
sum: sum,
|
||||||
|
buckets: buckets,
|
||||||
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustNewConstHistogram is a version of NewConstHistogram that panics where
|
||||||
|
// NewConstMetric would have returned an error.
|
||||||
|
func MustNewConstHistogram(
|
||||||
|
desc *Desc,
|
||||||
|
count uint64,
|
||||||
|
sum float64,
|
||||||
|
buckets map[float64]uint64,
|
||||||
|
labelValues ...string,
|
||||||
|
) Metric {
|
||||||
|
m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
type buckSort []*dto.Bucket
|
||||||
|
|
||||||
|
func (s buckSort) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s buckSort) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s buckSort) Less(i, j int) bool {
|
||||||
|
return s[i].GetUpperBound() < s[j].GetUpperBound()
|
||||||
|
}
|
523
vendor/github.com/prometheus/client_golang/prometheus/http.go
generated
vendored
Normal file
523
vendor/github.com/prometheus/client_golang/prometheus/http.go
generated
vendored
Normal file
|
@ -0,0 +1,523 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/expfmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(beorn7): Remove this whole file. It is a partial mirror of
|
||||||
|
// promhttp/http.go (to avoid circular import chains) where everything HTTP
|
||||||
|
// related should live. The functions here are just for avoiding
|
||||||
|
// breakage. Everything is deprecated.
|
||||||
|
|
||||||
|
const (
|
||||||
|
contentTypeHeader = "Content-Type"
|
||||||
|
contentLengthHeader = "Content-Length"
|
||||||
|
contentEncodingHeader = "Content-Encoding"
|
||||||
|
acceptEncodingHeader = "Accept-Encoding"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufPool sync.Pool
|
||||||
|
|
||||||
|
func getBuf() *bytes.Buffer {
|
||||||
|
buf := bufPool.Get()
|
||||||
|
if buf == nil {
|
||||||
|
return &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
return buf.(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func giveBuf(buf *bytes.Buffer) {
|
||||||
|
buf.Reset()
|
||||||
|
bufPool.Put(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler returns an HTTP handler for the DefaultGatherer. It is
|
||||||
|
// already instrumented with InstrumentHandler (using "prometheus" as handler
|
||||||
|
// name).
|
||||||
|
//
|
||||||
|
// Deprecated: Please note the issues described in the doc comment of
|
||||||
|
// InstrumentHandler. You might want to consider using
|
||||||
|
// promhttp.InstrumentedHandler instead.
|
||||||
|
func Handler() http.Handler {
|
||||||
|
return InstrumentHandler("prometheus", UninstrumentedHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UninstrumentedHandler returns an HTTP handler for the DefaultGatherer.
|
||||||
|
//
|
||||||
|
// Deprecated: Use promhttp.Handler instead. See there for further documentation.
|
||||||
|
func UninstrumentedHandler() http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
mfs, err := DefaultGatherer.Gather()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "An error has occurred during metrics collection:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := expfmt.Negotiate(req.Header)
|
||||||
|
buf := getBuf()
|
||||||
|
defer giveBuf(buf)
|
||||||
|
writer, encoding := decorateWriter(req, buf)
|
||||||
|
enc := expfmt.NewEncoder(writer, contentType)
|
||||||
|
var lastErr error
|
||||||
|
for _, mf := range mfs {
|
||||||
|
if err := enc.Encode(mf); err != nil {
|
||||||
|
lastErr = err
|
||||||
|
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if closer, ok := writer.(io.Closer); ok {
|
||||||
|
closer.Close()
|
||||||
|
}
|
||||||
|
if lastErr != nil && buf.Len() == 0 {
|
||||||
|
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header := w.Header()
|
||||||
|
header.Set(contentTypeHeader, string(contentType))
|
||||||
|
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
|
||||||
|
if encoding != "" {
|
||||||
|
header.Set(contentEncodingHeader, encoding)
|
||||||
|
}
|
||||||
|
w.Write(buf.Bytes())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// decorateWriter wraps a writer to handle gzip compression if requested. It
|
||||||
|
// returns the decorated writer and the appropriate "Content-Encoding" header
|
||||||
|
// (which is empty if no compression is enabled).
|
||||||
|
func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) {
|
||||||
|
header := request.Header.Get(acceptEncodingHeader)
|
||||||
|
parts := strings.Split(header, ",")
|
||||||
|
for _, part := range parts {
|
||||||
|
part := strings.TrimSpace(part)
|
||||||
|
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
||||||
|
return gzip.NewWriter(writer), "gzip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writer, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var instLabels = []string{"method", "code"}
|
||||||
|
|
||||||
|
type nower interface {
|
||||||
|
Now() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type nowFunc func() time.Time
|
||||||
|
|
||||||
|
func (n nowFunc) Now() time.Time {
|
||||||
|
return n()
|
||||||
|
}
|
||||||
|
|
||||||
|
var now nower = nowFunc(func() time.Time {
|
||||||
|
return time.Now()
|
||||||
|
})
|
||||||
|
|
||||||
|
func nowSeries(t ...time.Time) nower {
|
||||||
|
return nowFunc(func() time.Time {
|
||||||
|
defer func() {
|
||||||
|
t = t[1:]
|
||||||
|
}()
|
||||||
|
|
||||||
|
return t[0]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandler wraps the given HTTP handler for instrumentation. It
|
||||||
|
// registers four metric collectors (if not already done) and reports HTTP
|
||||||
|
// metrics to the (newly or already) registered collectors: http_requests_total
|
||||||
|
// (CounterVec), http_request_duration_microseconds (Summary),
|
||||||
|
// http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each
|
||||||
|
// has a constant label named "handler" with the provided handlerName as
|
||||||
|
// value. http_requests_total is a metric vector partitioned by HTTP method
|
||||||
|
// (label name "method") and HTTP status code (label name "code").
|
||||||
|
//
|
||||||
|
// Deprecated: InstrumentHandler has several issues. Use the tooling provided in
|
||||||
|
// package promhttp instead. The issues are the following:
|
||||||
|
//
|
||||||
|
// - It uses Summaries rather than Histograms. Summaries are not useful if
|
||||||
|
// aggregation across multiple instances is required.
|
||||||
|
//
|
||||||
|
// - It uses microseconds as unit, which is deprecated and should be replaced by
|
||||||
|
// seconds.
|
||||||
|
//
|
||||||
|
// - The size of the request is calculated in a separate goroutine. Since this
|
||||||
|
// calculator requires access to the request header, it creates a race with
|
||||||
|
// any writes to the header performed during request handling.
|
||||||
|
// httputil.ReverseProxy is a prominent example for a handler
|
||||||
|
// performing such writes.
|
||||||
|
//
|
||||||
|
// - It has additional issues with HTTP/2, cf.
|
||||||
|
// https://github.com/prometheus/client_golang/issues/272.
|
||||||
|
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
|
||||||
|
return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerFunc wraps the given function for instrumentation. It
|
||||||
|
// otherwise works in the same way as InstrumentHandler (and shares the same
|
||||||
|
// issues).
|
||||||
|
//
|
||||||
|
// Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
|
||||||
|
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||||
|
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||||
|
return InstrumentHandlerFuncWithOpts(
|
||||||
|
SummaryOpts{
|
||||||
|
Subsystem: "http",
|
||||||
|
ConstLabels: Labels{"handler": handlerName},
|
||||||
|
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
|
||||||
|
},
|
||||||
|
handlerFunc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerWithOpts works like InstrumentHandler (and shares the same
|
||||||
|
// issues) but provides more flexibility (at the cost of a more complex call
|
||||||
|
// syntax). As InstrumentHandler, this function registers four metric
|
||||||
|
// collectors, but it uses the provided SummaryOpts to create them. However, the
|
||||||
|
// fields "Name" and "Help" in the SummaryOpts are ignored. "Name" is replaced
|
||||||
|
// by "requests_total", "request_duration_microseconds", "request_size_bytes",
|
||||||
|
// and "response_size_bytes", respectively. "Help" is replaced by an appropriate
|
||||||
|
// help string. The names of the variable labels of the http_requests_total
|
||||||
|
// CounterVec are "method" (get, post, etc.), and "code" (HTTP status code).
|
||||||
|
//
|
||||||
|
// If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
|
||||||
|
// behavior of InstrumentHandler:
|
||||||
|
//
|
||||||
|
// prometheus.InstrumentHandlerWithOpts(
|
||||||
|
// prometheus.SummaryOpts{
|
||||||
|
// Subsystem: "http",
|
||||||
|
// ConstLabels: prometheus.Labels{"handler": handlerName},
|
||||||
|
// },
|
||||||
|
// handler,
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
|
||||||
|
// cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
|
||||||
|
// and all its fields are set to the equally named fields in the provided
|
||||||
|
// SummaryOpts.
|
||||||
|
//
|
||||||
|
// Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
|
||||||
|
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||||
|
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
|
||||||
|
return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc (and shares
|
||||||
|
// the same issues) but provides more flexibility (at the cost of a more complex
|
||||||
|
// call syntax). See InstrumentHandlerWithOpts for details how the provided
|
||||||
|
// SummaryOpts are used.
|
||||||
|
//
|
||||||
|
// Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
|
||||||
|
// as InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||||
|
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||||
|
reqCnt := NewCounterVec(
|
||||||
|
CounterOpts{
|
||||||
|
Namespace: opts.Namespace,
|
||||||
|
Subsystem: opts.Subsystem,
|
||||||
|
Name: "requests_total",
|
||||||
|
Help: "Total number of HTTP requests made.",
|
||||||
|
ConstLabels: opts.ConstLabels,
|
||||||
|
},
|
||||||
|
instLabels,
|
||||||
|
)
|
||||||
|
if err := Register(reqCnt); err != nil {
|
||||||
|
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||||
|
reqCnt = are.ExistingCollector.(*CounterVec)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.Name = "request_duration_microseconds"
|
||||||
|
opts.Help = "The HTTP request latencies in microseconds."
|
||||||
|
reqDur := NewSummary(opts)
|
||||||
|
if err := Register(reqDur); err != nil {
|
||||||
|
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||||
|
reqDur = are.ExistingCollector.(Summary)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.Name = "request_size_bytes"
|
||||||
|
opts.Help = "The HTTP request sizes in bytes."
|
||||||
|
reqSz := NewSummary(opts)
|
||||||
|
if err := Register(reqSz); err != nil {
|
||||||
|
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||||
|
reqSz = are.ExistingCollector.(Summary)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.Name = "response_size_bytes"
|
||||||
|
opts.Help = "The HTTP response sizes in bytes."
|
||||||
|
resSz := NewSummary(opts)
|
||||||
|
if err := Register(resSz); err != nil {
|
||||||
|
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||||
|
resSz = are.ExistingCollector.(Summary)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
delegate := &responseWriterDelegator{ResponseWriter: w}
|
||||||
|
out := computeApproximateRequestSize(r)
|
||||||
|
|
||||||
|
_, cn := w.(http.CloseNotifier)
|
||||||
|
_, fl := w.(http.Flusher)
|
||||||
|
_, hj := w.(http.Hijacker)
|
||||||
|
_, rf := w.(io.ReaderFrom)
|
||||||
|
var rw http.ResponseWriter
|
||||||
|
if cn && fl && hj && rf {
|
||||||
|
rw = &fancyResponseWriterDelegator{delegate}
|
||||||
|
} else {
|
||||||
|
rw = delegate
|
||||||
|
}
|
||||||
|
handlerFunc(rw, r)
|
||||||
|
|
||||||
|
elapsed := float64(time.Since(now)) / float64(time.Microsecond)
|
||||||
|
|
||||||
|
method := sanitizeMethod(r.Method)
|
||||||
|
code := sanitizeCode(delegate.status)
|
||||||
|
reqCnt.WithLabelValues(method, code).Inc()
|
||||||
|
reqDur.Observe(elapsed)
|
||||||
|
resSz.Observe(float64(delegate.written))
|
||||||
|
reqSz.Observe(float64(<-out))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeApproximateRequestSize(r *http.Request) <-chan int {
|
||||||
|
// Get URL length in current go routine for avoiding a race condition.
|
||||||
|
// HandlerFunc that runs in parallel may modify the URL.
|
||||||
|
s := 0
|
||||||
|
if r.URL != nil {
|
||||||
|
s += len(r.URL.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(chan int, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
s += len(r.Method)
|
||||||
|
s += len(r.Proto)
|
||||||
|
for name, values := range r.Header {
|
||||||
|
s += len(name)
|
||||||
|
for _, value := range values {
|
||||||
|
s += len(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += len(r.Host)
|
||||||
|
|
||||||
|
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
|
||||||
|
|
||||||
|
if r.ContentLength != -1 {
|
||||||
|
s += int(r.ContentLength)
|
||||||
|
}
|
||||||
|
out <- s
|
||||||
|
close(out)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseWriterDelegator struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
|
||||||
|
handler, method string
|
||||||
|
status int
|
||||||
|
written int64
|
||||||
|
wroteHeader bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseWriterDelegator) WriteHeader(code int) {
|
||||||
|
r.status = code
|
||||||
|
r.wroteHeader = true
|
||||||
|
r.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseWriterDelegator) Write(b []byte) (int, error) {
|
||||||
|
if !r.wroteHeader {
|
||||||
|
r.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
n, err := r.ResponseWriter.Write(b)
|
||||||
|
r.written += int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type fancyResponseWriterDelegator struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
|
||||||
|
return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fancyResponseWriterDelegator) Flush() {
|
||||||
|
f.ResponseWriter.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
return f.ResponseWriter.(http.Hijacker).Hijack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
if !f.wroteHeader {
|
||||||
|
f.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r)
|
||||||
|
f.written += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeMethod(m string) string {
|
||||||
|
switch m {
|
||||||
|
case "GET", "get":
|
||||||
|
return "get"
|
||||||
|
case "PUT", "put":
|
||||||
|
return "put"
|
||||||
|
case "HEAD", "head":
|
||||||
|
return "head"
|
||||||
|
case "POST", "post":
|
||||||
|
return "post"
|
||||||
|
case "DELETE", "delete":
|
||||||
|
return "delete"
|
||||||
|
case "CONNECT", "connect":
|
||||||
|
return "connect"
|
||||||
|
case "OPTIONS", "options":
|
||||||
|
return "options"
|
||||||
|
case "NOTIFY", "notify":
|
||||||
|
return "notify"
|
||||||
|
default:
|
||||||
|
return strings.ToLower(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeCode(s int) string {
|
||||||
|
switch s {
|
||||||
|
case 100:
|
||||||
|
return "100"
|
||||||
|
case 101:
|
||||||
|
return "101"
|
||||||
|
|
||||||
|
case 200:
|
||||||
|
return "200"
|
||||||
|
case 201:
|
||||||
|
return "201"
|
||||||
|
case 202:
|
||||||
|
return "202"
|
||||||
|
case 203:
|
||||||
|
return "203"
|
||||||
|
case 204:
|
||||||
|
return "204"
|
||||||
|
case 205:
|
||||||
|
return "205"
|
||||||
|
case 206:
|
||||||
|
return "206"
|
||||||
|
|
||||||
|
case 300:
|
||||||
|
return "300"
|
||||||
|
case 301:
|
||||||
|
return "301"
|
||||||
|
case 302:
|
||||||
|
return "302"
|
||||||
|
case 304:
|
||||||
|
return "304"
|
||||||
|
case 305:
|
||||||
|
return "305"
|
||||||
|
case 307:
|
||||||
|
return "307"
|
||||||
|
|
||||||
|
case 400:
|
||||||
|
return "400"
|
||||||
|
case 401:
|
||||||
|
return "401"
|
||||||
|
case 402:
|
||||||
|
return "402"
|
||||||
|
case 403:
|
||||||
|
return "403"
|
||||||
|
case 404:
|
||||||
|
return "404"
|
||||||
|
case 405:
|
||||||
|
return "405"
|
||||||
|
case 406:
|
||||||
|
return "406"
|
||||||
|
case 407:
|
||||||
|
return "407"
|
||||||
|
case 408:
|
||||||
|
return "408"
|
||||||
|
case 409:
|
||||||
|
return "409"
|
||||||
|
case 410:
|
||||||
|
return "410"
|
||||||
|
case 411:
|
||||||
|
return "411"
|
||||||
|
case 412:
|
||||||
|
return "412"
|
||||||
|
case 413:
|
||||||
|
return "413"
|
||||||
|
case 414:
|
||||||
|
return "414"
|
||||||
|
case 415:
|
||||||
|
return "415"
|
||||||
|
case 416:
|
||||||
|
return "416"
|
||||||
|
case 417:
|
||||||
|
return "417"
|
||||||
|
case 418:
|
||||||
|
return "418"
|
||||||
|
|
||||||
|
case 500:
|
||||||
|
return "500"
|
||||||
|
case 501:
|
||||||
|
return "501"
|
||||||
|
case 502:
|
||||||
|
return "502"
|
||||||
|
case 503:
|
||||||
|
return "503"
|
||||||
|
case 504:
|
||||||
|
return "504"
|
||||||
|
case 505:
|
||||||
|
return "505"
|
||||||
|
|
||||||
|
case 428:
|
||||||
|
return "428"
|
||||||
|
case 429:
|
||||||
|
return "429"
|
||||||
|
case 431:
|
||||||
|
return "431"
|
||||||
|
case 511:
|
||||||
|
return "511"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return strconv.Itoa(s)
|
||||||
|
}
|
||||||
|
}
|
57
vendor/github.com/prometheus/client_golang/prometheus/labels.go
generated
vendored
Normal file
57
vendor/github.com/prometheus/client_golang/prometheus/labels.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Labels represents a collection of label name -> value mappings. This type is
|
||||||
|
// commonly used with the With(Labels) and GetMetricWith(Labels) methods of
|
||||||
|
// metric vector Collectors, e.g.:
|
||||||
|
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
||||||
|
//
|
||||||
|
// The other use-case is the specification of constant label pairs in Opts or to
|
||||||
|
// create a Desc.
|
||||||
|
type Labels map[string]string
|
||||||
|
|
||||||
|
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||||
|
// label names.
|
||||||
|
const reservedLabelPrefix = "__"
|
||||||
|
|
||||||
|
var errInconsistentCardinality = errors.New("inconsistent label cardinality")
|
||||||
|
|
||||||
|
func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
|
||||||
|
if len(labels) != expectedNumberOfValues {
|
||||||
|
return errInconsistentCardinality
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, val := range labels {
|
||||||
|
if !utf8.ValidString(val) {
|
||||||
|
return fmt.Errorf("label %s: value %q is not valid UTF-8", name, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateLabelValues(vals []string, expectedNumberOfValues int) error {
|
||||||
|
if len(vals) != expectedNumberOfValues {
|
||||||
|
return errInconsistentCardinality
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range vals {
|
||||||
|
if !utf8.ValidString(val) {
|
||||||
|
return fmt.Errorf("label value %q is not valid UTF-8", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLabelName(l string) bool {
|
||||||
|
return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix)
|
||||||
|
}
|
158
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
Normal file
158
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const separatorByte byte = 255
|
||||||
|
|
||||||
|
// A Metric models a single sample value with its meta data being exported to
|
||||||
|
// Prometheus. Implementations of Metric in this package are Gauge, Counter,
|
||||||
|
// Histogram, Summary, and Untyped.
|
||||||
|
type Metric interface {
|
||||||
|
// Desc returns the descriptor for the Metric. This method idempotently
|
||||||
|
// returns the same descriptor throughout the lifetime of the
|
||||||
|
// Metric. The returned descriptor is immutable by contract. A Metric
|
||||||
|
// unable to describe itself must return an invalid descriptor (created
|
||||||
|
// with NewInvalidDesc).
|
||||||
|
Desc() *Desc
|
||||||
|
// Write encodes the Metric into a "Metric" Protocol Buffer data
|
||||||
|
// transmission object.
|
||||||
|
//
|
||||||
|
// Metric implementations must observe concurrency safety as reads of
|
||||||
|
// this metric may occur at any time, and any blocking occurs at the
|
||||||
|
// expense of total performance of rendering all registered
|
||||||
|
// metrics. Ideally, Metric implementations should support concurrent
|
||||||
|
// readers.
|
||||||
|
//
|
||||||
|
// While populating dto.Metric, it is the responsibility of the
|
||||||
|
// implementation to ensure validity of the Metric protobuf (like valid
|
||||||
|
// UTF-8 strings or syntactically valid metric and label names). It is
|
||||||
|
// recommended to sort labels lexicographically. (Implementers may find
|
||||||
|
// LabelPairSorter useful for that.) Callers of Write should still make
|
||||||
|
// sure of sorting if they depend on it.
|
||||||
|
Write(*dto.Metric) error
|
||||||
|
// TODO(beorn7): The original rationale of passing in a pre-allocated
|
||||||
|
// dto.Metric protobuf to save allocations has disappeared. The
|
||||||
|
// signature of this method should be changed to "Write() (*dto.Metric,
|
||||||
|
// error)".
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opts bundles the options for creating most Metric types. Each metric
|
||||||
|
// implementation XXX has its own XXXOpts type, but in most cases, it is just be
|
||||||
|
// an alias of this type (which might change when the requirement arises.)
|
||||||
|
//
|
||||||
|
// It is mandatory to set Name and Help to a non-empty string. All other fields
|
||||||
|
// are optional and can safely be left at their zero value.
|
||||||
|
type Opts struct {
|
||||||
|
// Namespace, Subsystem, and Name are components of the fully-qualified
|
||||||
|
// name of the Metric (created by joining these components with
|
||||||
|
// "_"). Only Name is mandatory, the others merely help structuring the
|
||||||
|
// name. Note that the fully-qualified name of the metric must be a
|
||||||
|
// valid Prometheus metric name.
|
||||||
|
Namespace string
|
||||||
|
Subsystem string
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Help provides information about this metric. Mandatory!
|
||||||
|
//
|
||||||
|
// Metrics with the same fully-qualified name must have the same Help
|
||||||
|
// string.
|
||||||
|
Help string
|
||||||
|
|
||||||
|
// ConstLabels are used to attach fixed labels to this metric. Metrics
|
||||||
|
// with the same fully-qualified name must have the same label names in
|
||||||
|
// their ConstLabels.
|
||||||
|
//
|
||||||
|
// ConstLabels are only used rarely. In particular, do not use them to
|
||||||
|
// attach the same labels to all your metrics. Those use cases are
|
||||||
|
// better covered by target labels set by the scraping Prometheus
|
||||||
|
// server, or by one specific metric (e.g. a build_info or a
|
||||||
|
// machine_role metric). See also
|
||||||
|
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||||
|
ConstLabels Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFQName joins the given three name components by "_". Empty name
|
||||||
|
// components are ignored. If the name parameter itself is empty, an empty
|
||||||
|
// string is returned, no matter what. Metric implementations included in this
|
||||||
|
// library use this function internally to generate the fully-qualified metric
|
||||||
|
// name from the name component in their Opts. Users of the library will only
|
||||||
|
// need this function if they implement their own Metric or instantiate a Desc
|
||||||
|
// (with NewDesc) directly.
|
||||||
|
func BuildFQName(namespace, subsystem, name string) string {
|
||||||
|
if name == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case namespace != "" && subsystem != "":
|
||||||
|
return strings.Join([]string{namespace, subsystem, name}, "_")
|
||||||
|
case namespace != "":
|
||||||
|
return strings.Join([]string{namespace, name}, "_")
|
||||||
|
case subsystem != "":
|
||||||
|
return strings.Join([]string{subsystem, name}, "_")
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelPairSorter implements sort.Interface. It is used to sort a slice of
|
||||||
|
// dto.LabelPair pointers. This is useful for implementing the Write method of
|
||||||
|
// custom metrics.
|
||||||
|
type LabelPairSorter []*dto.LabelPair
|
||||||
|
|
||||||
|
func (s LabelPairSorter) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s LabelPairSorter) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s LabelPairSorter) Less(i, j int) bool {
|
||||||
|
return s[i].GetName() < s[j].GetName()
|
||||||
|
}
|
||||||
|
|
||||||
|
type hashSorter []uint64
|
||||||
|
|
||||||
|
func (s hashSorter) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s hashSorter) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s hashSorter) Less(i, j int) bool {
|
||||||
|
return s[i] < s[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidMetric struct {
|
||||||
|
desc *Desc
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInvalidMetric returns a metric whose Write method always returns the
|
||||||
|
// provided error. It is useful if a Collector finds itself unable to collect
|
||||||
|
// a metric and wishes to report an error to the registry.
|
||||||
|
func NewInvalidMetric(desc *Desc, err error) Metric {
|
||||||
|
return &invalidMetric{desc, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *invalidMetric) Desc() *Desc { return m.desc }
|
||||||
|
|
||||||
|
func (m *invalidMetric) Write(*dto.Metric) error { return m.err }
|
52
vendor/github.com/prometheus/client_golang/prometheus/observer.go
generated
vendored
Normal file
52
vendor/github.com/prometheus/client_golang/prometheus/observer.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
// Observer is the interface that wraps the Observe method, which is used by
|
||||||
|
// Histogram and Summary to add observations.
|
||||||
|
type Observer interface {
|
||||||
|
Observe(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ObserverFunc type is an adapter to allow the use of ordinary
|
||||||
|
// functions as Observers. If f is a function with the appropriate
|
||||||
|
// signature, ObserverFunc(f) is an Observer that calls f.
|
||||||
|
//
|
||||||
|
// This adapter is usually used in connection with the Timer type, and there are
|
||||||
|
// two general use cases:
|
||||||
|
//
|
||||||
|
// The most common one is to use a Gauge as the Observer for a Timer.
|
||||||
|
// See the "Gauge" Timer example.
|
||||||
|
//
|
||||||
|
// The more advanced use case is to create a function that dynamically decides
|
||||||
|
// which Observer to use for observing the duration. See the "Complex" Timer
|
||||||
|
// example.
|
||||||
|
type ObserverFunc func(float64)
|
||||||
|
|
||||||
|
// Observe calls f(value). It implements Observer.
|
||||||
|
func (f ObserverFunc) Observe(value float64) {
|
||||||
|
f(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObserverVec is an interface implemented by `HistogramVec` and `SummaryVec`.
|
||||||
|
type ObserverVec interface {
|
||||||
|
GetMetricWith(Labels) (Observer, error)
|
||||||
|
GetMetricWithLabelValues(lvs ...string) (Observer, error)
|
||||||
|
With(Labels) Observer
|
||||||
|
WithLabelValues(...string) Observer
|
||||||
|
CurryWith(Labels) (ObserverVec, error)
|
||||||
|
MustCurryWith(Labels) ObserverVec
|
||||||
|
|
||||||
|
Collector
|
||||||
|
}
|
140
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
Normal file
140
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import "github.com/prometheus/procfs"
|
||||||
|
|
||||||
|
type processCollector struct {
|
||||||
|
pid int
|
||||||
|
collectFn func(chan<- Metric)
|
||||||
|
pidFn func() (int, error)
|
||||||
|
cpuTotal *Desc
|
||||||
|
openFDs, maxFDs *Desc
|
||||||
|
vsize, rss *Desc
|
||||||
|
startTime *Desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProcessCollector returns a collector which exports the current state of
|
||||||
|
// process metrics including CPU, memory and file descriptor usage as well as
|
||||||
|
// the process start time for the given process ID under the given namespace.
|
||||||
|
//
|
||||||
|
// Currently, the collector depends on a Linux-style proc filesystem and
|
||||||
|
// therefore only exports metrics for Linux.
|
||||||
|
func NewProcessCollector(pid int, namespace string) Collector {
|
||||||
|
return NewProcessCollectorPIDFn(
|
||||||
|
func() (int, error) { return pid, nil },
|
||||||
|
namespace,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProcessCollectorPIDFn works like NewProcessCollector but the process ID is
|
||||||
|
// determined on each collect anew by calling the given pidFn function.
|
||||||
|
func NewProcessCollectorPIDFn(
|
||||||
|
pidFn func() (int, error),
|
||||||
|
namespace string,
|
||||||
|
) Collector {
|
||||||
|
ns := ""
|
||||||
|
if len(namespace) > 0 {
|
||||||
|
ns = namespace + "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
c := processCollector{
|
||||||
|
pidFn: pidFn,
|
||||||
|
collectFn: func(chan<- Metric) {},
|
||||||
|
|
||||||
|
cpuTotal: NewDesc(
|
||||||
|
ns+"process_cpu_seconds_total",
|
||||||
|
"Total user and system CPU time spent in seconds.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
openFDs: NewDesc(
|
||||||
|
ns+"process_open_fds",
|
||||||
|
"Number of open file descriptors.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
maxFDs: NewDesc(
|
||||||
|
ns+"process_max_fds",
|
||||||
|
"Maximum number of open file descriptors.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
vsize: NewDesc(
|
||||||
|
ns+"process_virtual_memory_bytes",
|
||||||
|
"Virtual memory size in bytes.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
rss: NewDesc(
|
||||||
|
ns+"process_resident_memory_bytes",
|
||||||
|
"Resident memory size in bytes.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
startTime: NewDesc(
|
||||||
|
ns+"process_start_time_seconds",
|
||||||
|
"Start time of the process since unix epoch in seconds.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up process metric collection if supported by the runtime.
|
||||||
|
if _, err := procfs.NewStat(); err == nil {
|
||||||
|
c.collectFn = c.processCollect
|
||||||
|
}
|
||||||
|
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe returns all descriptions of the collector.
|
||||||
|
func (c *processCollector) Describe(ch chan<- *Desc) {
|
||||||
|
ch <- c.cpuTotal
|
||||||
|
ch <- c.openFDs
|
||||||
|
ch <- c.maxFDs
|
||||||
|
ch <- c.vsize
|
||||||
|
ch <- c.rss
|
||||||
|
ch <- c.startTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect returns the current state of all metrics of the collector.
|
||||||
|
func (c *processCollector) Collect(ch chan<- Metric) {
|
||||||
|
c.collectFn(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(ts): Bring back error reporting by reverting 7faf9e7 as soon as the
|
||||||
|
// client allows users to configure the error behavior.
|
||||||
|
func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||||
|
pid, err := c.pidFn()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := procfs.NewProc(pid)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat, err := p.NewStat(); err == nil {
|
||||||
|
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime())
|
||||||
|
ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory()))
|
||||||
|
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory()))
|
||||||
|
if startTime, err := stat.StartTime(); err == nil {
|
||||||
|
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fds, err := p.FileDescriptorsLen(); err == nil {
|
||||||
|
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds))
|
||||||
|
}
|
||||||
|
|
||||||
|
if limits, err := p.NewLimits(); err == nil {
|
||||||
|
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles))
|
||||||
|
}
|
||||||
|
}
|
199
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
generated
vendored
Normal file
199
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
generated
vendored
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package promhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
closeNotifier = 1 << iota
|
||||||
|
flusher
|
||||||
|
hijacker
|
||||||
|
readerFrom
|
||||||
|
pusher
|
||||||
|
)
|
||||||
|
|
||||||
|
type delegator interface {
|
||||||
|
http.ResponseWriter
|
||||||
|
|
||||||
|
Status() int
|
||||||
|
Written() int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseWriterDelegator struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
|
||||||
|
handler, method string
|
||||||
|
status int
|
||||||
|
written int64
|
||||||
|
wroteHeader bool
|
||||||
|
observeWriteHeader func(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseWriterDelegator) Status() int {
|
||||||
|
return r.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseWriterDelegator) Written() int64 {
|
||||||
|
return r.written
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseWriterDelegator) WriteHeader(code int) {
|
||||||
|
r.status = code
|
||||||
|
r.wroteHeader = true
|
||||||
|
r.ResponseWriter.WriteHeader(code)
|
||||||
|
if r.observeWriteHeader != nil {
|
||||||
|
r.observeWriteHeader(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseWriterDelegator) Write(b []byte) (int, error) {
|
||||||
|
if !r.wroteHeader {
|
||||||
|
r.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
n, err := r.ResponseWriter.Write(b)
|
||||||
|
r.written += int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type closeNotifierDelegator struct{ *responseWriterDelegator }
|
||||||
|
type flusherDelegator struct{ *responseWriterDelegator }
|
||||||
|
type hijackerDelegator struct{ *responseWriterDelegator }
|
||||||
|
type readerFromDelegator struct{ *responseWriterDelegator }
|
||||||
|
|
||||||
|
func (d *closeNotifierDelegator) CloseNotify() <-chan bool {
|
||||||
|
return d.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
|
}
|
||||||
|
func (d *flusherDelegator) Flush() {
|
||||||
|
d.ResponseWriter.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
func (d *hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
return d.ResponseWriter.(http.Hijacker).Hijack()
|
||||||
|
}
|
||||||
|
func (d *readerFromDelegator) ReadFrom(re io.Reader) (int64, error) {
|
||||||
|
if !d.wroteHeader {
|
||||||
|
d.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re)
|
||||||
|
d.written += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// TODO(beorn7): Code generation would help here.
|
||||||
|
pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1
|
||||||
|
return &closeNotifierDelegator{d}
|
||||||
|
}
|
||||||
|
pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2
|
||||||
|
return &flusherDelegator{d}
|
||||||
|
}
|
||||||
|
pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4
|
||||||
|
return &hijackerDelegator{d}
|
||||||
|
}
|
||||||
|
pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Hijacker
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &hijackerDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
}{d, &hijackerDelegator{d}, &flusherDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8
|
||||||
|
return readerFromDelegator{d}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
io.ReaderFrom
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &readerFromDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Flusher
|
||||||
|
}{d, &readerFromDelegator{d}, &flusherDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &readerFromDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
}
|
181
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
generated
vendored
Normal file
181
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package promhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pusherDelegator struct{ *responseWriterDelegator }
|
||||||
|
|
||||||
|
func (d *pusherDelegator) Push(target string, opts *http.PushOptions) error {
|
||||||
|
return d.ResponseWriter.(http.Pusher).Push(target, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16
|
||||||
|
return &pusherDelegator{d}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
http.Flusher
|
||||||
|
}{d, &pusherDelegator{d}, &flusherDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
http.Hijacker
|
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
http.Hijacker
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Flusher
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &flusherDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}}
|
||||||
|
}
|
||||||
|
pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31
|
||||||
|
return struct {
|
||||||
|
*responseWriterDelegator
|
||||||
|
http.Pusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Hijacker
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator {
|
||||||
|
d := &responseWriterDelegator{
|
||||||
|
ResponseWriter: w,
|
||||||
|
observeWriteHeader: observeWriteHeaderFunc,
|
||||||
|
}
|
||||||
|
|
||||||
|
id := 0
|
||||||
|
if _, ok := w.(http.CloseNotifier); ok {
|
||||||
|
id += closeNotifier
|
||||||
|
}
|
||||||
|
if _, ok := w.(http.Flusher); ok {
|
||||||
|
id += flusher
|
||||||
|
}
|
||||||
|
if _, ok := w.(http.Hijacker); ok {
|
||||||
|
id += hijacker
|
||||||
|
}
|
||||||
|
if _, ok := w.(io.ReaderFrom); ok {
|
||||||
|
id += readerFrom
|
||||||
|
}
|
||||||
|
if _, ok := w.(http.Pusher); ok {
|
||||||
|
id += pusher
|
||||||
|
}
|
||||||
|
|
||||||
|
return pickDelegator[id](d)
|
||||||
|
}
|
44
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
generated
vendored
Normal file
44
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build !go1.8
|
||||||
|
|
||||||
|
package promhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator {
|
||||||
|
d := &responseWriterDelegator{
|
||||||
|
ResponseWriter: w,
|
||||||
|
observeWriteHeader: observeWriteHeaderFunc,
|
||||||
|
}
|
||||||
|
|
||||||
|
id := 0
|
||||||
|
if _, ok := w.(http.CloseNotifier); ok {
|
||||||
|
id += closeNotifier
|
||||||
|
}
|
||||||
|
if _, ok := w.(http.Flusher); ok {
|
||||||
|
id += flusher
|
||||||
|
}
|
||||||
|
if _, ok := w.(http.Hijacker); ok {
|
||||||
|
id += hijacker
|
||||||
|
}
|
||||||
|
if _, ok := w.(io.ReaderFrom); ok {
|
||||||
|
id += readerFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
return pickDelegator[id](d)
|
||||||
|
}
|
267
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
generated
vendored
Normal file
267
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
generated
vendored
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
// Copyright 2016 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package promhttp provides tooling around HTTP servers and clients.
|
||||||
|
//
|
||||||
|
// First, the package allows the creation of http.Handler instances to expose
|
||||||
|
// Prometheus metrics via HTTP. promhttp.Handler acts on the
|
||||||
|
// prometheus.DefaultGatherer. With HandlerFor, you can create a handler for a
|
||||||
|
// custom registry or anything that implements the Gatherer interface. It also
|
||||||
|
// allows the creation of handlers that act differently on errors or allow to
|
||||||
|
// log errors.
|
||||||
|
//
|
||||||
|
// Second, the package provides tooling to instrument instances of http.Handler
|
||||||
|
// via middleware. Middleware wrappers follow the naming scheme
|
||||||
|
// InstrumentHandlerX, where X describes the intended use of the middleware.
|
||||||
|
// See each function's doc comment for specific details.
|
||||||
|
//
|
||||||
|
// Finally, the package allows for an http.RoundTripper to be instrumented via
|
||||||
|
// middleware. Middleware wrappers follow the naming scheme
|
||||||
|
// InstrumentRoundTripperX, where X describes the intended use of the
|
||||||
|
// middleware. See each function's doc comment for specific details.
|
||||||
|
package promhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/expfmt"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
contentTypeHeader = "Content-Type"
|
||||||
|
contentLengthHeader = "Content-Length"
|
||||||
|
contentEncodingHeader = "Content-Encoding"
|
||||||
|
acceptEncodingHeader = "Accept-Encoding"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufPool sync.Pool
|
||||||
|
|
||||||
|
func getBuf() *bytes.Buffer {
|
||||||
|
buf := bufPool.Get()
|
||||||
|
if buf == nil {
|
||||||
|
return &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
return buf.(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func giveBuf(buf *bytes.Buffer) {
|
||||||
|
buf.Reset()
|
||||||
|
bufPool.Put(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
|
||||||
|
// default HandlerOpts, i.e. it reports the first error as an HTTP error, it has
|
||||||
|
// no error logging, and it applies compression if requested by the client.
|
||||||
|
//
|
||||||
|
// The returned http.Handler is already instrumented using the
|
||||||
|
// InstrumentMetricHandler function and the prometheus.DefaultRegisterer. If you
|
||||||
|
// create multiple http.Handlers by separate calls of the Handler function, the
|
||||||
|
// metrics used for instrumentation will be shared between them, providing
|
||||||
|
// global scrape counts.
|
||||||
|
//
|
||||||
|
// This function is meant to cover the bulk of basic use cases. If you are doing
|
||||||
|
// anything that requires more customization (including using a non-default
|
||||||
|
// Gatherer, different instrumentation, and non-default HandlerOpts), use the
|
||||||
|
// HandlerFor function. See there for details.
|
||||||
|
func Handler() http.Handler {
|
||||||
|
return InstrumentMetricHandler(
|
||||||
|
prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFor returns an uninstrumented http.Handler for the provided
|
||||||
|
// Gatherer. The behavior of the Handler is defined by the provided
|
||||||
|
// HandlerOpts. Thus, HandlerFor is useful to create http.Handlers for custom
|
||||||
|
// Gatherers, with non-default HandlerOpts, and/or with custom (or no)
|
||||||
|
// instrumentation. Use the InstrumentMetricHandler function to apply the same
|
||||||
|
// kind of instrumentation as it is used by the Handler function.
|
||||||
|
func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
mfs, err := reg.Gather()
|
||||||
|
if err != nil {
|
||||||
|
if opts.ErrorLog != nil {
|
||||||
|
opts.ErrorLog.Println("error gathering metrics:", err)
|
||||||
|
}
|
||||||
|
switch opts.ErrorHandling {
|
||||||
|
case PanicOnError:
|
||||||
|
panic(err)
|
||||||
|
case ContinueOnError:
|
||||||
|
if len(mfs) == 0 {
|
||||||
|
http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case HTTPErrorOnError:
|
||||||
|
http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := expfmt.Negotiate(req.Header)
|
||||||
|
buf := getBuf()
|
||||||
|
defer giveBuf(buf)
|
||||||
|
writer, encoding := decorateWriter(req, buf, opts.DisableCompression)
|
||||||
|
enc := expfmt.NewEncoder(writer, contentType)
|
||||||
|
var lastErr error
|
||||||
|
for _, mf := range mfs {
|
||||||
|
if err := enc.Encode(mf); err != nil {
|
||||||
|
lastErr = err
|
||||||
|
if opts.ErrorLog != nil {
|
||||||
|
opts.ErrorLog.Println("error encoding metric family:", err)
|
||||||
|
}
|
||||||
|
switch opts.ErrorHandling {
|
||||||
|
case PanicOnError:
|
||||||
|
panic(err)
|
||||||
|
case ContinueOnError:
|
||||||
|
// Handled later.
|
||||||
|
case HTTPErrorOnError:
|
||||||
|
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if closer, ok := writer.(io.Closer); ok {
|
||||||
|
closer.Close()
|
||||||
|
}
|
||||||
|
if lastErr != nil && buf.Len() == 0 {
|
||||||
|
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header := w.Header()
|
||||||
|
header.Set(contentTypeHeader, string(contentType))
|
||||||
|
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
|
||||||
|
if encoding != "" {
|
||||||
|
header.Set(contentEncodingHeader, encoding)
|
||||||
|
}
|
||||||
|
w.Write(buf.Bytes())
|
||||||
|
// TODO(beorn7): Consider streaming serving of metrics.
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentMetricHandler is usually used with an http.Handler returned by the
|
||||||
|
// HandlerFor function. It instruments the provided http.Handler with two
|
||||||
|
// metrics: A counter vector "promhttp_metric_handler_requests_total" to count
|
||||||
|
// scrapes partitioned by HTTP status code, and a gauge
|
||||||
|
// "promhttp_metric_handler_requests_in_flight" to track the number of
|
||||||
|
// simultaneous scrapes. This function idempotently registers collectors for
|
||||||
|
// both metrics with the provided Registerer. It panics if the registration
|
||||||
|
// fails. The provided metrics are useful to see how many scrapes hit the
|
||||||
|
// monitored target (which could be from different Prometheus servers or other
|
||||||
|
// scrapers), and how often they overlap (which would result in more than one
|
||||||
|
// scrape in flight at the same time). Note that the scrapes-in-flight gauge
|
||||||
|
// will contain the scrape by which it is exposed, while the scrape counter will
|
||||||
|
// only get incremented after the scrape is complete (as only then the status
|
||||||
|
// code is known). For tracking scrape durations, use the
|
||||||
|
// "scrape_duration_seconds" gauge created by the Prometheus server upon each
|
||||||
|
// scrape.
|
||||||
|
func InstrumentMetricHandler(reg prometheus.Registerer, handler http.Handler) http.Handler {
|
||||||
|
cnt := prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "promhttp_metric_handler_requests_total",
|
||||||
|
Help: "Total number of scrapes by HTTP status code.",
|
||||||
|
},
|
||||||
|
[]string{"code"},
|
||||||
|
)
|
||||||
|
// Initialize the most likely HTTP status codes.
|
||||||
|
cnt.WithLabelValues("200")
|
||||||
|
cnt.WithLabelValues("500")
|
||||||
|
if err := reg.Register(cnt); err != nil {
|
||||||
|
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
|
||||||
|
cnt = are.ExistingCollector.(*prometheus.CounterVec)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gge := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "promhttp_metric_handler_requests_in_flight",
|
||||||
|
Help: "Current number of scrapes being served.",
|
||||||
|
})
|
||||||
|
if err := reg.Register(gge); err != nil {
|
||||||
|
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
|
||||||
|
gge = are.ExistingCollector.(prometheus.Gauge)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InstrumentHandlerCounter(cnt, InstrumentHandlerInFlight(gge, handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerErrorHandling defines how a Handler serving metrics will handle
|
||||||
|
// errors.
|
||||||
|
type HandlerErrorHandling int
|
||||||
|
|
||||||
|
// These constants cause handlers serving metrics to behave as described if
|
||||||
|
// errors are encountered.
|
||||||
|
const (
|
||||||
|
// Serve an HTTP status code 500 upon the first error
|
||||||
|
// encountered. Report the error message in the body.
|
||||||
|
HTTPErrorOnError HandlerErrorHandling = iota
|
||||||
|
// Ignore errors and try to serve as many metrics as possible. However,
|
||||||
|
// if no metrics can be served, serve an HTTP status code 500 and the
|
||||||
|
// last error message in the body. Only use this in deliberate "best
|
||||||
|
// effort" metrics collection scenarios. It is recommended to at least
|
||||||
|
// log errors (by providing an ErrorLog in HandlerOpts) to not mask
|
||||||
|
// errors completely.
|
||||||
|
ContinueOnError
|
||||||
|
// Panic upon the first error encountered (useful for "crash only" apps).
|
||||||
|
PanicOnError
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger is the minimal interface HandlerOpts needs for logging. Note that
|
||||||
|
// log.Logger from the standard library implements this interface, and it is
|
||||||
|
// easy to implement by custom loggers, if they don't do so already anyway.
|
||||||
|
type Logger interface {
|
||||||
|
Println(v ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerOpts specifies options how to serve metrics via an http.Handler. The
|
||||||
|
// zero value of HandlerOpts is a reasonable default.
|
||||||
|
type HandlerOpts struct {
|
||||||
|
// ErrorLog specifies an optional logger for errors collecting and
|
||||||
|
// serving metrics. If nil, errors are not logged at all.
|
||||||
|
ErrorLog Logger
|
||||||
|
// ErrorHandling defines how errors are handled. Note that errors are
|
||||||
|
// logged regardless of the configured ErrorHandling provided ErrorLog
|
||||||
|
// is not nil.
|
||||||
|
ErrorHandling HandlerErrorHandling
|
||||||
|
// If DisableCompression is true, the handler will never compress the
|
||||||
|
// response, even if requested by the client.
|
||||||
|
DisableCompression bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// decorateWriter wraps a writer to handle gzip compression if requested. It
|
||||||
|
// returns the decorated writer and the appropriate "Content-Encoding" header
|
||||||
|
// (which is empty if no compression is enabled).
|
||||||
|
func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) {
|
||||||
|
if compressionDisabled {
|
||||||
|
return writer, ""
|
||||||
|
}
|
||||||
|
header := request.Header.Get(acceptEncodingHeader)
|
||||||
|
parts := strings.Split(header, ",")
|
||||||
|
for _, part := range parts {
|
||||||
|
part := strings.TrimSpace(part)
|
||||||
|
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
||||||
|
return gzip.NewWriter(writer), "gzip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writer, ""
|
||||||
|
}
|
97
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
generated
vendored
Normal file
97
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package promhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The RoundTripperFunc type is an adapter to allow the use of ordinary
|
||||||
|
// functions as RoundTrippers. If f is a function with the appropriate
|
||||||
|
// signature, RountTripperFunc(f) is a RoundTripper that calls f.
|
||||||
|
type RoundTripperFunc func(req *http.Request) (*http.Response, error)
|
||||||
|
|
||||||
|
// RoundTrip implements the RoundTripper interface.
|
||||||
|
func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
|
return rt(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentRoundTripperInFlight is a middleware that wraps the provided
|
||||||
|
// http.RoundTripper. It sets the provided prometheus.Gauge to the number of
|
||||||
|
// requests currently handled by the wrapped http.RoundTripper.
|
||||||
|
//
|
||||||
|
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
|
||||||
|
func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc {
|
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
gauge.Inc()
|
||||||
|
defer gauge.Dec()
|
||||||
|
return next.RoundTrip(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentRoundTripperCounter is a middleware that wraps the provided
|
||||||
|
// http.RoundTripper to observe the request result with the provided CounterVec.
|
||||||
|
// The CounterVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. Partitioning of the CounterVec happens by HTTP status code
|
||||||
|
// and/or HTTP method if the respective instance label names are present in the
|
||||||
|
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
|
||||||
|
//
|
||||||
|
// If the wrapped RoundTripper panics or returns a non-nil error, the Counter
|
||||||
|
// is not incremented.
|
||||||
|
//
|
||||||
|
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
|
||||||
|
func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc {
|
||||||
|
code, method := checkLabels(counter)
|
||||||
|
|
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := next.RoundTrip(r)
|
||||||
|
if err == nil {
|
||||||
|
counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc()
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentRoundTripperDuration is a middleware that wraps the provided
|
||||||
|
// http.RoundTripper to observe the request duration with the provided
|
||||||
|
// ObserverVec. The ObserverVec must have zero, one, or two non-const
|
||||||
|
// non-curried labels. For those, the only allowed label names are "code" and
|
||||||
|
// "method". The function panics otherwise. The Observe method of the Observer
|
||||||
|
// in the ObserverVec is called with the request duration in
|
||||||
|
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||||
|
// respective instance label names are present in the ObserverVec. For
|
||||||
|
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||||
|
// partitioning of Histograms is expensive and should be used judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped RoundTripper panics or returns a non-nil error, no values are
|
||||||
|
// reported.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc {
|
||||||
|
code, method := checkLabels(obs)
|
||||||
|
|
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
start := time.Now()
|
||||||
|
resp, err := next.RoundTrip(r)
|
||||||
|
if err == nil {
|
||||||
|
obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
})
|
||||||
|
}
|
144
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
generated
vendored
Normal file
144
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package promhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstrumentTrace is used to offer flexibility in instrumenting the available
|
||||||
|
// httptrace.ClientTrace hook functions. Each function is passed a float64
|
||||||
|
// representing the time in seconds since the start of the http request. A user
|
||||||
|
// may choose to use separately buckets Histograms, or implement custom
|
||||||
|
// instance labels on a per function basis.
|
||||||
|
type InstrumentTrace struct {
|
||||||
|
GotConn func(float64)
|
||||||
|
PutIdleConn func(float64)
|
||||||
|
GotFirstResponseByte func(float64)
|
||||||
|
Got100Continue func(float64)
|
||||||
|
DNSStart func(float64)
|
||||||
|
DNSDone func(float64)
|
||||||
|
ConnectStart func(float64)
|
||||||
|
ConnectDone func(float64)
|
||||||
|
TLSHandshakeStart func(float64)
|
||||||
|
TLSHandshakeDone func(float64)
|
||||||
|
WroteHeaders func(float64)
|
||||||
|
Wait100Continue func(float64)
|
||||||
|
WroteRequest func(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentRoundTripperTrace is a middleware that wraps the provided
|
||||||
|
// RoundTripper and reports times to hook functions provided in the
|
||||||
|
// InstrumentTrace struct. Hook functions that are not present in the provided
|
||||||
|
// InstrumentTrace struct are ignored. Times reported to the hook functions are
|
||||||
|
// time since the start of the request. Only with Go1.9+, those times are
|
||||||
|
// guaranteed to never be negative. (Earlier Go versions are not using a
|
||||||
|
// monotonic clock.) Note that partitioning of Histograms is expensive and
|
||||||
|
// should be used judiciously.
|
||||||
|
//
|
||||||
|
// For hook functions that receive an error as an argument, no observations are
|
||||||
|
// made in the event of a non-nil error value.
|
||||||
|
//
|
||||||
|
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
|
||||||
|
func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc {
|
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
trace := &httptrace.ClientTrace{
|
||||||
|
GotConn: func(_ httptrace.GotConnInfo) {
|
||||||
|
if it.GotConn != nil {
|
||||||
|
it.GotConn(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PutIdleConn: func(err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if it.PutIdleConn != nil {
|
||||||
|
it.PutIdleConn(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DNSStart: func(_ httptrace.DNSStartInfo) {
|
||||||
|
if it.DNSStart != nil {
|
||||||
|
it.DNSStart(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DNSDone: func(_ httptrace.DNSDoneInfo) {
|
||||||
|
if it.DNSStart != nil {
|
||||||
|
it.DNSStart(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ConnectStart: func(_, _ string) {
|
||||||
|
if it.ConnectStart != nil {
|
||||||
|
it.ConnectStart(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ConnectDone: func(_, _ string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if it.ConnectDone != nil {
|
||||||
|
it.ConnectDone(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GotFirstResponseByte: func() {
|
||||||
|
if it.GotFirstResponseByte != nil {
|
||||||
|
it.GotFirstResponseByte(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Got100Continue: func() {
|
||||||
|
if it.Got100Continue != nil {
|
||||||
|
it.Got100Continue(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TLSHandshakeStart: func() {
|
||||||
|
if it.TLSHandshakeStart != nil {
|
||||||
|
it.TLSHandshakeStart(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TLSHandshakeDone: func(_ tls.ConnectionState, err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if it.TLSHandshakeDone != nil {
|
||||||
|
it.TLSHandshakeDone(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
WroteHeaders: func() {
|
||||||
|
if it.WroteHeaders != nil {
|
||||||
|
it.WroteHeaders(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Wait100Continue: func() {
|
||||||
|
if it.Wait100Continue != nil {
|
||||||
|
it.Wait100Continue(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
WroteRequest: func(_ httptrace.WroteRequestInfo) {
|
||||||
|
if it.WroteRequest != nil {
|
||||||
|
it.WroteRequest(time.Since(start).Seconds())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r = r.WithContext(httptrace.WithClientTrace(context.Background(), trace))
|
||||||
|
|
||||||
|
return next.RoundTrip(r)
|
||||||
|
})
|
||||||
|
}
|
447
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
generated
vendored
Normal file
447
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
generated
vendored
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package promhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// magicString is used for the hacky label test in checkLabels. Remove once fixed.
|
||||||
|
const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa"
|
||||||
|
|
||||||
|
// InstrumentHandlerInFlight is a middleware that wraps the provided
|
||||||
|
// http.Handler. It sets the provided prometheus.Gauge to the number of
|
||||||
|
// requests currently handled by the wrapped http.Handler.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
g.Inc()
|
||||||
|
defer g.Dec()
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerDuration is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe the request duration with the provided ObserverVec.
|
||||||
|
// The ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||||
|
// called with the request duration in seconds. Partitioning happens by HTTP
|
||||||
|
// status code and/or HTTP method if the respective instance label names are
|
||||||
|
// present in the ObserverVec. For unpartitioned observations, use an
|
||||||
|
// ObserverVec with zero labels. Note that partitioning of Histograms is
|
||||||
|
// expensive and should be used judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, no values are reported.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
|
||||||
|
code, method := checkLabels(obs)
|
||||||
|
|
||||||
|
if code {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
now := time.Now()
|
||||||
|
d := newDelegator(w, nil)
|
||||||
|
next.ServeHTTP(d, r)
|
||||||
|
|
||||||
|
obs.With(labels(code, method, r.Method, d.Status())).Observe(time.Since(now).Seconds())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
now := time.Now()
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
obs.With(labels(code, method, r.Method, 0)).Observe(time.Since(now).Seconds())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerCounter is a middleware that wraps the provided http.Handler
|
||||||
|
// to observe the request result with the provided CounterVec. The CounterVec
|
||||||
|
// must have zero, one, or two non-const non-curried labels. For those, the only
|
||||||
|
// allowed label names are "code" and "method". The function panics
|
||||||
|
// otherwise. Partitioning of the CounterVec happens by HTTP status code and/or
|
||||||
|
// HTTP method if the respective instance label names are present in the
|
||||||
|
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, the Counter is not incremented.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc {
|
||||||
|
code, method := checkLabels(counter)
|
||||||
|
|
||||||
|
if code {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
d := newDelegator(w, nil)
|
||||||
|
next.ServeHTTP(d, r)
|
||||||
|
counter.With(labels(code, method, r.Method, d.Status())).Inc()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
counter.With(labels(code, method, r.Method, 0)).Inc()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe with the provided ObserverVec the request duration
|
||||||
|
// until the response headers are written. The ObserverVec must have zero, one,
|
||||||
|
// or two non-const non-curried labels. For those, the only allowed label names
|
||||||
|
// are "code" and "method". The function panics otherwise. The Observe method of
|
||||||
|
// the Observer in the ObserverVec is called with the request duration in
|
||||||
|
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||||
|
// respective instance label names are present in the ObserverVec. For
|
||||||
|
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||||
|
// partitioning of Histograms is expensive and should be used judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics before calling WriteHeader, no value is
|
||||||
|
// reported.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
|
||||||
|
code, method := checkLabels(obs)
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
now := time.Now()
|
||||||
|
d := newDelegator(w, func(status int) {
|
||||||
|
obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds())
|
||||||
|
})
|
||||||
|
next.ServeHTTP(d, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerRequestSize is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe the request size with the provided ObserverVec. The
|
||||||
|
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||||
|
// called with the request size in bytes. Partitioning happens by HTTP status
|
||||||
|
// code and/or HTTP method if the respective instance label names are present in
|
||||||
|
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||||
|
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||||
|
// judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, no values are reported.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
|
||||||
|
code, method := checkLabels(obs)
|
||||||
|
|
||||||
|
if code {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
d := newDelegator(w, nil)
|
||||||
|
next.ServeHTTP(d, r)
|
||||||
|
size := computeApproximateRequestSize(r)
|
||||||
|
obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(size))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
size := computeApproximateRequestSize(r)
|
||||||
|
obs.With(labels(code, method, r.Method, 0)).Observe(float64(size))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerResponseSize is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe the response size with the provided ObserverVec. The
|
||||||
|
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||||
|
// called with the response size in bytes. Partitioning happens by HTTP status
|
||||||
|
// code and/or HTTP method if the respective instance label names are present in
|
||||||
|
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||||
|
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||||
|
// judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, no values are reported.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler) http.Handler {
|
||||||
|
code, method := checkLabels(obs)
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
d := newDelegator(w, nil)
|
||||||
|
next.ServeHTTP(d, r)
|
||||||
|
obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(d.Written()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLabels(c prometheus.Collector) (code bool, method bool) {
|
||||||
|
// TODO(beorn7): Remove this hacky way to check for instance labels
|
||||||
|
// once Descriptors can have their dimensionality queried.
|
||||||
|
var (
|
||||||
|
desc *prometheus.Desc
|
||||||
|
m prometheus.Metric
|
||||||
|
pm dto.Metric
|
||||||
|
lvs []string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get the Desc from the Collector.
|
||||||
|
descc := make(chan *prometheus.Desc, 1)
|
||||||
|
c.Describe(descc)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case desc = <-descc:
|
||||||
|
default:
|
||||||
|
panic("no description provided by collector")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-descc:
|
||||||
|
panic("more than one description provided by collector")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
close(descc)
|
||||||
|
|
||||||
|
// Create a ConstMetric with the Desc. Since we don't know how many
|
||||||
|
// variable labels there are, try for as long as it needs.
|
||||||
|
for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) {
|
||||||
|
m, err = prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, lvs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the metric into a proto message and look at the labels.
|
||||||
|
// If the value is not the magicString, it is a constLabel, which doesn't interest us.
|
||||||
|
// If the label is curried, it doesn't interest us.
|
||||||
|
// In all other cases, only "code" or "method" is allowed.
|
||||||
|
if err := m.Write(&pm); err != nil {
|
||||||
|
panic("error checking metric for labels")
|
||||||
|
}
|
||||||
|
for _, label := range pm.Label {
|
||||||
|
name, value := label.GetName(), label.GetValue()
|
||||||
|
if value != magicString || isLabelCurried(c, name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch name {
|
||||||
|
case "code":
|
||||||
|
code = true
|
||||||
|
case "method":
|
||||||
|
method = true
|
||||||
|
default:
|
||||||
|
panic("metric partitioned with non-supported labels")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLabelCurried(c prometheus.Collector, label string) bool {
|
||||||
|
// This is even hackier than the label test above.
|
||||||
|
// We essentially try to curry again and see if it works.
|
||||||
|
// But for that, we need to type-convert to the two
|
||||||
|
// types we use here, ObserverVec or *CounterVec.
|
||||||
|
switch v := c.(type) {
|
||||||
|
case *prometheus.CounterVec:
|
||||||
|
if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case prometheus.ObserverVec:
|
||||||
|
if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unsupported metric vec type")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// emptyLabels is a one-time allocation for non-partitioned metrics to avoid
|
||||||
|
// unnecessary allocations on each request.
|
||||||
|
var emptyLabels = prometheus.Labels{}
|
||||||
|
|
||||||
|
func labels(code, method bool, reqMethod string, status int) prometheus.Labels {
|
||||||
|
if !(code || method) {
|
||||||
|
return emptyLabels
|
||||||
|
}
|
||||||
|
labels := prometheus.Labels{}
|
||||||
|
|
||||||
|
if code {
|
||||||
|
labels["code"] = sanitizeCode(status)
|
||||||
|
}
|
||||||
|
if method {
|
||||||
|
labels["method"] = sanitizeMethod(reqMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeApproximateRequestSize(r *http.Request) int {
|
||||||
|
s := 0
|
||||||
|
if r.URL != nil {
|
||||||
|
s += len(r.URL.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
s += len(r.Method)
|
||||||
|
s += len(r.Proto)
|
||||||
|
for name, values := range r.Header {
|
||||||
|
s += len(name)
|
||||||
|
for _, value := range values {
|
||||||
|
s += len(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += len(r.Host)
|
||||||
|
|
||||||
|
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
|
||||||
|
|
||||||
|
if r.ContentLength != -1 {
|
||||||
|
s += int(r.ContentLength)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeMethod(m string) string {
|
||||||
|
switch m {
|
||||||
|
case "GET", "get":
|
||||||
|
return "get"
|
||||||
|
case "PUT", "put":
|
||||||
|
return "put"
|
||||||
|
case "HEAD", "head":
|
||||||
|
return "head"
|
||||||
|
case "POST", "post":
|
||||||
|
return "post"
|
||||||
|
case "DELETE", "delete":
|
||||||
|
return "delete"
|
||||||
|
case "CONNECT", "connect":
|
||||||
|
return "connect"
|
||||||
|
case "OPTIONS", "options":
|
||||||
|
return "options"
|
||||||
|
case "NOTIFY", "notify":
|
||||||
|
return "notify"
|
||||||
|
default:
|
||||||
|
return strings.ToLower(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the wrapped http.Handler has not set a status code, i.e. the value is
|
||||||
|
// currently 0, santizeCode will return 200, for consistency with behavior in
|
||||||
|
// the stdlib.
|
||||||
|
func sanitizeCode(s int) string {
|
||||||
|
switch s {
|
||||||
|
case 100:
|
||||||
|
return "100"
|
||||||
|
case 101:
|
||||||
|
return "101"
|
||||||
|
|
||||||
|
case 200, 0:
|
||||||
|
return "200"
|
||||||
|
case 201:
|
||||||
|
return "201"
|
||||||
|
case 202:
|
||||||
|
return "202"
|
||||||
|
case 203:
|
||||||
|
return "203"
|
||||||
|
case 204:
|
||||||
|
return "204"
|
||||||
|
case 205:
|
||||||
|
return "205"
|
||||||
|
case 206:
|
||||||
|
return "206"
|
||||||
|
|
||||||
|
case 300:
|
||||||
|
return "300"
|
||||||
|
case 301:
|
||||||
|
return "301"
|
||||||
|
case 302:
|
||||||
|
return "302"
|
||||||
|
case 304:
|
||||||
|
return "304"
|
||||||
|
case 305:
|
||||||
|
return "305"
|
||||||
|
case 307:
|
||||||
|
return "307"
|
||||||
|
|
||||||
|
case 400:
|
||||||
|
return "400"
|
||||||
|
case 401:
|
||||||
|
return "401"
|
||||||
|
case 402:
|
||||||
|
return "402"
|
||||||
|
case 403:
|
||||||
|
return "403"
|
||||||
|
case 404:
|
||||||
|
return "404"
|
||||||
|
case 405:
|
||||||
|
return "405"
|
||||||
|
case 406:
|
||||||
|
return "406"
|
||||||
|
case 407:
|
||||||
|
return "407"
|
||||||
|
case 408:
|
||||||
|
return "408"
|
||||||
|
case 409:
|
||||||
|
return "409"
|
||||||
|
case 410:
|
||||||
|
return "410"
|
||||||
|
case 411:
|
||||||
|
return "411"
|
||||||
|
case 412:
|
||||||
|
return "412"
|
||||||
|
case 413:
|
||||||
|
return "413"
|
||||||
|
case 414:
|
||||||
|
return "414"
|
||||||
|
case 415:
|
||||||
|
return "415"
|
||||||
|
case 416:
|
||||||
|
return "416"
|
||||||
|
case 417:
|
||||||
|
return "417"
|
||||||
|
case 418:
|
||||||
|
return "418"
|
||||||
|
|
||||||
|
case 500:
|
||||||
|
return "500"
|
||||||
|
case 501:
|
||||||
|
return "501"
|
||||||
|
case 502:
|
||||||
|
return "502"
|
||||||
|
case 503:
|
||||||
|
return "503"
|
||||||
|
case 504:
|
||||||
|
return "504"
|
||||||
|
case 505:
|
||||||
|
return "505"
|
||||||
|
|
||||||
|
case 428:
|
||||||
|
return "428"
|
||||||
|
case 429:
|
||||||
|
return "429"
|
||||||
|
case 431:
|
||||||
|
return "431"
|
||||||
|
case 511:
|
||||||
|
return "511"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return strconv.Itoa(s)
|
||||||
|
}
|
||||||
|
}
|
807
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
Normal file
807
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
Normal file
|
@ -0,0 +1,807 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Capacity for the channel to collect metrics and descriptors.
|
||||||
|
capMetricChan = 1000
|
||||||
|
capDescChan = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultRegisterer and DefaultGatherer are the implementations of the
|
||||||
|
// Registerer and Gatherer interface a number of convenience functions in this
|
||||||
|
// package act on. Initially, both variables point to the same Registry, which
|
||||||
|
// has a process collector (currently on Linux only, see NewProcessCollector)
|
||||||
|
// and a Go collector (see NewGoCollector) already registered. This approach to
|
||||||
|
// keep default instances as global state mirrors the approach of other packages
|
||||||
|
// in the Go standard library. Note that there are caveats. Change the variables
|
||||||
|
// with caution and only if you understand the consequences. Users who want to
|
||||||
|
// avoid global state altogether should not use the convenience functions and
|
||||||
|
// act on custom instances instead.
|
||||||
|
var (
|
||||||
|
defaultRegistry = NewRegistry()
|
||||||
|
DefaultRegisterer Registerer = defaultRegistry
|
||||||
|
DefaultGatherer Gatherer = defaultRegistry
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
MustRegister(NewProcessCollector(os.Getpid(), ""))
|
||||||
|
MustRegister(NewGoCollector())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegistry creates a new vanilla Registry without any Collectors
|
||||||
|
// pre-registered.
|
||||||
|
func NewRegistry() *Registry {
|
||||||
|
return &Registry{
|
||||||
|
collectorsByID: map[uint64]Collector{},
|
||||||
|
descIDs: map[uint64]struct{}{},
|
||||||
|
dimHashesByName: map[string]uint64{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPedanticRegistry returns a registry that checks during collection if each
|
||||||
|
// collected Metric is consistent with its reported Desc, and if the Desc has
|
||||||
|
// actually been registered with the registry.
|
||||||
|
//
|
||||||
|
// Usually, a Registry will be happy as long as the union of all collected
|
||||||
|
// Metrics is consistent and valid even if some metrics are not consistent with
|
||||||
|
// their own Desc or a Desc provided by their registered Collector. Well-behaved
|
||||||
|
// Collectors and Metrics will only provide consistent Descs. This Registry is
|
||||||
|
// useful to test the implementation of Collectors and Metrics.
|
||||||
|
func NewPedanticRegistry() *Registry {
|
||||||
|
r := NewRegistry()
|
||||||
|
r.pedanticChecksEnabled = true
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registerer is the interface for the part of a registry in charge of
|
||||||
|
// registering and unregistering. Users of custom registries should use
|
||||||
|
// Registerer as type for registration purposes (rather than the Registry type
|
||||||
|
// directly). In that way, they are free to use custom Registerer implementation
|
||||||
|
// (e.g. for testing purposes).
|
||||||
|
type Registerer interface {
|
||||||
|
// Register registers a new Collector to be included in metrics
|
||||||
|
// collection. It returns an error if the descriptors provided by the
|
||||||
|
// Collector are invalid or if they — in combination with descriptors of
|
||||||
|
// already registered Collectors — do not fulfill the consistency and
|
||||||
|
// uniqueness criteria described in the documentation of metric.Desc.
|
||||||
|
//
|
||||||
|
// If the provided Collector is equal to a Collector already registered
|
||||||
|
// (which includes the case of re-registering the same Collector), the
|
||||||
|
// returned error is an instance of AlreadyRegisteredError, which
|
||||||
|
// contains the previously registered Collector.
|
||||||
|
//
|
||||||
|
// It is in general not safe to register the same Collector multiple
|
||||||
|
// times concurrently.
|
||||||
|
Register(Collector) error
|
||||||
|
// MustRegister works like Register but registers any number of
|
||||||
|
// Collectors and panics upon the first registration that causes an
|
||||||
|
// error.
|
||||||
|
MustRegister(...Collector)
|
||||||
|
// Unregister unregisters the Collector that equals the Collector passed
|
||||||
|
// in as an argument. (Two Collectors are considered equal if their
|
||||||
|
// Describe method yields the same set of descriptors.) The function
|
||||||
|
// returns whether a Collector was unregistered.
|
||||||
|
//
|
||||||
|
// Note that even after unregistering, it will not be possible to
|
||||||
|
// register a new Collector that is inconsistent with the unregistered
|
||||||
|
// Collector, e.g. a Collector collecting metrics with the same name but
|
||||||
|
// a different help string. The rationale here is that the same registry
|
||||||
|
// instance must only collect consistent metrics throughout its
|
||||||
|
// lifetime.
|
||||||
|
Unregister(Collector) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gatherer is the interface for the part of a registry in charge of gathering
|
||||||
|
// the collected metrics into a number of MetricFamilies. The Gatherer interface
|
||||||
|
// comes with the same general implication as described for the Registerer
|
||||||
|
// interface.
|
||||||
|
type Gatherer interface {
|
||||||
|
// Gather calls the Collect method of the registered Collectors and then
|
||||||
|
// gathers the collected metrics into a lexicographically sorted slice
|
||||||
|
// of MetricFamily protobufs. Even if an error occurs, Gather attempts
|
||||||
|
// to gather as many metrics as possible. Hence, if a non-nil error is
|
||||||
|
// returned, the returned MetricFamily slice could be nil (in case of a
|
||||||
|
// fatal error that prevented any meaningful metric collection) or
|
||||||
|
// contain a number of MetricFamily protobufs, some of which might be
|
||||||
|
// incomplete, and some might be missing altogether. The returned error
|
||||||
|
// (which might be a MultiError) explains the details. In scenarios
|
||||||
|
// where complete collection is critical, the returned MetricFamily
|
||||||
|
// protobufs should be disregarded if the returned error is non-nil.
|
||||||
|
Gather() ([]*dto.MetricFamily, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register registers the provided Collector with the DefaultRegisterer.
|
||||||
|
//
|
||||||
|
// Register is a shortcut for DefaultRegisterer.Register(c). See there for more
|
||||||
|
// details.
|
||||||
|
func Register(c Collector) error {
|
||||||
|
return DefaultRegisterer.Register(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustRegister registers the provided Collectors with the DefaultRegisterer and
|
||||||
|
// panics if any error occurs.
|
||||||
|
//
|
||||||
|
// MustRegister is a shortcut for DefaultRegisterer.MustRegister(cs...). See
|
||||||
|
// there for more details.
|
||||||
|
func MustRegister(cs ...Collector) {
|
||||||
|
DefaultRegisterer.MustRegister(cs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister removes the registration of the provided Collector from the
|
||||||
|
// DefaultRegisterer.
|
||||||
|
//
|
||||||
|
// Unregister is a shortcut for DefaultRegisterer.Unregister(c). See there for
|
||||||
|
// more details.
|
||||||
|
func Unregister(c Collector) bool {
|
||||||
|
return DefaultRegisterer.Unregister(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GathererFunc turns a function into a Gatherer.
|
||||||
|
type GathererFunc func() ([]*dto.MetricFamily, error)
|
||||||
|
|
||||||
|
// Gather implements Gatherer.
|
||||||
|
func (gf GathererFunc) Gather() ([]*dto.MetricFamily, error) {
|
||||||
|
return gf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlreadyRegisteredError is returned by the Register method if the Collector to
|
||||||
|
// be registered has already been registered before, or a different Collector
|
||||||
|
// that collects the same metrics has been registered before. Registration fails
|
||||||
|
// in that case, but you can detect from the kind of error what has
|
||||||
|
// happened. The error contains fields for the existing Collector and the
|
||||||
|
// (rejected) new Collector that equals the existing one. This can be used to
|
||||||
|
// find out if an equal Collector has been registered before and switch over to
|
||||||
|
// using the old one, as demonstrated in the example.
|
||||||
|
type AlreadyRegisteredError struct {
|
||||||
|
ExistingCollector, NewCollector Collector
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err AlreadyRegisteredError) Error() string {
|
||||||
|
return "duplicate metrics collector registration attempted"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiError is a slice of errors implementing the error interface. It is used
|
||||||
|
// by a Gatherer to report multiple errors during MetricFamily gathering.
|
||||||
|
type MultiError []error
|
||||||
|
|
||||||
|
func (errs MultiError) Error() string {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
fmt.Fprintf(buf, "%d error(s) occurred:", len(errs))
|
||||||
|
for _, err := range errs {
|
||||||
|
fmt.Fprintf(buf, "\n* %s", err)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append appends the provided error if it is not nil.
|
||||||
|
func (errs *MultiError) Append(err error) {
|
||||||
|
if err != nil {
|
||||||
|
*errs = append(*errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only
|
||||||
|
// contained error as error if len(errs is 1). In all other cases, it returns
|
||||||
|
// the MultiError directly. This is helpful for returning a MultiError in a way
|
||||||
|
// that only uses the MultiError if needed.
|
||||||
|
func (errs MultiError) MaybeUnwrap() error {
|
||||||
|
switch len(errs) {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
return errs[0]
|
||||||
|
default:
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry registers Prometheus collectors, collects their metrics, and gathers
|
||||||
|
// them into MetricFamilies for exposition. It implements both Registerer and
|
||||||
|
// Gatherer. The zero value is not usable. Create instances with NewRegistry or
|
||||||
|
// NewPedanticRegistry.
|
||||||
|
type Registry struct {
|
||||||
|
mtx sync.RWMutex
|
||||||
|
collectorsByID map[uint64]Collector // ID is a hash of the descIDs.
|
||||||
|
descIDs map[uint64]struct{}
|
||||||
|
dimHashesByName map[string]uint64
|
||||||
|
pedanticChecksEnabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register implements Registerer.
|
||||||
|
func (r *Registry) Register(c Collector) error {
|
||||||
|
var (
|
||||||
|
descChan = make(chan *Desc, capDescChan)
|
||||||
|
newDescIDs = map[uint64]struct{}{}
|
||||||
|
newDimHashesByName = map[string]uint64{}
|
||||||
|
collectorID uint64 // Just a sum of all desc IDs.
|
||||||
|
duplicateDescErr error
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
c.Describe(descChan)
|
||||||
|
close(descChan)
|
||||||
|
}()
|
||||||
|
r.mtx.Lock()
|
||||||
|
defer r.mtx.Unlock()
|
||||||
|
// Conduct various tests...
|
||||||
|
for desc := range descChan {
|
||||||
|
|
||||||
|
// Is the descriptor valid at all?
|
||||||
|
if desc.err != nil {
|
||||||
|
return fmt.Errorf("descriptor %s is invalid: %s", desc, desc.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the descID unique?
|
||||||
|
// (In other words: Is the fqName + constLabel combination unique?)
|
||||||
|
if _, exists := r.descIDs[desc.id]; exists {
|
||||||
|
duplicateDescErr = fmt.Errorf("descriptor %s already exists with the same fully-qualified name and const label values", desc)
|
||||||
|
}
|
||||||
|
// If it is not a duplicate desc in this collector, add it to
|
||||||
|
// the collectorID. (We allow duplicate descs within the same
|
||||||
|
// collector, but their existence must be a no-op.)
|
||||||
|
if _, exists := newDescIDs[desc.id]; !exists {
|
||||||
|
newDescIDs[desc.id] = struct{}{}
|
||||||
|
collectorID += desc.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are all the label names and the help string consistent with
|
||||||
|
// previous descriptors of the same name?
|
||||||
|
// First check existing descriptors...
|
||||||
|
if dimHash, exists := r.dimHashesByName[desc.fqName]; exists {
|
||||||
|
if dimHash != desc.dimHash {
|
||||||
|
return fmt.Errorf("a previously registered descriptor with the same fully-qualified name as %s has different label names or a different help string", desc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ...then check the new descriptors already seen.
|
||||||
|
if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
|
||||||
|
if dimHash != desc.dimHash {
|
||||||
|
return fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newDimHashesByName[desc.fqName] = desc.dimHash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Did anything happen at all?
|
||||||
|
if len(newDescIDs) == 0 {
|
||||||
|
return errors.New("collector has no descriptors")
|
||||||
|
}
|
||||||
|
if existing, exists := r.collectorsByID[collectorID]; exists {
|
||||||
|
return AlreadyRegisteredError{
|
||||||
|
ExistingCollector: existing,
|
||||||
|
NewCollector: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the collectorID is new, but at least one of the descs existed
|
||||||
|
// before, we are in trouble.
|
||||||
|
if duplicateDescErr != nil {
|
||||||
|
return duplicateDescErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only after all tests have passed, actually register.
|
||||||
|
r.collectorsByID[collectorID] = c
|
||||||
|
for hash := range newDescIDs {
|
||||||
|
r.descIDs[hash] = struct{}{}
|
||||||
|
}
|
||||||
|
for name, dimHash := range newDimHashesByName {
|
||||||
|
r.dimHashesByName[name] = dimHash
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister implements Registerer.
|
||||||
|
func (r *Registry) Unregister(c Collector) bool {
|
||||||
|
var (
|
||||||
|
descChan = make(chan *Desc, capDescChan)
|
||||||
|
descIDs = map[uint64]struct{}{}
|
||||||
|
collectorID uint64 // Just a sum of the desc IDs.
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
c.Describe(descChan)
|
||||||
|
close(descChan)
|
||||||
|
}()
|
||||||
|
for desc := range descChan {
|
||||||
|
if _, exists := descIDs[desc.id]; !exists {
|
||||||
|
collectorID += desc.id
|
||||||
|
descIDs[desc.id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.mtx.RLock()
|
||||||
|
if _, exists := r.collectorsByID[collectorID]; !exists {
|
||||||
|
r.mtx.RUnlock()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
r.mtx.RUnlock()
|
||||||
|
|
||||||
|
r.mtx.Lock()
|
||||||
|
defer r.mtx.Unlock()
|
||||||
|
|
||||||
|
delete(r.collectorsByID, collectorID)
|
||||||
|
for id := range descIDs {
|
||||||
|
delete(r.descIDs, id)
|
||||||
|
}
|
||||||
|
// dimHashesByName is left untouched as those must be consistent
|
||||||
|
// throughout the lifetime of a program.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustRegister implements Registerer.
|
||||||
|
func (r *Registry) MustRegister(cs ...Collector) {
|
||||||
|
for _, c := range cs {
|
||||||
|
if err := r.Register(c); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather implements Gatherer.
|
||||||
|
func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||||
|
var (
|
||||||
|
metricChan = make(chan Metric, capMetricChan)
|
||||||
|
metricHashes = map[uint64]struct{}{}
|
||||||
|
dimHashes = map[string]uint64{}
|
||||||
|
wg sync.WaitGroup
|
||||||
|
errs MultiError // The collected errors to return in the end.
|
||||||
|
registeredDescIDs map[uint64]struct{} // Only used for pedantic checks
|
||||||
|
)
|
||||||
|
|
||||||
|
r.mtx.RLock()
|
||||||
|
goroutineBudget := len(r.collectorsByID)
|
||||||
|
metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
|
||||||
|
collectors := make(chan Collector, len(r.collectorsByID))
|
||||||
|
for _, collector := range r.collectorsByID {
|
||||||
|
collectors <- collector
|
||||||
|
}
|
||||||
|
// In case pedantic checks are enabled, we have to copy the map before
|
||||||
|
// giving up the RLock.
|
||||||
|
if r.pedanticChecksEnabled {
|
||||||
|
registeredDescIDs = make(map[uint64]struct{}, len(r.descIDs))
|
||||||
|
for id := range r.descIDs {
|
||||||
|
registeredDescIDs[id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mtx.RUnlock()
|
||||||
|
|
||||||
|
wg.Add(goroutineBudget)
|
||||||
|
|
||||||
|
collectWorker := func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case collector := <-collectors:
|
||||||
|
collector.Collect(metricChan)
|
||||||
|
wg.Done()
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the first worker now to make sure at least one is running.
|
||||||
|
go collectWorker()
|
||||||
|
goroutineBudget--
|
||||||
|
|
||||||
|
// Close the metricChan once all collectors are collected.
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(metricChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Drain metricChan in case of premature return.
|
||||||
|
defer func() {
|
||||||
|
for range metricChan {
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
collectLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case metric, ok := <-metricChan:
|
||||||
|
if !ok {
|
||||||
|
// metricChan is closed, we are done.
|
||||||
|
break collectLoop
|
||||||
|
}
|
||||||
|
errs.Append(processMetric(
|
||||||
|
metric, metricFamiliesByName,
|
||||||
|
metricHashes, dimHashes,
|
||||||
|
registeredDescIDs,
|
||||||
|
))
|
||||||
|
default:
|
||||||
|
if goroutineBudget <= 0 || len(collectors) == 0 {
|
||||||
|
// All collectors are aleady being worked on or
|
||||||
|
// we have already as many goroutines started as
|
||||||
|
// there are collectors. Just process metrics
|
||||||
|
// from now on.
|
||||||
|
for metric := range metricChan {
|
||||||
|
errs.Append(processMetric(
|
||||||
|
metric, metricFamiliesByName,
|
||||||
|
metricHashes, dimHashes,
|
||||||
|
registeredDescIDs,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
break collectLoop
|
||||||
|
}
|
||||||
|
// Start more workers.
|
||||||
|
go collectWorker()
|
||||||
|
goroutineBudget--
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// processMetric is an internal helper method only used by the Gather method.
|
||||||
|
func processMetric(
|
||||||
|
metric Metric,
|
||||||
|
metricFamiliesByName map[string]*dto.MetricFamily,
|
||||||
|
metricHashes map[uint64]struct{},
|
||||||
|
dimHashes map[string]uint64,
|
||||||
|
registeredDescIDs map[uint64]struct{},
|
||||||
|
) error {
|
||||||
|
desc := metric.Desc()
|
||||||
|
dtoMetric := &dto.Metric{}
|
||||||
|
if err := metric.Write(dtoMetric); err != nil {
|
||||||
|
return fmt.Errorf("error collecting metric %v: %s", desc, err)
|
||||||
|
}
|
||||||
|
metricFamily, ok := metricFamiliesByName[desc.fqName]
|
||||||
|
if ok {
|
||||||
|
if metricFamily.GetHelp() != desc.help {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s has help %q but should have %q",
|
||||||
|
desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// TODO(beorn7): Simplify switch once Desc has type.
|
||||||
|
switch metricFamily.GetType() {
|
||||||
|
case dto.MetricType_COUNTER:
|
||||||
|
if dtoMetric.Counter == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Counter",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_GAUGE:
|
||||||
|
if dtoMetric.Gauge == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Gauge",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_SUMMARY:
|
||||||
|
if dtoMetric.Summary == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Summary",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_UNTYPED:
|
||||||
|
if dtoMetric.Untyped == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be Untyped",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_HISTOGRAM:
|
||||||
|
if dtoMetric.Histogram == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Histogram",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("encountered MetricFamily with invalid type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metricFamily = &dto.MetricFamily{}
|
||||||
|
metricFamily.Name = proto.String(desc.fqName)
|
||||||
|
metricFamily.Help = proto.String(desc.help)
|
||||||
|
// TODO(beorn7): Simplify switch once Desc has type.
|
||||||
|
switch {
|
||||||
|
case dtoMetric.Gauge != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_GAUGE.Enum()
|
||||||
|
case dtoMetric.Counter != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_COUNTER.Enum()
|
||||||
|
case dtoMetric.Summary != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_SUMMARY.Enum()
|
||||||
|
case dtoMetric.Untyped != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_UNTYPED.Enum()
|
||||||
|
case dtoMetric.Histogram != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("empty metric collected: %s", dtoMetric)
|
||||||
|
}
|
||||||
|
metricFamiliesByName[desc.fqName] = metricFamily
|
||||||
|
}
|
||||||
|
if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if registeredDescIDs != nil {
|
||||||
|
// Is the desc registered at all?
|
||||||
|
if _, exist := registeredDescIDs[desc.id]; !exist {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s with unregistered descriptor %s",
|
||||||
|
metricFamily.GetName(), dtoMetric, desc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gatherers is a slice of Gatherer instances that implements the Gatherer
|
||||||
|
// interface itself. Its Gather method calls Gather on all Gatherers in the
|
||||||
|
// slice in order and returns the merged results. Errors returned from the
|
||||||
|
// Gather calles are all returned in a flattened MultiError. Duplicate and
|
||||||
|
// inconsistent Metrics are skipped (first occurrence in slice order wins) and
|
||||||
|
// reported in the returned error.
|
||||||
|
//
|
||||||
|
// Gatherers can be used to merge the Gather results from multiple
|
||||||
|
// Registries. It also provides a way to directly inject existing MetricFamily
|
||||||
|
// protobufs into the gathering by creating a custom Gatherer with a Gather
|
||||||
|
// method that simply returns the existing MetricFamily protobufs. Note that no
|
||||||
|
// registration is involved (in contrast to Collector registration), so
|
||||||
|
// obviously registration-time checks cannot happen. Any inconsistencies between
|
||||||
|
// the gathered MetricFamilies are reported as errors by the Gather method, and
|
||||||
|
// inconsistent Metrics are dropped. Invalid parts of the MetricFamilies
|
||||||
|
// (e.g. syntactically invalid metric or label names) will go undetected.
|
||||||
|
type Gatherers []Gatherer
|
||||||
|
|
||||||
|
// Gather implements Gatherer.
|
||||||
|
func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) {
|
||||||
|
var (
|
||||||
|
metricFamiliesByName = map[string]*dto.MetricFamily{}
|
||||||
|
metricHashes = map[uint64]struct{}{}
|
||||||
|
dimHashes = map[string]uint64{}
|
||||||
|
errs MultiError // The collected errors to return in the end.
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, g := range gs {
|
||||||
|
mfs, err := g.Gather()
|
||||||
|
if err != nil {
|
||||||
|
if multiErr, ok := err.(MultiError); ok {
|
||||||
|
for _, err := range multiErr {
|
||||||
|
errs = append(errs, fmt.Errorf("[from Gatherer #%d] %s", i+1, err))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errs = append(errs, fmt.Errorf("[from Gatherer #%d] %s", i+1, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, mf := range mfs {
|
||||||
|
existingMF, exists := metricFamiliesByName[mf.GetName()]
|
||||||
|
if exists {
|
||||||
|
if existingMF.GetHelp() != mf.GetHelp() {
|
||||||
|
errs = append(errs, fmt.Errorf(
|
||||||
|
"gathered metric family %s has help %q but should have %q",
|
||||||
|
mf.GetName(), mf.GetHelp(), existingMF.GetHelp(),
|
||||||
|
))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if existingMF.GetType() != mf.GetType() {
|
||||||
|
errs = append(errs, fmt.Errorf(
|
||||||
|
"gathered metric family %s has type %s but should have %s",
|
||||||
|
mf.GetName(), mf.GetType(), existingMF.GetType(),
|
||||||
|
))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
existingMF = &dto.MetricFamily{}
|
||||||
|
existingMF.Name = mf.Name
|
||||||
|
existingMF.Help = mf.Help
|
||||||
|
existingMF.Type = mf.Type
|
||||||
|
metricFamiliesByName[mf.GetName()] = existingMF
|
||||||
|
}
|
||||||
|
for _, m := range mf.Metric {
|
||||||
|
if err := checkMetricConsistency(existingMF, m, metricHashes, dimHashes); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existingMF.Metric = append(existingMF.Metric, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// metricSorter is a sortable slice of *dto.Metric.
|
||||||
|
type metricSorter []*dto.Metric
|
||||||
|
|
||||||
|
func (s metricSorter) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s metricSorter) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s metricSorter) Less(i, j int) bool {
|
||||||
|
if len(s[i].Label) != len(s[j].Label) {
|
||||||
|
// This should not happen. The metrics are
|
||||||
|
// inconsistent. However, we have to deal with the fact, as
|
||||||
|
// people might use custom collectors or metric family injection
|
||||||
|
// to create inconsistent metrics. So let's simply compare the
|
||||||
|
// number of labels in this case. That will still yield
|
||||||
|
// reproducible sorting.
|
||||||
|
return len(s[i].Label) < len(s[j].Label)
|
||||||
|
}
|
||||||
|
for n, lp := range s[i].Label {
|
||||||
|
vi := lp.GetValue()
|
||||||
|
vj := s[j].Label[n].GetValue()
|
||||||
|
if vi != vj {
|
||||||
|
return vi < vj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should never arrive here. Multiple metrics with the same
|
||||||
|
// label set in the same scrape will lead to undefined ingestion
|
||||||
|
// behavior. However, as above, we have to provide stable sorting
|
||||||
|
// here, even for inconsistent metrics. So sort equal metrics
|
||||||
|
// by their timestamp, with missing timestamps (implying "now")
|
||||||
|
// coming last.
|
||||||
|
if s[i].TimestampMs == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[j].TimestampMs == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return s[i].GetTimestampMs() < s[j].GetTimestampMs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeMetricFamilies returns a MetricFamily slice with empty
|
||||||
|
// MetricFamilies pruned and the remaining MetricFamilies sorted by name within
|
||||||
|
// the slice, with the contained Metrics sorted within each MetricFamily.
|
||||||
|
func normalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) []*dto.MetricFamily {
|
||||||
|
for _, mf := range metricFamiliesByName {
|
||||||
|
sort.Sort(metricSorter(mf.Metric))
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(metricFamiliesByName))
|
||||||
|
for name, mf := range metricFamiliesByName {
|
||||||
|
if len(mf.Metric) > 0 {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
result := make([]*dto.MetricFamily, 0, len(names))
|
||||||
|
for _, name := range names {
|
||||||
|
result = append(result, metricFamiliesByName[name])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkMetricConsistency checks if the provided Metric is consistent with the
|
||||||
|
// provided MetricFamily. It also hashed the Metric labels and the MetricFamily
|
||||||
|
// name. If the resulting hash is already in the provided metricHashes, an error
|
||||||
|
// is returned. If not, it is added to metricHashes. The provided dimHashes maps
|
||||||
|
// MetricFamily names to their dimHash (hashed sorted label names). If dimHashes
|
||||||
|
// doesn't yet contain a hash for the provided MetricFamily, it is
|
||||||
|
// added. Otherwise, an error is returned if the existing dimHashes in not equal
|
||||||
|
// the calculated dimHash.
|
||||||
|
func checkMetricConsistency(
|
||||||
|
metricFamily *dto.MetricFamily,
|
||||||
|
dtoMetric *dto.Metric,
|
||||||
|
metricHashes map[uint64]struct{},
|
||||||
|
dimHashes map[string]uint64,
|
||||||
|
) error {
|
||||||
|
// Type consistency with metric family.
|
||||||
|
if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
|
||||||
|
metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
|
||||||
|
metricFamily.GetType() == dto.MetricType_SUMMARY && dtoMetric.Summary == nil ||
|
||||||
|
metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil ||
|
||||||
|
metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s is not a %s",
|
||||||
|
metricFamily.GetName(), dtoMetric, metricFamily.GetType(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, labelPair := range dtoMetric.GetLabel() {
|
||||||
|
if !utf8.ValidString(*labelPair.Value) {
|
||||||
|
return fmt.Errorf("collected metric's label %s is not utf8: %#v", *labelPair.Name, *labelPair.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the metric unique (i.e. no other metric with the same name and the same label values)?
|
||||||
|
h := hashNew()
|
||||||
|
h = hashAdd(h, metricFamily.GetName())
|
||||||
|
h = hashAddByte(h, separatorByte)
|
||||||
|
dh := hashNew()
|
||||||
|
// Make sure label pairs are sorted. We depend on it for the consistency
|
||||||
|
// check.
|
||||||
|
sort.Sort(LabelPairSorter(dtoMetric.Label))
|
||||||
|
for _, lp := range dtoMetric.Label {
|
||||||
|
h = hashAdd(h, lp.GetValue())
|
||||||
|
h = hashAddByte(h, separatorByte)
|
||||||
|
dh = hashAdd(dh, lp.GetName())
|
||||||
|
dh = hashAddByte(dh, separatorByte)
|
||||||
|
}
|
||||||
|
if _, exists := metricHashes[h]; exists {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s was collected before with the same name and label values",
|
||||||
|
metricFamily.GetName(), dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if dimHash, ok := dimHashes[metricFamily.GetName()]; ok {
|
||||||
|
if dimHash != dh {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s has label dimensions inconsistent with previously collected metrics in the same metric family",
|
||||||
|
metricFamily.GetName(), dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dimHashes[metricFamily.GetName()] = dh
|
||||||
|
}
|
||||||
|
metricHashes[h] = struct{}{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDescConsistency(
|
||||||
|
metricFamily *dto.MetricFamily,
|
||||||
|
dtoMetric *dto.Metric,
|
||||||
|
desc *Desc,
|
||||||
|
) error {
|
||||||
|
// Desc help consistency with metric family help.
|
||||||
|
if metricFamily.GetHelp() != desc.help {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s has help %q but should have %q",
|
||||||
|
metricFamily.GetName(), dtoMetric, metricFamily.GetHelp(), desc.help,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the desc consistent with the content of the metric?
|
||||||
|
lpsFromDesc := make([]*dto.LabelPair, 0, len(dtoMetric.Label))
|
||||||
|
lpsFromDesc = append(lpsFromDesc, desc.constLabelPairs...)
|
||||||
|
for _, l := range desc.variableLabels {
|
||||||
|
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
|
||||||
|
Name: proto.String(l),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(lpsFromDesc) != len(dtoMetric.Label) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"labels in collected metric %s %s are inconsistent with descriptor %s",
|
||||||
|
metricFamily.GetName(), dtoMetric, desc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sort.Sort(LabelPairSorter(lpsFromDesc))
|
||||||
|
for i, lpFromDesc := range lpsFromDesc {
|
||||||
|
lpFromMetric := dtoMetric.Label[i]
|
||||||
|
if lpFromDesc.GetName() != lpFromMetric.GetName() ||
|
||||||
|
lpFromDesc.Value != nil && lpFromDesc.GetValue() != lpFromMetric.GetValue() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"labels in collected metric %s %s are inconsistent with descriptor %s",
|
||||||
|
metricFamily.GetName(), dtoMetric, desc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
609
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
Normal file
609
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
Normal file
|
@ -0,0 +1,609 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/beorn7/perks/quantile"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// quantileLabel is used for the label that defines the quantile in a
|
||||||
|
// summary.
|
||||||
|
const quantileLabel = "quantile"
|
||||||
|
|
||||||
|
// A Summary captures individual observations from an event or sample stream and
|
||||||
|
// summarizes them in a manner similar to traditional summary statistics: 1. sum
|
||||||
|
// of observations, 2. observation count, 3. rank estimations.
|
||||||
|
//
|
||||||
|
// A typical use-case is the observation of request latencies. By default, a
|
||||||
|
// Summary provides the median, the 90th and the 99th percentile of the latency
|
||||||
|
// as rank estimations. However, the default behavior will change in the
|
||||||
|
// upcoming v0.10 of the library. There will be no rank estiamtions at all by
|
||||||
|
// default. For a sane transition, it is recommended to set the desired rank
|
||||||
|
// estimations explicitly.
|
||||||
|
//
|
||||||
|
// Note that the rank estimations cannot be aggregated in a meaningful way with
|
||||||
|
// the Prometheus query language (i.e. you cannot average or add them). If you
|
||||||
|
// need aggregatable quantiles (e.g. you want the 99th percentile latency of all
|
||||||
|
// queries served across all instances of a service), consider the Histogram
|
||||||
|
// metric type. See the Prometheus documentation for more details.
|
||||||
|
//
|
||||||
|
// To create Summary instances, use NewSummary.
|
||||||
|
type Summary interface {
|
||||||
|
Metric
|
||||||
|
Collector
|
||||||
|
|
||||||
|
// Observe adds a single observation to the summary.
|
||||||
|
Observe(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefObjectives are the default Summary quantile values.
|
||||||
|
//
|
||||||
|
// Deprecated: DefObjectives will not be used as the default objectives in
|
||||||
|
// v0.10 of the library. The default Summary will have no quantiles then.
|
||||||
|
var (
|
||||||
|
DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
|
||||||
|
|
||||||
|
errQuantileLabelNotAllowed = fmt.Errorf(
|
||||||
|
"%q is not allowed as label name in summaries", quantileLabel,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default values for SummaryOpts.
|
||||||
|
const (
|
||||||
|
// DefMaxAge is the default duration for which observations stay
|
||||||
|
// relevant.
|
||||||
|
DefMaxAge time.Duration = 10 * time.Minute
|
||||||
|
// DefAgeBuckets is the default number of buckets used to calculate the
|
||||||
|
// age of observations.
|
||||||
|
DefAgeBuckets = 5
|
||||||
|
// DefBufCap is the standard buffer size for collecting Summary observations.
|
||||||
|
DefBufCap = 500
|
||||||
|
)
|
||||||
|
|
||||||
|
// SummaryOpts bundles the options for creating a Summary metric. It is
|
||||||
|
// mandatory to set Name and Help to a non-empty string. While all other fields
|
||||||
|
// are optional and can safely be left at their zero value, it is recommended to
|
||||||
|
// explicitly set the Objectives field to the desired value as the default value
|
||||||
|
// will change in the upcoming v0.10 of the library.
|
||||||
|
type SummaryOpts struct {
|
||||||
|
// Namespace, Subsystem, and Name are components of the fully-qualified
|
||||||
|
// name of the Summary (created by joining these components with
|
||||||
|
// "_"). Only Name is mandatory, the others merely help structuring the
|
||||||
|
// name. Note that the fully-qualified name of the Summary must be a
|
||||||
|
// valid Prometheus metric name.
|
||||||
|
Namespace string
|
||||||
|
Subsystem string
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Help provides information about this Summary. Mandatory!
|
||||||
|
//
|
||||||
|
// Metrics with the same fully-qualified name must have the same Help
|
||||||
|
// string.
|
||||||
|
Help string
|
||||||
|
|
||||||
|
// ConstLabels are used to attach fixed labels to this metric. Metrics
|
||||||
|
// with the same fully-qualified name must have the same label names in
|
||||||
|
// their ConstLabels.
|
||||||
|
//
|
||||||
|
// ConstLabels are only used rarely. In particular, do not use them to
|
||||||
|
// attach the same labels to all your metrics. Those use cases are
|
||||||
|
// better covered by target labels set by the scraping Prometheus
|
||||||
|
// server, or by one specific metric (e.g. a build_info or a
|
||||||
|
// machine_role metric). See also
|
||||||
|
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||||
|
ConstLabels Labels
|
||||||
|
|
||||||
|
// Objectives defines the quantile rank estimates with their respective
|
||||||
|
// absolute error. If Objectives[q] = e, then the value reported for q
|
||||||
|
// will be the φ-quantile value for some φ between q-e and q+e. The
|
||||||
|
// default value is DefObjectives. It is used if Objectives is left at
|
||||||
|
// its zero value (i.e. nil). To create a Summary without Objectives,
|
||||||
|
// set it to an empty map (i.e. map[float64]float64{}).
|
||||||
|
//
|
||||||
|
// Deprecated: Note that the current value of DefObjectives is
|
||||||
|
// deprecated. It will be replaced by an empty map in v0.10 of the
|
||||||
|
// library. Please explicitly set Objectives to the desired value.
|
||||||
|
Objectives map[float64]float64
|
||||||
|
|
||||||
|
// MaxAge defines the duration for which an observation stays relevant
|
||||||
|
// for the summary. Must be positive. The default value is DefMaxAge.
|
||||||
|
MaxAge time.Duration
|
||||||
|
|
||||||
|
// AgeBuckets is the number of buckets used to exclude observations that
|
||||||
|
// are older than MaxAge from the summary. A higher number has a
|
||||||
|
// resource penalty, so only increase it if the higher resolution is
|
||||||
|
// really required. For very high observation rates, you might want to
|
||||||
|
// reduce the number of age buckets. With only one age bucket, you will
|
||||||
|
// effectively see a complete reset of the summary each time MaxAge has
|
||||||
|
// passed. The default value is DefAgeBuckets.
|
||||||
|
AgeBuckets uint32
|
||||||
|
|
||||||
|
// BufCap defines the default sample stream buffer size. The default
|
||||||
|
// value of DefBufCap should suffice for most uses. If there is a need
|
||||||
|
// to increase the value, a multiple of 500 is recommended (because that
|
||||||
|
// is the internal buffer size of the underlying package
|
||||||
|
// "github.com/bmizerany/perks/quantile").
|
||||||
|
BufCap uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Great fuck-up with the sliding-window decay algorithm... The Merge method of
|
||||||
|
// perk/quantile is actually not working as advertised - and it might be
|
||||||
|
// unfixable, as the underlying algorithm is apparently not capable of merging
|
||||||
|
// summaries in the first place. To avoid using Merge, we are currently adding
|
||||||
|
// observations to _each_ age bucket, i.e. the effort to add a sample is
|
||||||
|
// essentially multiplied by the number of age buckets. When rotating age
|
||||||
|
// buckets, we empty the previous head stream. On scrape time, we simply take
|
||||||
|
// the quantiles from the head stream (no merging required). Result: More effort
|
||||||
|
// on observation time, less effort on scrape time, which is exactly the
|
||||||
|
// opposite of what we try to accomplish, but at least the results are correct.
|
||||||
|
//
|
||||||
|
// The quite elegant previous contraption to merge the age buckets efficiently
|
||||||
|
// on scrape time (see code up commit 6b9530d72ea715f0ba612c0120e6e09fbf1d49d0)
|
||||||
|
// can't be used anymore.
|
||||||
|
|
||||||
|
// NewSummary creates a new Summary based on the provided SummaryOpts.
|
||||||
|
func NewSummary(opts SummaryOpts) Summary {
|
||||||
|
return newSummary(
|
||||||
|
NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
nil,
|
||||||
|
opts.ConstLabels,
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||||
|
if len(desc.variableLabels) != len(labelValues) {
|
||||||
|
panic(errInconsistentCardinality)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range desc.variableLabels {
|
||||||
|
if n == quantileLabel {
|
||||||
|
panic(errQuantileLabelNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, lp := range desc.constLabelPairs {
|
||||||
|
if lp.GetName() == quantileLabel {
|
||||||
|
panic(errQuantileLabelNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Objectives == nil {
|
||||||
|
opts.Objectives = DefObjectives
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.MaxAge < 0 {
|
||||||
|
panic(fmt.Errorf("illegal max age MaxAge=%v", opts.MaxAge))
|
||||||
|
}
|
||||||
|
if opts.MaxAge == 0 {
|
||||||
|
opts.MaxAge = DefMaxAge
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.AgeBuckets == 0 {
|
||||||
|
opts.AgeBuckets = DefAgeBuckets
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.BufCap == 0 {
|
||||||
|
opts.BufCap = DefBufCap
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &summary{
|
||||||
|
desc: desc,
|
||||||
|
|
||||||
|
objectives: opts.Objectives,
|
||||||
|
sortedObjectives: make([]float64, 0, len(opts.Objectives)),
|
||||||
|
|
||||||
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
|
|
||||||
|
hotBuf: make([]float64, 0, opts.BufCap),
|
||||||
|
coldBuf: make([]float64, 0, opts.BufCap),
|
||||||
|
streamDuration: opts.MaxAge / time.Duration(opts.AgeBuckets),
|
||||||
|
}
|
||||||
|
s.headStreamExpTime = time.Now().Add(s.streamDuration)
|
||||||
|
s.hotBufExpTime = s.headStreamExpTime
|
||||||
|
|
||||||
|
for i := uint32(0); i < opts.AgeBuckets; i++ {
|
||||||
|
s.streams = append(s.streams, s.newStream())
|
||||||
|
}
|
||||||
|
s.headStream = s.streams[0]
|
||||||
|
|
||||||
|
for qu := range s.objectives {
|
||||||
|
s.sortedObjectives = append(s.sortedObjectives, qu)
|
||||||
|
}
|
||||||
|
sort.Float64s(s.sortedObjectives)
|
||||||
|
|
||||||
|
s.init(s) // Init self-collection.
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type summary struct {
|
||||||
|
selfCollector
|
||||||
|
|
||||||
|
bufMtx sync.Mutex // Protects hotBuf and hotBufExpTime.
|
||||||
|
mtx sync.Mutex // Protects every other moving part.
|
||||||
|
// Lock bufMtx before mtx if both are needed.
|
||||||
|
|
||||||
|
desc *Desc
|
||||||
|
|
||||||
|
objectives map[float64]float64
|
||||||
|
sortedObjectives []float64
|
||||||
|
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
|
||||||
|
sum float64
|
||||||
|
cnt uint64
|
||||||
|
|
||||||
|
hotBuf, coldBuf []float64
|
||||||
|
|
||||||
|
streams []*quantile.Stream
|
||||||
|
streamDuration time.Duration
|
||||||
|
headStream *quantile.Stream
|
||||||
|
headStreamIdx int
|
||||||
|
headStreamExpTime, hotBufExpTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *summary) Desc() *Desc {
|
||||||
|
return s.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *summary) Observe(v float64) {
|
||||||
|
s.bufMtx.Lock()
|
||||||
|
defer s.bufMtx.Unlock()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
if now.After(s.hotBufExpTime) {
|
||||||
|
s.asyncFlush(now)
|
||||||
|
}
|
||||||
|
s.hotBuf = append(s.hotBuf, v)
|
||||||
|
if len(s.hotBuf) == cap(s.hotBuf) {
|
||||||
|
s.asyncFlush(now)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *summary) Write(out *dto.Metric) error {
|
||||||
|
sum := &dto.Summary{}
|
||||||
|
qs := make([]*dto.Quantile, 0, len(s.objectives))
|
||||||
|
|
||||||
|
s.bufMtx.Lock()
|
||||||
|
s.mtx.Lock()
|
||||||
|
// Swap bufs even if hotBuf is empty to set new hotBufExpTime.
|
||||||
|
s.swapBufs(time.Now())
|
||||||
|
s.bufMtx.Unlock()
|
||||||
|
|
||||||
|
s.flushColdBuf()
|
||||||
|
sum.SampleCount = proto.Uint64(s.cnt)
|
||||||
|
sum.SampleSum = proto.Float64(s.sum)
|
||||||
|
|
||||||
|
for _, rank := range s.sortedObjectives {
|
||||||
|
var q float64
|
||||||
|
if s.headStream.Count() == 0 {
|
||||||
|
q = math.NaN()
|
||||||
|
} else {
|
||||||
|
q = s.headStream.Query(rank)
|
||||||
|
}
|
||||||
|
qs = append(qs, &dto.Quantile{
|
||||||
|
Quantile: proto.Float64(rank),
|
||||||
|
Value: proto.Float64(q),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mtx.Unlock()
|
||||||
|
|
||||||
|
if len(qs) > 0 {
|
||||||
|
sort.Sort(quantSort(qs))
|
||||||
|
}
|
||||||
|
sum.Quantile = qs
|
||||||
|
|
||||||
|
out.Summary = sum
|
||||||
|
out.Label = s.labelPairs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *summary) newStream() *quantile.Stream {
|
||||||
|
return quantile.NewTargeted(s.objectives)
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncFlush needs bufMtx locked.
|
||||||
|
func (s *summary) asyncFlush(now time.Time) {
|
||||||
|
s.mtx.Lock()
|
||||||
|
s.swapBufs(now)
|
||||||
|
|
||||||
|
// Unblock the original goroutine that was responsible for the mutation
|
||||||
|
// that triggered the compaction. But hold onto the global non-buffer
|
||||||
|
// state mutex until the operation finishes.
|
||||||
|
go func() {
|
||||||
|
s.flushColdBuf()
|
||||||
|
s.mtx.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotateStreams needs mtx AND bufMtx locked.
|
||||||
|
func (s *summary) maybeRotateStreams() {
|
||||||
|
for !s.hotBufExpTime.Equal(s.headStreamExpTime) {
|
||||||
|
s.headStream.Reset()
|
||||||
|
s.headStreamIdx++
|
||||||
|
if s.headStreamIdx >= len(s.streams) {
|
||||||
|
s.headStreamIdx = 0
|
||||||
|
}
|
||||||
|
s.headStream = s.streams[s.headStreamIdx]
|
||||||
|
s.headStreamExpTime = s.headStreamExpTime.Add(s.streamDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flushColdBuf needs mtx locked.
|
||||||
|
func (s *summary) flushColdBuf() {
|
||||||
|
for _, v := range s.coldBuf {
|
||||||
|
for _, stream := range s.streams {
|
||||||
|
stream.Insert(v)
|
||||||
|
}
|
||||||
|
s.cnt++
|
||||||
|
s.sum += v
|
||||||
|
}
|
||||||
|
s.coldBuf = s.coldBuf[0:0]
|
||||||
|
s.maybeRotateStreams()
|
||||||
|
}
|
||||||
|
|
||||||
|
// swapBufs needs mtx AND bufMtx locked, coldBuf must be empty.
|
||||||
|
func (s *summary) swapBufs(now time.Time) {
|
||||||
|
if len(s.coldBuf) != 0 {
|
||||||
|
panic("coldBuf is not empty")
|
||||||
|
}
|
||||||
|
s.hotBuf, s.coldBuf = s.coldBuf, s.hotBuf
|
||||||
|
// hotBuf is now empty and gets new expiration set.
|
||||||
|
for now.After(s.hotBufExpTime) {
|
||||||
|
s.hotBufExpTime = s.hotBufExpTime.Add(s.streamDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type quantSort []*dto.Quantile
|
||||||
|
|
||||||
|
func (s quantSort) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s quantSort) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s quantSort) Less(i, j int) bool {
|
||||||
|
return s[i].GetQuantile() < s[j].GetQuantile()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SummaryVec is a Collector that bundles a set of Summaries that all share the
|
||||||
|
// same Desc, but have different values for their variable labels. This is used
|
||||||
|
// if you want to count the same thing partitioned by various dimensions
|
||||||
|
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||||
|
// instances with NewSummaryVec.
|
||||||
|
type SummaryVec struct {
|
||||||
|
*metricVec
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
|
||||||
|
// partitioned by the given label names.
|
||||||
|
func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||||
|
desc := NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
labelNames,
|
||||||
|
opts.ConstLabels,
|
||||||
|
)
|
||||||
|
return &SummaryVec{
|
||||||
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
|
return newSummary(desc, opts, lvs...)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWithLabelValues returns the Summary for the given slice of label
|
||||||
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
|
// label values is accessed for the first time, a new Summary is created.
|
||||||
|
//
|
||||||
|
// It is possible to call this method without using the returned Summary to only
|
||||||
|
// create the new Summary but leave it at its starting value, a Summary without
|
||||||
|
// any observations.
|
||||||
|
//
|
||||||
|
// Keeping the Summary for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Summary from the SummaryVec. In that case,
|
||||||
|
// the Summary will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Summary with the same label values is created later. See also the CounterVec
|
||||||
|
// example.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the GaugeVec example.
|
||||||
|
func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Observer), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetricWith returns the Summary for the given Labels map (the label names
|
||||||
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
|
// accessed for the first time, a new Summary is created. Implications of
|
||||||
|
// creating a Summary without using it and keeping the Summary for later use are
|
||||||
|
// the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
|
if metric != nil {
|
||||||
|
return metric.(Observer), err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
|
// error allows shortcuts like
|
||||||
|
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
||||||
|
func (v *SummaryVec) WithLabelValues(lvs ...string) Observer {
|
||||||
|
s, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||||
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
||||||
|
func (v *SummaryVec) With(labels Labels) Observer {
|
||||||
|
s, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the SummaryVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *SummaryVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &SummaryVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *SummaryVec) MustCurryWith(labels Labels) ObserverVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
|
}
|
||||||
|
|
||||||
|
type constSummary struct {
|
||||||
|
desc *Desc
|
||||||
|
count uint64
|
||||||
|
sum float64
|
||||||
|
quantiles map[float64]float64
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *constSummary) Desc() *Desc {
|
||||||
|
return s.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *constSummary) Write(out *dto.Metric) error {
|
||||||
|
sum := &dto.Summary{}
|
||||||
|
qs := make([]*dto.Quantile, 0, len(s.quantiles))
|
||||||
|
|
||||||
|
sum.SampleCount = proto.Uint64(s.count)
|
||||||
|
sum.SampleSum = proto.Float64(s.sum)
|
||||||
|
|
||||||
|
for rank, q := range s.quantiles {
|
||||||
|
qs = append(qs, &dto.Quantile{
|
||||||
|
Quantile: proto.Float64(rank),
|
||||||
|
Value: proto.Float64(q),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(qs) > 0 {
|
||||||
|
sort.Sort(quantSort(qs))
|
||||||
|
}
|
||||||
|
sum.Quantile = qs
|
||||||
|
|
||||||
|
out.Summary = sum
|
||||||
|
out.Label = s.labelPairs
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConstSummary returns a metric representing a Prometheus summary with fixed
|
||||||
|
// values for the count, sum, and quantiles. As those parameters cannot be
|
||||||
|
// changed, the returned value does not implement the Summary interface (but
|
||||||
|
// only the Metric interface). Users of this package will not have much use for
|
||||||
|
// it in regular operations. However, when implementing custom Collectors, it is
|
||||||
|
// useful as a throw-away metric that is generated on the fly to send it to
|
||||||
|
// Prometheus in the Collect method.
|
||||||
|
//
|
||||||
|
// quantiles maps ranks to quantile values. For example, a median latency of
|
||||||
|
// 0.23s and a 99th percentile latency of 0.56s would be expressed as:
|
||||||
|
// map[float64]float64{0.5: 0.23, 0.99: 0.56}
|
||||||
|
//
|
||||||
|
// NewConstSummary returns an error if the length of labelValues is not
|
||||||
|
// consistent with the variable labels in Desc.
|
||||||
|
func NewConstSummary(
|
||||||
|
desc *Desc,
|
||||||
|
count uint64,
|
||||||
|
sum float64,
|
||||||
|
quantiles map[float64]float64,
|
||||||
|
labelValues ...string,
|
||||||
|
) (Metric, error) {
|
||||||
|
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &constSummary{
|
||||||
|
desc: desc,
|
||||||
|
count: count,
|
||||||
|
sum: sum,
|
||||||
|
quantiles: quantiles,
|
||||||
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustNewConstSummary is a version of NewConstSummary that panics where
|
||||||
|
// NewConstMetric would have returned an error.
|
||||||
|
func MustNewConstSummary(
|
||||||
|
desc *Desc,
|
||||||
|
count uint64,
|
||||||
|
sum float64,
|
||||||
|
quantiles map[float64]float64,
|
||||||
|
labelValues ...string,
|
||||||
|
) Metric {
|
||||||
|
m, err := NewConstSummary(desc, count, sum, quantiles, labelValues...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
51
vendor/github.com/prometheus/client_golang/prometheus/timer.go
generated
vendored
Normal file
51
vendor/github.com/prometheus/client_golang/prometheus/timer.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright 2016 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Timer is a helper type to time functions. Use NewTimer to create new
|
||||||
|
// instances.
|
||||||
|
type Timer struct {
|
||||||
|
begin time.Time
|
||||||
|
observer Observer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTimer creates a new Timer. The provided Observer is used to observe a
|
||||||
|
// duration in seconds. Timer is usually used to time a function call in the
|
||||||
|
// following way:
|
||||||
|
// func TimeMe() {
|
||||||
|
// timer := NewTimer(myHistogram)
|
||||||
|
// defer timer.ObserveDuration()
|
||||||
|
// // Do actual work.
|
||||||
|
// }
|
||||||
|
func NewTimer(o Observer) *Timer {
|
||||||
|
return &Timer{
|
||||||
|
begin: time.Now(),
|
||||||
|
observer: o,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObserveDuration records the duration passed since the Timer was created with
|
||||||
|
// NewTimer. It calls the Observe method of the Observer provided during
|
||||||
|
// construction with the duration in seconds as an argument. ObserveDuration is
|
||||||
|
// usually called with a defer statement.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
func (t *Timer) ObserveDuration() {
|
||||||
|
if t.observer != nil {
|
||||||
|
t.observer.Observe(time.Since(t.begin).Seconds())
|
||||||
|
}
|
||||||
|
}
|
42
vendor/github.com/prometheus/client_golang/prometheus/untyped.go
generated
vendored
Normal file
42
vendor/github.com/prometheus/client_golang/prometheus/untyped.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
// UntypedOpts is an alias for Opts. See there for doc comments.
|
||||||
|
type UntypedOpts Opts
|
||||||
|
|
||||||
|
// UntypedFunc works like GaugeFunc but the collected metric is of type
|
||||||
|
// "Untyped". UntypedFunc is useful to mirror an external metric of unknown
|
||||||
|
// type.
|
||||||
|
//
|
||||||
|
// To create UntypedFunc instances, use NewUntypedFunc.
|
||||||
|
type UntypedFunc interface {
|
||||||
|
Metric
|
||||||
|
Collector
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUntypedFunc creates a new UntypedFunc based on the provided
|
||||||
|
// UntypedOpts. The value reported is determined by calling the given function
|
||||||
|
// from within the Write method. Take into account that metric collection may
|
||||||
|
// happen concurrently. If that results in concurrent calls to Write, like in
|
||||||
|
// the case where an UntypedFunc is directly registered with Prometheus, the
|
||||||
|
// provided function must be concurrency-safe.
|
||||||
|
func NewUntypedFunc(opts UntypedOpts, function func() float64) UntypedFunc {
|
||||||
|
return newValueFunc(NewDesc(
|
||||||
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
opts.Help,
|
||||||
|
nil,
|
||||||
|
opts.ConstLabels,
|
||||||
|
), UntypedValue, function)
|
||||||
|
}
|
160
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
Normal file
160
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValueType is an enumeration of metric types that represent a simple value.
|
||||||
|
type ValueType int
|
||||||
|
|
||||||
|
// Possible values for the ValueType enum.
|
||||||
|
const (
|
||||||
|
_ ValueType = iota
|
||||||
|
CounterValue
|
||||||
|
GaugeValue
|
||||||
|
UntypedValue
|
||||||
|
)
|
||||||
|
|
||||||
|
// valueFunc is a generic metric for simple values retrieved on collect time
|
||||||
|
// from a function. It implements Metric and Collector. Its effective type is
|
||||||
|
// determined by ValueType. This is a low-level building block used by the
|
||||||
|
// library to back the implementations of CounterFunc, GaugeFunc, and
|
||||||
|
// UntypedFunc.
|
||||||
|
type valueFunc struct {
|
||||||
|
selfCollector
|
||||||
|
|
||||||
|
desc *Desc
|
||||||
|
valType ValueType
|
||||||
|
function func() float64
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
// newValueFunc returns a newly allocated valueFunc with the given Desc and
|
||||||
|
// ValueType. The value reported is determined by calling the given function
|
||||||
|
// from within the Write method. Take into account that metric collection may
|
||||||
|
// happen concurrently. If that results in concurrent calls to Write, like in
|
||||||
|
// the case where a valueFunc is directly registered with Prometheus, the
|
||||||
|
// provided function must be concurrency-safe.
|
||||||
|
func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *valueFunc {
|
||||||
|
result := &valueFunc{
|
||||||
|
desc: desc,
|
||||||
|
valType: valueType,
|
||||||
|
function: function,
|
||||||
|
labelPairs: makeLabelPairs(desc, nil),
|
||||||
|
}
|
||||||
|
result.init(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *valueFunc) Desc() *Desc {
|
||||||
|
return v.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *valueFunc) Write(out *dto.Metric) error {
|
||||||
|
return populateMetric(v.valType, v.function(), v.labelPairs, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConstMetric returns a metric with one fixed value that cannot be
|
||||||
|
// changed. Users of this package will not have much use for it in regular
|
||||||
|
// operations. However, when implementing custom Collectors, it is useful as a
|
||||||
|
// throw-away metric that is generated on the fly to send it to Prometheus in
|
||||||
|
// the Collect method. NewConstMetric returns an error if the length of
|
||||||
|
// labelValues is not consistent with the variable labels in Desc.
|
||||||
|
func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
|
||||||
|
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &constMetric{
|
||||||
|
desc: desc,
|
||||||
|
valType: valueType,
|
||||||
|
val: value,
|
||||||
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustNewConstMetric is a version of NewConstMetric that panics where
|
||||||
|
// NewConstMetric would have returned an error.
|
||||||
|
func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric {
|
||||||
|
m, err := NewConstMetric(desc, valueType, value, labelValues...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
type constMetric struct {
|
||||||
|
desc *Desc
|
||||||
|
valType ValueType
|
||||||
|
val float64
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *constMetric) Desc() *Desc {
|
||||||
|
return m.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *constMetric) Write(out *dto.Metric) error {
|
||||||
|
return populateMetric(m.valType, m.val, m.labelPairs, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateMetric(
|
||||||
|
t ValueType,
|
||||||
|
v float64,
|
||||||
|
labelPairs []*dto.LabelPair,
|
||||||
|
m *dto.Metric,
|
||||||
|
) error {
|
||||||
|
m.Label = labelPairs
|
||||||
|
switch t {
|
||||||
|
case CounterValue:
|
||||||
|
m.Counter = &dto.Counter{Value: proto.Float64(v)}
|
||||||
|
case GaugeValue:
|
||||||
|
m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
|
||||||
|
case UntypedValue:
|
||||||
|
m.Untyped = &dto.Untyped{Value: proto.Float64(v)}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("encountered unknown type %v", t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||||
|
totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
|
||||||
|
if totalLen == 0 {
|
||||||
|
// Super fast path.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(desc.variableLabels) == 0 {
|
||||||
|
// Moderately fast path.
|
||||||
|
return desc.constLabelPairs
|
||||||
|
}
|
||||||
|
labelPairs := make([]*dto.LabelPair, 0, totalLen)
|
||||||
|
for i, n := range desc.variableLabels {
|
||||||
|
labelPairs = append(labelPairs, &dto.LabelPair{
|
||||||
|
Name: proto.String(n),
|
||||||
|
Value: proto.String(labelValues[i]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, lp := range desc.constLabelPairs {
|
||||||
|
labelPairs = append(labelPairs, lp)
|
||||||
|
}
|
||||||
|
sort.Sort(LabelPairSorter(labelPairs))
|
||||||
|
return labelPairs
|
||||||
|
}
|
469
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
Normal file
469
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// metricVec is a Collector to bundle metrics of the same name that differ in
|
||||||
|
// their label values. metricVec is not used directly (and therefore
|
||||||
|
// unexported). It is used as a building block for implementations of vectors of
|
||||||
|
// a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec.
|
||||||
|
// It also handles label currying. It uses basicMetricVec internally.
|
||||||
|
type metricVec struct {
|
||||||
|
*metricMap
|
||||||
|
|
||||||
|
curry []curriedLabelValue
|
||||||
|
|
||||||
|
// hashAdd and hashAddByte can be replaced for testing collision handling.
|
||||||
|
hashAdd func(h uint64, s string) uint64
|
||||||
|
hashAddByte func(h uint64, b byte) uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// newMetricVec returns an initialized metricVec.
|
||||||
|
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
|
||||||
|
return &metricVec{
|
||||||
|
metricMap: &metricMap{
|
||||||
|
metrics: map[uint64][]metricWithLabelValues{},
|
||||||
|
desc: desc,
|
||||||
|
newMetric: newMetric,
|
||||||
|
},
|
||||||
|
hashAdd: hashAdd,
|
||||||
|
hashAddByte: hashAddByte,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLabelValues removes the metric where the variable labels are the same
|
||||||
|
// as those passed in as labels (same order as the VariableLabels in Desc). It
|
||||||
|
// returns true if a metric was deleted.
|
||||||
|
//
|
||||||
|
// It is not an error if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc. However, such inconsistent label count can
|
||||||
|
// never match an actual metric, so the method will always return false in that
|
||||||
|
// case.
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider Delete(Labels) as an
|
||||||
|
// alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the CounterVec example.
|
||||||
|
func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
|
||||||
|
h, err := m.hashLabelValues(lvs)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the metric where the variable labels are the same as those
|
||||||
|
// passed in as labels. It returns true if a metric was deleted.
|
||||||
|
//
|
||||||
|
// It is not an error if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc. However, such inconsistent Labels
|
||||||
|
// can never match an actual metric, so the method will always return false in
|
||||||
|
// that case.
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
||||||
|
// there for pros and cons of the two methods.
|
||||||
|
func (m *metricVec) Delete(labels Labels) bool {
|
||||||
|
h, err := m.hashLabels(labels)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
||||||
|
var (
|
||||||
|
newCurry []curriedLabelValue
|
||||||
|
oldCurry = m.curry
|
||||||
|
iCurry int
|
||||||
|
)
|
||||||
|
for i, label := range m.desc.variableLabels {
|
||||||
|
val, ok := labels[label]
|
||||||
|
if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
|
||||||
|
if ok {
|
||||||
|
return nil, fmt.Errorf("label name %q is already curried", label)
|
||||||
|
}
|
||||||
|
newCurry = append(newCurry, oldCurry[iCurry])
|
||||||
|
iCurry++
|
||||||
|
} else {
|
||||||
|
if !ok {
|
||||||
|
continue // Label stays uncurried.
|
||||||
|
}
|
||||||
|
newCurry = append(newCurry, curriedLabelValue{i, val})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
|
||||||
|
return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &metricVec{
|
||||||
|
metricMap: m.metricMap,
|
||||||
|
curry: newCurry,
|
||||||
|
hashAdd: m.hashAdd,
|
||||||
|
hashAddByte: m.hashAddByte,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||||
|
h, err := m.hashLabelValues(lvs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
|
||||||
|
h, err := m.hashLabels(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||||
|
if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
h = hashNew()
|
||||||
|
curry = m.curry
|
||||||
|
iVals, iCurry int
|
||||||
|
)
|
||||||
|
for i := 0; i < len(m.desc.variableLabels); i++ {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
h = m.hashAdd(h, curry[iCurry].value)
|
||||||
|
iCurry++
|
||||||
|
} else {
|
||||||
|
h = m.hashAdd(h, vals[iVals])
|
||||||
|
iVals++
|
||||||
|
}
|
||||||
|
h = m.hashAddByte(h, model.SeparatorByte)
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
|
||||||
|
if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
h = hashNew()
|
||||||
|
curry = m.curry
|
||||||
|
iCurry int
|
||||||
|
)
|
||||||
|
for i, label := range m.desc.variableLabels {
|
||||||
|
val, ok := labels[label]
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
if ok {
|
||||||
|
return 0, fmt.Errorf("label name %q is already curried", label)
|
||||||
|
}
|
||||||
|
h = m.hashAdd(h, curry[iCurry].value)
|
||||||
|
iCurry++
|
||||||
|
} else {
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("label name %q missing in label map", label)
|
||||||
|
}
|
||||||
|
h = m.hashAdd(h, val)
|
||||||
|
}
|
||||||
|
h = m.hashAddByte(h, model.SeparatorByte)
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// metricWithLabelValues provides the metric and its label values for
|
||||||
|
// disambiguation on hash collision.
|
||||||
|
type metricWithLabelValues struct {
|
||||||
|
values []string
|
||||||
|
metric Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// curriedLabelValue sets the curried value for a label at the given index.
|
||||||
|
type curriedLabelValue struct {
|
||||||
|
index int
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// metricMap is a helper for metricVec and shared between differently curried
|
||||||
|
// metricVecs.
|
||||||
|
type metricMap struct {
|
||||||
|
mtx sync.RWMutex // Protects metrics.
|
||||||
|
metrics map[uint64][]metricWithLabelValues
|
||||||
|
desc *Desc
|
||||||
|
newMetric func(labelValues ...string) Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe implements Collector. It will send exactly one Desc to the provided
|
||||||
|
// channel.
|
||||||
|
func (m *metricMap) Describe(ch chan<- *Desc) {
|
||||||
|
ch <- m.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect implements Collector.
|
||||||
|
func (m *metricMap) Collect(ch chan<- Metric) {
|
||||||
|
m.mtx.RLock()
|
||||||
|
defer m.mtx.RUnlock()
|
||||||
|
|
||||||
|
for _, metrics := range m.metrics {
|
||||||
|
for _, metric := range metrics {
|
||||||
|
ch <- metric.metric
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset deletes all metrics in this vector.
|
||||||
|
func (m *metricMap) Reset() {
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
|
||||||
|
for h := range m.metrics {
|
||||||
|
delete(m.metrics, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteByHashWithLabelValues removes the metric from the hash bucket h. If
|
||||||
|
// there are multiple matches in the bucket, use lvs to select a metric and
|
||||||
|
// remove only that metric.
|
||||||
|
func (m *metricMap) deleteByHashWithLabelValues(
|
||||||
|
h uint64, lvs []string, curry []curriedLabelValue,
|
||||||
|
) bool {
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
i := findMetricWithLabelValues(metrics, lvs, curry)
|
||||||
|
if i >= len(metrics) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(metrics) > 1 {
|
||||||
|
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||||
|
} else {
|
||||||
|
delete(m.metrics, h)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteByHashWithLabels removes the metric from the hash bucket h. If there
|
||||||
|
// are multiple matches in the bucket, use lvs to select a metric and remove
|
||||||
|
// only that metric.
|
||||||
|
func (m *metricMap) deleteByHashWithLabels(
|
||||||
|
h uint64, labels Labels, curry []curriedLabelValue,
|
||||||
|
) bool {
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i := findMetricWithLabels(m.desc, metrics, labels, curry)
|
||||||
|
if i >= len(metrics) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(metrics) > 1 {
|
||||||
|
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||||
|
} else {
|
||||||
|
delete(m.metrics, h)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
|
||||||
|
// or creates it and returns the new one.
|
||||||
|
//
|
||||||
|
// This function holds the mutex.
|
||||||
|
func (m *metricMap) getOrCreateMetricWithLabelValues(
|
||||||
|
hash uint64, lvs []string, curry []curriedLabelValue,
|
||||||
|
) Metric {
|
||||||
|
m.mtx.RLock()
|
||||||
|
metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
|
||||||
|
m.mtx.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return metric
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
|
||||||
|
if !ok {
|
||||||
|
inlinedLVs := inlineLabelValues(lvs, curry)
|
||||||
|
metric = m.newMetric(inlinedLVs...)
|
||||||
|
m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
|
||||||
|
}
|
||||||
|
return metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
|
||||||
|
// or creates it and returns the new one.
|
||||||
|
//
|
||||||
|
// This function holds the mutex.
|
||||||
|
func (m *metricMap) getOrCreateMetricWithLabels(
|
||||||
|
hash uint64, labels Labels, curry []curriedLabelValue,
|
||||||
|
) Metric {
|
||||||
|
m.mtx.RLock()
|
||||||
|
metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
|
||||||
|
m.mtx.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return metric
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
|
||||||
|
if !ok {
|
||||||
|
lvs := extractLabelValues(m.desc, labels, curry)
|
||||||
|
metric = m.newMetric(lvs...)
|
||||||
|
m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
|
||||||
|
}
|
||||||
|
return metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMetricWithHashAndLabelValues gets a metric while handling possible
|
||||||
|
// collisions in the hash space. Must be called while holding the read mutex.
|
||||||
|
func (m *metricMap) getMetricWithHashAndLabelValues(
|
||||||
|
h uint64, lvs []string, curry []curriedLabelValue,
|
||||||
|
) (Metric, bool) {
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
|
if ok {
|
||||||
|
if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
|
||||||
|
return metrics[i].metric, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMetricWithHashAndLabels gets a metric while handling possible collisions in
|
||||||
|
// the hash space. Must be called while holding read mutex.
|
||||||
|
func (m *metricMap) getMetricWithHashAndLabels(
|
||||||
|
h uint64, labels Labels, curry []curriedLabelValue,
|
||||||
|
) (Metric, bool) {
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
|
if ok {
|
||||||
|
if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
|
||||||
|
return metrics[i].metric, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMetricWithLabelValues returns the index of the matching metric or
|
||||||
|
// len(metrics) if not found.
|
||||||
|
func findMetricWithLabelValues(
|
||||||
|
metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
|
||||||
|
) int {
|
||||||
|
for i, metric := range metrics {
|
||||||
|
if matchLabelValues(metric.values, lvs, curry) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMetricWithLabels returns the index of the matching metric or len(metrics)
|
||||||
|
// if not found.
|
||||||
|
func findMetricWithLabels(
|
||||||
|
desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
|
||||||
|
) int {
|
||||||
|
for i, metric := range metrics {
|
||||||
|
if matchLabels(desc, metric.values, labels, curry) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool {
|
||||||
|
if len(values) != len(lvs)+len(curry) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var iLVs, iCurry int
|
||||||
|
for i, v := range values {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
if v != curry[iCurry].value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v != lvs[iLVs] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iLVs++
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
|
||||||
|
if len(values) != len(labels)+len(curry) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iCurry := 0
|
||||||
|
for i, k := range desc.variableLabels {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
if values[i] != curry[iCurry].value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if values[i] != labels[k] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
|
||||||
|
labelValues := make([]string, len(labels)+len(curry))
|
||||||
|
iCurry := 0
|
||||||
|
for i, k := range desc.variableLabels {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
labelValues[i] = curry[iCurry].value
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labelValues[i] = labels[k]
|
||||||
|
}
|
||||||
|
return labelValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
|
||||||
|
labelValues := make([]string, len(lvs)+len(curry))
|
||||||
|
var iCurry, iLVs int
|
||||||
|
for i := range labelValues {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
labelValues[i] = curry[iCurry].value
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labelValues[i] = lvs[iLVs]
|
||||||
|
iLVs++
|
||||||
|
}
|
||||||
|
return labelValues
|
||||||
|
}
|
201
vendor/github.com/prometheus/client_model/LICENSE
generated
vendored
Normal file
201
vendor/github.com/prometheus/client_model/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
5
vendor/github.com/prometheus/client_model/NOTICE
generated
vendored
Normal file
5
vendor/github.com/prometheus/client_model/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Data model artifacts for Prometheus.
|
||||||
|
Copyright 2012-2015 The Prometheus Authors
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
SoundCloud Ltd. (http://soundcloud.com/).
|
26
vendor/github.com/prometheus/client_model/README.md
generated
vendored
Normal file
26
vendor/github.com/prometheus/client_model/README.md
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Background
|
||||||
|
Under most circumstances, manually downloading this repository should never
|
||||||
|
be required.
|
||||||
|
|
||||||
|
# Prerequisites
|
||||||
|
# Base
|
||||||
|
* [Google Protocol Buffers](https://developers.google.com/protocol-buffers)
|
||||||
|
|
||||||
|
## Java
|
||||||
|
* [Apache Maven](http://maven.apache.org)
|
||||||
|
* [Prometheus Maven Repository](https://github.com/prometheus/io.prometheus-maven-repository) checked out into ../io.prometheus-maven-repository
|
||||||
|
|
||||||
|
## Go
|
||||||
|
* [Go](http://golang.org)
|
||||||
|
* [goprotobuf](https://code.google.com/p/goprotobuf)
|
||||||
|
|
||||||
|
## Ruby
|
||||||
|
* [Ruby](https://www.ruby-lang.org)
|
||||||
|
* [bundler](https://rubygems.org/gems/bundler)
|
||||||
|
|
||||||
|
# Building
|
||||||
|
$ make
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
* The Go source code is periodically indexed: [Go Protocol Buffer Model](http://godoc.org/github.com/prometheus/client_model/go).
|
||||||
|
* All of the core developers are accessible via the [Prometheus Developers Mailinglist](https://groups.google.com/forum/?fromgroups#!forum/prometheus-developers).
|
364
vendor/github.com/prometheus/client_model/go/metrics.pb.go
generated
vendored
Normal file
364
vendor/github.com/prometheus/client_model/go/metrics.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
// Code generated by protoc-gen-go.
|
||||||
|
// source: metrics.proto
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package io_prometheus_client is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
metrics.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
LabelPair
|
||||||
|
Gauge
|
||||||
|
Counter
|
||||||
|
Quantile
|
||||||
|
Summary
|
||||||
|
Untyped
|
||||||
|
Histogram
|
||||||
|
Bucket
|
||||||
|
Metric
|
||||||
|
MetricFamily
|
||||||
|
*/
|
||||||
|
package io_prometheus_client
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
type MetricType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetricType_COUNTER MetricType = 0
|
||||||
|
MetricType_GAUGE MetricType = 1
|
||||||
|
MetricType_SUMMARY MetricType = 2
|
||||||
|
MetricType_UNTYPED MetricType = 3
|
||||||
|
MetricType_HISTOGRAM MetricType = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
var MetricType_name = map[int32]string{
|
||||||
|
0: "COUNTER",
|
||||||
|
1: "GAUGE",
|
||||||
|
2: "SUMMARY",
|
||||||
|
3: "UNTYPED",
|
||||||
|
4: "HISTOGRAM",
|
||||||
|
}
|
||||||
|
var MetricType_value = map[string]int32{
|
||||||
|
"COUNTER": 0,
|
||||||
|
"GAUGE": 1,
|
||||||
|
"SUMMARY": 2,
|
||||||
|
"UNTYPED": 3,
|
||||||
|
"HISTOGRAM": 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x MetricType) Enum() *MetricType {
|
||||||
|
p := new(MetricType)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
func (x MetricType) String() string {
|
||||||
|
return proto.EnumName(MetricType_name, int32(x))
|
||||||
|
}
|
||||||
|
func (x *MetricType) UnmarshalJSON(data []byte) error {
|
||||||
|
value, err := proto.UnmarshalJSONEnum(MetricType_value, data, "MetricType")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = MetricType(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LabelPair struct {
|
||||||
|
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
|
Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LabelPair) Reset() { *m = LabelPair{} }
|
||||||
|
func (m *LabelPair) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*LabelPair) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *LabelPair) GetName() string {
|
||||||
|
if m != nil && m.Name != nil {
|
||||||
|
return *m.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LabelPair) GetValue() string {
|
||||||
|
if m != nil && m.Value != nil {
|
||||||
|
return *m.Value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type Gauge struct {
|
||||||
|
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Gauge) Reset() { *m = Gauge{} }
|
||||||
|
func (m *Gauge) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Gauge) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Gauge) GetValue() float64 {
|
||||||
|
if m != nil && m.Value != nil {
|
||||||
|
return *m.Value
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Counter) Reset() { *m = Counter{} }
|
||||||
|
func (m *Counter) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Counter) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Counter) GetValue() float64 {
|
||||||
|
if m != nil && m.Value != nil {
|
||||||
|
return *m.Value
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Quantile struct {
|
||||||
|
Quantile *float64 `protobuf:"fixed64,1,opt,name=quantile" json:"quantile,omitempty"`
|
||||||
|
Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Quantile) Reset() { *m = Quantile{} }
|
||||||
|
func (m *Quantile) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Quantile) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Quantile) GetQuantile() float64 {
|
||||||
|
if m != nil && m.Quantile != nil {
|
||||||
|
return *m.Quantile
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Quantile) GetValue() float64 {
|
||||||
|
if m != nil && m.Value != nil {
|
||||||
|
return *m.Value
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Summary struct {
|
||||||
|
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count" json:"sample_count,omitempty"`
|
||||||
|
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum" json:"sample_sum,omitempty"`
|
||||||
|
Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Summary) Reset() { *m = Summary{} }
|
||||||
|
func (m *Summary) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Summary) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Summary) GetSampleCount() uint64 {
|
||||||
|
if m != nil && m.SampleCount != nil {
|
||||||
|
return *m.SampleCount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Summary) GetSampleSum() float64 {
|
||||||
|
if m != nil && m.SampleSum != nil {
|
||||||
|
return *m.SampleSum
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Summary) GetQuantile() []*Quantile {
|
||||||
|
if m != nil {
|
||||||
|
return m.Quantile
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Untyped struct {
|
||||||
|
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Untyped) Reset() { *m = Untyped{} }
|
||||||
|
func (m *Untyped) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Untyped) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Untyped) GetValue() float64 {
|
||||||
|
if m != nil && m.Value != nil {
|
||||||
|
return *m.Value
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Histogram struct {
|
||||||
|
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count" json:"sample_count,omitempty"`
|
||||||
|
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum" json:"sample_sum,omitempty"`
|
||||||
|
Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Histogram) Reset() { *m = Histogram{} }
|
||||||
|
func (m *Histogram) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Histogram) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Histogram) GetSampleCount() uint64 {
|
||||||
|
if m != nil && m.SampleCount != nil {
|
||||||
|
return *m.SampleCount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Histogram) GetSampleSum() float64 {
|
||||||
|
if m != nil && m.SampleSum != nil {
|
||||||
|
return *m.SampleSum
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Histogram) GetBucket() []*Bucket {
|
||||||
|
if m != nil {
|
||||||
|
return m.Bucket
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bucket struct {
|
||||||
|
CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count" json:"cumulative_count,omitempty"`
|
||||||
|
UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound" json:"upper_bound,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Bucket) Reset() { *m = Bucket{} }
|
||||||
|
func (m *Bucket) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Bucket) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Bucket) GetCumulativeCount() uint64 {
|
||||||
|
if m != nil && m.CumulativeCount != nil {
|
||||||
|
return *m.CumulativeCount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Bucket) GetUpperBound() float64 {
|
||||||
|
if m != nil && m.UpperBound != nil {
|
||||||
|
return *m.UpperBound
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metric struct {
|
||||||
|
Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"`
|
||||||
|
Gauge *Gauge `protobuf:"bytes,2,opt,name=gauge" json:"gauge,omitempty"`
|
||||||
|
Counter *Counter `protobuf:"bytes,3,opt,name=counter" json:"counter,omitempty"`
|
||||||
|
Summary *Summary `protobuf:"bytes,4,opt,name=summary" json:"summary,omitempty"`
|
||||||
|
Untyped *Untyped `protobuf:"bytes,5,opt,name=untyped" json:"untyped,omitempty"`
|
||||||
|
Histogram *Histogram `protobuf:"bytes,7,opt,name=histogram" json:"histogram,omitempty"`
|
||||||
|
TimestampMs *int64 `protobuf:"varint,6,opt,name=timestamp_ms" json:"timestamp_ms,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) Reset() { *m = Metric{} }
|
||||||
|
func (m *Metric) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Metric) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Metric) GetLabel() []*LabelPair {
|
||||||
|
if m != nil {
|
||||||
|
return m.Label
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) GetGauge() *Gauge {
|
||||||
|
if m != nil {
|
||||||
|
return m.Gauge
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) GetCounter() *Counter {
|
||||||
|
if m != nil {
|
||||||
|
return m.Counter
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) GetSummary() *Summary {
|
||||||
|
if m != nil {
|
||||||
|
return m.Summary
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) GetUntyped() *Untyped {
|
||||||
|
if m != nil {
|
||||||
|
return m.Untyped
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) GetHistogram() *Histogram {
|
||||||
|
if m != nil {
|
||||||
|
return m.Histogram
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) GetTimestampMs() int64 {
|
||||||
|
if m != nil && m.TimestampMs != nil {
|
||||||
|
return *m.TimestampMs
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetricFamily struct {
|
||||||
|
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
|
Help *string `protobuf:"bytes,2,opt,name=help" json:"help,omitempty"`
|
||||||
|
Type *MetricType `protobuf:"varint,3,opt,name=type,enum=io.prometheus.client.MetricType" json:"type,omitempty"`
|
||||||
|
Metric []*Metric `protobuf:"bytes,4,rep,name=metric" json:"metric,omitempty"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MetricFamily) Reset() { *m = MetricFamily{} }
|
||||||
|
func (m *MetricFamily) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*MetricFamily) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *MetricFamily) GetName() string {
|
||||||
|
if m != nil && m.Name != nil {
|
||||||
|
return *m.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MetricFamily) GetHelp() string {
|
||||||
|
if m != nil && m.Help != nil {
|
||||||
|
return *m.Help
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MetricFamily) GetType() MetricType {
|
||||||
|
if m != nil && m.Type != nil {
|
||||||
|
return *m.Type
|
||||||
|
}
|
||||||
|
return MetricType_COUNTER
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MetricFamily) GetMetric() []*Metric {
|
||||||
|
if m != nil {
|
||||||
|
return m.Metric
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterEnum("io.prometheus.client.MetricType", MetricType_name, MetricType_value)
|
||||||
|
}
|
201
vendor/github.com/prometheus/common/LICENSE
generated
vendored
Normal file
201
vendor/github.com/prometheus/common/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
5
vendor/github.com/prometheus/common/NOTICE
generated
vendored
Normal file
5
vendor/github.com/prometheus/common/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Common libraries shared by Prometheus Go components.
|
||||||
|
Copyright 2015 The Prometheus Authors
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
SoundCloud Ltd. (http://soundcloud.com/).
|
12
vendor/github.com/prometheus/common/README.md
generated
vendored
Normal file
12
vendor/github.com/prometheus/common/README.md
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Common
|
||||||
|
[![Build Status](https://travis-ci.org/prometheus/common.svg)](https://travis-ci.org/prometheus/common)
|
||||||
|
|
||||||
|
This repository contains Go libraries that are shared across Prometheus
|
||||||
|
components and libraries.
|
||||||
|
|
||||||
|
* **config**: Common configuration structures
|
||||||
|
* **expfmt**: Decoding and encoding for the exposition format
|
||||||
|
* **log**: A logging wrapper around [logrus](https://github.com/sirupsen/logrus)
|
||||||
|
* **model**: Shared data structures
|
||||||
|
* **route**: A routing wrapper around [httprouter](https://github.com/julienschmidt/httprouter) using `context.Context`
|
||||||
|
* **version**: Version informations and metric
|
429
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
Normal file
429
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
Normal file
|
@ -0,0 +1,429 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package expfmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
|
||||||
|
"github.com/matttproud/golang_protobuf_extensions/pbutil"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decoder types decode an input stream into metric families.
|
||||||
|
type Decoder interface {
|
||||||
|
Decode(*dto.MetricFamily) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeOptions contains options used by the Decoder and in sample extraction.
|
||||||
|
type DecodeOptions struct {
|
||||||
|
// Timestamp is added to each value from the stream that has no explicit timestamp set.
|
||||||
|
Timestamp model.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseFormat extracts the correct format from a HTTP response header.
|
||||||
|
// If no matching format can be found FormatUnknown is returned.
|
||||||
|
func ResponseFormat(h http.Header) Format {
|
||||||
|
ct := h.Get(hdrContentType)
|
||||||
|
|
||||||
|
mediatype, params, err := mime.ParseMediaType(ct)
|
||||||
|
if err != nil {
|
||||||
|
return FmtUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
const textType = "text/plain"
|
||||||
|
|
||||||
|
switch mediatype {
|
||||||
|
case ProtoType:
|
||||||
|
if p, ok := params["proto"]; ok && p != ProtoProtocol {
|
||||||
|
return FmtUnknown
|
||||||
|
}
|
||||||
|
if e, ok := params["encoding"]; ok && e != "delimited" {
|
||||||
|
return FmtUnknown
|
||||||
|
}
|
||||||
|
return FmtProtoDelim
|
||||||
|
|
||||||
|
case textType:
|
||||||
|
if v, ok := params["version"]; ok && v != TextVersion {
|
||||||
|
return FmtUnknown
|
||||||
|
}
|
||||||
|
return FmtText
|
||||||
|
}
|
||||||
|
|
||||||
|
return FmtUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new decoder based on the given input format.
|
||||||
|
// If the input format does not imply otherwise, a text format decoder is returned.
|
||||||
|
func NewDecoder(r io.Reader, format Format) Decoder {
|
||||||
|
switch format {
|
||||||
|
case FmtProtoDelim:
|
||||||
|
return &protoDecoder{r: r}
|
||||||
|
}
|
||||||
|
return &textDecoder{r: r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// protoDecoder implements the Decoder interface for protocol buffers.
|
||||||
|
type protoDecoder struct {
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode implements the Decoder interface.
|
||||||
|
func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
|
||||||
|
_, err := pbutil.ReadDelimited(d.r, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !model.IsValidMetricName(model.LabelValue(v.GetName())) {
|
||||||
|
return fmt.Errorf("invalid metric name %q", v.GetName())
|
||||||
|
}
|
||||||
|
for _, m := range v.GetMetric() {
|
||||||
|
if m == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, l := range m.GetLabel() {
|
||||||
|
if l == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !model.LabelValue(l.GetValue()).IsValid() {
|
||||||
|
return fmt.Errorf("invalid label value %q", l.GetValue())
|
||||||
|
}
|
||||||
|
if !model.LabelName(l.GetName()).IsValid() {
|
||||||
|
return fmt.Errorf("invalid label name %q", l.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// textDecoder implements the Decoder interface for the text protocol.
|
||||||
|
type textDecoder struct {
|
||||||
|
r io.Reader
|
||||||
|
p TextParser
|
||||||
|
fams []*dto.MetricFamily
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode implements the Decoder interface.
|
||||||
|
func (d *textDecoder) Decode(v *dto.MetricFamily) error {
|
||||||
|
// TODO(fabxc): Wrap this as a line reader to make streaming safer.
|
||||||
|
if len(d.fams) == 0 {
|
||||||
|
// No cached metric families, read everything and parse metrics.
|
||||||
|
fams, err := d.p.TextToMetricFamilies(d.r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(fams) == 0 {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
d.fams = make([]*dto.MetricFamily, 0, len(fams))
|
||||||
|
for _, f := range fams {
|
||||||
|
d.fams = append(d.fams, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*v = *d.fams[0]
|
||||||
|
d.fams = d.fams[1:]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampleDecoder wraps a Decoder to extract samples from the metric families
|
||||||
|
// decoded by the wrapped Decoder.
|
||||||
|
type SampleDecoder struct {
|
||||||
|
Dec Decoder
|
||||||
|
Opts *DecodeOptions
|
||||||
|
|
||||||
|
f dto.MetricFamily
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode calls the Decode method of the wrapped Decoder and then extracts the
|
||||||
|
// samples from the decoded MetricFamily into the provided model.Vector.
|
||||||
|
func (sd *SampleDecoder) Decode(s *model.Vector) error {
|
||||||
|
err := sd.Dec.Decode(&sd.f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*s, err = extractSamples(&sd.f, sd.Opts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractSamples builds a slice of samples from the provided metric
|
||||||
|
// families. If an error occurs during sample extraction, it continues to
|
||||||
|
// extract from the remaining metric families. The returned error is the last
|
||||||
|
// error that has occured.
|
||||||
|
func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
|
||||||
|
var (
|
||||||
|
all model.Vector
|
||||||
|
lastErr error
|
||||||
|
)
|
||||||
|
for _, f := range fams {
|
||||||
|
some, err := extractSamples(f, o)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
all = append(all, some...)
|
||||||
|
}
|
||||||
|
return all, lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
|
||||||
|
switch f.GetType() {
|
||||||
|
case dto.MetricType_COUNTER:
|
||||||
|
return extractCounter(o, f), nil
|
||||||
|
case dto.MetricType_GAUGE:
|
||||||
|
return extractGauge(o, f), nil
|
||||||
|
case dto.MetricType_SUMMARY:
|
||||||
|
return extractSummary(o, f), nil
|
||||||
|
case dto.MetricType_UNTYPED:
|
||||||
|
return extractUntyped(o, f), nil
|
||||||
|
case dto.MetricType_HISTOGRAM:
|
||||||
|
return extractHistogram(o, f), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||||
|
samples := make(model.Vector, 0, len(f.Metric))
|
||||||
|
|
||||||
|
for _, m := range f.Metric {
|
||||||
|
if m.Counter == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+1)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
|
||||||
|
|
||||||
|
smpl := &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(m.Counter.GetValue()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.TimestampMs != nil {
|
||||||
|
smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
|
||||||
|
} else {
|
||||||
|
smpl.Timestamp = o.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = append(samples, smpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return samples
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||||
|
samples := make(model.Vector, 0, len(f.Metric))
|
||||||
|
|
||||||
|
for _, m := range f.Metric {
|
||||||
|
if m.Gauge == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+1)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
|
||||||
|
|
||||||
|
smpl := &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(m.Gauge.GetValue()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.TimestampMs != nil {
|
||||||
|
smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
|
||||||
|
} else {
|
||||||
|
smpl.Timestamp = o.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = append(samples, smpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return samples
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||||
|
samples := make(model.Vector, 0, len(f.Metric))
|
||||||
|
|
||||||
|
for _, m := range f.Metric {
|
||||||
|
if m.Untyped == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+1)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
|
||||||
|
|
||||||
|
smpl := &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(m.Untyped.GetValue()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.TimestampMs != nil {
|
||||||
|
smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
|
||||||
|
} else {
|
||||||
|
smpl.Timestamp = o.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = append(samples, smpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return samples
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||||
|
samples := make(model.Vector, 0, len(f.Metric))
|
||||||
|
|
||||||
|
for _, m := range f.Metric {
|
||||||
|
if m.Summary == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := o.Timestamp
|
||||||
|
if m.TimestampMs != nil {
|
||||||
|
timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, q := range m.Summary.Quantile {
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+2)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
// BUG(matt): Update other names to "quantile".
|
||||||
|
lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile()))
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
|
||||||
|
|
||||||
|
samples = append(samples, &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(q.GetValue()),
|
||||||
|
Timestamp: timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+1)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
|
||||||
|
|
||||||
|
samples = append(samples, &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(m.Summary.GetSampleSum()),
|
||||||
|
Timestamp: timestamp,
|
||||||
|
})
|
||||||
|
|
||||||
|
lset = make(model.LabelSet, len(m.Label)+1)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
|
||||||
|
|
||||||
|
samples = append(samples, &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(m.Summary.GetSampleCount()),
|
||||||
|
Timestamp: timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return samples
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||||
|
samples := make(model.Vector, 0, len(f.Metric))
|
||||||
|
|
||||||
|
for _, m := range f.Metric {
|
||||||
|
if m.Histogram == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := o.Timestamp
|
||||||
|
if m.TimestampMs != nil {
|
||||||
|
timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
infSeen := false
|
||||||
|
|
||||||
|
for _, q := range m.Histogram.Bucket {
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+2)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound()))
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
|
||||||
|
|
||||||
|
if math.IsInf(q.GetUpperBound(), +1) {
|
||||||
|
infSeen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = append(samples, &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(q.GetCumulativeCount()),
|
||||||
|
Timestamp: timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+1)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
|
||||||
|
|
||||||
|
samples = append(samples, &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(m.Histogram.GetSampleSum()),
|
||||||
|
Timestamp: timestamp,
|
||||||
|
})
|
||||||
|
|
||||||
|
lset = make(model.LabelSet, len(m.Label)+1)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
|
||||||
|
|
||||||
|
count := &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: model.SampleValue(m.Histogram.GetSampleCount()),
|
||||||
|
Timestamp: timestamp,
|
||||||
|
}
|
||||||
|
samples = append(samples, count)
|
||||||
|
|
||||||
|
if !infSeen {
|
||||||
|
// Append an infinity bucket sample.
|
||||||
|
lset := make(model.LabelSet, len(m.Label)+2)
|
||||||
|
for _, p := range m.Label {
|
||||||
|
lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
|
||||||
|
}
|
||||||
|
lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf")
|
||||||
|
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
|
||||||
|
|
||||||
|
samples = append(samples, &model.Sample{
|
||||||
|
Metric: model.Metric(lset),
|
||||||
|
Value: count.Value,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return samples
|
||||||
|
}
|
88
vendor/github.com/prometheus/common/expfmt/encode.go
generated
vendored
Normal file
88
vendor/github.com/prometheus/common/expfmt/encode.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package expfmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/matttproud/golang_protobuf_extensions/pbutil"
|
||||||
|
"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encoder types encode metric families into an underlying wire protocol.
|
||||||
|
type Encoder interface {
|
||||||
|
Encode(*dto.MetricFamily) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type encoder func(*dto.MetricFamily) error
|
||||||
|
|
||||||
|
func (e encoder) Encode(v *dto.MetricFamily) error {
|
||||||
|
return e(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negotiate returns the Content-Type based on the given Accept header.
|
||||||
|
// If no appropriate accepted type is found, FmtText is returned.
|
||||||
|
func Negotiate(h http.Header) Format {
|
||||||
|
for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
|
||||||
|
// Check for protocol buffer
|
||||||
|
if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
|
||||||
|
switch ac.Params["encoding"] {
|
||||||
|
case "delimited":
|
||||||
|
return FmtProtoDelim
|
||||||
|
case "text":
|
||||||
|
return FmtProtoText
|
||||||
|
case "compact-text":
|
||||||
|
return FmtProtoCompact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for text format.
|
||||||
|
ver := ac.Params["version"]
|
||||||
|
if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
|
||||||
|
return FmtText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FmtText
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new encoder based on content type negotiation.
|
||||||
|
func NewEncoder(w io.Writer, format Format) Encoder {
|
||||||
|
switch format {
|
||||||
|
case FmtProtoDelim:
|
||||||
|
return encoder(func(v *dto.MetricFamily) error {
|
||||||
|
_, err := pbutil.WriteDelimited(w, v)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
case FmtProtoCompact:
|
||||||
|
return encoder(func(v *dto.MetricFamily) error {
|
||||||
|
_, err := fmt.Fprintln(w, v.String())
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
case FmtProtoText:
|
||||||
|
return encoder(func(v *dto.MetricFamily) error {
|
||||||
|
_, err := fmt.Fprintln(w, proto.MarshalTextString(v))
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
case FmtText:
|
||||||
|
return encoder(func(v *dto.MetricFamily) error {
|
||||||
|
_, err := MetricFamilyToText(w, v)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
panic("expfmt.NewEncoder: unknown format")
|
||||||
|
}
|
38
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
Normal file
38
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package expfmt contains tools for reading and writing Prometheus metrics.
|
||||||
|
package expfmt
|
||||||
|
|
||||||
|
// Format specifies the HTTP content type of the different wire protocols.
|
||||||
|
type Format string
|
||||||
|
|
||||||
|
// Constants to assemble the Content-Type values for the different wire protocols.
|
||||||
|
const (
|
||||||
|
TextVersion = "0.0.4"
|
||||||
|
ProtoType = `application/vnd.google.protobuf`
|
||||||
|
ProtoProtocol = `io.prometheus.client.MetricFamily`
|
||||||
|
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
|
||||||
|
|
||||||
|
// The Content-Type values for the different wire protocols.
|
||||||
|
FmtUnknown Format = `<unknown>`
|
||||||
|
FmtText Format = `text/plain; version=` + TextVersion
|
||||||
|
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
||||||
|
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
||||||
|
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
hdrContentType = "Content-Type"
|
||||||
|
hdrAccept = "Accept"
|
||||||
|
)
|
36
vendor/github.com/prometheus/common/expfmt/fuzz.go
generated
vendored
Normal file
36
vendor/github.com/prometheus/common/expfmt/fuzz.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Build only when actually fuzzing
|
||||||
|
// +build gofuzz
|
||||||
|
|
||||||
|
package expfmt
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
// Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
|
||||||
|
//
|
||||||
|
// go-fuzz-build github.com/prometheus/common/expfmt
|
||||||
|
// go-fuzz -bin expfmt-fuzz.zip -workdir fuzz
|
||||||
|
//
|
||||||
|
// Further input samples should go in the folder fuzz/corpus.
|
||||||
|
func Fuzz(in []byte) int {
|
||||||
|
parser := TextParser{}
|
||||||
|
_, err := parser.TextToMetricFamilies(bytes.NewReader(in))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
303
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
Normal file
303
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package expfmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
||||||
|
// writes the resulting lines to 'out'. It returns the number of bytes written
|
||||||
|
// and any error encountered. The output will have the same order as the input,
|
||||||
|
// no further sorting is performed. Furthermore, this function assumes the input
|
||||||
|
// is already sanitized and does not perform any sanity checks. If the input
|
||||||
|
// contains duplicate metrics or invalid metric or label names, the conversion
|
||||||
|
// will result in invalid text format output.
|
||||||
|
//
|
||||||
|
// This method fulfills the type 'prometheus.encoder'.
|
||||||
|
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
|
var written int
|
||||||
|
|
||||||
|
// Fail-fast checks.
|
||||||
|
if len(in.Metric) == 0 {
|
||||||
|
return written, fmt.Errorf("MetricFamily has no metrics: %s", in)
|
||||||
|
}
|
||||||
|
name := in.GetName()
|
||||||
|
if name == "" {
|
||||||
|
return written, fmt.Errorf("MetricFamily has no name: %s", in)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comments, first HELP, then TYPE.
|
||||||
|
if in.Help != nil {
|
||||||
|
n, err := fmt.Fprintf(
|
||||||
|
out, "# HELP %s %s\n",
|
||||||
|
name, escapeString(*in.Help, false),
|
||||||
|
)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metricType := in.GetType()
|
||||||
|
n, err := fmt.Fprintf(
|
||||||
|
out, "# TYPE %s %s\n",
|
||||||
|
name, strings.ToLower(metricType.String()),
|
||||||
|
)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally the samples, one line for each.
|
||||||
|
for _, metric := range in.Metric {
|
||||||
|
switch metricType {
|
||||||
|
case dto.MetricType_COUNTER:
|
||||||
|
if metric.Counter == nil {
|
||||||
|
return written, fmt.Errorf(
|
||||||
|
"expected counter in metric %s %s", name, metric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
n, err = writeSample(
|
||||||
|
name, metric, "", "",
|
||||||
|
metric.Counter.GetValue(),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
case dto.MetricType_GAUGE:
|
||||||
|
if metric.Gauge == nil {
|
||||||
|
return written, fmt.Errorf(
|
||||||
|
"expected gauge in metric %s %s", name, metric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
n, err = writeSample(
|
||||||
|
name, metric, "", "",
|
||||||
|
metric.Gauge.GetValue(),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
case dto.MetricType_UNTYPED:
|
||||||
|
if metric.Untyped == nil {
|
||||||
|
return written, fmt.Errorf(
|
||||||
|
"expected untyped in metric %s %s", name, metric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
n, err = writeSample(
|
||||||
|
name, metric, "", "",
|
||||||
|
metric.Untyped.GetValue(),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
case dto.MetricType_SUMMARY:
|
||||||
|
if metric.Summary == nil {
|
||||||
|
return written, fmt.Errorf(
|
||||||
|
"expected summary in metric %s %s", name, metric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for _, q := range metric.Summary.Quantile {
|
||||||
|
n, err = writeSample(
|
||||||
|
name, metric,
|
||||||
|
model.QuantileLabel, fmt.Sprint(q.GetQuantile()),
|
||||||
|
q.GetValue(),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err = writeSample(
|
||||||
|
name+"_sum", metric, "", "",
|
||||||
|
metric.Summary.GetSampleSum(),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
written += n
|
||||||
|
n, err = writeSample(
|
||||||
|
name+"_count", metric, "", "",
|
||||||
|
float64(metric.Summary.GetSampleCount()),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
case dto.MetricType_HISTOGRAM:
|
||||||
|
if metric.Histogram == nil {
|
||||||
|
return written, fmt.Errorf(
|
||||||
|
"expected histogram in metric %s %s", name, metric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
infSeen := false
|
||||||
|
for _, q := range metric.Histogram.Bucket {
|
||||||
|
n, err = writeSample(
|
||||||
|
name+"_bucket", metric,
|
||||||
|
model.BucketLabel, fmt.Sprint(q.GetUpperBound()),
|
||||||
|
float64(q.GetCumulativeCount()),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
if math.IsInf(q.GetUpperBound(), +1) {
|
||||||
|
infSeen = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !infSeen {
|
||||||
|
n, err = writeSample(
|
||||||
|
name+"_bucket", metric,
|
||||||
|
model.BucketLabel, "+Inf",
|
||||||
|
float64(metric.Histogram.GetSampleCount()),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
written += n
|
||||||
|
}
|
||||||
|
n, err = writeSample(
|
||||||
|
name+"_sum", metric, "", "",
|
||||||
|
metric.Histogram.GetSampleSum(),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
written += n
|
||||||
|
n, err = writeSample(
|
||||||
|
name+"_count", metric, "", "",
|
||||||
|
float64(metric.Histogram.GetSampleCount()),
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return written, fmt.Errorf(
|
||||||
|
"unexpected type in metric %s %s", name, metric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return written, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSample writes a single sample in text format to out, given the metric
|
||||||
|
// name, the metric proto message itself, optionally an additional label name
|
||||||
|
// and value (use empty strings if not required), and the value. The function
|
||||||
|
// returns the number of bytes written and any error encountered.
|
||||||
|
func writeSample(
|
||||||
|
name string,
|
||||||
|
metric *dto.Metric,
|
||||||
|
additionalLabelName, additionalLabelValue string,
|
||||||
|
value float64,
|
||||||
|
out io.Writer,
|
||||||
|
) (int, error) {
|
||||||
|
var written int
|
||||||
|
n, err := fmt.Fprint(out, name)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
n, err = labelPairsToText(
|
||||||
|
metric.Label,
|
||||||
|
additionalLabelName, additionalLabelValue,
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
n, err = fmt.Fprintf(out, " %v", value)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
if metric.TimestampMs != nil {
|
||||||
|
n, err = fmt.Fprintf(out, " %v", *metric.TimestampMs)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err = out.Write([]byte{'\n'})
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
return written, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelPairsToText converts a slice of LabelPair proto messages plus the
|
||||||
|
// explicitly given additional label pair into text formatted as required by the
|
||||||
|
// text format and writes it to 'out'. An empty slice in combination with an
|
||||||
|
// empty string 'additionalLabelName' results in nothing being
|
||||||
|
// written. Otherwise, the label pairs are written, escaped as required by the
|
||||||
|
// text format, and enclosed in '{...}'. The function returns the number of
|
||||||
|
// bytes written and any error encountered.
|
||||||
|
func labelPairsToText(
|
||||||
|
in []*dto.LabelPair,
|
||||||
|
additionalLabelName, additionalLabelValue string,
|
||||||
|
out io.Writer,
|
||||||
|
) (int, error) {
|
||||||
|
if len(in) == 0 && additionalLabelName == "" {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
var written int
|
||||||
|
separator := '{'
|
||||||
|
for _, lp := range in {
|
||||||
|
n, err := fmt.Fprintf(
|
||||||
|
out, `%c%s="%s"`,
|
||||||
|
separator, lp.GetName(), escapeString(lp.GetValue(), true),
|
||||||
|
)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
separator = ','
|
||||||
|
}
|
||||||
|
if additionalLabelName != "" {
|
||||||
|
n, err := fmt.Fprintf(
|
||||||
|
out, `%c%s="%s"`,
|
||||||
|
separator, additionalLabelName,
|
||||||
|
escapeString(additionalLabelValue, true),
|
||||||
|
)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err := out.Write([]byte{'}'})
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
return written, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
escape = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
||||||
|
escapeWithDoubleQuote = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
||||||
|
// includeDoubleQuote is true - '"' by '\"'.
|
||||||
|
func escapeString(v string, includeDoubleQuote bool) string {
|
||||||
|
if includeDoubleQuote {
|
||||||
|
return escapeWithDoubleQuote.Replace(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return escape.Replace(v)
|
||||||
|
}
|
757
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
Normal file
757
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
Normal file
|
@ -0,0 +1,757 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package expfmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A stateFn is a function that represents a state in a state machine. By
|
||||||
|
// executing it, the state is progressed to the next state. The stateFn returns
|
||||||
|
// another stateFn, which represents the new state. The end state is represented
|
||||||
|
// by nil.
|
||||||
|
type stateFn func() stateFn
|
||||||
|
|
||||||
|
// ParseError signals errors while parsing the simple and flat text-based
|
||||||
|
// exchange format.
|
||||||
|
type ParseError struct {
|
||||||
|
Line int
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e ParseError) Error() string {
|
||||||
|
return fmt.Sprintf("text format parsing error in line %d: %s", e.Line, e.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextParser is used to parse the simple and flat text-based exchange format. Its
|
||||||
|
// zero value is ready to use.
|
||||||
|
type TextParser struct {
|
||||||
|
metricFamiliesByName map[string]*dto.MetricFamily
|
||||||
|
buf *bufio.Reader // Where the parsed input is read through.
|
||||||
|
err error // Most recent error.
|
||||||
|
lineCount int // Tracks the line count for error messages.
|
||||||
|
currentByte byte // The most recent byte read.
|
||||||
|
currentToken bytes.Buffer // Re-used each time a token has to be gathered from multiple bytes.
|
||||||
|
currentMF *dto.MetricFamily
|
||||||
|
currentMetric *dto.Metric
|
||||||
|
currentLabelPair *dto.LabelPair
|
||||||
|
|
||||||
|
// The remaining member variables are only used for summaries/histograms.
|
||||||
|
currentLabels map[string]string // All labels including '__name__' but excluding 'quantile'/'le'
|
||||||
|
// Summary specific.
|
||||||
|
summaries map[uint64]*dto.Metric // Key is created with LabelsToSignature.
|
||||||
|
currentQuantile float64
|
||||||
|
// Histogram specific.
|
||||||
|
histograms map[uint64]*dto.Metric // Key is created with LabelsToSignature.
|
||||||
|
currentBucket float64
|
||||||
|
// These tell us if the currently processed line ends on '_count' or
|
||||||
|
// '_sum' respectively and belong to a summary/histogram, representing the sample
|
||||||
|
// count and sum of that summary/histogram.
|
||||||
|
currentIsSummaryCount, currentIsSummarySum bool
|
||||||
|
currentIsHistogramCount, currentIsHistogramSum bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextToMetricFamilies reads 'in' as the simple and flat text-based exchange
|
||||||
|
// format and creates MetricFamily proto messages. It returns the MetricFamily
|
||||||
|
// proto messages in a map where the metric names are the keys, along with any
|
||||||
|
// error encountered.
|
||||||
|
//
|
||||||
|
// If the input contains duplicate metrics (i.e. lines with the same metric name
|
||||||
|
// and exactly the same label set), the resulting MetricFamily will contain
|
||||||
|
// duplicate Metric proto messages. Similar is true for duplicate label
|
||||||
|
// names. Checks for duplicates have to be performed separately, if required.
|
||||||
|
// Also note that neither the metrics within each MetricFamily are sorted nor
|
||||||
|
// the label pairs within each Metric. Sorting is not required for the most
|
||||||
|
// frequent use of this method, which is sample ingestion in the Prometheus
|
||||||
|
// server. However, for presentation purposes, you might want to sort the
|
||||||
|
// metrics, and in some cases, you must sort the labels, e.g. for consumption by
|
||||||
|
// the metric family injection hook of the Prometheus registry.
|
||||||
|
//
|
||||||
|
// Summaries and histograms are rather special beasts. You would probably not
|
||||||
|
// use them in the simple text format anyway. This method can deal with
|
||||||
|
// summaries and histograms if they are presented in exactly the way the
|
||||||
|
// text.Create function creates them.
|
||||||
|
//
|
||||||
|
// This method must not be called concurrently. If you want to parse different
|
||||||
|
// input concurrently, instantiate a separate Parser for each goroutine.
|
||||||
|
func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
|
||||||
|
p.reset(in)
|
||||||
|
for nextState := p.startOfLine; nextState != nil; nextState = nextState() {
|
||||||
|
// Magic happens here...
|
||||||
|
}
|
||||||
|
// Get rid of empty metric families.
|
||||||
|
for k, mf := range p.metricFamiliesByName {
|
||||||
|
if len(mf.GetMetric()) == 0 {
|
||||||
|
delete(p.metricFamiliesByName, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If p.err is io.EOF now, we have run into a premature end of the input
|
||||||
|
// stream. Turn this error into something nicer and more
|
||||||
|
// meaningful. (io.EOF is often used as a signal for the legitimate end
|
||||||
|
// of an input stream.)
|
||||||
|
if p.err == io.EOF {
|
||||||
|
p.parseError("unexpected end of input stream")
|
||||||
|
}
|
||||||
|
return p.metricFamiliesByName, p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextParser) reset(in io.Reader) {
|
||||||
|
p.metricFamiliesByName = map[string]*dto.MetricFamily{}
|
||||||
|
if p.buf == nil {
|
||||||
|
p.buf = bufio.NewReader(in)
|
||||||
|
} else {
|
||||||
|
p.buf.Reset(in)
|
||||||
|
}
|
||||||
|
p.err = nil
|
||||||
|
p.lineCount = 0
|
||||||
|
if p.summaries == nil || len(p.summaries) > 0 {
|
||||||
|
p.summaries = map[uint64]*dto.Metric{}
|
||||||
|
}
|
||||||
|
if p.histograms == nil || len(p.histograms) > 0 {
|
||||||
|
p.histograms = map[uint64]*dto.Metric{}
|
||||||
|
}
|
||||||
|
p.currentQuantile = math.NaN()
|
||||||
|
p.currentBucket = math.NaN()
|
||||||
|
}
|
||||||
|
|
||||||
|
// startOfLine represents the state where the next byte read from p.buf is the
|
||||||
|
// start of a line (or whitespace leading up to it).
|
||||||
|
func (p *TextParser) startOfLine() stateFn {
|
||||||
|
p.lineCount++
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
// End of input reached. This is the only case where
|
||||||
|
// that is not an error but a signal that we are done.
|
||||||
|
p.err = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch p.currentByte {
|
||||||
|
case '#':
|
||||||
|
return p.startComment
|
||||||
|
case '\n':
|
||||||
|
return p.startOfLine // Empty line, start the next one.
|
||||||
|
}
|
||||||
|
return p.readingMetricName
|
||||||
|
}
|
||||||
|
|
||||||
|
// startComment represents the state where the next byte read from p.buf is the
|
||||||
|
// start of a comment (or whitespace leading up to it).
|
||||||
|
func (p *TextParser) startComment() stateFn {
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentByte == '\n' {
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
if p.readTokenUntilWhitespace(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
// If we have hit the end of line already, there is nothing left
|
||||||
|
// to do. This is not considered a syntax error.
|
||||||
|
if p.currentByte == '\n' {
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
keyword := p.currentToken.String()
|
||||||
|
if keyword != "HELP" && keyword != "TYPE" {
|
||||||
|
// Generic comment, ignore by fast forwarding to end of line.
|
||||||
|
for p.currentByte != '\n' {
|
||||||
|
if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
// There is something. Next has to be a metric name.
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.readTokenAsMetricName(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentByte == '\n' {
|
||||||
|
// At the end of the line already.
|
||||||
|
// Again, this is not considered a syntax error.
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
if !isBlankOrTab(p.currentByte) {
|
||||||
|
p.parseError("invalid metric name in comment")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.setOrCreateCurrentMF()
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentByte == '\n' {
|
||||||
|
// At the end of the line already.
|
||||||
|
// Again, this is not considered a syntax error.
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
switch keyword {
|
||||||
|
case "HELP":
|
||||||
|
return p.readingHelp
|
||||||
|
case "TYPE":
|
||||||
|
return p.readingType
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("code error: unexpected keyword %q", keyword))
|
||||||
|
}
|
||||||
|
|
||||||
|
// readingMetricName represents the state where the last byte read (now in
|
||||||
|
// p.currentByte) is the first byte of a metric name.
|
||||||
|
func (p *TextParser) readingMetricName() stateFn {
|
||||||
|
if p.readTokenAsMetricName(); p.err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if p.currentToken.Len() == 0 {
|
||||||
|
p.parseError("invalid metric name")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.setOrCreateCurrentMF()
|
||||||
|
// Now is the time to fix the type if it hasn't happened yet.
|
||||||
|
if p.currentMF.Type == nil {
|
||||||
|
p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
|
||||||
|
}
|
||||||
|
p.currentMetric = &dto.Metric{}
|
||||||
|
// Do not append the newly created currentMetric to
|
||||||
|
// currentMF.Metric right now. First wait if this is a summary,
|
||||||
|
// and the metric exists already, which we can only know after
|
||||||
|
// having read all the labels.
|
||||||
|
if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
return p.readingLabels
|
||||||
|
}
|
||||||
|
|
||||||
|
// readingLabels represents the state where the last byte read (now in
|
||||||
|
// p.currentByte) is either the first byte of the label set (i.e. a '{'), or the
|
||||||
|
// first byte of the value (otherwise).
|
||||||
|
func (p *TextParser) readingLabels() stateFn {
|
||||||
|
// Summaries/histograms are special. We have to reset the
|
||||||
|
// currentLabels map, currentQuantile and currentBucket before starting to
|
||||||
|
// read labels.
|
||||||
|
if p.currentMF.GetType() == dto.MetricType_SUMMARY || p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||||
|
p.currentLabels = map[string]string{}
|
||||||
|
p.currentLabels[string(model.MetricNameLabel)] = p.currentMF.GetName()
|
||||||
|
p.currentQuantile = math.NaN()
|
||||||
|
p.currentBucket = math.NaN()
|
||||||
|
}
|
||||||
|
if p.currentByte != '{' {
|
||||||
|
return p.readingValue
|
||||||
|
}
|
||||||
|
return p.startLabelName
|
||||||
|
}
|
||||||
|
|
||||||
|
// startLabelName represents the state where the next byte read from p.buf is
|
||||||
|
// the start of a label name (or whitespace leading up to it).
|
||||||
|
func (p *TextParser) startLabelName() stateFn {
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentByte == '}' {
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
return p.readingValue
|
||||||
|
}
|
||||||
|
if p.readTokenAsLabelName(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentToken.Len() == 0 {
|
||||||
|
p.parseError(fmt.Sprintf("invalid label name for metric %q", p.currentMF.GetName()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())}
|
||||||
|
if p.currentLabelPair.GetName() == string(model.MetricNameLabel) {
|
||||||
|
p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Special summary/histogram treatment. Don't add 'quantile' and 'le'
|
||||||
|
// labels to 'real' labels.
|
||||||
|
if !(p.currentMF.GetType() == dto.MetricType_SUMMARY && p.currentLabelPair.GetName() == model.QuantileLabel) &&
|
||||||
|
!(p.currentMF.GetType() == dto.MetricType_HISTOGRAM && p.currentLabelPair.GetName() == model.BucketLabel) {
|
||||||
|
p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPair)
|
||||||
|
}
|
||||||
|
if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentByte != '=' {
|
||||||
|
p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.startLabelValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// startLabelValue represents the state where the next byte read from p.buf is
|
||||||
|
// the start of a (quoted) label value (or whitespace leading up to it).
|
||||||
|
func (p *TextParser) startLabelValue() stateFn {
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentByte != '"' {
|
||||||
|
p.parseError(fmt.Sprintf("expected '\"' at start of label value, found %q", p.currentByte))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if p.readTokenAsLabelValue(); p.err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !model.LabelValue(p.currentToken.String()).IsValid() {
|
||||||
|
p.parseError(fmt.Sprintf("invalid label value %q", p.currentToken.String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.currentLabelPair.Value = proto.String(p.currentToken.String())
|
||||||
|
// Special treatment of summaries:
|
||||||
|
// - Quantile labels are special, will result in dto.Quantile later.
|
||||||
|
// - Other labels have to be added to currentLabels for signature calculation.
|
||||||
|
if p.currentMF.GetType() == dto.MetricType_SUMMARY {
|
||||||
|
if p.currentLabelPair.GetName() == model.QuantileLabel {
|
||||||
|
if p.currentQuantile, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
|
||||||
|
// Create a more helpful error message.
|
||||||
|
p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Similar special treatment of histograms.
|
||||||
|
if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||||
|
if p.currentLabelPair.GetName() == model.BucketLabel {
|
||||||
|
if p.currentBucket, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
|
||||||
|
// Create a more helpful error message.
|
||||||
|
p.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", p.currentLabelPair.GetValue()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
switch p.currentByte {
|
||||||
|
case ',':
|
||||||
|
return p.startLabelName
|
||||||
|
|
||||||
|
case '}':
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
return p.readingValue
|
||||||
|
default:
|
||||||
|
p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.Value))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readingValue represents the state where the last byte read (now in
|
||||||
|
// p.currentByte) is the first byte of the sample value (i.e. a float).
|
||||||
|
func (p *TextParser) readingValue() stateFn {
|
||||||
|
// When we are here, we have read all the labels, so for the
|
||||||
|
// special case of a summary/histogram, we can finally find out
|
||||||
|
// if the metric already exists.
|
||||||
|
if p.currentMF.GetType() == dto.MetricType_SUMMARY {
|
||||||
|
signature := model.LabelsToSignature(p.currentLabels)
|
||||||
|
if summary := p.summaries[signature]; summary != nil {
|
||||||
|
p.currentMetric = summary
|
||||||
|
} else {
|
||||||
|
p.summaries[signature] = p.currentMetric
|
||||||
|
p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
|
||||||
|
}
|
||||||
|
} else if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||||
|
signature := model.LabelsToSignature(p.currentLabels)
|
||||||
|
if histogram := p.histograms[signature]; histogram != nil {
|
||||||
|
p.currentMetric = histogram
|
||||||
|
} else {
|
||||||
|
p.histograms[signature] = p.currentMetric
|
||||||
|
p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
|
||||||
|
}
|
||||||
|
if p.readTokenUntilWhitespace(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
value, err := strconv.ParseFloat(p.currentToken.String(), 64)
|
||||||
|
if err != nil {
|
||||||
|
// Create a more helpful error message.
|
||||||
|
p.parseError(fmt.Sprintf("expected float as value, got %q", p.currentToken.String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch p.currentMF.GetType() {
|
||||||
|
case dto.MetricType_COUNTER:
|
||||||
|
p.currentMetric.Counter = &dto.Counter{Value: proto.Float64(value)}
|
||||||
|
case dto.MetricType_GAUGE:
|
||||||
|
p.currentMetric.Gauge = &dto.Gauge{Value: proto.Float64(value)}
|
||||||
|
case dto.MetricType_UNTYPED:
|
||||||
|
p.currentMetric.Untyped = &dto.Untyped{Value: proto.Float64(value)}
|
||||||
|
case dto.MetricType_SUMMARY:
|
||||||
|
// *sigh*
|
||||||
|
if p.currentMetric.Summary == nil {
|
||||||
|
p.currentMetric.Summary = &dto.Summary{}
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case p.currentIsSummaryCount:
|
||||||
|
p.currentMetric.Summary.SampleCount = proto.Uint64(uint64(value))
|
||||||
|
case p.currentIsSummarySum:
|
||||||
|
p.currentMetric.Summary.SampleSum = proto.Float64(value)
|
||||||
|
case !math.IsNaN(p.currentQuantile):
|
||||||
|
p.currentMetric.Summary.Quantile = append(
|
||||||
|
p.currentMetric.Summary.Quantile,
|
||||||
|
&dto.Quantile{
|
||||||
|
Quantile: proto.Float64(p.currentQuantile),
|
||||||
|
Value: proto.Float64(value),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_HISTOGRAM:
|
||||||
|
// *sigh*
|
||||||
|
if p.currentMetric.Histogram == nil {
|
||||||
|
p.currentMetric.Histogram = &dto.Histogram{}
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case p.currentIsHistogramCount:
|
||||||
|
p.currentMetric.Histogram.SampleCount = proto.Uint64(uint64(value))
|
||||||
|
case p.currentIsHistogramSum:
|
||||||
|
p.currentMetric.Histogram.SampleSum = proto.Float64(value)
|
||||||
|
case !math.IsNaN(p.currentBucket):
|
||||||
|
p.currentMetric.Histogram.Bucket = append(
|
||||||
|
p.currentMetric.Histogram.Bucket,
|
||||||
|
&dto.Bucket{
|
||||||
|
UpperBound: proto.Float64(p.currentBucket),
|
||||||
|
CumulativeCount: proto.Uint64(uint64(value)),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
p.err = fmt.Errorf("unexpected type for metric name %q", p.currentMF.GetName())
|
||||||
|
}
|
||||||
|
if p.currentByte == '\n' {
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
return p.startTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// startTimestamp represents the state where the next byte read from p.buf is
|
||||||
|
// the start of the timestamp (or whitespace leading up to it).
|
||||||
|
func (p *TextParser) startTimestamp() stateFn {
|
||||||
|
if p.skipBlankTab(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.readTokenUntilWhitespace(); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
timestamp, err := strconv.ParseInt(p.currentToken.String(), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
// Create a more helpful error message.
|
||||||
|
p.parseError(fmt.Sprintf("expected integer as timestamp, got %q", p.currentToken.String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.currentMetric.TimestampMs = proto.Int64(timestamp)
|
||||||
|
if p.readTokenUntilNewline(false); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
if p.currentToken.Len() > 0 {
|
||||||
|
p.parseError(fmt.Sprintf("spurious string after timestamp: %q", p.currentToken.String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
|
||||||
|
// readingHelp represents the state where the last byte read (now in
|
||||||
|
// p.currentByte) is the first byte of the docstring after 'HELP'.
|
||||||
|
func (p *TextParser) readingHelp() stateFn {
|
||||||
|
if p.currentMF.Help != nil {
|
||||||
|
p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Rest of line is the docstring.
|
||||||
|
if p.readTokenUntilNewline(true); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
p.currentMF.Help = proto.String(p.currentToken.String())
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
|
||||||
|
// readingType represents the state where the last byte read (now in
|
||||||
|
// p.currentByte) is the first byte of the type hint after 'HELP'.
|
||||||
|
func (p *TextParser) readingType() stateFn {
|
||||||
|
if p.currentMF.Type != nil {
|
||||||
|
p.parseError(fmt.Sprintf("second TYPE line for metric name %q, or TYPE reported after samples", p.currentMF.GetName()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Rest of line is the type.
|
||||||
|
if p.readTokenUntilNewline(false); p.err != nil {
|
||||||
|
return nil // Unexpected end of input.
|
||||||
|
}
|
||||||
|
metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
|
||||||
|
if !ok {
|
||||||
|
p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.currentMF.Type = dto.MetricType(metricType).Enum()
|
||||||
|
return p.startOfLine
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseError sets p.err to a ParseError at the current line with the given
|
||||||
|
// message.
|
||||||
|
func (p *TextParser) parseError(msg string) {
|
||||||
|
p.err = ParseError{
|
||||||
|
Line: p.lineCount,
|
||||||
|
Msg: msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skipBlankTab reads (and discards) bytes from p.buf until it encounters a byte
|
||||||
|
// that is neither ' ' nor '\t'. That byte is left in p.currentByte.
|
||||||
|
func (p *TextParser) skipBlankTab() {
|
||||||
|
for {
|
||||||
|
if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do
|
||||||
|
// anything if p.currentByte is neither ' ' nor '\t'.
|
||||||
|
func (p *TextParser) skipBlankTabIfCurrentBlankTab() {
|
||||||
|
if isBlankOrTab(p.currentByte) {
|
||||||
|
p.skipBlankTab()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTokenUntilWhitespace copies bytes from p.buf into p.currentToken. The
|
||||||
|
// first byte considered is the byte already read (now in p.currentByte). The
|
||||||
|
// first whitespace byte encountered is still copied into p.currentByte, but not
|
||||||
|
// into p.currentToken.
|
||||||
|
func (p *TextParser) readTokenUntilWhitespace() {
|
||||||
|
p.currentToken.Reset()
|
||||||
|
for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' {
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
p.currentByte, p.err = p.buf.ReadByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTokenUntilNewline copies bytes from p.buf into p.currentToken. The first
|
||||||
|
// byte considered is the byte already read (now in p.currentByte). The first
|
||||||
|
// newline byte encountered is still copied into p.currentByte, but not into
|
||||||
|
// p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
|
||||||
|
// recognized: '\\' tranlates into '\', and '\n' into a line-feed character. All
|
||||||
|
// other escape sequences are invalid and cause an error.
|
||||||
|
func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
|
||||||
|
p.currentToken.Reset()
|
||||||
|
escaped := false
|
||||||
|
for p.err == nil {
|
||||||
|
if recognizeEscapeSequence && escaped {
|
||||||
|
switch p.currentByte {
|
||||||
|
case '\\':
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
case 'n':
|
||||||
|
p.currentToken.WriteByte('\n')
|
||||||
|
default:
|
||||||
|
p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
escaped = false
|
||||||
|
} else {
|
||||||
|
switch p.currentByte {
|
||||||
|
case '\n':
|
||||||
|
return
|
||||||
|
case '\\':
|
||||||
|
escaped = true
|
||||||
|
default:
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.currentByte, p.err = p.buf.ReadByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTokenAsMetricName copies a metric name from p.buf into p.currentToken.
|
||||||
|
// The first byte considered is the byte already read (now in p.currentByte).
|
||||||
|
// The first byte not part of a metric name is still copied into p.currentByte,
|
||||||
|
// but not into p.currentToken.
|
||||||
|
func (p *TextParser) readTokenAsMetricName() {
|
||||||
|
p.currentToken.Reset()
|
||||||
|
if !isValidMetricNameStart(p.currentByte) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
p.currentByte, p.err = p.buf.ReadByte()
|
||||||
|
if p.err != nil || !isValidMetricNameContinuation(p.currentByte) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTokenAsLabelName copies a label name from p.buf into p.currentToken.
|
||||||
|
// The first byte considered is the byte already read (now in p.currentByte).
|
||||||
|
// The first byte not part of a label name is still copied into p.currentByte,
|
||||||
|
// but not into p.currentToken.
|
||||||
|
func (p *TextParser) readTokenAsLabelName() {
|
||||||
|
p.currentToken.Reset()
|
||||||
|
if !isValidLabelNameStart(p.currentByte) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
p.currentByte, p.err = p.buf.ReadByte()
|
||||||
|
if p.err != nil || !isValidLabelNameContinuation(p.currentByte) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTokenAsLabelValue copies a label value from p.buf into p.currentToken.
|
||||||
|
// In contrast to the other 'readTokenAs...' functions, which start with the
|
||||||
|
// last read byte in p.currentByte, this method ignores p.currentByte and starts
|
||||||
|
// with reading a new byte from p.buf. The first byte not part of a label value
|
||||||
|
// is still copied into p.currentByte, but not into p.currentToken.
|
||||||
|
func (p *TextParser) readTokenAsLabelValue() {
|
||||||
|
p.currentToken.Reset()
|
||||||
|
escaped := false
|
||||||
|
for {
|
||||||
|
if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if escaped {
|
||||||
|
switch p.currentByte {
|
||||||
|
case '"', '\\':
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
case 'n':
|
||||||
|
p.currentToken.WriteByte('\n')
|
||||||
|
default:
|
||||||
|
p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
escaped = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch p.currentByte {
|
||||||
|
case '"':
|
||||||
|
return
|
||||||
|
case '\n':
|
||||||
|
p.parseError(fmt.Sprintf("label value %q contains unescaped new-line", p.currentToken.String()))
|
||||||
|
return
|
||||||
|
case '\\':
|
||||||
|
escaped = true
|
||||||
|
default:
|
||||||
|
p.currentToken.WriteByte(p.currentByte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TextParser) setOrCreateCurrentMF() {
|
||||||
|
p.currentIsSummaryCount = false
|
||||||
|
p.currentIsSummarySum = false
|
||||||
|
p.currentIsHistogramCount = false
|
||||||
|
p.currentIsHistogramSum = false
|
||||||
|
name := p.currentToken.String()
|
||||||
|
if p.currentMF = p.metricFamiliesByName[name]; p.currentMF != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Try out if this is a _sum or _count for a summary/histogram.
|
||||||
|
summaryName := summaryMetricName(name)
|
||||||
|
if p.currentMF = p.metricFamiliesByName[summaryName]; p.currentMF != nil {
|
||||||
|
if p.currentMF.GetType() == dto.MetricType_SUMMARY {
|
||||||
|
if isCount(name) {
|
||||||
|
p.currentIsSummaryCount = true
|
||||||
|
}
|
||||||
|
if isSum(name) {
|
||||||
|
p.currentIsSummarySum = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
histogramName := histogramMetricName(name)
|
||||||
|
if p.currentMF = p.metricFamiliesByName[histogramName]; p.currentMF != nil {
|
||||||
|
if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||||
|
if isCount(name) {
|
||||||
|
p.currentIsHistogramCount = true
|
||||||
|
}
|
||||||
|
if isSum(name) {
|
||||||
|
p.currentIsHistogramSum = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.currentMF = &dto.MetricFamily{Name: proto.String(name)}
|
||||||
|
p.metricFamiliesByName[name] = p.currentMF
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidLabelNameStart(b byte) bool {
|
||||||
|
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidLabelNameContinuation(b byte) bool {
|
||||||
|
return isValidLabelNameStart(b) || (b >= '0' && b <= '9')
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidMetricNameStart(b byte) bool {
|
||||||
|
return isValidLabelNameStart(b) || b == ':'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidMetricNameContinuation(b byte) bool {
|
||||||
|
return isValidLabelNameContinuation(b) || b == ':'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBlankOrTab(b byte) bool {
|
||||||
|
return b == ' ' || b == '\t'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCount(name string) bool {
|
||||||
|
return len(name) > 6 && name[len(name)-6:] == "_count"
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSum(name string) bool {
|
||||||
|
return len(name) > 4 && name[len(name)-4:] == "_sum"
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBucket(name string) bool {
|
||||||
|
return len(name) > 7 && name[len(name)-7:] == "_bucket"
|
||||||
|
}
|
||||||
|
|
||||||
|
func summaryMetricName(name string) string {
|
||||||
|
switch {
|
||||||
|
case isCount(name):
|
||||||
|
return name[:len(name)-6]
|
||||||
|
case isSum(name):
|
||||||
|
return name[:len(name)-4]
|
||||||
|
default:
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func histogramMetricName(name string) string {
|
||||||
|
switch {
|
||||||
|
case isCount(name):
|
||||||
|
return name[:len(name)-6]
|
||||||
|
case isSum(name):
|
||||||
|
return name[:len(name)-4]
|
||||||
|
case isBucket(name):
|
||||||
|
return name[:len(name)-7]
|
||||||
|
default:
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
67
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/README.txt
generated
vendored
Normal file
67
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/README.txt
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
PACKAGE
|
||||||
|
|
||||||
|
package goautoneg
|
||||||
|
import "bitbucket.org/ww/goautoneg"
|
||||||
|
|
||||||
|
HTTP Content-Type Autonegotiation.
|
||||||
|
|
||||||
|
The functions in this package implement the behaviour specified in
|
||||||
|
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||||
|
|
||||||
|
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
Neither the name of the Open Knowledge Foundation Ltd. nor the
|
||||||
|
names of its contributors may be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
FUNCTIONS
|
||||||
|
|
||||||
|
func Negotiate(header string, alternatives []string) (content_type string)
|
||||||
|
Negotiate the most appropriate content_type given the accept header
|
||||||
|
and a list of alternatives.
|
||||||
|
|
||||||
|
func ParseAccept(header string) (accept []Accept)
|
||||||
|
Parse an Accept Header string returning a sorted list
|
||||||
|
of clauses
|
||||||
|
|
||||||
|
|
||||||
|
TYPES
|
||||||
|
|
||||||
|
type Accept struct {
|
||||||
|
Type, SubType string
|
||||||
|
Q float32
|
||||||
|
Params map[string]string
|
||||||
|
}
|
||||||
|
Structure to represent a clause in an HTTP Accept Header
|
||||||
|
|
||||||
|
|
||||||
|
SUBDIRECTORIES
|
||||||
|
|
||||||
|
.hg
|
162
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
Normal file
162
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
HTTP Content-Type Autonegotiation.
|
||||||
|
|
||||||
|
The functions in this package implement the behaviour specified in
|
||||||
|
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||||
|
|
||||||
|
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
Neither the name of the Open Knowledge Foundation Ltd. nor the
|
||||||
|
names of its contributors may be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
package goautoneg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Structure to represent a clause in an HTTP Accept Header
|
||||||
|
type Accept struct {
|
||||||
|
Type, SubType string
|
||||||
|
Q float64
|
||||||
|
Params map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// For internal use, so that we can use the sort interface
|
||||||
|
type accept_slice []Accept
|
||||||
|
|
||||||
|
func (accept accept_slice) Len() int {
|
||||||
|
slice := []Accept(accept)
|
||||||
|
return len(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (accept accept_slice) Less(i, j int) bool {
|
||||||
|
slice := []Accept(accept)
|
||||||
|
ai, aj := slice[i], slice[j]
|
||||||
|
if ai.Q > aj.Q {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ai.Type != "*" && aj.Type == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ai.SubType != "*" && aj.SubType == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (accept accept_slice) Swap(i, j int) {
|
||||||
|
slice := []Accept(accept)
|
||||||
|
slice[i], slice[j] = slice[j], slice[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse an Accept Header string returning a sorted list
|
||||||
|
// of clauses
|
||||||
|
func ParseAccept(header string) (accept []Accept) {
|
||||||
|
parts := strings.Split(header, ",")
|
||||||
|
accept = make([]Accept, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
part := strings.Trim(part, " ")
|
||||||
|
|
||||||
|
a := Accept{}
|
||||||
|
a.Params = make(map[string]string)
|
||||||
|
a.Q = 1.0
|
||||||
|
|
||||||
|
mrp := strings.Split(part, ";")
|
||||||
|
|
||||||
|
media_range := mrp[0]
|
||||||
|
sp := strings.Split(media_range, "/")
|
||||||
|
a.Type = strings.Trim(sp[0], " ")
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(sp) == 1 && a.Type == "*":
|
||||||
|
a.SubType = "*"
|
||||||
|
case len(sp) == 2:
|
||||||
|
a.SubType = strings.Trim(sp[1], " ")
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mrp) == 1 {
|
||||||
|
accept = append(accept, a)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, param := range mrp[1:] {
|
||||||
|
sp := strings.SplitN(param, "=", 2)
|
||||||
|
if len(sp) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
token := strings.Trim(sp[0], " ")
|
||||||
|
if token == "q" {
|
||||||
|
a.Q, _ = strconv.ParseFloat(sp[1], 32)
|
||||||
|
} else {
|
||||||
|
a.Params[token] = strings.Trim(sp[1], " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accept = append(accept, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
slice := accept_slice(accept)
|
||||||
|
sort.Sort(slice)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negotiate the most appropriate content_type given the accept header
|
||||||
|
// and a list of alternatives.
|
||||||
|
func Negotiate(header string, alternatives []string) (content_type string) {
|
||||||
|
asp := make([][]string, 0, len(alternatives))
|
||||||
|
for _, ctype := range alternatives {
|
||||||
|
asp = append(asp, strings.SplitN(ctype, "/", 2))
|
||||||
|
}
|
||||||
|
for _, clause := range ParseAccept(header) {
|
||||||
|
for i, ctsp := range asp {
|
||||||
|
if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
|
||||||
|
content_type = alternatives[i]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if clause.Type == ctsp[0] && clause.SubType == "*" {
|
||||||
|
content_type = alternatives[i]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if clause.Type == "*" && clause.SubType == "*" {
|
||||||
|
content_type = alternatives[i]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
136
vendor/github.com/prometheus/common/model/alert.go
generated
vendored
Normal file
136
vendor/github.com/prometheus/common/model/alert.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AlertStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AlertFiring AlertStatus = "firing"
|
||||||
|
AlertResolved AlertStatus = "resolved"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Alert is a generic representation of an alert in the Prometheus eco-system.
|
||||||
|
type Alert struct {
|
||||||
|
// Label value pairs for purpose of aggregation, matching, and disposition
|
||||||
|
// dispatching. This must minimally include an "alertname" label.
|
||||||
|
Labels LabelSet `json:"labels"`
|
||||||
|
|
||||||
|
// Extra key/value information which does not define alert identity.
|
||||||
|
Annotations LabelSet `json:"annotations"`
|
||||||
|
|
||||||
|
// The known time range for this alert. Both ends are optional.
|
||||||
|
StartsAt time.Time `json:"startsAt,omitempty"`
|
||||||
|
EndsAt time.Time `json:"endsAt,omitempty"`
|
||||||
|
GeneratorURL string `json:"generatorURL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the alert. It is equivalent to the "alertname" label.
|
||||||
|
func (a *Alert) Name() string {
|
||||||
|
return string(a.Labels[AlertNameLabel])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprint returns a unique hash for the alert. It is equivalent to
|
||||||
|
// the fingerprint of the alert's label set.
|
||||||
|
func (a *Alert) Fingerprint() Fingerprint {
|
||||||
|
return a.Labels.Fingerprint()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Alert) String() string {
|
||||||
|
s := fmt.Sprintf("%s[%s]", a.Name(), a.Fingerprint().String()[:7])
|
||||||
|
if a.Resolved() {
|
||||||
|
return s + "[resolved]"
|
||||||
|
}
|
||||||
|
return s + "[active]"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolved returns true iff the activity interval ended in the past.
|
||||||
|
func (a *Alert) Resolved() bool {
|
||||||
|
return a.ResolvedAt(time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvedAt returns true off the activity interval ended before
|
||||||
|
// the given timestamp.
|
||||||
|
func (a *Alert) ResolvedAt(ts time.Time) bool {
|
||||||
|
if a.EndsAt.IsZero() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !a.EndsAt.After(ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns the status of the alert.
|
||||||
|
func (a *Alert) Status() AlertStatus {
|
||||||
|
if a.Resolved() {
|
||||||
|
return AlertResolved
|
||||||
|
}
|
||||||
|
return AlertFiring
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks whether the alert data is inconsistent.
|
||||||
|
func (a *Alert) Validate() error {
|
||||||
|
if a.StartsAt.IsZero() {
|
||||||
|
return fmt.Errorf("start time missing")
|
||||||
|
}
|
||||||
|
if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) {
|
||||||
|
return fmt.Errorf("start time must be before end time")
|
||||||
|
}
|
||||||
|
if err := a.Labels.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid label set: %s", err)
|
||||||
|
}
|
||||||
|
if len(a.Labels) == 0 {
|
||||||
|
return fmt.Errorf("at least one label pair required")
|
||||||
|
}
|
||||||
|
if err := a.Annotations.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid annotations: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert is a list of alerts that can be sorted in chronological order.
|
||||||
|
type Alerts []*Alert
|
||||||
|
|
||||||
|
func (as Alerts) Len() int { return len(as) }
|
||||||
|
func (as Alerts) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
|
||||||
|
|
||||||
|
func (as Alerts) Less(i, j int) bool {
|
||||||
|
if as[i].StartsAt.Before(as[j].StartsAt) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if as[i].EndsAt.Before(as[j].EndsAt) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return as[i].Fingerprint() < as[j].Fingerprint()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFiring returns true iff one of the alerts is not resolved.
|
||||||
|
func (as Alerts) HasFiring() bool {
|
||||||
|
for _, a := range as {
|
||||||
|
if !a.Resolved() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns StatusFiring iff at least one of the alerts is firing.
|
||||||
|
func (as Alerts) Status() AlertStatus {
|
||||||
|
if as.HasFiring() {
|
||||||
|
return AlertFiring
|
||||||
|
}
|
||||||
|
return AlertResolved
|
||||||
|
}
|
105
vendor/github.com/prometheus/common/model/fingerprinting.go
generated
vendored
Normal file
105
vendor/github.com/prometheus/common/model/fingerprinting.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fingerprint provides a hash-capable representation of a Metric.
|
||||||
|
// For our purposes, FNV-1A 64-bit is used.
|
||||||
|
type Fingerprint uint64
|
||||||
|
|
||||||
|
// FingerprintFromString transforms a string representation into a Fingerprint.
|
||||||
|
func FingerprintFromString(s string) (Fingerprint, error) {
|
||||||
|
num, err := strconv.ParseUint(s, 16, 64)
|
||||||
|
return Fingerprint(num), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFingerprint parses the input string into a fingerprint.
|
||||||
|
func ParseFingerprint(s string) (Fingerprint, error) {
|
||||||
|
num, err := strconv.ParseUint(s, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return Fingerprint(num), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Fingerprint) String() string {
|
||||||
|
return fmt.Sprintf("%016x", uint64(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprints represents a collection of Fingerprint subject to a given
|
||||||
|
// natural sorting scheme. It implements sort.Interface.
|
||||||
|
type Fingerprints []Fingerprint
|
||||||
|
|
||||||
|
// Len implements sort.Interface.
|
||||||
|
func (f Fingerprints) Len() int {
|
||||||
|
return len(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less implements sort.Interface.
|
||||||
|
func (f Fingerprints) Less(i, j int) bool {
|
||||||
|
return f[i] < f[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap implements sort.Interface.
|
||||||
|
func (f Fingerprints) Swap(i, j int) {
|
||||||
|
f[i], f[j] = f[j], f[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FingerprintSet is a set of Fingerprints.
|
||||||
|
type FingerprintSet map[Fingerprint]struct{}
|
||||||
|
|
||||||
|
// Equal returns true if both sets contain the same elements (and not more).
|
||||||
|
func (s FingerprintSet) Equal(o FingerprintSet) bool {
|
||||||
|
if len(s) != len(o) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range s {
|
||||||
|
if _, ok := o[k]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection returns the elements contained in both sets.
|
||||||
|
func (s FingerprintSet) Intersection(o FingerprintSet) FingerprintSet {
|
||||||
|
myLength, otherLength := len(s), len(o)
|
||||||
|
if myLength == 0 || otherLength == 0 {
|
||||||
|
return FingerprintSet{}
|
||||||
|
}
|
||||||
|
|
||||||
|
subSet := s
|
||||||
|
superSet := o
|
||||||
|
|
||||||
|
if otherLength < myLength {
|
||||||
|
subSet = o
|
||||||
|
superSet = s
|
||||||
|
}
|
||||||
|
|
||||||
|
out := FingerprintSet{}
|
||||||
|
|
||||||
|
for k := range subSet {
|
||||||
|
if _, ok := superSet[k]; ok {
|
||||||
|
out[k] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
42
vendor/github.com/prometheus/common/model/fnv.go
generated
vendored
Normal file
42
vendor/github.com/prometheus/common/model/fnv.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
// Inline and byte-free variant of hash/fnv's fnv64a.
|
||||||
|
|
||||||
|
const (
|
||||||
|
offset64 = 14695981039346656037
|
||||||
|
prime64 = 1099511628211
|
||||||
|
)
|
||||||
|
|
||||||
|
// hashNew initializies a new fnv64a hash value.
|
||||||
|
func hashNew() uint64 {
|
||||||
|
return offset64
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashAdd adds a string to a fnv64a hash value, returning the updated hash.
|
||||||
|
func hashAdd(h uint64, s string) uint64 {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
h ^= uint64(s[i])
|
||||||
|
h *= prime64
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash.
|
||||||
|
func hashAddByte(h uint64, b byte) uint64 {
|
||||||
|
h ^= uint64(b)
|
||||||
|
h *= prime64
|
||||||
|
return h
|
||||||
|
}
|
210
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
Normal file
210
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AlertNameLabel is the name of the label containing the an alert's name.
|
||||||
|
AlertNameLabel = "alertname"
|
||||||
|
|
||||||
|
// ExportedLabelPrefix is the prefix to prepend to the label names present in
|
||||||
|
// exported metrics if a label of the same name is added by the server.
|
||||||
|
ExportedLabelPrefix = "exported_"
|
||||||
|
|
||||||
|
// MetricNameLabel is the label name indicating the metric name of a
|
||||||
|
// timeseries.
|
||||||
|
MetricNameLabel = "__name__"
|
||||||
|
|
||||||
|
// SchemeLabel is the name of the label that holds the scheme on which to
|
||||||
|
// scrape a target.
|
||||||
|
SchemeLabel = "__scheme__"
|
||||||
|
|
||||||
|
// AddressLabel is the name of the label that holds the address of
|
||||||
|
// a scrape target.
|
||||||
|
AddressLabel = "__address__"
|
||||||
|
|
||||||
|
// MetricsPathLabel is the name of the label that holds the path on which to
|
||||||
|
// scrape a target.
|
||||||
|
MetricsPathLabel = "__metrics_path__"
|
||||||
|
|
||||||
|
// ReservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||||
|
// label names.
|
||||||
|
ReservedLabelPrefix = "__"
|
||||||
|
|
||||||
|
// MetaLabelPrefix is a prefix for labels that provide meta information.
|
||||||
|
// Labels with this prefix are used for intermediate label processing and
|
||||||
|
// will not be attached to time series.
|
||||||
|
MetaLabelPrefix = "__meta_"
|
||||||
|
|
||||||
|
// TmpLabelPrefix is a prefix for temporary labels as part of relabelling.
|
||||||
|
// Labels with this prefix are used for intermediate label processing and
|
||||||
|
// will not be attached to time series. This is reserved for use in
|
||||||
|
// Prometheus configuration files by users.
|
||||||
|
TmpLabelPrefix = "__tmp_"
|
||||||
|
|
||||||
|
// ParamLabelPrefix is a prefix for labels that provide URL parameters
|
||||||
|
// used to scrape a target.
|
||||||
|
ParamLabelPrefix = "__param_"
|
||||||
|
|
||||||
|
// JobLabel is the label name indicating the job from which a timeseries
|
||||||
|
// was scraped.
|
||||||
|
JobLabel = "job"
|
||||||
|
|
||||||
|
// InstanceLabel is the label name used for the instance label.
|
||||||
|
InstanceLabel = "instance"
|
||||||
|
|
||||||
|
// BucketLabel is used for the label that defines the upper bound of a
|
||||||
|
// bucket of a histogram ("le" -> "less or equal").
|
||||||
|
BucketLabel = "le"
|
||||||
|
|
||||||
|
// QuantileLabel is used for the label that defines the quantile in a
|
||||||
|
// summary.
|
||||||
|
QuantileLabel = "quantile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LabelNameRE is a regular expression matching valid label names. Note that the
|
||||||
|
// IsValid method of LabelName performs the same check but faster than a match
|
||||||
|
// with this regular expression.
|
||||||
|
var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
|
||||||
|
|
||||||
|
// A LabelName is a key for a LabelSet or Metric. It has a value associated
|
||||||
|
// therewith.
|
||||||
|
type LabelName string
|
||||||
|
|
||||||
|
// IsValid is true iff the label name matches the pattern of LabelNameRE. This
|
||||||
|
// method, however, does not use LabelNameRE for the check but a much faster
|
||||||
|
// hardcoded implementation.
|
||||||
|
func (ln LabelName) IsValid() bool {
|
||||||
|
if len(ln) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, b := range ln {
|
||||||
|
if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||||
|
func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var s string
|
||||||
|
if err := unmarshal(&s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !LabelName(s).IsValid() {
|
||||||
|
return fmt.Errorf("%q is not a valid label name", s)
|
||||||
|
}
|
||||||
|
*ln = LabelName(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (ln *LabelName) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !LabelName(s).IsValid() {
|
||||||
|
return fmt.Errorf("%q is not a valid label name", s)
|
||||||
|
}
|
||||||
|
*ln = LabelName(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelNames is a sortable LabelName slice. In implements sort.Interface.
|
||||||
|
type LabelNames []LabelName
|
||||||
|
|
||||||
|
func (l LabelNames) Len() int {
|
||||||
|
return len(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelNames) Less(i, j int) bool {
|
||||||
|
return l[i] < l[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelNames) Swap(i, j int) {
|
||||||
|
l[i], l[j] = l[j], l[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelNames) String() string {
|
||||||
|
labelStrings := make([]string, 0, len(l))
|
||||||
|
for _, label := range l {
|
||||||
|
labelStrings = append(labelStrings, string(label))
|
||||||
|
}
|
||||||
|
return strings.Join(labelStrings, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LabelValue is an associated value for a LabelName.
|
||||||
|
type LabelValue string
|
||||||
|
|
||||||
|
// IsValid returns true iff the string is a valid UTF8.
|
||||||
|
func (lv LabelValue) IsValid() bool {
|
||||||
|
return utf8.ValidString(string(lv))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelValues is a sortable LabelValue slice. It implements sort.Interface.
|
||||||
|
type LabelValues []LabelValue
|
||||||
|
|
||||||
|
func (l LabelValues) Len() int {
|
||||||
|
return len(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelValues) Less(i, j int) bool {
|
||||||
|
return string(l[i]) < string(l[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelValues) Swap(i, j int) {
|
||||||
|
l[i], l[j] = l[j], l[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelPair pairs a name with a value.
|
||||||
|
type LabelPair struct {
|
||||||
|
Name LabelName
|
||||||
|
Value LabelValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelPairs is a sortable slice of LabelPair pointers. It implements
|
||||||
|
// sort.Interface.
|
||||||
|
type LabelPairs []*LabelPair
|
||||||
|
|
||||||
|
func (l LabelPairs) Len() int {
|
||||||
|
return len(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelPairs) Less(i, j int) bool {
|
||||||
|
switch {
|
||||||
|
case l[i].Name > l[j].Name:
|
||||||
|
return false
|
||||||
|
case l[i].Name < l[j].Name:
|
||||||
|
return true
|
||||||
|
case l[i].Value > l[j].Value:
|
||||||
|
return false
|
||||||
|
case l[i].Value < l[j].Value:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelPairs) Swap(i, j int) {
|
||||||
|
l[i], l[j] = l[j], l[i]
|
||||||
|
}
|
169
vendor/github.com/prometheus/common/model/labelset.go
generated
vendored
Normal file
169
vendor/github.com/prometheus/common/model/labelset.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet
|
||||||
|
// may be fully-qualified down to the point where it may resolve to a single
|
||||||
|
// Metric in the data store or not. All operations that occur within the realm
|
||||||
|
// of a LabelSet can emit a vector of Metric entities to which the LabelSet may
|
||||||
|
// match.
|
||||||
|
type LabelSet map[LabelName]LabelValue
|
||||||
|
|
||||||
|
// Validate checks whether all names and values in the label set
|
||||||
|
// are valid.
|
||||||
|
func (ls LabelSet) Validate() error {
|
||||||
|
for ln, lv := range ls {
|
||||||
|
if !ln.IsValid() {
|
||||||
|
return fmt.Errorf("invalid name %q", ln)
|
||||||
|
}
|
||||||
|
if !lv.IsValid() {
|
||||||
|
return fmt.Errorf("invalid value %q", lv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true iff both label sets have exactly the same key/value pairs.
|
||||||
|
func (ls LabelSet) Equal(o LabelSet) bool {
|
||||||
|
if len(ls) != len(o) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for ln, lv := range ls {
|
||||||
|
olv, ok := o[ln]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if olv != lv {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before compares the metrics, using the following criteria:
|
||||||
|
//
|
||||||
|
// If m has fewer labels than o, it is before o. If it has more, it is not.
|
||||||
|
//
|
||||||
|
// If the number of labels is the same, the superset of all label names is
|
||||||
|
// sorted alphanumerically. The first differing label pair found in that order
|
||||||
|
// determines the outcome: If the label does not exist at all in m, then m is
|
||||||
|
// before o, and vice versa. Otherwise the label value is compared
|
||||||
|
// alphanumerically.
|
||||||
|
//
|
||||||
|
// If m and o are equal, the method returns false.
|
||||||
|
func (ls LabelSet) Before(o LabelSet) bool {
|
||||||
|
if len(ls) < len(o) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(ls) > len(o) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
lns := make(LabelNames, 0, len(ls)+len(o))
|
||||||
|
for ln := range ls {
|
||||||
|
lns = append(lns, ln)
|
||||||
|
}
|
||||||
|
for ln := range o {
|
||||||
|
lns = append(lns, ln)
|
||||||
|
}
|
||||||
|
// It's probably not worth it to de-dup lns.
|
||||||
|
sort.Sort(lns)
|
||||||
|
for _, ln := range lns {
|
||||||
|
mlv, ok := ls[ln]
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
olv, ok := o[ln]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if mlv < olv {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if mlv > olv {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a copy of the label set.
|
||||||
|
func (ls LabelSet) Clone() LabelSet {
|
||||||
|
lsn := make(LabelSet, len(ls))
|
||||||
|
for ln, lv := range ls {
|
||||||
|
lsn[ln] = lv
|
||||||
|
}
|
||||||
|
return lsn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge is a helper function to non-destructively merge two label sets.
|
||||||
|
func (l LabelSet) Merge(other LabelSet) LabelSet {
|
||||||
|
result := make(LabelSet, len(l))
|
||||||
|
|
||||||
|
for k, v := range l {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range other {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelSet) String() string {
|
||||||
|
lstrs := make([]string, 0, len(l))
|
||||||
|
for l, v := range l {
|
||||||
|
lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(lstrs)
|
||||||
|
return fmt.Sprintf("{%s}", strings.Join(lstrs, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprint returns the LabelSet's fingerprint.
|
||||||
|
func (ls LabelSet) Fingerprint() Fingerprint {
|
||||||
|
return labelSetToFingerprint(ls)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FastFingerprint returns the LabelSet's Fingerprint calculated by a faster hashing
|
||||||
|
// algorithm, which is, however, more susceptible to hash collisions.
|
||||||
|
func (ls LabelSet) FastFingerprint() Fingerprint {
|
||||||
|
return labelSetToFastFingerprint(ls)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (l *LabelSet) UnmarshalJSON(b []byte) error {
|
||||||
|
var m map[LabelName]LabelValue
|
||||||
|
if err := json.Unmarshal(b, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// encoding/json only unmarshals maps of the form map[string]T. It treats
|
||||||
|
// LabelName as a string and does not call its UnmarshalJSON method.
|
||||||
|
// Thus, we have to replicate the behavior here.
|
||||||
|
for ln := range m {
|
||||||
|
if !ln.IsValid() {
|
||||||
|
return fmt.Errorf("%q is not a valid label name", ln)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*l = LabelSet(m)
|
||||||
|
return nil
|
||||||
|
}
|
103
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
Normal file
103
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
separator = []byte{0}
|
||||||
|
// MetricNameRE is a regular expression matching valid metric
|
||||||
|
// names. Note that the IsValidMetricName function performs the same
|
||||||
|
// check but faster than a match with this regular expression.
|
||||||
|
MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Metric is similar to a LabelSet, but the key difference is that a Metric is
|
||||||
|
// a singleton and refers to one and only one stream of samples.
|
||||||
|
type Metric LabelSet
|
||||||
|
|
||||||
|
// Equal compares the metrics.
|
||||||
|
func (m Metric) Equal(o Metric) bool {
|
||||||
|
return LabelSet(m).Equal(LabelSet(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before compares the metrics' underlying label sets.
|
||||||
|
func (m Metric) Before(o Metric) bool {
|
||||||
|
return LabelSet(m).Before(LabelSet(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a copy of the Metric.
|
||||||
|
func (m Metric) Clone() Metric {
|
||||||
|
clone := make(Metric, len(m))
|
||||||
|
for k, v := range m {
|
||||||
|
clone[k] = v
|
||||||
|
}
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Metric) String() string {
|
||||||
|
metricName, hasName := m[MetricNameLabel]
|
||||||
|
numLabels := len(m) - 1
|
||||||
|
if !hasName {
|
||||||
|
numLabels = len(m)
|
||||||
|
}
|
||||||
|
labelStrings := make([]string, 0, numLabels)
|
||||||
|
for label, value := range m {
|
||||||
|
if label != MetricNameLabel {
|
||||||
|
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch numLabels {
|
||||||
|
case 0:
|
||||||
|
if hasName {
|
||||||
|
return string(metricName)
|
||||||
|
}
|
||||||
|
return "{}"
|
||||||
|
default:
|
||||||
|
sort.Strings(labelStrings)
|
||||||
|
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprint returns a Metric's Fingerprint.
|
||||||
|
func (m Metric) Fingerprint() Fingerprint {
|
||||||
|
return LabelSet(m).Fingerprint()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FastFingerprint returns a Metric's Fingerprint calculated by a faster hashing
|
||||||
|
// algorithm, which is, however, more susceptible to hash collisions.
|
||||||
|
func (m Metric) FastFingerprint() Fingerprint {
|
||||||
|
return LabelSet(m).FastFingerprint()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
|
||||||
|
// This function, however, does not use MetricNameRE for the check but a much
|
||||||
|
// faster hardcoded implementation.
|
||||||
|
func IsValidMetricName(n LabelValue) bool {
|
||||||
|
if len(n) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, b := range n {
|
||||||
|
if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
16
vendor/github.com/prometheus/common/model/model.go
generated
vendored
Normal file
16
vendor/github.com/prometheus/common/model/model.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package model contains common data structures that are shared across
|
||||||
|
// Prometheus components and libraries.
|
||||||
|
package model
|
144
vendor/github.com/prometheus/common/model/signature.go
generated
vendored
Normal file
144
vendor/github.com/prometheus/common/model/signature.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SeparatorByte is a byte that cannot occur in valid UTF-8 sequences and is
|
||||||
|
// used to separate label names, label values, and other strings from each other
|
||||||
|
// when calculating their combined hash value (aka signature aka fingerprint).
|
||||||
|
const SeparatorByte byte = 255
|
||||||
|
|
||||||
|
var (
|
||||||
|
// cache the signature of an empty label set.
|
||||||
|
emptyLabelSignature = hashNew()
|
||||||
|
)
|
||||||
|
|
||||||
|
// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a
|
||||||
|
// given label set. (Collisions are possible but unlikely if the number of label
|
||||||
|
// sets the function is applied to is small.)
|
||||||
|
func LabelsToSignature(labels map[string]string) uint64 {
|
||||||
|
if len(labels) == 0 {
|
||||||
|
return emptyLabelSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
labelNames := make([]string, 0, len(labels))
|
||||||
|
for labelName := range labels {
|
||||||
|
labelNames = append(labelNames, labelName)
|
||||||
|
}
|
||||||
|
sort.Strings(labelNames)
|
||||||
|
|
||||||
|
sum := hashNew()
|
||||||
|
for _, labelName := range labelNames {
|
||||||
|
sum = hashAdd(sum, labelName)
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
sum = hashAdd(sum, labels[labelName])
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as
|
||||||
|
// parameter (rather than a label map) and returns a Fingerprint.
|
||||||
|
func labelSetToFingerprint(ls LabelSet) Fingerprint {
|
||||||
|
if len(ls) == 0 {
|
||||||
|
return Fingerprint(emptyLabelSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
labelNames := make(LabelNames, 0, len(ls))
|
||||||
|
for labelName := range ls {
|
||||||
|
labelNames = append(labelNames, labelName)
|
||||||
|
}
|
||||||
|
sort.Sort(labelNames)
|
||||||
|
|
||||||
|
sum := hashNew()
|
||||||
|
for _, labelName := range labelNames {
|
||||||
|
sum = hashAdd(sum, string(labelName))
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
sum = hashAdd(sum, string(ls[labelName]))
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
}
|
||||||
|
return Fingerprint(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelSetToFastFingerprint works similar to labelSetToFingerprint but uses a
|
||||||
|
// faster and less allocation-heavy hash function, which is more susceptible to
|
||||||
|
// create hash collisions. Therefore, collision detection should be applied.
|
||||||
|
func labelSetToFastFingerprint(ls LabelSet) Fingerprint {
|
||||||
|
if len(ls) == 0 {
|
||||||
|
return Fingerprint(emptyLabelSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result uint64
|
||||||
|
for labelName, labelValue := range ls {
|
||||||
|
sum := hashNew()
|
||||||
|
sum = hashAdd(sum, string(labelName))
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
sum = hashAdd(sum, string(labelValue))
|
||||||
|
result ^= sum
|
||||||
|
}
|
||||||
|
return Fingerprint(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureForLabels works like LabelsToSignature but takes a Metric as
|
||||||
|
// parameter (rather than a label map) and only includes the labels with the
|
||||||
|
// specified LabelNames into the signature calculation. The labels passed in
|
||||||
|
// will be sorted by this function.
|
||||||
|
func SignatureForLabels(m Metric, labels ...LabelName) uint64 {
|
||||||
|
if len(labels) == 0 {
|
||||||
|
return emptyLabelSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(LabelNames(labels))
|
||||||
|
|
||||||
|
sum := hashNew()
|
||||||
|
for _, label := range labels {
|
||||||
|
sum = hashAdd(sum, string(label))
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
sum = hashAdd(sum, string(m[label]))
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureWithoutLabels works like LabelsToSignature but takes a Metric as
|
||||||
|
// parameter (rather than a label map) and excludes the labels with any of the
|
||||||
|
// specified LabelNames from the signature calculation.
|
||||||
|
func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return emptyLabelSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
labelNames := make(LabelNames, 0, len(m))
|
||||||
|
for labelName := range m {
|
||||||
|
if _, exclude := labels[labelName]; !exclude {
|
||||||
|
labelNames = append(labelNames, labelName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(labelNames) == 0 {
|
||||||
|
return emptyLabelSignature
|
||||||
|
}
|
||||||
|
sort.Sort(labelNames)
|
||||||
|
|
||||||
|
sum := hashNew()
|
||||||
|
for _, labelName := range labelNames {
|
||||||
|
sum = hashAdd(sum, string(labelName))
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
sum = hashAdd(sum, string(m[labelName]))
|
||||||
|
sum = hashAddByte(sum, SeparatorByte)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
106
vendor/github.com/prometheus/common/model/silence.go
generated
vendored
Normal file
106
vendor/github.com/prometheus/common/model/silence.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Matcher describes a matches the value of a given label.
|
||||||
|
type Matcher struct {
|
||||||
|
Name LabelName `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
IsRegex bool `json:"isRegex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) UnmarshalJSON(b []byte) error {
|
||||||
|
type plain Matcher
|
||||||
|
if err := json.Unmarshal(b, (*plain)(m)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.Name) == 0 {
|
||||||
|
return fmt.Errorf("label name in matcher must not be empty")
|
||||||
|
}
|
||||||
|
if m.IsRegex {
|
||||||
|
if _, err := regexp.Compile(m.Value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate returns true iff all fields of the matcher have valid values.
|
||||||
|
func (m *Matcher) Validate() error {
|
||||||
|
if !m.Name.IsValid() {
|
||||||
|
return fmt.Errorf("invalid name %q", m.Name)
|
||||||
|
}
|
||||||
|
if m.IsRegex {
|
||||||
|
if _, err := regexp.Compile(m.Value); err != nil {
|
||||||
|
return fmt.Errorf("invalid regular expression %q", m.Value)
|
||||||
|
}
|
||||||
|
} else if !LabelValue(m.Value).IsValid() || len(m.Value) == 0 {
|
||||||
|
return fmt.Errorf("invalid value %q", m.Value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Silence defines the representation of a silence definiton
|
||||||
|
// in the Prometheus eco-system.
|
||||||
|
type Silence struct {
|
||||||
|
ID uint64 `json:"id,omitempty"`
|
||||||
|
|
||||||
|
Matchers []*Matcher `json:"matchers"`
|
||||||
|
|
||||||
|
StartsAt time.Time `json:"startsAt"`
|
||||||
|
EndsAt time.Time `json:"endsAt"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||||
|
CreatedBy string `json:"createdBy"`
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate returns true iff all fields of the silence have valid values.
|
||||||
|
func (s *Silence) Validate() error {
|
||||||
|
if len(s.Matchers) == 0 {
|
||||||
|
return fmt.Errorf("at least one matcher required")
|
||||||
|
}
|
||||||
|
for _, m := range s.Matchers {
|
||||||
|
if err := m.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid matcher: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.StartsAt.IsZero() {
|
||||||
|
return fmt.Errorf("start time missing")
|
||||||
|
}
|
||||||
|
if s.EndsAt.IsZero() {
|
||||||
|
return fmt.Errorf("end time missing")
|
||||||
|
}
|
||||||
|
if s.EndsAt.Before(s.StartsAt) {
|
||||||
|
return fmt.Errorf("start time must be before end time")
|
||||||
|
}
|
||||||
|
if s.CreatedBy == "" {
|
||||||
|
return fmt.Errorf("creator information missing")
|
||||||
|
}
|
||||||
|
if s.Comment == "" {
|
||||||
|
return fmt.Errorf("comment missing")
|
||||||
|
}
|
||||||
|
if s.CreatedAt.IsZero() {
|
||||||
|
return fmt.Errorf("creation timestamp missing")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
264
vendor/github.com/prometheus/common/model/time.go
generated
vendored
Normal file
264
vendor/github.com/prometheus/common/model/time.go
generated
vendored
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MinimumTick is the minimum supported time resolution. This has to be
|
||||||
|
// at least time.Second in order for the code below to work.
|
||||||
|
minimumTick = time.Millisecond
|
||||||
|
// second is the Time duration equivalent to one second.
|
||||||
|
second = int64(time.Second / minimumTick)
|
||||||
|
// The number of nanoseconds per minimum tick.
|
||||||
|
nanosPerTick = int64(minimumTick / time.Nanosecond)
|
||||||
|
|
||||||
|
// Earliest is the earliest Time representable. Handy for
|
||||||
|
// initializing a high watermark.
|
||||||
|
Earliest = Time(math.MinInt64)
|
||||||
|
// Latest is the latest Time representable. Handy for initializing
|
||||||
|
// a low watermark.
|
||||||
|
Latest = Time(math.MaxInt64)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Time is the number of milliseconds since the epoch
|
||||||
|
// (1970-01-01 00:00 UTC) excluding leap seconds.
|
||||||
|
type Time int64
|
||||||
|
|
||||||
|
// Interval describes and interval between two timestamps.
|
||||||
|
type Interval struct {
|
||||||
|
Start, End Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now returns the current time as a Time.
|
||||||
|
func Now() Time {
|
||||||
|
return TimeFromUnixNano(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeFromUnix returns the Time equivalent to the Unix Time t
|
||||||
|
// provided in seconds.
|
||||||
|
func TimeFromUnix(t int64) Time {
|
||||||
|
return Time(t * second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeFromUnixNano returns the Time equivalent to the Unix Time
|
||||||
|
// t provided in nanoseconds.
|
||||||
|
func TimeFromUnixNano(t int64) Time {
|
||||||
|
return Time(t / nanosPerTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal reports whether two Times represent the same instant.
|
||||||
|
func (t Time) Equal(o Time) bool {
|
||||||
|
return t == o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before reports whether the Time t is before o.
|
||||||
|
func (t Time) Before(o Time) bool {
|
||||||
|
return t < o
|
||||||
|
}
|
||||||
|
|
||||||
|
// After reports whether the Time t is after o.
|
||||||
|
func (t Time) After(o Time) bool {
|
||||||
|
return t > o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add returns the Time t + d.
|
||||||
|
func (t Time) Add(d time.Duration) Time {
|
||||||
|
return t + Time(d/minimumTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub returns the Duration t - o.
|
||||||
|
func (t Time) Sub(o Time) time.Duration {
|
||||||
|
return time.Duration(t-o) * minimumTick
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns the time.Time representation of t.
|
||||||
|
func (t Time) Time() time.Time {
|
||||||
|
return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unix returns t as a Unix time, the number of seconds elapsed
|
||||||
|
// since January 1, 1970 UTC.
|
||||||
|
func (t Time) Unix() int64 {
|
||||||
|
return int64(t) / second
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
||||||
|
// since January 1, 1970 UTC.
|
||||||
|
func (t Time) UnixNano() int64 {
|
||||||
|
return int64(t) * nanosPerTick
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of digits after the dot.
|
||||||
|
var dotPrecision = int(math.Log10(float64(second)))
|
||||||
|
|
||||||
|
// String returns a string representation of the Time.
|
||||||
|
func (t Time) String() string {
|
||||||
|
return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
func (t Time) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(t.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (t *Time) UnmarshalJSON(b []byte) error {
|
||||||
|
p := strings.Split(string(b), ".")
|
||||||
|
switch len(p) {
|
||||||
|
case 1:
|
||||||
|
v, err := strconv.ParseInt(string(p[0]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = Time(v * second)
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
v, err := strconv.ParseInt(string(p[0]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v *= second
|
||||||
|
|
||||||
|
prec := dotPrecision - len(p[1])
|
||||||
|
if prec < 0 {
|
||||||
|
p[1] = p[1][:dotPrecision]
|
||||||
|
} else if prec > 0 {
|
||||||
|
p[1] = p[1] + strings.Repeat("0", prec)
|
||||||
|
}
|
||||||
|
|
||||||
|
va, err := strconv.ParseInt(p[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*t = Time(v + va)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid time %q", string(b))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration wraps time.Duration. It is used to parse the custom duration format
|
||||||
|
// from YAML.
|
||||||
|
// This type should not propagate beyond the scope of input/output processing.
|
||||||
|
type Duration time.Duration
|
||||||
|
|
||||||
|
// Set implements pflag/flag.Value
|
||||||
|
func (d *Duration) Set(s string) error {
|
||||||
|
var err error
|
||||||
|
*d, err = ParseDuration(s)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements pflag.Value
|
||||||
|
func (d *Duration) Type() string {
|
||||||
|
return "duration"
|
||||||
|
}
|
||||||
|
|
||||||
|
var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
|
||||||
|
|
||||||
|
// ParseDuration parses a string into a time.Duration, assuming that a year
|
||||||
|
// always has 365d, a week always has 7d, and a day always has 24h.
|
||||||
|
func ParseDuration(durationStr string) (Duration, error) {
|
||||||
|
matches := durationRE.FindStringSubmatch(durationStr)
|
||||||
|
if len(matches) != 3 {
|
||||||
|
return 0, fmt.Errorf("not a valid duration string: %q", durationStr)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
n, _ = strconv.Atoi(matches[1])
|
||||||
|
dur = time.Duration(n) * time.Millisecond
|
||||||
|
)
|
||||||
|
switch unit := matches[2]; unit {
|
||||||
|
case "y":
|
||||||
|
dur *= 1000 * 60 * 60 * 24 * 365
|
||||||
|
case "w":
|
||||||
|
dur *= 1000 * 60 * 60 * 24 * 7
|
||||||
|
case "d":
|
||||||
|
dur *= 1000 * 60 * 60 * 24
|
||||||
|
case "h":
|
||||||
|
dur *= 1000 * 60 * 60
|
||||||
|
case "m":
|
||||||
|
dur *= 1000 * 60
|
||||||
|
case "s":
|
||||||
|
dur *= 1000
|
||||||
|
case "ms":
|
||||||
|
// Value already correct
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("invalid time unit in duration string: %q", unit)
|
||||||
|
}
|
||||||
|
return Duration(dur), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Duration) String() string {
|
||||||
|
var (
|
||||||
|
ms = int64(time.Duration(d) / time.Millisecond)
|
||||||
|
unit = "ms"
|
||||||
|
)
|
||||||
|
if ms == 0 {
|
||||||
|
return "0s"
|
||||||
|
}
|
||||||
|
factors := map[string]int64{
|
||||||
|
"y": 1000 * 60 * 60 * 24 * 365,
|
||||||
|
"w": 1000 * 60 * 60 * 24 * 7,
|
||||||
|
"d": 1000 * 60 * 60 * 24,
|
||||||
|
"h": 1000 * 60 * 60,
|
||||||
|
"m": 1000 * 60,
|
||||||
|
"s": 1000,
|
||||||
|
"ms": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch int64(0) {
|
||||||
|
case ms % factors["y"]:
|
||||||
|
unit = "y"
|
||||||
|
case ms % factors["w"]:
|
||||||
|
unit = "w"
|
||||||
|
case ms % factors["d"]:
|
||||||
|
unit = "d"
|
||||||
|
case ms % factors["h"]:
|
||||||
|
unit = "h"
|
||||||
|
case ms % factors["m"]:
|
||||||
|
unit = "m"
|
||||||
|
case ms % factors["s"]:
|
||||||
|
unit = "s"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v%v", ms/factors[unit], unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML implements the yaml.Marshaler interface.
|
||||||
|
func (d Duration) MarshalYAML() (interface{}, error) {
|
||||||
|
return d.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||||
|
func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var s string
|
||||||
|
if err := unmarshal(&s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dur, err := ParseDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*d = dur
|
||||||
|
return nil
|
||||||
|
}
|
416
vendor/github.com/prometheus/common/model/value.go
generated
vendored
Normal file
416
vendor/github.com/prometheus/common/model/value.go
generated
vendored
Normal file
|
@ -0,0 +1,416 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
|
||||||
|
// non-existing sample pair. It is a SamplePair with timestamp Earliest and
|
||||||
|
// value 0.0. Note that the natural zero value of SamplePair has a timestamp
|
||||||
|
// of 0, which is possible to appear in a real SamplePair and thus not
|
||||||
|
// suitable to signal a non-existing SamplePair.
|
||||||
|
ZeroSamplePair = SamplePair{Timestamp: Earliest}
|
||||||
|
|
||||||
|
// ZeroSample is the pseudo zero-value of Sample used to signal a
|
||||||
|
// non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
|
||||||
|
// and metric nil. Note that the natural zero value of Sample has a timestamp
|
||||||
|
// of 0, which is possible to appear in a real Sample and thus not suitable
|
||||||
|
// to signal a non-existing Sample.
|
||||||
|
ZeroSample = Sample{Timestamp: Earliest}
|
||||||
|
)
|
||||||
|
|
||||||
|
// A SampleValue is a representation of a value for a given sample at a given
|
||||||
|
// time.
|
||||||
|
type SampleValue float64
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (v SampleValue) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (v *SampleValue) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
|
||||||
|
return fmt.Errorf("sample value must be a quoted string")
|
||||||
|
}
|
||||||
|
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*v = SampleValue(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if the value of v and o is equal or if both are NaN. Note
|
||||||
|
// that v==o is false if both are NaN. If you want the conventional float
|
||||||
|
// behavior, use == to compare two SampleValues.
|
||||||
|
func (v SampleValue) Equal(o SampleValue) bool {
|
||||||
|
if v == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v SampleValue) String() string {
|
||||||
|
return strconv.FormatFloat(float64(v), 'f', -1, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SamplePair pairs a SampleValue with a Timestamp.
|
||||||
|
type SamplePair struct {
|
||||||
|
Timestamp Time
|
||||||
|
Value SampleValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s SamplePair) MarshalJSON() ([]byte, error) {
|
||||||
|
t, err := json.Marshal(s.Timestamp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v, err := json.Marshal(s.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
||||||
|
v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
|
||||||
|
return json.Unmarshal(b, &v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if this SamplePair and o have equal Values and equal
|
||||||
|
// Timestamps. The sematics of Value equality is defined by SampleValue.Equal.
|
||||||
|
func (s *SamplePair) Equal(o *SamplePair) bool {
|
||||||
|
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SamplePair) String() string {
|
||||||
|
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample is a sample pair associated with a metric.
|
||||||
|
type Sample struct {
|
||||||
|
Metric Metric `json:"metric"`
|
||||||
|
Value SampleValue `json:"value"`
|
||||||
|
Timestamp Time `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal compares first the metrics, then the timestamp, then the value. The
|
||||||
|
// sematics of value equality is defined by SampleValue.Equal.
|
||||||
|
func (s *Sample) Equal(o *Sample) bool {
|
||||||
|
if s == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.Metric.Equal(o.Metric) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !s.Timestamp.Equal(o.Timestamp) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Value.Equal(o.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Sample) String() string {
|
||||||
|
return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
|
||||||
|
Timestamp: s.Timestamp,
|
||||||
|
Value: s.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s Sample) MarshalJSON() ([]byte, error) {
|
||||||
|
v := struct {
|
||||||
|
Metric Metric `json:"metric"`
|
||||||
|
Value SamplePair `json:"value"`
|
||||||
|
}{
|
||||||
|
Metric: s.Metric,
|
||||||
|
Value: SamplePair{
|
||||||
|
Timestamp: s.Timestamp,
|
||||||
|
Value: s.Value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(&v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *Sample) UnmarshalJSON(b []byte) error {
|
||||||
|
v := struct {
|
||||||
|
Metric Metric `json:"metric"`
|
||||||
|
Value SamplePair `json:"value"`
|
||||||
|
}{
|
||||||
|
Metric: s.Metric,
|
||||||
|
Value: SamplePair{
|
||||||
|
Timestamp: s.Timestamp,
|
||||||
|
Value: s.Value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Metric = v.Metric
|
||||||
|
s.Timestamp = v.Value.Timestamp
|
||||||
|
s.Value = v.Value.Value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples is a sortable Sample slice. It implements sort.Interface.
|
||||||
|
type Samples []*Sample
|
||||||
|
|
||||||
|
func (s Samples) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less compares first the metrics, then the timestamp.
|
||||||
|
func (s Samples) Less(i, j int) bool {
|
||||||
|
switch {
|
||||||
|
case s[i].Metric.Before(s[j].Metric):
|
||||||
|
return true
|
||||||
|
case s[j].Metric.Before(s[i].Metric):
|
||||||
|
return false
|
||||||
|
case s[i].Timestamp.Before(s[j].Timestamp):
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Samples) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal compares two sets of samples and returns true if they are equal.
|
||||||
|
func (s Samples) Equal(o Samples) bool {
|
||||||
|
if len(s) != len(o) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, sample := range s {
|
||||||
|
if !sample.Equal(o[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampleStream is a stream of Values belonging to an attached COWMetric.
|
||||||
|
type SampleStream struct {
|
||||||
|
Metric Metric `json:"metric"`
|
||||||
|
Values []SamplePair `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss SampleStream) String() string {
|
||||||
|
vals := make([]string, len(ss.Values))
|
||||||
|
for i, v := range ss.Values {
|
||||||
|
vals[i] = v.String()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value is a generic interface for values resulting from a query evaluation.
|
||||||
|
type Value interface {
|
||||||
|
Type() ValueType
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Matrix) Type() ValueType { return ValMatrix }
|
||||||
|
func (Vector) Type() ValueType { return ValVector }
|
||||||
|
func (*Scalar) Type() ValueType { return ValScalar }
|
||||||
|
func (*String) Type() ValueType { return ValString }
|
||||||
|
|
||||||
|
type ValueType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ValNone ValueType = iota
|
||||||
|
ValScalar
|
||||||
|
ValVector
|
||||||
|
ValMatrix
|
||||||
|
ValString
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (et ValueType) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(et.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (et *ValueType) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch s {
|
||||||
|
case "<ValNone>":
|
||||||
|
*et = ValNone
|
||||||
|
case "scalar":
|
||||||
|
*et = ValScalar
|
||||||
|
case "vector":
|
||||||
|
*et = ValVector
|
||||||
|
case "matrix":
|
||||||
|
*et = ValMatrix
|
||||||
|
case "string":
|
||||||
|
*et = ValString
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown value type %q", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ValueType) String() string {
|
||||||
|
switch e {
|
||||||
|
case ValNone:
|
||||||
|
return "<ValNone>"
|
||||||
|
case ValScalar:
|
||||||
|
return "scalar"
|
||||||
|
case ValVector:
|
||||||
|
return "vector"
|
||||||
|
case ValMatrix:
|
||||||
|
return "matrix"
|
||||||
|
case ValString:
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
panic("ValueType.String: unhandled value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scalar is a scalar value evaluated at the set timestamp.
|
||||||
|
type Scalar struct {
|
||||||
|
Value SampleValue `json:"value"`
|
||||||
|
Timestamp Time `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Scalar) String() string {
|
||||||
|
return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s Scalar) MarshalJSON() ([]byte, error) {
|
||||||
|
v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64)
|
||||||
|
return json.Marshal([...]interface{}{s.Timestamp, string(v)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *Scalar) UnmarshalJSON(b []byte) error {
|
||||||
|
var f string
|
||||||
|
v := [...]interface{}{&s.Timestamp, &f}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.ParseFloat(f, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing sample value: %s", err)
|
||||||
|
}
|
||||||
|
s.Value = SampleValue(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a string value evaluated at the set timestamp.
|
||||||
|
type String struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
Timestamp Time `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) String() string {
|
||||||
|
return s.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s String) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal([]interface{}{s.Timestamp, s.Value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *String) UnmarshalJSON(b []byte) error {
|
||||||
|
v := [...]interface{}{&s.Timestamp, &s.Value}
|
||||||
|
return json.Unmarshal(b, &v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vector is basically only an alias for Samples, but the
|
||||||
|
// contract is that in a Vector, all Samples have the same timestamp.
|
||||||
|
type Vector []*Sample
|
||||||
|
|
||||||
|
func (vec Vector) String() string {
|
||||||
|
entries := make([]string, len(vec))
|
||||||
|
for i, s := range vec {
|
||||||
|
entries[i] = s.String()
|
||||||
|
}
|
||||||
|
return strings.Join(entries, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vec Vector) Len() int { return len(vec) }
|
||||||
|
func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] }
|
||||||
|
|
||||||
|
// Less compares first the metrics, then the timestamp.
|
||||||
|
func (vec Vector) Less(i, j int) bool {
|
||||||
|
switch {
|
||||||
|
case vec[i].Metric.Before(vec[j].Metric):
|
||||||
|
return true
|
||||||
|
case vec[j].Metric.Before(vec[i].Metric):
|
||||||
|
return false
|
||||||
|
case vec[i].Timestamp.Before(vec[j].Timestamp):
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal compares two sets of samples and returns true if they are equal.
|
||||||
|
func (vec Vector) Equal(o Vector) bool {
|
||||||
|
if len(vec) != len(o) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, sample := range vec {
|
||||||
|
if !sample.Equal(o[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix is a list of time series.
|
||||||
|
type Matrix []*SampleStream
|
||||||
|
|
||||||
|
func (m Matrix) Len() int { return len(m) }
|
||||||
|
func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) }
|
||||||
|
func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
||||||
|
|
||||||
|
func (mat Matrix) String() string {
|
||||||
|
matCp := make(Matrix, len(mat))
|
||||||
|
copy(matCp, mat)
|
||||||
|
sort.Sort(matCp)
|
||||||
|
|
||||||
|
strs := make([]string, len(matCp))
|
||||||
|
|
||||||
|
for i, ss := range matCp {
|
||||||
|
strs[i] = ss.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(strs, "\n")
|
||||||
|
}
|
201
vendor/github.com/prometheus/procfs/LICENSE
generated
vendored
Normal file
201
vendor/github.com/prometheus/procfs/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
7
vendor/github.com/prometheus/procfs/NOTICE
generated
vendored
Normal file
7
vendor/github.com/prometheus/procfs/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
procfs provides functions to retrieve system, kernel and process
|
||||||
|
metrics from the pseudo-filesystem proc.
|
||||||
|
|
||||||
|
Copyright 2014-2015 The Prometheus Authors
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
SoundCloud Ltd. (http://soundcloud.com/).
|
11
vendor/github.com/prometheus/procfs/README.md
generated
vendored
Normal file
11
vendor/github.com/prometheus/procfs/README.md
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# procfs
|
||||||
|
|
||||||
|
This procfs package provides functions to retrieve system, kernel and process
|
||||||
|
metrics from the pseudo-filesystem proc.
|
||||||
|
|
||||||
|
*WARNING*: This package is a work in progress. Its API may still break in
|
||||||
|
backwards-incompatible ways without warnings. Use it at your own risk.
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/prometheus/procfs?status.png)](https://godoc.org/github.com/prometheus/procfs)
|
||||||
|
[![Build Status](https://travis-ci.org/prometheus/procfs.svg?branch=master)](https://travis-ci.org/prometheus/procfs)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/prometheus/procfs)](https://goreportcard.com/report/github.com/prometheus/procfs)
|
95
vendor/github.com/prometheus/procfs/buddyinfo.go
generated
vendored
Normal file
95
vendor/github.com/prometheus/procfs/buddyinfo.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A BuddyInfo is the details parsed from /proc/buddyinfo.
|
||||||
|
// The data is comprised of an array of free fragments of each size.
|
||||||
|
// The sizes are 2^n*PAGE_SIZE, where n is the array index.
|
||||||
|
type BuddyInfo struct {
|
||||||
|
Node string
|
||||||
|
Zone string
|
||||||
|
Sizes []float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuddyInfo reads the buddyinfo statistics.
|
||||||
|
func NewBuddyInfo() ([]BuddyInfo, error) {
|
||||||
|
fs, err := NewFS(DefaultMountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewBuddyInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuddyInfo reads the buddyinfo statistics from the specified `proc` filesystem.
|
||||||
|
func (fs FS) NewBuddyInfo() ([]BuddyInfo, error) {
|
||||||
|
file, err := os.Open(fs.Path("buddyinfo"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return parseBuddyInfo(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) {
|
||||||
|
var (
|
||||||
|
buddyInfo = []BuddyInfo{}
|
||||||
|
scanner = bufio.NewScanner(r)
|
||||||
|
bucketCount = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
var err error
|
||||||
|
line := scanner.Text()
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
|
||||||
|
if len(parts) < 4 {
|
||||||
|
return nil, fmt.Errorf("invalid number of fields when parsing buddyinfo")
|
||||||
|
}
|
||||||
|
|
||||||
|
node := strings.TrimRight(parts[1], ",")
|
||||||
|
zone := strings.TrimRight(parts[3], ",")
|
||||||
|
arraySize := len(parts[4:])
|
||||||
|
|
||||||
|
if bucketCount == -1 {
|
||||||
|
bucketCount = arraySize
|
||||||
|
} else {
|
||||||
|
if bucketCount != arraySize {
|
||||||
|
return nil, fmt.Errorf("mismatch in number of buddyinfo buckets, previous count %d, new count %d", bucketCount, arraySize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sizes := make([]float64, arraySize)
|
||||||
|
for i := 0; i < arraySize; i++ {
|
||||||
|
sizes[i], err = strconv.ParseFloat(parts[i+4], 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid value in buddyinfo: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buddyInfo = append(buddyInfo, BuddyInfo{node, zone, sizes})
|
||||||
|
}
|
||||||
|
|
||||||
|
return buddyInfo, scanner.Err()
|
||||||
|
}
|
45
vendor/github.com/prometheus/procfs/doc.go
generated
vendored
Normal file
45
vendor/github.com/prometheus/procfs/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2014 Prometheus Team
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package procfs provides functions to retrieve system, kernel and process
|
||||||
|
// metrics from the pseudo-filesystem proc.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "fmt"
|
||||||
|
// "log"
|
||||||
|
//
|
||||||
|
// "github.com/prometheus/procfs"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// p, err := procfs.Self()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("could not get process: %s", err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// stat, err := p.NewStat()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("could not get process stat: %s", err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("command: %s\n", stat.Comm)
|
||||||
|
// fmt.Printf("cpu time: %fs\n", stat.CPUTime())
|
||||||
|
// fmt.Printf("vsize: %dB\n", stat.VirtualMemory())
|
||||||
|
// fmt.Printf("rss: %dB\n", stat.ResidentMemory())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
package procfs
|
69
vendor/github.com/prometheus/procfs/fs.go
generated
vendored
Normal file
69
vendor/github.com/prometheus/procfs/fs.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/prometheus/procfs/nfs"
|
||||||
|
"github.com/prometheus/procfs/xfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FS represents the pseudo-filesystem proc, which provides an interface to
|
||||||
|
// kernel data structures.
|
||||||
|
type FS string
|
||||||
|
|
||||||
|
// DefaultMountPoint is the common mount point of the proc filesystem.
|
||||||
|
const DefaultMountPoint = "/proc"
|
||||||
|
|
||||||
|
// NewFS returns a new FS mounted under the given mountPoint. It will error
|
||||||
|
// if the mount point can't be read.
|
||||||
|
func NewFS(mountPoint string) (FS, error) {
|
||||||
|
info, err := os.Stat(mountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return FS(mountPoint), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the path of the given subsystem relative to the procfs root.
|
||||||
|
func (fs FS) Path(p ...string) string {
|
||||||
|
return path.Join(append([]string{string(fs)}, p...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XFSStats retrieves XFS filesystem runtime statistics.
|
||||||
|
func (fs FS) XFSStats() (*xfs.Stats, error) {
|
||||||
|
f, err := os.Open(fs.Path("fs/xfs/stat"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return xfs.ParseStats(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NFSdClientRPCStats retrieves NFS daemon RPC statistics.
|
||||||
|
func (fs FS) NFSdClientRPCStats() (*nfs.ClientRPCStats, error) {
|
||||||
|
f, err := os.Open(fs.Path("net/rpc/nfs"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return nfs.ParseClientRPCStats(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NFSdServerRPCStats retrieves NFS daemon RPC statistics.
|
||||||
|
func (fs FS) NFSdServerRPCStats() (*nfs.ServerRPCStats, error) {
|
||||||
|
f, err := os.Open(fs.Path("net/rpc/nfsd"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return nfs.ParseServerRPCStats(f)
|
||||||
|
}
|
46
vendor/github.com/prometheus/procfs/internal/util/parse.go
generated
vendored
Normal file
46
vendor/github.com/prometheus/procfs/internal/util/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// ParseUint32s parses a slice of strings into a slice of uint32s.
|
||||||
|
func ParseUint32s(ss []string) ([]uint32, error) {
|
||||||
|
us := make([]uint32, 0, len(ss))
|
||||||
|
for _, s := range ss {
|
||||||
|
u, err := strconv.ParseUint(s, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
us = append(us, uint32(u))
|
||||||
|
}
|
||||||
|
|
||||||
|
return us, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUint64s parses a slice of strings into a slice of uint64s.
|
||||||
|
func ParseUint64s(ss []string) ([]uint64, error) {
|
||||||
|
us := make([]uint64, 0, len(ss))
|
||||||
|
for _, s := range ss {
|
||||||
|
u, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
us = append(us, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return us, nil
|
||||||
|
}
|
246
vendor/github.com/prometheus/procfs/ipvs.go
generated
vendored
Normal file
246
vendor/github.com/prometheus/procfs/ipvs.go
generated
vendored
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
|
||||||
|
type IPVSStats struct {
|
||||||
|
// Total count of connections.
|
||||||
|
Connections uint64
|
||||||
|
// Total incoming packages processed.
|
||||||
|
IncomingPackets uint64
|
||||||
|
// Total outgoing packages processed.
|
||||||
|
OutgoingPackets uint64
|
||||||
|
// Total incoming traffic.
|
||||||
|
IncomingBytes uint64
|
||||||
|
// Total outgoing traffic.
|
||||||
|
OutgoingBytes uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPVSBackendStatus holds current metrics of one virtual / real address pair.
|
||||||
|
type IPVSBackendStatus struct {
|
||||||
|
// The local (virtual) IP address.
|
||||||
|
LocalAddress net.IP
|
||||||
|
// The remote (real) IP address.
|
||||||
|
RemoteAddress net.IP
|
||||||
|
// The local (virtual) port.
|
||||||
|
LocalPort uint16
|
||||||
|
// The remote (real) port.
|
||||||
|
RemotePort uint16
|
||||||
|
// The local firewall mark
|
||||||
|
LocalMark string
|
||||||
|
// The transport protocol (TCP, UDP).
|
||||||
|
Proto string
|
||||||
|
// The current number of active connections for this virtual/real address pair.
|
||||||
|
ActiveConn uint64
|
||||||
|
// The current number of inactive connections for this virtual/real address pair.
|
||||||
|
InactConn uint64
|
||||||
|
// The current weight of this virtual/real address pair.
|
||||||
|
Weight uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPVSStats reads the IPVS statistics.
|
||||||
|
func NewIPVSStats() (IPVSStats, error) {
|
||||||
|
fs, err := NewFS(DefaultMountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewIPVSStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
|
||||||
|
func (fs FS) NewIPVSStats() (IPVSStats, error) {
|
||||||
|
file, err := os.Open(fs.Path("net/ip_vs_stats"))
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return parseIPVSStats(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseIPVSStats performs the actual parsing of `ip_vs_stats`.
|
||||||
|
func parseIPVSStats(file io.Reader) (IPVSStats, error) {
|
||||||
|
var (
|
||||||
|
statContent []byte
|
||||||
|
statLines []string
|
||||||
|
statFields []string
|
||||||
|
stats IPVSStats
|
||||||
|
)
|
||||||
|
|
||||||
|
statContent, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
statLines = strings.SplitN(string(statContent), "\n", 4)
|
||||||
|
if len(statLines) != 4 {
|
||||||
|
return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
statFields = strings.Fields(statLines[2])
|
||||||
|
if len(statFields) != 5 {
|
||||||
|
return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return IPVSStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs.
|
||||||
|
func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
||||||
|
fs, err := NewFS(DefaultMountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return []IPVSBackendStatus{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewIPVSBackendStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
|
||||||
|
func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
||||||
|
file, err := os.Open(fs.Path("net/ip_vs"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return parseIPVSBackendStatus(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
|
||||||
|
var (
|
||||||
|
status []IPVSBackendStatus
|
||||||
|
scanner = bufio.NewScanner(file)
|
||||||
|
proto string
|
||||||
|
localMark string
|
||||||
|
localAddress net.IP
|
||||||
|
localPort uint16
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
fields := strings.Fields(scanner.Text())
|
||||||
|
if len(fields) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
|
||||||
|
continue
|
||||||
|
case fields[0] == "TCP" || fields[0] == "UDP":
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
proto = fields[0]
|
||||||
|
localMark = ""
|
||||||
|
localAddress, localPort, err = parseIPPort(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case fields[0] == "FWM":
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
proto = fields[0]
|
||||||
|
localMark = fields[1]
|
||||||
|
localAddress = nil
|
||||||
|
localPort = 0
|
||||||
|
case fields[0] == "->":
|
||||||
|
if len(fields) < 6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
remoteAddress, remotePort, err := parseIPPort(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
weight, err := strconv.ParseUint(fields[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
activeConn, err := strconv.ParseUint(fields[4], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inactConn, err := strconv.ParseUint(fields[5], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status = append(status, IPVSBackendStatus{
|
||||||
|
LocalAddress: localAddress,
|
||||||
|
LocalPort: localPort,
|
||||||
|
LocalMark: localMark,
|
||||||
|
RemoteAddress: remoteAddress,
|
||||||
|
RemotePort: remotePort,
|
||||||
|
Proto: proto,
|
||||||
|
Weight: weight,
|
||||||
|
ActiveConn: activeConn,
|
||||||
|
InactConn: inactConn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIPPort(s string) (net.IP, uint16, error) {
|
||||||
|
var (
|
||||||
|
ip net.IP
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
switch len(s) {
|
||||||
|
case 13:
|
||||||
|
ip, err = hex.DecodeString(s[0:8])
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
case 46:
|
||||||
|
ip = net.ParseIP(s[1:40])
|
||||||
|
if ip == nil {
|
||||||
|
return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
portString := s[len(s)-4:]
|
||||||
|
if len(portString) != 4 {
|
||||||
|
return nil, 0, fmt.Errorf("unexpected port string format: %s", portString)
|
||||||
|
}
|
||||||
|
port, err := strconv.ParseUint(portString, 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, uint16(port), nil
|
||||||
|
}
|
138
vendor/github.com/prometheus/procfs/mdstat.go
generated
vendored
Normal file
138
vendor/github.com/prometheus/procfs/mdstat.go
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
|
||||||
|
buildlineRE = regexp.MustCompile(`\((\d+)/\d+\)`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// MDStat holds info parsed from /proc/mdstat.
|
||||||
|
type MDStat struct {
|
||||||
|
// Name of the device.
|
||||||
|
Name string
|
||||||
|
// activity-state of the device.
|
||||||
|
ActivityState string
|
||||||
|
// Number of active disks.
|
||||||
|
DisksActive int64
|
||||||
|
// Total number of disks the device consists of.
|
||||||
|
DisksTotal int64
|
||||||
|
// Number of blocks the device holds.
|
||||||
|
BlocksTotal int64
|
||||||
|
// Number of blocks on the device that are in sync.
|
||||||
|
BlocksSynced int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
|
||||||
|
func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
||||||
|
mdStatusFilePath := fs.Path("mdstat")
|
||||||
|
content, err := ioutil.ReadFile(mdStatusFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mdStates := []MDStat{}
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
for i, l := range lines {
|
||||||
|
if l == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if l[0] == ' ' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(l, "Personalities") || strings.HasPrefix(l, "unused") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mainLine := strings.Split(l, " ")
|
||||||
|
if len(mainLine) < 3 {
|
||||||
|
return mdStates, fmt.Errorf("error parsing mdline: %s", l)
|
||||||
|
}
|
||||||
|
mdName := mainLine[0]
|
||||||
|
activityState := mainLine[2]
|
||||||
|
|
||||||
|
if len(lines) <= i+3 {
|
||||||
|
return mdStates, fmt.Errorf(
|
||||||
|
"error parsing %s: too few lines for md device %s",
|
||||||
|
mdStatusFilePath,
|
||||||
|
mdName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
active, total, size, err := evalStatusline(lines[i+1])
|
||||||
|
if err != nil {
|
||||||
|
return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// j is the line number of the syncing-line.
|
||||||
|
j := i + 2
|
||||||
|
if strings.Contains(lines[i+2], "bitmap") { // skip bitmap line
|
||||||
|
j = i + 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// If device is syncing at the moment, get the number of currently
|
||||||
|
// synced bytes, otherwise that number equals the size of the device.
|
||||||
|
syncedBlocks := size
|
||||||
|
if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") {
|
||||||
|
syncedBlocks, err = evalBuildline(lines[j])
|
||||||
|
if err != nil {
|
||||||
|
return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mdStates = append(mdStates, MDStat{
|
||||||
|
Name: mdName,
|
||||||
|
ActivityState: activityState,
|
||||||
|
DisksActive: active,
|
||||||
|
DisksTotal: total,
|
||||||
|
BlocksTotal: size,
|
||||||
|
BlocksSynced: syncedBlocks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return mdStates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalStatusline(statusline string) (active, total, size int64, err error) {
|
||||||
|
matches := statuslineRE.FindStringSubmatch(statusline)
|
||||||
|
if len(matches) != 4 {
|
||||||
|
return 0, 0, 0, fmt.Errorf("unexpected statusline: %s", statusline)
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err = strconv.ParseInt(matches[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err = strconv.ParseInt(matches[2], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
active, err = strconv.ParseInt(matches[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return active, total, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBuildline(buildline string) (syncedBlocks int64, err error) {
|
||||||
|
matches := buildlineRE.FindStringSubmatch(buildline)
|
||||||
|
if len(matches) != 2 {
|
||||||
|
return 0, fmt.Errorf("unexpected buildline: %s", buildline)
|
||||||
|
}
|
||||||
|
|
||||||
|
syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("%s in buildline: %s", err, buildline)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncedBlocks, nil
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue