github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/observer/metricobserver/metricobserver.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package metricobserver
     5  
     6  import (
     7  	"net/http"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/juju/clock"
    12  	"github.com/juju/errors"
    13  	"github.com/prometheus/client_golang/prometheus"
    14  	"gopkg.in/juju/names.v2"
    15  
    16  	"github.com/juju/juju/apiserver/observer"
    17  	"github.com/juju/juju/rpc"
    18  )
    19  
    20  // MetricLabels used for setting labels for the Counter and Summary vectors.
    21  const (
    22  	MetricLabelFacade    = "facade"
    23  	MetricLabelVersion   = "version"
    24  	MetricLabelMethod    = "method"
    25  	MetricLabelErrorCode = "error_code"
    26  )
    27  
    28  // MetricLabelNames holds the names for reporting the names of the metric
    29  // types when calling the observers.
    30  var MetricLabelNames = []string{
    31  	MetricLabelFacade,
    32  	MetricLabelVersion,
    33  	MetricLabelMethod,
    34  	MetricLabelErrorCode,
    35  }
    36  
    37  // CounterVec is a Collector that bundles a set of Counters that all share the
    38  // same description.
    39  type CounterVec interface {
    40  	// With returns a Counter for a given labels slice
    41  	With(prometheus.Labels) prometheus.Counter
    42  }
    43  
    44  // SummaryVec is a Collector that bundles a set of Summaries that all share the
    45  // same description.
    46  type SummaryVec interface {
    47  	// With returns a Summary for a given labels slice
    48  	With(prometheus.Labels) prometheus.Observer
    49  }
    50  
    51  // MetricsCollector represents a bundle of metrics that is used by the observer
    52  // factory.
    53  //go:generate mockgen -package mocks -destination mocks/metrics_collector_mock.go github.com/juju/juju/apiserver/observer/metricobserver MetricsCollector,CounterVec,SummaryVec
    54  //go:generate mockgen -package mocks -destination mocks/metrics_mock.go github.com/prometheus/client_golang/prometheus Counter,Summary
    55  type MetricsCollector interface {
    56  	// APIRequestDuration returns a SummaryVec for updating the duration of
    57  	// api request duration.
    58  	APIRequestDuration() SummaryVec
    59  
    60  	// DeprecatedAPIRequestsTotal returns a CounterVec for updating the number of
    61  	// api requests total.
    62  	// The following is obsolete and should be removed for 2.6 release
    63  	DeprecatedAPIRequestsTotal() CounterVec
    64  
    65  	// DeprecatedAPIRequestDuration returns a SummaryVec for updating the duration of
    66  	// api request duration.
    67  	// The following is obsolete and should be removed for 2.6 release
    68  	DeprecatedAPIRequestDuration() SummaryVec
    69  }
    70  
    71  // Config contains the configuration for an Observer.
    72  type Config struct {
    73  	// Clock is the clock to use for all time-related operations.
    74  	Clock clock.Clock
    75  
    76  	// MetricsCollector defines .
    77  	MetricsCollector MetricsCollector
    78  }
    79  
    80  // Validate validates the observer factory configuration.
    81  func (cfg Config) Validate() error {
    82  	if cfg.Clock == nil {
    83  		return errors.NotValidf("nil Clock")
    84  	}
    85  	if cfg.MetricsCollector == nil {
    86  		return errors.NotValidf("nil MetricsCollector")
    87  	}
    88  	return nil
    89  }
    90  
    91  // NewObserverFactory returns a function that, when called, returns a new
    92  // Observer. NewObserverFactory registers the API request metrics, and
    93  // each Observer updates those metrics.
    94  func NewObserverFactory(config Config) (observer.ObserverFactory, error) {
    95  	if err := config.Validate(); err != nil {
    96  		return nil, errors.Annotate(err, "validating config")
    97  	}
    98  
    99  	// Observer is currently stateless, so we return the same one for each
   100  	// API connection. Individual RPC requests still get their own RPC
   101  	// observers.
   102  	o := &Observer{
   103  		clock: config.Clock,
   104  		metrics: metrics{
   105  			apiRequestDuration:           config.MetricsCollector.APIRequestDuration(),
   106  			deprecatedAPIRequestsTotal:   config.MetricsCollector.DeprecatedAPIRequestsTotal(),
   107  			deprecatedAPIRequestDuration: config.MetricsCollector.DeprecatedAPIRequestDuration(),
   108  		},
   109  	}
   110  	return func() observer.Observer {
   111  		return o
   112  	}, nil
   113  }
   114  
   115  // Observer is an API server request observer that collects Prometheus metrics.
   116  type Observer struct {
   117  	clock   clock.Clock
   118  	metrics metrics
   119  }
   120  
   121  type metrics struct {
   122  	apiRequestDuration           SummaryVec
   123  	deprecatedAPIRequestDuration SummaryVec
   124  	deprecatedAPIRequestsTotal   CounterVec
   125  }
   126  
   127  // Login is part of the observer.Observer interface.
   128  func (*Observer) Login(entity names.Tag, _ names.ModelTag, _ bool, _ string) {}
   129  
   130  // Join is part of the observer.Observer interface.
   131  func (*Observer) Join(req *http.Request, connectionID uint64) {}
   132  
   133  // Leave is part of the observer.Observer interface.
   134  func (*Observer) Leave() {}
   135  
   136  // RPCObserver is part of the observer.Observer interface.
   137  func (o *Observer) RPCObserver() rpc.Observer {
   138  	return &rpcObserver{
   139  		clock:   o.clock,
   140  		metrics: o.metrics,
   141  	}
   142  }
   143  
   144  type rpcObserver struct {
   145  	clock        clock.Clock
   146  	metrics      metrics
   147  	requestStart time.Time
   148  }
   149  
   150  // ServerRequest is part of the rpc.Observer interface.
   151  func (o *rpcObserver) ServerRequest(hdr *rpc.Header, body interface{}) {
   152  	o.requestStart = o.clock.Now()
   153  }
   154  
   155  // ServerReply is part of the rpc.Observer interface.
   156  func (o *rpcObserver) ServerReply(req rpc.Request, hdr *rpc.Header, body interface{}) {
   157  	labels := prometheus.Labels{
   158  		MetricLabelFacade:    req.Type,
   159  		MetricLabelVersion:   strconv.Itoa(req.Version),
   160  		MetricLabelMethod:    req.Action,
   161  		MetricLabelErrorCode: hdr.ErrorCode,
   162  	}
   163  	duration := o.clock.Now().Sub(o.requestStart)
   164  	o.metrics.apiRequestDuration.With(labels).Observe(duration.Seconds())
   165  
   166  	// The following is obsolete and should be removed for 2.6 release
   167  	o.metrics.deprecatedAPIRequestDuration.With(labels).Observe(duration.Seconds())
   168  	o.metrics.deprecatedAPIRequestsTotal.With(labels).Inc()
   169  }