trpc.group/trpc-go/trpc-go@v1.0.3/docs/developer_guide/develop_plugins/metrics.md (about)

     1  English | [中文](metrics.zh_CN.md)
     2  
     3  # How to develop a metric type plugin
     4  
     5  This guide will introduce how to develop a metric type plugin that depends on configuration for loading.
     6  The plugin will report the time it takes for the client to send a request to the server and receive a reply when initiating an RPC, as well as the time it takes for the server to receive a request and reply to the client.
     7  To develop this plugin, the following three sub-functions need to be implemented:
     8  
     9  - Implement the loading of the plugin's dependencies through configuration. For detailed instructions, please refer to [plugin](/plugin/README.md).
    10  - Implement reporting metrics to an external platform. For detailed instructions, please refer to [metrics](/metrics/README.md).
    11  - Implement reporting metrics in the filter. For detailed instructions, please refer to [filter](/filter/README.md).
    12  
    13  The following will use [trpc-metrics-prometheus](https://github.com/trpc-ecosystem/go-metrics-prometheus) as an example to introduce the relevant development steps.
    14  
    15  ## Implement the loading of the plugin's dependencies through configuration
    16  
    17  ### 1. Determine the configuration of the plugin
    18  
    19  ```yaml
    20  plugins:                                          # Plugin configuration
    21    metrics:                                        # Reference metrics
    22      prometheus:                                   # Start prometheus
    23        ip: 0.0.0.0                                 # Promethean binding address
    24        port: 8090                                  # Promethean binding port
    25        path: /metrics                              # Metrics path
    26        namespace: Development                      # Namespace
    27        subsystem: trpc                             # Subsystem
    28        rawmode:   false                            # Raw mode, special characters in metrics will not be converted
    29        enablepush: true                            # Enable push mode, not enabled by default
    30        gateway: http://localhost:9091              # Prometheus gateway address
    31        password: username:MyPassword               # Set account password, separated by colons
    32        job: job                                    # Job name
    33        pushinterval: 1                             # Push interval, default is 1s
    34  ```
    35  
    36  ```go
    37  const (
    38      pluginType = "metrics"
    39      pluginName = "prometheus"
    40  )
    41  
    42  type Config struct {
    43      IP           string `yaml:"ip"`           // metrics monitoring address.
    44      Port         int32  `yaml:"port"`         // metrics listens to the port.
    45      Path         string `yaml:"path"`         // metrics path.
    46      Namespace    string `yaml:"namespace"`    // formal or test.
    47      Subsystem    string `yaml:"subsystem"`    // default trpc.
    48      RawMode      bool   `yaml:"rawmode"`      // by default, the special character in metrics will be converted.
    49      EnablePush   bool   `yaml:"enablepush"`   // push is not enabled by default.
    50      Password     string `yaml:"password"`     // account Password.
    51      Gateway      string `yaml:"gateway"`      // push gateway address.
    52      PushInterval uint32 `yaml:"pushinterval"` // push interval,default 1s.
    53      Job          string `yaml:"job"`          // reported task name.
    54  }
    55  ```
    56  
    57  ### 2. Implement the `plugin.Factory` interface
    58  
    59  ```go
    60  type Plugin struct {
    61  }
    62  
    63  func (p *Plugin) Type() string {
    64      return pluginType
    65  }
    66  
    67  func (p *Plugin) Setup(name string, decoder plugin.Decoder) error {
    68      cfg := Config{}.Default()
    69      
    70      err := decoder.Decode(cfg)
    71      if err != nil {
    72          log.Errorf("trpc-metrics-prometheus:conf Decode error:%v", err)
    73          return err
    74      }
    75      go func() {
    76          err := initMetrics(cfg.IP, cfg.Port, cfg.Path)
    77          if err != nil {
    78              log.Errorf("trpc-metrics-prometheus:running:%v", err)
    79          }
    80      }()
    81      
    82      initSink(cfg)
    83      
    84      return nil
    85  }
    86  ```
    87  
    88  ### 3. Call `plugin.Register` to register the plugin with the plugin package
    89  
    90  ```go
    91  func init() {
    92      plugin.Register(pluginName, &Plugin{})
    93  }
    94  ```
    95  
    96  ## Implement reporting metrics to an external platform
    97  
    98  ### 1. Implement the `metrics.Sink` interface
    99  
   100  ```go
   101  const (
   102      sinkName = "prometheus"
   103  )
   104  
   105  func (s *Sink) Name() string {
   106      return sinkName
   107  }
   108  
   109  func (s *Sink) Report(rec metrics.Record, opts ...metrics.Option) error {
   110      if len(rec.GetDimensions()) <= 0 {
   111          return s.ReportSingleLabel(rec, opts...)
   112  }
   113      labels := make([]string, 0)
   114      values := make([]string, 0)
   115      prefix := rec.GetName()
   116      
   117      if len(labels) != len(values) {
   118          return errLength
   119      }
   120  
   121      for _, dimension := range rec.GetDimensions() {
   122          labels = append(labels, dimension.Name)
   123          values = append(values, dimension.Value)
   124      }
   125      for _, m := range rec.GetMetrics() {
   126          name := s.GetMetricsName(m)
   127          if prefix != "" {
   128              name = prefix + "_" + name
   129          }
   130          if !checkMetricsValid(name) {
   131              log.Errorf("metrics %s(%s) is invalid", name, m.Name())
   132              continue
   133          }
   134          s.reportVec(name, m, labels, values)
   135      }
   136      return nil
   137  }
   138  ```
   139  
   140  ### 2. Register the implemented Sink with the metrics package.
   141  
   142  ```go
   143  func initSink(cfg *Config) {
   144      defaultPrometheusPusher = push.New(cfg.Gateway, cfg.Job) 
   145      // set basic auth if set. 
   146      if len(cfg.Password) > 0 { 
   147          defaultPrometheusPusher.BasicAuth(basicAuthForPasswordOption(cfg.Password))
   148      }
   149      defaultPrometheusSink = &Sink{
   150          ns:         cfg.Namespace,
   151          subsystem:  cfg.Subsystem,
   152          rawMode:    cfg.RawMode,
   153          enablePush: cfg.EnablePush,
   154          pusher:     defaultPrometheusPusher
   155      }
   156      metrics.RegisterMetricsSink(defaultPrometheusSink)
   157      // start up pusher if needed.
   158      if cfg.EnablePush {
   159      defaultPrometheusPusher.Gatherer(prometheus.DefaultGatherer)
   160          go pusherRun(cfg, defaultPrometheusPusher)
   161      }
   162  }
   163  ```
   164  
   165  ## Implement reporting metrics in the filters
   166  
   167  ### 1. Determine the configuration of the filter
   168  
   169  ```yaml
   170    filter:
   171      - prometheus                                   # Add prometheus filter
   172  ```
   173  
   174  ### 2. Implement `filter.ServerFilter` and `filter.ServerFilter`
   175  
   176  ```go
   177  func ClientFilter(ctx context.Context, req, rsp interface{}, handler filter.ClientHandleFunc) error {
   178  	begin := time.Now()
   179  	hErr := handler(ctx, req, rsp)
   180  	msg := trpc.Message(ctx)
   181  	labels := getLabels(msg, hErr)
   182  	ms := make([]*metrics.Metrics, 0)
   183  	t := float64(time.Since(begin)) / float64(time.Millisecond)
   184  	ms = append(ms,
   185  		metrics.NewMetrics("time", t, metrics.PolicyHistogram),
   186  		metrics.NewMetrics("requests", 1.0, metrics.PolicySUM))
   187  	metrics.Histogram("ClientFilter_time", clientBounds)
   188  	r := metrics.NewMultiDimensionMetricsX("ClientFilter", labels, ms)
   189  	_ = GetDefaultPrometheusSink().Report(r)
   190  	return hErr
   191  }
   192  
   193  func ServerFilter(ctx context.Context, req interface{}, handler filter.ServerHandleFunc) (rsp interface{}, err error) {
   194  	begin := time.Now()
   195  	rsp, err = handler(ctx, req)
   196  	msg := trpc.Message(ctx)
   197  	labels := getLabels(msg, err)
   198  	ms := make([]*metrics.Metrics, 0)
   199  	t := float64(time.Since(begin)) / float64(time.Millisecond)
   200  	ms = append(ms,
   201  		metrics.NewMetrics("time", t, metrics.PolicyHistogram),
   202  		metrics.NewMetrics("requests", 1.0, metrics.PolicySUM))
   203  	metrics.Histogram("ServerFilter_time", serverBounds)
   204  	r := metrics.NewMultiDimensionMetricsX("ServerFilter", labels, ms)
   205  	_ = GetDefaultPrometheusSink().Report(r)
   206  	return rsp, err
   207  }
   208  ```
   209  
   210  ### 3. Register the filter with the `filter` package
   211  
   212  ```go
   213  func init() {
   214      filter.Register(pluginName, ServerFilter, ClientFilter)
   215  }
   216  ```