|
1 | 1 | package metrics
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "net/url" |
4 | 5 | "strings"
|
5 |
| - "time" |
6 | 6 |
|
7 |
| - "github.com/prometheus/client_golang/prometheus" |
| 7 | + gocontext "golang.org/x/net/context" |
8 | 8 |
|
9 | 9 | "github.com/docker/distribution"
|
10 | 10 | "github.com/docker/distribution/context"
|
| 11 | + "github.com/docker/distribution/registry/api/errcode" |
11 | 12 |
|
12 | 13 | "github.com/openshift/image-registry/pkg/dockerregistry/server/wrapped"
|
| 14 | + "github.com/openshift/image-registry/pkg/origin-common/image/registryclient" |
13 | 15 | )
|
14 | 16 |
|
15 |
| -const ( |
16 |
| - registryNamespace = "openshift" |
17 |
| - registrySubsystem = "registry" |
18 |
| -) |
| 17 | +// Observer captures individual observations. |
| 18 | +type Observer interface { |
| 19 | + Observe(float64) |
| 20 | +} |
19 | 21 |
|
20 |
| -var ( |
21 |
| - registryAPIRequests *prometheus.HistogramVec |
22 |
| -) |
| 22 | +// Counter represents a single numerical value that only goes up. |
| 23 | +type Counter interface { |
| 24 | + Inc() |
| 25 | +} |
23 | 26 |
|
24 |
| -// Register the metrics. |
25 |
| -func Register() { |
26 |
| - registryAPIRequests = prometheus.NewHistogramVec( |
27 |
| - prometheus.HistogramOpts{ |
28 |
| - Namespace: registryNamespace, |
29 |
| - Subsystem: registrySubsystem, |
30 |
| - Name: "request_duration_seconds", |
31 |
| - Help: "Request latency summary in microseconds for each operation", |
32 |
| - }, |
33 |
| - []string{"operation", "name"}, |
34 |
| - ) |
35 |
| - prometheus.MustRegister(registryAPIRequests) |
36 |
| -} |
37 |
| - |
38 |
| -// Timer is a helper type to time functions. |
39 |
| -type Timer interface { |
40 |
| - // Stop records the duration passed since the Timer was created with NewTimer. |
41 |
| - Stop() |
42 |
| -} |
43 |
| - |
44 |
| -// NewTimer wraps the HistogramVec and used to track amount of time passed since the Timer was created. |
45 |
| -func NewTimer(collector *prometheus.HistogramVec, labels []string) Timer { |
46 |
| - return &metricTimer{ |
47 |
| - collector: collector, |
48 |
| - labels: labels, |
49 |
| - startTime: time.Now(), |
| 27 | +// Sink provides an interface for exposing metrics. |
| 28 | +type Sink interface { |
| 29 | + RequestDuration(funcname, reponame string) Observer |
| 30 | + PullthroughBlobstoreCacheRequests(resultType string) Counter |
| 31 | + PullthroughRepositoryDuration(registry, funcname string) Observer |
| 32 | + PullthroughRepositoryErrors(registry, funcname, errcode string) Counter |
| 33 | +} |
| 34 | + |
| 35 | +// Metrics is a set of all metrics that can be provided. |
| 36 | +type Metrics interface { |
| 37 | + Core |
| 38 | + Pullthrough |
| 39 | +} |
| 40 | + |
| 41 | +// Core is a set of metrics for the core functionality. |
| 42 | +type Core interface { |
| 43 | + // Repository wraps a distribution.Repository to collect statistics. |
| 44 | + Repository(r distribution.Repository, reponame string) distribution.Repository |
| 45 | +} |
| 46 | + |
| 47 | +// Pullthrough is a set of metrics for the pullthrough subsystem. |
| 48 | +type Pullthrough interface { |
| 49 | + // RepositoryRetriever wraps RepositoryRetriever to collect statistics. |
| 50 | + RepositoryRetriever(retriever registryclient.RepositoryRetriever) registryclient.RepositoryRetriever |
| 51 | + |
| 52 | + // DigestBlobStoreCache() returns an interface to count cache hits/misses |
| 53 | + // for pullthrough blobstores. |
| 54 | + DigestBlobStoreCache() Cache |
| 55 | +} |
| 56 | + |
| 57 | +func dockerErrorCode(err error) string { |
| 58 | + if e, ok := err.(errcode.Error); ok { |
| 59 | + return e.ErrorCode().String() |
50 | 60 | }
|
| 61 | + return "UNKNOWN" |
51 | 62 | }
|
52 | 63 |
|
53 |
| -type metricTimer struct { |
54 |
| - collector *prometheus.HistogramVec |
55 |
| - labels []string |
56 |
| - startTime time.Time |
| 64 | +func pullthroughRepositoryWrapper(ctx context.Context, sink Sink, registry string, funcname string, f func(ctx context.Context) error) error { |
| 65 | + registry = strings.ToLower(registry) |
| 66 | + defer NewTimer(sink.PullthroughRepositoryDuration(registry, funcname)).Stop() |
| 67 | + err := f(ctx) |
| 68 | + if err != nil { |
| 69 | + sink.PullthroughRepositoryErrors(registry, funcname, dockerErrorCode(err)).Inc() |
| 70 | + } |
| 71 | + return err |
| 72 | +} |
| 73 | + |
| 74 | +type repositoryRetriever struct { |
| 75 | + retriever registryclient.RepositoryRetriever |
| 76 | + sink Sink |
57 | 77 | }
|
58 | 78 |
|
59 |
| -func (m *metricTimer) Stop() { |
60 |
| - m.collector.WithLabelValues(m.labels...).Observe(float64(time.Since(m.startTime)) / float64(time.Second)) |
| 79 | +func (rr repositoryRetriever) Repository(ctx gocontext.Context, registry *url.URL, repoName string, insecure bool) (repo distribution.Repository, err error) { |
| 80 | + err = pullthroughRepositoryWrapper(ctx, rr.sink, registry.Host, "Init", func(ctx context.Context) error { |
| 81 | + repo, err = rr.retriever.Repository(ctx, registry, repoName, insecure) |
| 82 | + return err |
| 83 | + }) |
| 84 | + if err != nil { |
| 85 | + return repo, err |
| 86 | + } |
| 87 | + return wrapped.NewRepository(repo, func(ctx context.Context, funcname string, f func(ctx context.Context) error) error { |
| 88 | + return pullthroughRepositoryWrapper(ctx, rr.sink, registry.Host, funcname, f) |
| 89 | + }), nil |
61 | 90 | }
|
62 | 91 |
|
63 |
| -func newWrapper(reponame string) wrapped.Wrapper { |
64 |
| - return func(ctx context.Context, funcname string, f func(ctx context.Context) error) error { |
65 |
| - defer NewTimer(registryAPIRequests, []string{strings.ToLower(funcname), reponame}).Stop() |
| 92 | +type metrics struct { |
| 93 | + sink Sink |
| 94 | +} |
| 95 | + |
| 96 | +var _ Metrics = &metrics{} |
| 97 | + |
| 98 | +// NewMetrics returns a helper that exposes the metrics through sink to |
| 99 | +// instrument the application. |
| 100 | +func NewMetrics(sink Sink) Metrics { |
| 101 | + return &metrics{ |
| 102 | + sink: sink, |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +func (m *metrics) Repository(r distribution.Repository, reponame string) distribution.Repository { |
| 107 | + return wrapped.NewRepository(r, func(ctx context.Context, funcname string, f func(ctx context.Context) error) error { |
| 108 | + defer NewTimer(m.sink.RequestDuration(funcname, reponame)).Stop() |
66 | 109 | return f(ctx)
|
| 110 | + }) |
| 111 | +} |
| 112 | + |
| 113 | +func (m *metrics) RepositoryRetriever(retriever registryclient.RepositoryRetriever) registryclient.RepositoryRetriever { |
| 114 | + return repositoryRetriever{ |
| 115 | + retriever: retriever, |
| 116 | + sink: m.sink, |
67 | 117 | }
|
68 | 118 | }
|
69 | 119 |
|
70 |
| -// NewBlobStore wraps a distribution.BlobStore to collect statistics. |
71 |
| -func NewBlobStore(bs distribution.BlobStore, reponame string) distribution.BlobStore { |
72 |
| - return wrapped.NewBlobStore(bs, newWrapper(reponame)) |
| 120 | +func (m *metrics) DigestBlobStoreCache() Cache { |
| 121 | + return &cache{ |
| 122 | + hitCounter: m.sink.PullthroughBlobstoreCacheRequests("Hit"), |
| 123 | + missCounter: m.sink.PullthroughBlobstoreCacheRequests("Miss"), |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +type noopMetrics struct { |
| 128 | +} |
| 129 | + |
| 130 | +var _ Metrics = noopMetrics{} |
| 131 | + |
| 132 | +// NewNoopMetrics return a helper that implements the Metrics interface, but |
| 133 | +// does nothing. |
| 134 | +func NewNoopMetrics() Metrics { |
| 135 | + return noopMetrics{} |
| 136 | +} |
| 137 | + |
| 138 | +func (m noopMetrics) Repository(r distribution.Repository, reponame string) distribution.Repository { |
| 139 | + return r |
73 | 140 | }
|
74 | 141 |
|
75 |
| -// NewManifestService wraps a distribution.ManifestService to collect statistics |
76 |
| -func NewManifestService(ms distribution.ManifestService, reponame string) distribution.ManifestService { |
77 |
| - return wrapped.NewManifestService(ms, newWrapper(reponame)) |
| 142 | +func (m noopMetrics) RepositoryRetriever(retriever registryclient.RepositoryRetriever) registryclient.RepositoryRetriever { |
| 143 | + return retriever |
78 | 144 | }
|
79 | 145 |
|
80 |
| -// NewTagService wraps a distribution.TagService to collect statistics |
81 |
| -func NewTagService(ts distribution.TagService, reponame string) distribution.TagService { |
82 |
| - return wrapped.NewTagService(ts, newWrapper(reponame)) |
| 146 | +func (m noopMetrics) DigestBlobStoreCache() Cache { |
| 147 | + return noopCache{} |
83 | 148 | }
|
0 commit comments