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 ```