github.com/uber-go/tally/v4@v4.1.17/prometheus/config.go (about) 1 // Copyright (c) 2021 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package prometheus 22 23 import ( 24 "fmt" 25 "log" 26 "net" 27 "net/http" 28 "os" 29 "strings" 30 31 prom "github.com/prometheus/client_golang/prometheus" 32 ) 33 34 // Configuration is a configuration for a Prometheus reporter. 35 type Configuration struct { 36 // HandlerPath if specified will be used instead of using the default 37 // HTTP handler path "/metrics". 38 HandlerPath string `yaml:"handlerPath"` 39 40 // ListenNetwork if specified will be used instead of using tcp network. 41 // Supported networks: tcp, tcp4, tcp6 and unix. 42 ListenNetwork string `yaml:"listenNetwork"` 43 44 // ListenAddress if specified will be used instead of just registering the 45 // handler on the default HTTP serve mux without listening. 46 ListenAddress string `yaml:"listenAddress"` 47 48 // TimerType is the default Prometheus type to use for Tally timers. 49 TimerType string `yaml:"timerType"` 50 51 // DefaultHistogramBuckets if specified will set the default histogram 52 // buckets to be used by the reporter. 53 DefaultHistogramBuckets []HistogramObjective `yaml:"defaultHistogramBuckets"` 54 55 // DefaultSummaryObjectives if specified will set the default summary 56 // objectives to be used by the reporter. 57 DefaultSummaryObjectives []SummaryObjective `yaml:"defaultSummaryObjectives"` 58 59 // OnError specifies what to do when an error either with listening 60 // on the specified listen address or registering a metric with the 61 // Prometheus. By default the registerer will panic. 62 OnError string `yaml:"onError"` 63 } 64 65 // HistogramObjective is a Prometheus histogram bucket. 66 // See: https://godoc.org/github.com/prometheus/client_golang/prometheus#HistogramOpts 67 type HistogramObjective struct { 68 Upper float64 `yaml:"upper"` 69 } 70 71 // SummaryObjective is a Prometheus summary objective. 72 // See: https://godoc.org/github.com/prometheus/client_golang/prometheus#SummaryOpts 73 type SummaryObjective struct { 74 Percentile float64 `yaml:"percentile"` 75 AllowedError float64 `yaml:"allowedError"` 76 } 77 78 // ConfigurationOptions allows some programatic options, such as using a 79 // specific registry and what error callback to register. 80 type ConfigurationOptions struct { 81 // Registry if not nil will specify the specific registry to use 82 // for registering metrics. 83 Registry *prom.Registry 84 // OnError allows for customization of what to do when a metric 85 // registration error fails, the default is to panic. 86 OnError func(e error) 87 } 88 89 // NewReporter creates a new M3 reporter from this configuration. 90 func (c Configuration) NewReporter( 91 configOpts ConfigurationOptions, 92 ) (Reporter, error) { 93 var opts Options 94 95 if configOpts.Registry != nil { 96 opts.Registerer = configOpts.Registry 97 } 98 99 if configOpts.OnError != nil { 100 opts.OnRegisterError = configOpts.OnError 101 } else { 102 switch c.OnError { 103 case "stderr": 104 opts.OnRegisterError = func(err error) { 105 fmt.Fprintf(os.Stderr, "tally prometheus reporter error: %v\n", err) 106 } 107 case "log": 108 opts.OnRegisterError = func(err error) { 109 log.Printf("tally prometheus reporter error: %v\n", err) 110 } 111 case "none": 112 opts.OnRegisterError = func(err error) {} 113 default: 114 opts.OnRegisterError = func(err error) { 115 panic(err) 116 } 117 } 118 } 119 120 switch c.TimerType { 121 case "summary": 122 opts.DefaultTimerType = SummaryTimerType 123 case "histogram": 124 opts.DefaultTimerType = HistogramTimerType 125 } 126 127 if len(c.DefaultHistogramBuckets) > 0 { 128 var values []float64 129 for _, value := range c.DefaultHistogramBuckets { 130 values = append(values, value.Upper) 131 } 132 opts.DefaultHistogramBuckets = values 133 } 134 135 if len(c.DefaultSummaryObjectives) > 0 { 136 values := make(map[float64]float64) 137 for _, value := range c.DefaultSummaryObjectives { 138 values[value.Percentile] = value.AllowedError 139 } 140 opts.DefaultSummaryObjectives = values 141 } 142 143 reporter := NewReporter(opts) 144 145 path := "/metrics" 146 if handlerPath := strings.TrimSpace(c.HandlerPath); handlerPath != "" { 147 path = handlerPath 148 } 149 150 if addr := strings.TrimSpace(c.ListenAddress); addr == "" { 151 http.Handle(path, reporter.HTTPHandler()) 152 } else { 153 mux := http.NewServeMux() 154 mux.Handle(path, reporter.HTTPHandler()) 155 go func() { 156 network := c.ListenNetwork 157 if network == "" { 158 network = "tcp" 159 } 160 161 listener, err := net.Listen(network, addr) 162 if err != nil { 163 opts.OnRegisterError(err) 164 return 165 } 166 167 defer listener.Close() 168 169 if err = http.Serve(listener, mux); err != nil { 170 opts.OnRegisterError(err) 171 } 172 }() 173 } 174 175 return reporter, nil 176 }