dubbo.apache.org/dubbo-go/v3@v3.1.1/metrics/prometheus/registry.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package prometheus
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"net/http"
    24  	"sync"
    25  	"time"
    26  )
    27  
    28  import (
    29  	"github.com/dubbogo/gost/log/logger"
    30  
    31  	prom "github.com/prometheus/client_golang/prometheus"
    32  	"github.com/prometheus/client_golang/prometheus/promhttp"
    33  	"github.com/prometheus/client_golang/prometheus/push"
    34  
    35  	"github.com/prometheus/common/expfmt"
    36  )
    37  
    38  import (
    39  	"dubbo.apache.org/dubbo-go/v3/common"
    40  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    41  	"dubbo.apache.org/dubbo-go/v3/common/extension"
    42  	"dubbo.apache.org/dubbo-go/v3/metrics"
    43  )
    44  
    45  func init() {
    46  	metrics.SetRegistry(constant.ProtocolPrometheus, func(url *common.URL) metrics.MetricRegistry {
    47  		return &promMetricRegistry{r: prom.DefaultRegisterer, gather: prom.DefaultGatherer, url: url}
    48  	})
    49  }
    50  
    51  type promMetricRegistry struct {
    52  	r      prom.Registerer
    53  	gather prom.Gatherer
    54  	vecs   sync.Map
    55  	url    *common.URL
    56  }
    57  
    58  func NewPromMetricRegistry(reg *prom.Registry, url *common.URL) *promMetricRegistry {
    59  	return &promMetricRegistry{r: reg, gather: reg, url: url}
    60  }
    61  
    62  func (p *promMetricRegistry) getOrComputeVec(key string, supplier func() prom.Collector) interface{} {
    63  	v, ok := p.vecs.Load(key)
    64  	if !ok {
    65  		v, ok = p.vecs.LoadOrStore(key, supplier())
    66  		if !ok {
    67  			p.r.MustRegister(v.(prom.Collector)) // only registe collector which stored success
    68  		}
    69  	}
    70  	return v
    71  }
    72  
    73  func (p *promMetricRegistry) Counter(m *metrics.MetricId) metrics.CounterMetric {
    74  	vec := p.getOrComputeVec(m.Name, func() prom.Collector {
    75  		return prom.NewCounterVec(prom.CounterOpts{
    76  			Name: m.Name,
    77  			Help: m.Desc,
    78  		}, m.TagKeys())
    79  	}).(*prom.CounterVec)
    80  	return vec.With(m.Tags)
    81  }
    82  
    83  func (p *promMetricRegistry) Gauge(m *metrics.MetricId) metrics.GaugeMetric {
    84  	vec := p.getOrComputeVec(m.Name, func() prom.Collector {
    85  		return prom.NewGaugeVec(prom.GaugeOpts{
    86  			Name: m.Name,
    87  			Help: m.Desc,
    88  		}, m.TagKeys())
    89  	}).(*prom.GaugeVec)
    90  	return vec.With(m.Tags)
    91  }
    92  
    93  func (p *promMetricRegistry) Histogram(m *metrics.MetricId) metrics.ObservableMetric {
    94  	vec := p.getOrComputeVec(m.Name, func() prom.Collector {
    95  		return prom.NewHistogramVec(prom.HistogramOpts{
    96  			Name: m.Name,
    97  			Help: m.Desc,
    98  		}, m.TagKeys())
    99  	}).(*prom.HistogramVec)
   100  	return vec.With(m.Tags)
   101  }
   102  
   103  func (p *promMetricRegistry) Summary(m *metrics.MetricId) metrics.ObservableMetric {
   104  	vec := p.getOrComputeVec(m.Name, func() prom.Collector {
   105  		return prom.NewSummaryVec(prom.SummaryOpts{
   106  			Name: m.Name,
   107  			Help: m.Desc,
   108  		}, m.TagKeys())
   109  	}).(*prom.SummaryVec)
   110  	return vec.With(m.Tags)
   111  }
   112  
   113  func (p *promMetricRegistry) Rt(m *metrics.MetricId, opts *metrics.RtOpts) metrics.ObservableMetric {
   114  	key := m.Name
   115  	var supplier func() prom.Collector
   116  	if opts != nil && opts.Aggregate {
   117  		key += "_aggregate"
   118  		if opts.BucketNum == 0 {
   119  			opts.BucketNum = p.url.GetParamByIntValue(constant.AggregationBucketNumKey, constant.AggregationDefaultBucketNum)
   120  		}
   121  		if opts.TimeWindowSeconds == 0 {
   122  			opts.TimeWindowSeconds = p.url.GetParamInt(constant.AggregationTimeWindowSecondsKey, constant.AggregationDefaultTimeWindowSeconds)
   123  		}
   124  		supplier = func() prom.Collector {
   125  			return NewAggRtVec(&RtOpts{
   126  				Name:              m.Name,
   127  				Help:              m.Desc,
   128  				bucketNum:         opts.BucketNum,
   129  				timeWindowSeconds: opts.TimeWindowSeconds,
   130  			}, m.TagKeys())
   131  		}
   132  	} else {
   133  		supplier = func() prom.Collector {
   134  			return NewRtVec(&RtOpts{
   135  				Name: m.Name,
   136  				Help: m.Desc,
   137  			}, m.TagKeys())
   138  		}
   139  	}
   140  	vec := p.getOrComputeVec(key, supplier).(*RtVec)
   141  	return vec.With(m.Tags)
   142  }
   143  
   144  func (p *promMetricRegistry) Export() {
   145  	if p.url.GetParamBool(constant.PrometheusExporterEnabledKey, false) {
   146  		go p.exportHttp()
   147  	}
   148  	if p.url.GetParamBool(constant.PrometheusPushgatewayEnabledKey, false) {
   149  		p.exportPushgateway()
   150  	}
   151  }
   152  
   153  func (p *promMetricRegistry) exportHttp() {
   154  	mux := http.NewServeMux()
   155  	path := p.url.GetParam(constant.PrometheusExporterMetricsPathKey, constant.PrometheusDefaultMetricsPath)
   156  	port := p.url.GetParam(constant.PrometheusExporterMetricsPortKey, constant.PrometheusDefaultMetricsPort)
   157  	mux.Handle(path, promhttp.InstrumentMetricHandler(p.r, promhttp.HandlerFor(p.gather, promhttp.HandlerOpts{})))
   158  	srv := &http.Server{Addr: ":" + port, Handler: mux}
   159  	extension.AddCustomShutdownCallback(func() {
   160  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   161  		defer cancel()
   162  		if err := srv.Shutdown(ctx); nil != err {
   163  			logger.Fatalf("prometheus server shutdown failed, err: %v", err)
   164  		} else {
   165  			logger.Info("prometheus server gracefully shutdown success")
   166  		}
   167  	})
   168  	logger.Infof("prometheus endpoint :%s%s", port, path)
   169  	if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { // except Shutdown or Close
   170  		logger.Errorf("new prometheus server with error: %v", err)
   171  	}
   172  }
   173  
   174  func (p *promMetricRegistry) exportPushgateway() {
   175  	baseUrl, exist := p.url.GetNonDefaultParam(constant.PrometheusPushgatewayBaseUrlKey)
   176  	if !exist {
   177  		logger.Error("no pushgateway base url found in config path: metrics.prometheus.pushgateway.base-url, please check your config")
   178  		return
   179  	}
   180  	username := p.url.GetParam(constant.PrometheusPushgatewayBaseUrlKey, "")
   181  	password := p.url.GetParam(constant.PrometheusPushgatewayBaseUrlKey, "")
   182  	job := p.url.GetParam(constant.PrometheusPushgatewayJobKey, constant.PrometheusDefaultJobName)
   183  	pushInterval := p.url.GetParamByIntValue(constant.PrometheusPushgatewayPushIntervalKey, constant.PrometheusDefaultPushInterval)
   184  	pusher := push.New(baseUrl, job).Gatherer(p.gather)
   185  	if len(username) != 0 {
   186  		pusher.BasicAuth(username, password)
   187  	}
   188  	logger.Infof("prometheus pushgateway will push to %s every %d seconds", baseUrl, pushInterval)
   189  	ticker := time.NewTicker(time.Duration(pushInterval) * time.Second)
   190  	go func() {
   191  		for range ticker.C {
   192  			err := pusher.Add()
   193  			if err != nil {
   194  				logger.Errorf("push metric data to prometheus pushgateway error: %v", err)
   195  			} else {
   196  				logger.Debugf("prometheus pushgateway push to %s success", baseUrl)
   197  			}
   198  		}
   199  	}()
   200  }
   201  
   202  func (p *promMetricRegistry) Scrape() (string, error) {
   203  	gathering, err := p.gather.Gather()
   204  	if err != nil {
   205  		return "", err
   206  	}
   207  	out := &bytes.Buffer{}
   208  	for _, mf := range gathering {
   209  		if _, err := expfmt.MetricFamilyToText(out, mf); err != nil {
   210  			return "", err
   211  		}
   212  	}
   213  	return out.String(), nil
   214  }