github.com/Jeffail/benthos/v3@v3.65.0/lib/metrics/constructor.go (about) 1 package metrics 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "sort" 9 "strings" 10 11 "github.com/Jeffail/benthos/v3/internal/docs" 12 "github.com/Jeffail/benthos/v3/lib/log" 13 "github.com/Jeffail/benthos/v3/lib/util/config" 14 "gopkg.in/yaml.v3" 15 ) 16 17 //------------------------------------------------------------------------------ 18 19 // Errors for the metrics package. 20 var ( 21 ErrInvalidMetricOutputType = errors.New("invalid metrics output type") 22 ) 23 24 //------------------------------------------------------------------------------ 25 26 // TypeSpec is a constructor and a usage description for each metric output 27 // type. 28 type TypeSpec struct { 29 constructor func(conf Config, opts ...func(Type)) (Type, error) 30 31 Status docs.Status 32 Version string 33 Summary string 34 Description string 35 Footnotes string 36 config docs.FieldSpec 37 FieldSpecs docs.FieldSpecs 38 } 39 40 // ConstructorFunc is a func signature able to construct a metrics output. 41 type ConstructorFunc func(Config, ...func(Type)) (Type, error) 42 43 // WalkConstructors iterates each component constructor. 44 func WalkConstructors(fn func(ConstructorFunc, docs.ComponentSpec)) { 45 inferred := docs.ComponentFieldsFromConf(NewConfig()) 46 for k, v := range Constructors { 47 conf := v.config 48 if len(v.FieldSpecs) > 0 { 49 conf = docs.FieldComponent().WithChildren(v.FieldSpecs.DefaultAndTypeFrom(inferred[k])...) 50 } else { 51 conf.Children = conf.Children.DefaultAndTypeFrom(inferred[k]) 52 } 53 spec := docs.ComponentSpec{ 54 Type: docs.TypeMetrics, 55 Name: k, 56 Summary: v.Summary, 57 Description: v.Description, 58 Footnotes: v.Footnotes, 59 Config: conf, 60 Status: v.Status, 61 Version: v.Version, 62 } 63 fn(ConstructorFunc(v.constructor), spec) 64 } 65 } 66 67 // Constructors is a map of all metrics types with their specs. 68 var Constructors = map[string]TypeSpec{} 69 70 //------------------------------------------------------------------------------ 71 72 // String constants representing each metric type. 73 const ( 74 TypeAWSCloudWatch = "aws_cloudwatch" 75 TypeBlackList = "blacklist" 76 TypeCloudWatch = "cloudwatch" 77 TypeHTTPServer = "http_server" 78 TypeInfluxDB = "influxdb" 79 TypeNone = "none" 80 TypePrometheus = "prometheus" 81 TypeRename = "rename" 82 TypeStatsd = "statsd" 83 TypeStdout = "stdout" 84 TypeWhiteList = "whitelist" 85 ) 86 87 //------------------------------------------------------------------------------ 88 89 // Config is the all encompassing configuration struct for all metric output 90 // types. 91 type Config struct { 92 Type string `json:"type" yaml:"type"` 93 AWSCloudWatch CloudWatchConfig `json:"aws_cloudwatch" yaml:"aws_cloudwatch"` 94 Blacklist BlacklistConfig `json:"blacklist" yaml:"blacklist"` 95 CloudWatch CloudWatchConfig `json:"cloudwatch" yaml:"cloudwatch"` 96 HTTP HTTPConfig `json:"http_server" yaml:"http_server"` 97 InfluxDB InfluxDBConfig `json:"influxdb" yaml:"influxdb"` 98 None struct{} `json:"none" yaml:"none"` 99 Prometheus PrometheusConfig `json:"prometheus" yaml:"prometheus"` 100 Rename RenameConfig `json:"rename" yaml:"rename"` 101 Statsd StatsdConfig `json:"statsd" yaml:"statsd"` 102 Stdout StdoutConfig `json:"stdout" yaml:"stdout"` 103 Whitelist WhitelistConfig `json:"whitelist" yaml:"whitelist"` 104 } 105 106 // NewConfig returns a configuration struct fully populated with default values. 107 func NewConfig() Config { 108 return Config{ 109 Type: "http_server", 110 AWSCloudWatch: NewCloudWatchConfig(), 111 Blacklist: NewBlacklistConfig(), 112 CloudWatch: NewCloudWatchConfig(), 113 HTTP: NewHTTPConfig(), 114 InfluxDB: NewInfluxDBConfig(), 115 None: struct{}{}, 116 Prometheus: NewPrometheusConfig(), 117 Rename: NewRenameConfig(), 118 Statsd: NewStatsdConfig(), 119 Stdout: NewStdoutConfig(), 120 Whitelist: NewWhitelistConfig(), 121 } 122 } 123 124 // SanitiseConfig returns a sanitised version of the Config, meaning sections 125 // that aren't relevant to behaviour are removed. 126 func SanitiseConfig(conf Config) (interface{}, error) { 127 return conf.Sanitised(false) 128 } 129 130 // Sanitised returns a sanitised version of the config, meaning sections that 131 // aren't relevant to behaviour are removed. Also optionally removes deprecated 132 // fields. 133 func (conf Config) Sanitised(removeDeprecated bool) (interface{}, error) { 134 outputMap, err := config.SanitizeComponent(conf) 135 if err != nil { 136 return nil, err 137 } 138 if err := docs.SanitiseComponentConfig( 139 docs.TypeMetrics, 140 map[string]interface{}(outputMap), 141 docs.ShouldDropDeprecated(removeDeprecated), 142 ); err != nil { 143 return nil, err 144 } 145 return outputMap, nil 146 } 147 148 //------------------------------------------------------------------------------ 149 150 // UnmarshalJSON ensures that when parsing configs that are in a map or slice 151 // the default values are still applied. 152 func (conf *Config) UnmarshalJSON(bytes []byte) error { 153 type confAlias Config 154 aliased := confAlias(NewConfig()) 155 156 if err := json.Unmarshal(bytes, &aliased); err != nil { 157 return err 158 } 159 160 *conf = Config(aliased) 161 return nil 162 } 163 164 // UnmarshalYAML ensures that when parsing configs that are in a map or slice 165 // the default values are still applied. 166 func (conf *Config) UnmarshalYAML(value *yaml.Node) error { 167 type confAlias Config 168 aliased := confAlias(NewConfig()) 169 170 err := value.Decode(&aliased) 171 if err != nil { 172 return fmt.Errorf("line %v: %v", value.Line, err) 173 } 174 175 if aliased.Type, _, err = docs.GetInferenceCandidateFromYAML(nil, docs.TypeMetrics, aliased.Type, value); err != nil { 176 return fmt.Errorf("line %v: %w", value.Line, err) 177 } 178 179 *conf = Config(aliased) 180 return nil 181 } 182 183 //------------------------------------------------------------------------------ 184 185 // OptSetLogger sets the logging output to be used by the metrics clients. 186 func OptSetLogger(log log.Modular) func(Type) { 187 return func(t Type) { 188 t.SetLogger(log) 189 } 190 } 191 192 //------------------------------------------------------------------------------ 193 194 var header = "This document was generated with `benthos --list-metrics`" + ` 195 196 A metrics type represents a destination for Benthos metrics to be aggregated 197 such as Statsd, Prometheus, or for debugging purposes an HTTP endpoint that 198 exposes a JSON object of metrics. 199 200 A metrics config section looks like this: 201 202 ` + "``` yaml" + ` 203 metrics: 204 statsd: 205 prefix: foo 206 address: localhost:8125 207 flush_period: 100ms 208 ` + "```" + ` 209 210 Benthos exposes lots of metrics and their paths will depend on your pipeline 211 configuration. However, there are some critical metrics that will always be 212 present that are outlined in [this document](#paths).` 213 214 // Descriptions returns a formatted string of collated descriptions of each 215 // type. 216 func Descriptions() string { 217 // Order our input types alphabetically 218 names := []string{} 219 for name := range Constructors { 220 names = append(names, name) 221 } 222 sort.Strings(names) 223 224 buf := bytes.Buffer{} 225 buf.WriteString("Metric Target Types\n") 226 buf.WriteString(strings.Repeat("=", 19)) 227 buf.WriteString("\n\n") 228 buf.WriteString(header) 229 buf.WriteString("\n\n") 230 231 // Append each description 232 for i, name := range names { 233 var confBytes []byte 234 235 conf := NewConfig() 236 conf.Type = name 237 if confSanit, err := SanitiseConfig(conf); err == nil { 238 confBytes, _ = config.MarshalYAML(confSanit) 239 } 240 241 buf.WriteString("## ") 242 buf.WriteString("`" + name + "`") 243 buf.WriteString("\n") 244 if confBytes != nil { 245 buf.WriteString("\n``` yaml\n") 246 buf.Write(confBytes) 247 buf.WriteString("```\n") 248 } 249 buf.WriteString(Constructors[name].Description) 250 buf.WriteString("\n") 251 if i != (len(names) - 1) { 252 buf.WriteString("\n---\n") 253 } 254 } 255 return buf.String() 256 } 257 258 // New creates a metric output type based on a configuration. 259 func New(conf Config, opts ...func(Type)) (Type, error) { 260 if c, ok := Constructors[conf.Type]; ok { 261 return c.constructor(conf, opts...) 262 } 263 return nil, ErrInvalidMetricOutputType 264 } 265 266 //------------------------------------------------------------------------------