diff --git a/config/metric_config.go b/config/metric_config.go index e3dacdaad691e1cba5cd2ae60e09b6ee42e80fa5..7b9222f29b3eef93868ea7a852979876aea2c87e 100644 --- a/config/metric_config.go +++ b/config/metric_config.go @@ -17,9 +17,14 @@ package config +var ( + defaultHistogramBucket = []float64{10, 50, 100, 200, 500, 1000, 10000} +) + // This is the config struct for all metrics implementation type MetricConfig struct { - Reporters []string `yaml:"reporters" json:"reporters,omitempty"` + Reporters []string `yaml:"reporters" json:"reporters,omitempty"` + HistogramBucket []float64 `yaml:"histogram_bucket" json:"histogram_bucket,omitempty"` } // find the MetricConfig @@ -30,3 +35,12 @@ func GetMetricConfig() *MetricConfig { } return metricConfig } + +// find the histogram bucket +// if it's empty, the default value will be return +func (mc *MetricConfig) GetHistogramBucket() []float64 { + if len(mc.HistogramBucket) == 0 { + mc.HistogramBucket = defaultHistogramBucket + } + return mc.HistogramBucket +} diff --git a/metrics/prometheus/reporter.go b/metrics/prometheus/reporter.go index 5a2ec2016a34989508f8ded1be0870964727d756..8914902ab034fcce836e66062f023b366b72fa52 100644 --- a/metrics/prometheus/reporter.go +++ b/metrics/prometheus/reporter.go @@ -32,6 +32,7 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/metrics" "github.com/apache/dubbo-go/protocol" ) @@ -48,15 +49,28 @@ const ( providerKey = "provider" consumerKey = "consumer" + + // to identify the metric's type + histogramSuffix = "_histogram" + // to identify the metric's type + summarySuffix = "_summary" +) + +var ( + labelNames = []string{serviceKey, groupKey, versionKey, methodKey, timeoutKey, remoteKey, localKey} ) // should initialize after loading configuration func init() { rpt := &PrometheusReporter{ - consumerVec: newSummaryVec(consumerKey), - providerVec: newSummaryVec(providerKey), + consumerSummaryVec: newSummaryVec(consumerKey), + providerSummaryVec: newSummaryVec(providerKey), + + consumerHistogramVec: newHistogramVec(consumerKey), + providerHistogramVec: newHistogramVec(providerKey), } - prometheus.MustRegister(rpt.consumerVec, rpt.providerVec) + prometheus.MustRegister(rpt.consumerSummaryVec, rpt.providerSummaryVec, + rpt.consumerHistogramVec, rpt.providerHistogramVec) extension.SetMetricReporter(reporterName, rpt) } @@ -65,27 +79,35 @@ func init() { // https://prometheus.io/docs/guides/go-application/ type PrometheusReporter struct { - // report the consumer-side's data - consumerVec *prometheus.SummaryVec - // report the provider-side's data - providerVec *prometheus.SummaryVec + // report the consumer-side's summary data + consumerSummaryVec *prometheus.SummaryVec + // report the provider-side's summary data + providerSummaryVec *prometheus.SummaryVec + + // report the provider-side's histogram data + providerHistogramVec *prometheus.HistogramVec + // report the consumer-side's histogram data + consumerHistogramVec *prometheus.HistogramVec } // report the duration to Prometheus func (reporter *PrometheusReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) { url := invoker.GetUrl() var sumVec *prometheus.SummaryVec + var hisVec *prometheus.HistogramVec if isProvider(url) { - sumVec = reporter.providerVec + sumVec = reporter.providerSummaryVec + hisVec = reporter.providerHistogramVec } else if isConsumer(url) { - sumVec = reporter.consumerVec + sumVec = reporter.consumerSummaryVec + hisVec = reporter.consumerHistogramVec } else { logger.Warnf("The url is not the consumer's or provider's, "+ "so the invocation will be ignored. url: %s", url.String()) return } - sumVec.With(prometheus.Labels{ + labels := prometheus.Labels{ serviceKey: url.Service(), groupKey: url.GetParam(groupKey, ""), versionKey: url.GetParam(versionKey, ""), @@ -93,7 +115,24 @@ func (reporter *PrometheusReporter) Report(ctx context.Context, invoker protocol timeoutKey: url.GetParam(timeoutKey, ""), remoteKey: invocation.AttachmentsByKey(constant.REMOTE_ADDR, ""), localKey: invocation.AttachmentsByKey(constant.REMOTE_ADDR, ""), - }).Observe(float64(cost.Nanoseconds() / constant.MsToNanoRate)) + } + + costMs := float64(cost.Nanoseconds() / constant.MsToNanoRate) + sumVec.With(labels).Observe(costMs) + hisVec.With(labels).Observe(costMs) +} + +func newHistogramVec(side string) *prometheus.HistogramVec { + mc := config.GetMetricConfig() + return prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: metrics.NameSpace, + Subsystem: side, + Name: serviceKey + histogramSuffix, + Help: "This is the dubbo's histogram metrics", + Buckets: mc.GetHistogramBucket(), + }, + labelNames) } // whether this url represents the application received the request as server @@ -115,9 +154,9 @@ func newSummaryVec(side string) *prometheus.SummaryVec { return prometheus.NewSummaryVec( prometheus.SummaryOpts{ Namespace: metrics.NameSpace, - Help: "this is the dubbo's metrics", + Help: "This is the dubbo's summary metrics", Subsystem: side, - Name: serviceKey, + Name: serviceKey + summarySuffix, Objectives: map[float64]float64{ 0.5: 0.01, 0.75: 0.01, @@ -127,6 +166,6 @@ func newSummaryVec(side string) *prometheus.SummaryVec { 0.999: 0.0001, }, }, - []string{serviceKey, groupKey, versionKey, methodKey, timeoutKey, remoteKey, localKey}, + labelNames, ) } diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go index 52e8b50c57f65e1495931354bb5a55861cbb457b..d1741d16d03f09ffc19b227e6a405f60bf306f9b 100644 --- a/metrics/prometheus/reporter_test.go +++ b/metrics/prometheus/reporter_test.go @@ -49,4 +49,24 @@ func TestPrometheusReporter_Report(t *testing.T) { assert.False(t, isConsumer(url)) ctx := context.Background() reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil) + + // consumer side + url, _ = common.NewURL(context.Background(), + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider"+ + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser."+ + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name="+ + "BDTService&organization=ikurento.com&owner=ZX®istry.role=0&retries=&"+ + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker = protocol.NewBaseInvoker(url) + reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil) + + // invalid role + url, _ = common.NewURL(context.Background(), + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider"+ + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser."+ + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name="+ + "BDTService&organization=ikurento.com&owner=ZX®istry.role=9&retries=&"+ + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker = protocol.NewBaseInvoker(url) + reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil) }