github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/scrape/config/config.go (about) 1 // Copyright 2013 The Prometheus Authors 2 // Copyright 2021 The Pyroscope Authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package config 17 18 import ( 19 "errors" 20 "fmt" 21 "net/url" 22 "strings" 23 "time" 24 25 "github.com/imdario/mergo" 26 "github.com/pyroscope-io/pyroscope/pkg/storage/metadata" 27 28 "github.com/pyroscope-io/pyroscope/pkg/scrape/discovery" 29 "github.com/pyroscope-io/pyroscope/pkg/scrape/relabel" 30 profile "github.com/pyroscope-io/pyroscope/pkg/storage/tree" 31 "github.com/pyroscope-io/pyroscope/pkg/util/bytesize" 32 ) 33 34 // revive:disable:max-public-structs complex domain 35 36 var ( 37 profileDeltaMem = &Profile{ 38 Path: "/debug/pprof/delta_heap", 39 Params: nil, 40 SampleTypes: map[string]*profile.SampleTypeConfig{ 41 "inuse_objects": { 42 Units: metadata.ObjectsUnits, 43 Aggregation: metadata.AverageAggregationType, 44 }, 45 "alloc_objects": { 46 Units: metadata.ObjectsUnits, 47 }, 48 "inuse_space": { 49 Units: metadata.BytesUnits, 50 Aggregation: metadata.AverageAggregationType, 51 }, 52 "alloc_space": { 53 Units: metadata.BytesUnits, 54 }, 55 }, 56 } 57 profileDeltaMutex = &Profile{ 58 Path: "/debug/pprof/delta_mutex", 59 Params: nil, 60 SampleTypes: map[string]*profile.SampleTypeConfig{ 61 "contentions": { 62 DisplayName: "mutex_count", 63 Units: metadata.LockSamplesUnits, 64 }, 65 "delay": { 66 DisplayName: "mutex_duration", 67 Units: metadata.LockNanosecondsUnits, 68 }, 69 }, 70 } 71 profileDeltaBlock = &Profile{ 72 Path: "/debug/pprof/delta_block", 73 Params: nil, 74 SampleTypes: map[string]*profile.SampleTypeConfig{ 75 "contentions": { 76 DisplayName: "block_count", 77 Units: metadata.LockSamplesUnits, 78 }, 79 "delay": { 80 DisplayName: "block_duration", 81 Units: metadata.LockNanosecondsUnits, 82 }, 83 }, 84 } 85 ) 86 87 // DefaultConfig returns the default scrape configuration. 88 func DefaultConfig() *Config { 89 return &Config{ 90 ScrapeInterval: 10 * time.Second, 91 ScrapeTimeout: 15 * time.Second, 92 93 Profiles: map[string]*Profile{ 94 "cpu": { 95 Path: "/debug/pprof/profile", 96 Params: url.Values{ 97 "seconds": []string{"10"}, 98 }, 99 SampleTypes: map[string]*profile.SampleTypeConfig{ 100 "samples": { 101 DisplayName: "cpu", 102 Units: metadata.SamplesUnits, 103 Sampled: true, 104 }, 105 }, 106 }, 107 "mem": { 108 Path: "/debug/pprof/heap", 109 Params: nil, // url.Values{"gc": []string{"1"}}, 110 SampleTypes: map[string]*profile.SampleTypeConfig{ 111 "inuse_objects": { 112 Units: metadata.ObjectsUnits, 113 Aggregation: metadata.AverageAggregationType, 114 }, 115 "alloc_objects": { 116 Units: metadata.ObjectsUnits, 117 Cumulative: true, 118 }, 119 "inuse_space": { 120 Units: metadata.BytesUnits, 121 Aggregation: metadata.AverageAggregationType, 122 }, 123 "alloc_space": { 124 Units: metadata.BytesUnits, 125 Cumulative: true, 126 }, 127 }, 128 }, 129 "goroutines": { 130 Path: "/debug/pprof/goroutine", 131 Params: nil, 132 SampleTypes: map[string]*profile.SampleTypeConfig{ 133 "goroutine": { 134 DisplayName: "goroutines", 135 Units: metadata.GoroutinesUnits, 136 Aggregation: metadata.AverageAggregationType, 137 }, 138 }, 139 }, 140 "mutex": { 141 Path: "/debug/pprof/mutex", 142 Params: nil, 143 SampleTypes: map[string]*profile.SampleTypeConfig{ 144 "contentions": { 145 DisplayName: "mutex_count", 146 Units: metadata.LockSamplesUnits, 147 Cumulative: true, 148 }, 149 "delay": { 150 DisplayName: "mutex_duration", 151 Units: metadata.LockNanosecondsUnits, 152 Cumulative: true, 153 }, 154 }, 155 }, 156 "block": { 157 Path: "/debug/pprof/block", 158 Params: nil, 159 SampleTypes: map[string]*profile.SampleTypeConfig{ 160 "contentions": { 161 DisplayName: "block_count", 162 Units: metadata.LockSamplesUnits, 163 Cumulative: true, 164 }, 165 "delay": { 166 DisplayName: "block_duration", 167 Units: metadata.LockNanosecondsUnits, 168 Cumulative: true, 169 }, 170 }, 171 }, 172 }, 173 174 HTTPClientConfig: DefaultHTTPClientConfig, 175 Scheme: "http", 176 } 177 } 178 179 type Config struct { 180 // The job name to which the job label is set by default. 181 JobName string `yaml:"job-name"` 182 // How frequently to scrape the targets of this scrape config. 183 ScrapeInterval time.Duration `yaml:"scrape-interval,omitempty"` 184 // The timeout for scraping targets of this config. 185 ScrapeTimeout time.Duration `yaml:"scrape-timeout,omitempty"` 186 187 // The URL scheme with which to fetch metrics from targets. 188 Scheme string `yaml:"scheme,omitempty"` 189 // An uncompressed response body larger than this many bytes will cause the 190 // scrape to fail. 0 means no limit. 191 BodySizeLimit bytesize.ByteSize `yaml:"body-size-limit,omitempty"` 192 // TODO(kolesnikovae): Label limits. 193 194 // We cannot do proper Go type embedding below as the parser will then parse 195 // values arbitrarily into the overflow maps of further-down types. 196 ServiceDiscoveryConfigs discovery.Configs `yaml:"-"` 197 HTTPClientConfig HTTPClientConfig `yaml:",inline"` 198 199 // List of target relabel configurations. 200 RelabelConfigs []*relabel.Config `yaml:"relabel-configs,omitempty"` 201 202 // List of profiles to be scraped. 203 EnabledProfiles []string `yaml:"enabled-profiles,omitempty"` 204 // Profiles parameters. 205 Profiles map[string]*Profile `yaml:"profiles,omitempty"` 206 207 UseDeltaProfiles bool `yaml:"use-delta-profiles,omitempty"` 208 // TODO(kolesnikovae): Implement. 209 // List of profiles relabel configurations. 210 // ProfilesRelabelConfigs []*relabel.Config `yaml:"profiles-relabel-configs,omitempty"` 211 } 212 213 type Profile struct { 214 Path string `yaml:"path,omitempty"` 215 // A set of query parameters with which the target is scraped. 216 Params url.Values `yaml:"params,omitempty"` 217 // SampleTypes contains overrides for pprof sample types. 218 SampleTypes map[string]*profile.SampleTypeConfig `yaml:"sample-types,omitempty"` 219 // AllSampleTypes specifies whether to parse samples of 220 // types not listed in SampleTypes member. 221 AllSampleTypes bool `yaml:"all-sample-types,omitempty"` 222 // TODO(kolesnikovae): Overrides for interval, timeout, and limits? 223 } 224 225 // SetDirectory joins any relative file paths with dir. 226 func (c *Config) SetDirectory(dir string) { 227 c.ServiceDiscoveryConfigs.SetDirectory(dir) 228 c.HTTPClientConfig.SetDirectory(dir) 229 } 230 231 // IsProfileEnabled reports whether the given profile is enabled. 232 func (c *Config) IsProfileEnabled(p string) bool { 233 for _, v := range c.EnabledProfiles { 234 if v == p { 235 return true 236 } 237 } 238 return false 239 } 240 241 // UnmarshalYAML implements the yaml.Unmarshaler interface. 242 func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { 243 if err := discovery.UnmarshalYAMLWithInlineConfigs(c, unmarshal); err != nil { 244 return err 245 } 246 if len(c.JobName) == 0 { 247 return errors.New("job-name is empty") 248 } 249 if err := mergo.Merge(c, DefaultConfig()); err != nil { 250 return fmt.Errorf("failed to apply defaults: %w", err) 251 } 252 253 // The UnmarshalYAML method of HTTPClientConfig is not being called because it's not a pointer. 254 // We cannot make it a pointer as the parser panics for inlined pointer structs. 255 // Thus we just do its validation here. 256 if err := c.HTTPClientConfig.Validate(); err != nil { 257 return err 258 } 259 260 // Check for users putting URLs in target groups. 261 if len(c.RelabelConfigs) == 0 { 262 if err := checkStaticTargets(c.ServiceDiscoveryConfigs); err != nil { 263 return err 264 } 265 } 266 267 for _, rlcfg := range c.RelabelConfigs { 268 if rlcfg == nil { 269 return errors.New("empty or null target relabeling rule in scrape config") 270 } 271 } 272 273 if c.UseDeltaProfiles { 274 enableDeltaProfiles(c.Profiles) 275 } 276 277 return nil 278 } 279 280 func enableDeltaProfiles(profiles map[string]*Profile) { 281 var ok bool 282 _, ok = profiles["mem"] 283 if ok { 284 profiles["mem"] = profileDeltaMem 285 } 286 _, ok = profiles["block"] 287 if ok { 288 profiles["block"] = profileDeltaBlock 289 } 290 _, ok = profiles["mutex"] 291 if ok { 292 profiles["mutex"] = profileDeltaMutex 293 } 294 } 295 296 func checkStaticTargets(configs discovery.Configs) error { 297 for _, cfg := range configs { 298 sc, ok := cfg.(discovery.StaticConfig) 299 if !ok { 300 continue 301 } 302 for _, tg := range sc { 303 for _, t := range tg.Targets { 304 if err := CheckTargetAddress(string(t["__name__"])); err != nil { 305 return err 306 } 307 } 308 } 309 } 310 return nil 311 } 312 313 func CheckTargetAddress(address string) error { 314 if strings.Contains(address, "/") { 315 return fmt.Errorf("%q is not a valid hostname", address) 316 } 317 return nil 318 } 319 320 // MarshalYAML implements the yaml.Marshaler interface. 321 func (c *Config) MarshalYAML() (interface{}, error) { 322 return discovery.MarshalYAMLWithInlineConfigs(c) 323 }