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  }