github.com/grafana/pyroscope@v1.18.0/pkg/pyroscope/pyroscope.go (about)

     1  package pyroscope
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"flag"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"os"
    12  	"runtime"
    13  	"runtime/debug"
    14  	"slices"
    15  	"sort"
    16  	"strings"
    17  	"time"
    18  
    19  	"connectrpc.com/connect"
    20  	"github.com/go-kit/log"
    21  	"github.com/go-kit/log/level"
    22  	"github.com/gorilla/mux"
    23  	"github.com/grafana/dskit/flagext"
    24  	"github.com/grafana/dskit/grpcutil"
    25  	"github.com/grafana/dskit/kv/memberlist"
    26  	dslog "github.com/grafana/dskit/log"
    27  	"github.com/grafana/dskit/modules"
    28  	"github.com/grafana/dskit/ring"
    29  	"github.com/grafana/dskit/runtimeconfig"
    30  	"github.com/grafana/dskit/server"
    31  	"github.com/grafana/dskit/services"
    32  	"github.com/grafana/dskit/signals"
    33  	"github.com/grafana/dskit/spanlogger"
    34  	"github.com/grafana/dskit/spanprofiler"
    35  	wwtracing "github.com/grafana/dskit/tracing"
    36  	"github.com/grafana/pyroscope-go"
    37  	grpcgw "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    38  	"github.com/opentracing/opentracing-go"
    39  	"github.com/prometheus/client_golang/prometheus"
    40  	"github.com/prometheus/common/version"
    41  	"github.com/samber/lo"
    42  	"google.golang.org/grpc/health"
    43  
    44  	"github.com/grafana/pyroscope/pkg/api"
    45  	apiversion "github.com/grafana/pyroscope/pkg/api/version"
    46  	"github.com/grafana/pyroscope/pkg/cfg"
    47  	"github.com/grafana/pyroscope/pkg/compactionworker"
    48  	"github.com/grafana/pyroscope/pkg/compactor"
    49  	"github.com/grafana/pyroscope/pkg/distributor"
    50  	"github.com/grafana/pyroscope/pkg/embedded/grafana"
    51  	"github.com/grafana/pyroscope/pkg/frontend"
    52  	"github.com/grafana/pyroscope/pkg/ingester"
    53  	"github.com/grafana/pyroscope/pkg/metastore"
    54  	metastoreadmin "github.com/grafana/pyroscope/pkg/metastore/admin"
    55  	metastoreclient "github.com/grafana/pyroscope/pkg/metastore/client"
    56  	phlareobj "github.com/grafana/pyroscope/pkg/objstore"
    57  	objstoreclient "github.com/grafana/pyroscope/pkg/objstore/client"
    58  	"github.com/grafana/pyroscope/pkg/phlaredb"
    59  	"github.com/grafana/pyroscope/pkg/querier"
    60  	"github.com/grafana/pyroscope/pkg/querier/worker"
    61  	"github.com/grafana/pyroscope/pkg/querybackend"
    62  	querybackendclient "github.com/grafana/pyroscope/pkg/querybackend/client"
    63  	"github.com/grafana/pyroscope/pkg/scheduler"
    64  	"github.com/grafana/pyroscope/pkg/scheduler/schedulerdiscovery"
    65  	"github.com/grafana/pyroscope/pkg/segmentwriter"
    66  	segmentwriterclient "github.com/grafana/pyroscope/pkg/segmentwriter/client"
    67  	placement "github.com/grafana/pyroscope/pkg/segmentwriter/client/distributor/placement/adaptiveplacement"
    68  	"github.com/grafana/pyroscope/pkg/settings"
    69  	recordingrulesclient "github.com/grafana/pyroscope/pkg/settings/recording/client"
    70  	"github.com/grafana/pyroscope/pkg/storegateway"
    71  	"github.com/grafana/pyroscope/pkg/symbolizer"
    72  	"github.com/grafana/pyroscope/pkg/tenant"
    73  	"github.com/grafana/pyroscope/pkg/tracing"
    74  	"github.com/grafana/pyroscope/pkg/usagestats"
    75  	"github.com/grafana/pyroscope/pkg/util"
    76  	"github.com/grafana/pyroscope/pkg/util/cli"
    77  	"github.com/grafana/pyroscope/pkg/validation"
    78  	"github.com/grafana/pyroscope/pkg/validation/exporter"
    79  )
    80  
    81  type Config struct {
    82  	Target            flagext.StringSliceCSV `yaml:"target,omitempty"`
    83  	API               api.Config             `yaml:"api"`
    84  	Server            server.Config          `yaml:"server,omitempty"`
    85  	Distributor       distributor.Config     `yaml:"distributor,omitempty"`
    86  	Querier           querier.Config         `yaml:"querier,omitempty"`
    87  	Frontend          frontend.Config        `yaml:"frontend,omitempty"`
    88  	Worker            worker.Config          `yaml:"frontend_worker"`
    89  	LimitsConfig      validation.Limits      `yaml:"limits"`
    90  	QueryScheduler    scheduler.Config       `yaml:"query_scheduler"`
    91  	Ingester          ingester.Config        `yaml:"ingester,omitempty"`
    92  	StoreGateway      storegateway.Config    `yaml:"store_gateway,omitempty"`
    93  	MemberlistKV      memberlist.KVConfig    `yaml:"memberlist"`
    94  	PhlareDB          phlaredb.Config        `yaml:"pyroscopedb,omitempty"`
    95  	Tracing           tracing.Config         `yaml:"tracing"`
    96  	OverridesExporter exporter.Config        `yaml:"overrides_exporter"      doc:"hidden"`
    97  	RuntimeConfig     runtimeconfig.Config   `yaml:"runtime_config"`
    98  	Compactor         compactor.Config       `yaml:"compactor"`
    99  	TenantSettings    settings.Config        `yaml:"tenant_settings"`
   100  
   101  	Storage       StorageConfig       `yaml:"storage"`
   102  	SelfProfiling SelfProfilingConfig `yaml:"self_profiling,omitempty"`
   103  
   104  	MultitenancyEnabled bool              `yaml:"multitenancy_enabled,omitempty"`
   105  	Analytics           usagestats.Config `yaml:"analytics"`
   106  	ShowBanner          bool              `yaml:"show_banner,omitempty"`
   107  	ShutdownDelay       time.Duration     `yaml:"shutdown_delay,omitempty"`
   108  
   109  	EmbeddedGrafana grafana.Config `yaml:"embedded_grafana,omitempty"`
   110  
   111  	ConfigFile      string `yaml:"-"`
   112  	ConfigExpandEnv bool   `yaml:"-"`
   113  
   114  	V2                bool                    `yaml:"-" doc:"hidden"`
   115  	SegmentWriter     segmentwriter.Config    `yaml:"segment_writer"     doc:"hidden"`
   116  	Metastore         metastore.Config        `yaml:"metastore"          doc:"hidden"`
   117  	QueryBackend      querybackend.Config     `yaml:"query_backend"      doc:"hidden"`
   118  	CompactionWorker  compactionworker.Config `yaml:"compaction_worker"  doc:"hidden"`
   119  	AdaptivePlacement placement.Config        `yaml:"adaptive_placement" doc:"hidden"`
   120  	Symbolizer        symbolizer.Config       `yaml:"symbolizer"         doc:"hidden"`
   121  }
   122  
   123  func newDefaultConfig() *Config {
   124  	defaultConfig := &Config{}
   125  	defaultFS := flag.NewFlagSet("", flag.PanicOnError)
   126  	defaultConfig.RegisterFlags(defaultFS)
   127  	return defaultConfig
   128  }
   129  
   130  type StorageConfig struct {
   131  	Bucket objstoreclient.Config `yaml:",inline"`
   132  }
   133  
   134  func (c *StorageConfig) RegisterFlags(f *flag.FlagSet) {
   135  	c.Bucket.RegisterFlagsWithPrefix("storage.", f)
   136  }
   137  
   138  type SelfProfilingConfig struct {
   139  	DisablePush          bool `yaml:"disable_push,omitempty"`
   140  	MutexProfileFraction int  `yaml:"mutex_profile_fraction,omitempty"`
   141  	BlockProfileRate     int  `yaml:"block_profile_rate,omitempty"`
   142  	UseK6Middleware      bool `yaml:"use_k6_middleware,omitempty"`
   143  }
   144  
   145  func (c *SelfProfilingConfig) RegisterFlags(f *flag.FlagSet) {
   146  	// these are values that worked well in OG Pyroscope Cloud without adding much overhead
   147  	f.IntVar(&c.MutexProfileFraction, "self-profiling.mutex-profile-fraction", 5, "")
   148  	f.IntVar(&c.BlockProfileRate, "self-profiling.block-profile-rate", 5, "")
   149  	f.BoolVar(
   150  		&c.DisablePush,
   151  		"self-profiling.disable-push",
   152  		false,
   153  		"When running in single binary (--target=all) Pyroscope will push (Go SDK) profiles to itself. Set to true to disable self-profiling.",
   154  	)
   155  	f.BoolVar(
   156  		&c.UseK6Middleware,
   157  		"self-profiling.use-k6-middleware",
   158  		false,
   159  		"Read k6 labels from request headers and set them as dynamic profile tags.",
   160  	)
   161  }
   162  
   163  func (c *Config) RegisterFlags(f *flag.FlagSet) {
   164  	c.RegisterFlagsWithContext(f)
   165  }
   166  
   167  // RegisterFlagsWithContext registers flag.
   168  func (c *Config) RegisterFlagsWithContext(f *flag.FlagSet) {
   169  	// Set the default module list to 'all'
   170  	c.Target = []string{All}
   171  	f.StringVar(&c.ConfigFile, "config.file", "", "yaml file to load")
   172  	f.Var(&c.Target, "target", "Comma-separated list of Pyroscope modules to load. "+
   173  		"The alias 'all' can be used in the list to load a number of core modules and will enable single-binary mode. ")
   174  	f.BoolVar(
   175  		&c.MultitenancyEnabled,
   176  		"auth.multitenancy-enabled",
   177  		false,
   178  		"When set to true, incoming HTTP requests must specify tenant ID in HTTP X-Scope-OrgId header. When set to false, tenant ID anonymous is used instead.",
   179  	)
   180  	f.BoolVar(
   181  		&c.ConfigExpandEnv,
   182  		"config.expand-env",
   183  		false,
   184  		"Expands ${var} in config according to the values of the environment variables.",
   185  	)
   186  	f.BoolVar(&c.ShowBanner, "config.show_banner", true, "Prints the application banner at startup.")
   187  	f.DurationVar(&c.ShutdownDelay, "shutdown-delay", 0, "Wait time before shutting down after a termination signal.")
   188  
   189  	c.registerServerFlagsWithChangedDefaultValues(f)
   190  	c.MemberlistKV.RegisterFlags(f)
   191  	c.Querier.RegisterFlags(f)
   192  	c.StoreGateway.RegisterFlags(f, util.Logger)
   193  	c.PhlareDB.RegisterFlags(f)
   194  	c.Tracing.RegisterFlags(f)
   195  	c.SelfProfiling.RegisterFlags(f)
   196  	c.RuntimeConfig.RegisterFlags(f)
   197  	c.Analytics.RegisterFlags(f)
   198  	c.LimitsConfig.RegisterFlags(f)
   199  	c.Compactor.RegisterFlags(f, log.NewLogfmtLogger(os.Stderr))
   200  	c.API.RegisterFlags(f)
   201  	c.EmbeddedGrafana.RegisterFlags(f)
   202  	c.TenantSettings.RegisterFlags(f)
   203  }
   204  
   205  // registerServerFlagsWithChangedDefaultValues registers *Config.Server flags, but overrides some defaults set by the dskit package.
   206  func (c *Config) registerServerFlagsWithChangedDefaultValues(fs *flag.FlagSet) {
   207  	throwaway := flag.NewFlagSet("throwaway", flag.PanicOnError)
   208  
   209  	// Register to throwaway flags first. Default values are remembered during registration and cannot be changed,
   210  	// but we can take values from throwaway flag set and reregister into supplied flags with new default values.
   211  	c.Storage.RegisterFlags(throwaway)
   212  	c.Server.RegisterFlags(throwaway)
   213  	c.Ingester.RegisterFlags(throwaway)
   214  	c.Distributor.RegisterFlags(throwaway, log.NewLogfmtLogger(os.Stderr))
   215  	c.Frontend.RegisterFlags(throwaway, log.NewLogfmtLogger(os.Stderr))
   216  	c.QueryScheduler.RegisterFlags(throwaway, log.NewLogfmtLogger(os.Stderr))
   217  	c.Worker.RegisterFlags(throwaway)
   218  	c.OverridesExporter.RegisterFlags(throwaway, log.NewLogfmtLogger(os.Stderr))
   219  
   220  	overrides := map[string]string{
   221  		"server.http-listen-port":                "4040",
   222  		"distributor.replication-factor":         "1",
   223  		"query-scheduler.service-discovery-mode": schedulerdiscovery.ModeRing,
   224  	}
   225  
   226  	if c.V2 {
   227  		for k, v := range map[string]string{
   228  			"server.grpc-max-recv-msg-size-bytes":                    "104857600",
   229  			"server.grpc-max-send-msg-size-bytes":                    "104857600",
   230  			"server.grpc.keepalive.min-time-between-pings":           "1s",
   231  			"segment-writer.grpc-client-config.connect-timeout":      "1s",
   232  			"segment-writer.num-tokens":                              "4",
   233  			"segment-writer.heartbeat-timeout":                       "1m",
   234  			"segment-writer.unregister-on-shutdown":                  "false",
   235  			"segment-writer.min-ready-duration":                      "30s",
   236  			"storage.s3.http.idle-conn-timeout":                      "10m",
   237  			"storage.s3.max-idle-connections-per-host":               "1000",
   238  			"storage.gcs.http.idle-conn-timeout":                     "10m",
   239  			"storage.gcs.max-idle-connections-per-host":              "1000",
   240  			"compaction-worker.metrics-exporter.rules-source.static": "[]",
   241  		} {
   242  			overrides[k] = v
   243  		}
   244  
   245  		c.Metastore.RegisterFlags(throwaway)
   246  		c.SegmentWriter.RegisterFlags(throwaway)
   247  		c.QueryBackend.RegisterFlags(throwaway)
   248  		c.CompactionWorker.RegisterFlags(throwaway)
   249  		c.AdaptivePlacement.RegisterFlags(throwaway)
   250  		c.LimitsConfig.WritePathOverrides.RegisterFlags(throwaway)
   251  		c.LimitsConfig.ReadPathOverrides.RegisterFlags(throwaway)
   252  		c.LimitsConfig.AdaptivePlacementLimits.RegisterFlags(throwaway)
   253  		c.LimitsConfig.Retention.RegisterFlags(throwaway)
   254  		c.LimitsConfig.RecordingRules.RegisterFlags(throwaway)
   255  		c.LimitsConfig.Symbolizer.RegisterFlags(throwaway)
   256  		c.Symbolizer.RegisterFlags(throwaway)
   257  	}
   258  
   259  	throwaway.VisitAll(func(f *flag.Flag) {
   260  		if v, ok := overrides[f.Name]; ok {
   261  			// Ignore errors when setting new values. We have a test to verify that it works.
   262  			_ = f.Value.Set(v)
   263  		}
   264  		fs.Var(f.Value, f.Name, f.Usage)
   265  	})
   266  }
   267  
   268  func (c *Config) Validate() error {
   269  	if len(c.Target) == 0 {
   270  		return errors.New("no modules specified")
   271  	}
   272  
   273  	if err := c.Frontend.Validate(); err != nil {
   274  		return err
   275  	}
   276  
   277  	if err := c.Worker.Validate(util.Logger); err != nil {
   278  		return err
   279  	}
   280  
   281  	if err := c.LimitsConfig.Validate(); err != nil {
   282  		return err
   283  	}
   284  
   285  	if err := c.QueryScheduler.Validate(); err != nil {
   286  		return err
   287  	}
   288  
   289  	if err := c.Ingester.Validate(); err != nil {
   290  		return err
   291  	}
   292  
   293  	if err := c.StoreGateway.Validate(c.LimitsConfig); err != nil {
   294  		return err
   295  	}
   296  
   297  	if err := c.OverridesExporter.Validate(); err != nil {
   298  		return err
   299  	}
   300  
   301  	if err := c.Compactor.Validate(c.PhlareDB.MaxBlockDuration); err != nil {
   302  		return err
   303  	}
   304  
   305  	if err := c.Storage.Bucket.Validate(util.Logger); err != nil {
   306  		return err
   307  	}
   308  
   309  	if err := c.TenantSettings.Validate(); err != nil {
   310  		return err
   311  	}
   312  
   313  	return nil
   314  }
   315  
   316  func (c *Config) ApplyDynamicConfig() cfg.Source {
   317  	c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store = "memberlist"
   318  	c.SegmentWriter.LifecyclerConfig.RingConfig.KVStore.Store = "memberlist"
   319  	c.Distributor.DistributorRing.KVStore.Store = c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store
   320  	c.OverridesExporter.Ring.Ring.KVStore.Store = c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store
   321  	c.Frontend.QuerySchedulerDiscovery.SchedulerRing.KVStore.Store = c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store
   322  	c.Worker.QuerySchedulerDiscovery.SchedulerRing.KVStore.Store = c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store
   323  	c.QueryScheduler.ServiceDiscovery.SchedulerRing.KVStore.Store = c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store
   324  	c.StoreGateway.ShardingRing.Ring.KVStore.Store = c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store
   325  	c.Compactor.ShardingRing.Common.KVStore.Store = c.Ingester.LifecyclerConfig.RingConfig.KVStore.Store
   326  
   327  	return func(dst cfg.Cloneable) error {
   328  		return nil
   329  	}
   330  }
   331  
   332  func (c *Config) Clone() flagext.Registerer {
   333  	return func(c Config) *Config {
   334  		return &c
   335  	}(*c)
   336  }
   337  
   338  type Pyroscope struct {
   339  	Cfg    Config
   340  	reg    prometheus.Registerer
   341  	logger *logger
   342  	tracer io.Closer
   343  
   344  	ModuleManager *modules.Manager
   345  	serviceMap    map[string]services.Service
   346  	deps          map[string][]string
   347  
   348  	API            *api.API
   349  	Server         *server.Server
   350  	SignalHandler  *signals.Handler
   351  	MemberlistKV   *memberlist.KVInitService
   352  	ingesterRing   *ring.Ring
   353  	usageReport    *usagestats.Reporter
   354  	RuntimeConfig  *runtimeconfig.Manager
   355  	Overrides      *validation.Overrides
   356  	Compactor      *compactor.MultitenantCompactor
   357  	admin          api.AdminService
   358  	versions       *apiversion.Service
   359  	serviceManager *services.Manager
   360  
   361  	TenantLimits validation.TenantLimits
   362  
   363  	storageBucket phlareobj.Bucket
   364  
   365  	grpcGatewayMux *grpcgw.ServeMux
   366  
   367  	auth     connect.Option
   368  	ingester *ingester.Ingester
   369  	frontend *frontend.Frontend
   370  
   371  	// Experimental modules.
   372  	segmentWriter        *segmentwriter.SegmentWriterService
   373  	segmentWriterClient  *segmentwriterclient.Client
   374  	segmentWriterRing    *ring.Ring
   375  	placementAgent       *placement.Agent
   376  	placementManager     *placement.Manager
   377  	metastore            *metastore.Metastore
   378  	metastoreClient      *metastoreclient.Client
   379  	metastoreAdmin       *metastoreadmin.Admin
   380  	queryBackendClient   *querybackendclient.Client
   381  	compactionWorker     *compactionworker.Worker
   382  	healthServer         *health.Server
   383  	recordingRulesClient *recordingrulesclient.Client
   384  	symbolizer           *symbolizer.Symbolizer
   385  }
   386  
   387  func New(cfg Config) (*Pyroscope, error) {
   388  	logger := initLogger(cfg.Server.LogFormat, cfg.Server.LogLevel)
   389  	cfg.Server.Log = logger
   390  	usagestats.Edition("oss")
   391  
   392  	phlare := &Pyroscope{
   393  		Cfg:    cfg,
   394  		logger: logger,
   395  		reg:    prometheus.DefaultRegisterer,
   396  	}
   397  	if err := cfg.Validate(); err != nil {
   398  		return nil, err
   399  	}
   400  	if err := phlare.setupModuleManager(); err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	runtime.SetMutexProfileFraction(cfg.SelfProfiling.MutexProfileFraction)
   405  	runtime.SetBlockProfileRate(cfg.SelfProfiling.BlockProfileRate)
   406  
   407  	if cfg.Tracing.Enabled {
   408  		// Setting the environment variable JAEGER_AGENT_HOST enables tracing
   409  		name := os.Getenv("JAEGER_SERVICE_NAME")
   410  		if name == "" {
   411  			name = fmt.Sprintf("pyroscope-%s", cfg.Target)
   412  		}
   413  		trace, err := wwtracing.NewFromEnv(name)
   414  		if err != nil {
   415  			level.Error(logger).Log("msg", "error in initializing tracing. tracing will not be enabled", "err", err)
   416  		}
   417  		if cfg.Tracing.ProfilingEnabled {
   418  			opentracing.SetGlobalTracer(spanprofiler.NewTracer(opentracing.GlobalTracer()))
   419  		}
   420  		phlare.tracer = trace
   421  	}
   422  
   423  	phlare.auth = connect.WithInterceptors(tenant.NewAuthInterceptor(cfg.MultitenancyEnabled))
   424  	phlare.Cfg.API.HTTPAuthMiddleware = util.AuthenticateUser(cfg.MultitenancyEnabled)
   425  	phlare.Cfg.API.GrpcAuthMiddleware = phlare.auth
   426  
   427  	return phlare, nil
   428  }
   429  
   430  func (f *Pyroscope) setupModuleManager() error {
   431  	mm := modules.NewManager(f.logger)
   432  
   433  	mm.RegisterModule(Storage, f.initStorage, modules.UserInvisibleModule)
   434  	mm.RegisterModule(GRPCGateway, f.initGRPCGateway, modules.UserInvisibleModule)
   435  	mm.RegisterModule(MemberlistKV, f.initMemberlistKV, modules.UserInvisibleModule)
   436  	mm.RegisterModule(IngesterRing, f.initIngesterRing, modules.UserInvisibleModule)
   437  	mm.RegisterModule(RuntimeConfig, f.initRuntimeConfig, modules.UserInvisibleModule)
   438  	mm.RegisterModule(Overrides, f.initOverrides, modules.UserInvisibleModule)
   439  	mm.RegisterModule(OverridesExporter, f.initOverridesExporter)
   440  	mm.RegisterModule(Ingester, f.initIngester)
   441  	mm.RegisterModule(Server, f.initServer, modules.UserInvisibleModule)
   442  	mm.RegisterModule(API, f.initAPI, modules.UserInvisibleModule)
   443  	mm.RegisterModule(Version, f.initVersion, modules.UserInvisibleModule)
   444  	mm.RegisterModule(Distributor, f.initDistributor)
   445  	mm.RegisterModule(Querier, f.initQuerier)
   446  	mm.RegisterModule(StoreGateway, f.initStoreGateway)
   447  	mm.RegisterModule(UsageReport, f.initUsageReport)
   448  	mm.RegisterModule(QueryFrontend, f.initQueryFrontend)
   449  	mm.RegisterModule(QueryScheduler, f.initQueryScheduler)
   450  	mm.RegisterModule(Compactor, f.initCompactor)
   451  	mm.RegisterModule(Admin, f.initAdmin)
   452  	mm.RegisterModule(All, nil)
   453  	mm.RegisterModule(TenantSettings, f.initTenantSettings)
   454  	mm.RegisterModule(AdHocProfiles, f.initAdHocProfiles)
   455  	mm.RegisterModule(EmbeddedGrafana, f.initEmbeddedGrafana)
   456  	mm.RegisterModule(FeatureFlags, f.initFeatureFlags)
   457  
   458  	// Add dependencies
   459  	deps := map[string][]string{
   460  		All: {
   461  			Ingester,
   462  			Distributor,
   463  			QueryFrontend,
   464  			QueryScheduler,
   465  			Querier,
   466  			StoreGateway,
   467  			Compactor,
   468  			Admin,
   469  			TenantSettings,
   470  			AdHocProfiles,
   471  		},
   472  
   473  		Server:            {GRPCGateway},
   474  		API:               {Server},
   475  		Distributor:       {Overrides, IngesterRing, API, UsageReport},
   476  		Querier:           {Overrides, API, MemberlistKV, IngesterRing, UsageReport, Version, FeatureFlags},
   477  		QueryFrontend:     {OverridesExporter, API, MemberlistKV, UsageReport, Version, FeatureFlags},
   478  		QueryScheduler:    {Overrides, API, MemberlistKV, UsageReport},
   479  		Ingester:          {Overrides, API, MemberlistKV, Storage, UsageReport, Version},
   480  		StoreGateway:      {API, Storage, Overrides, MemberlistKV, UsageReport, Admin, Version},
   481  		Compactor:         {API, Storage, Overrides, MemberlistKV, UsageReport},
   482  		UsageReport:       {Storage, MemberlistKV},
   483  		Overrides:         {RuntimeConfig},
   484  		OverridesExporter: {Overrides, MemberlistKV},
   485  		RuntimeConfig:     {API},
   486  		IngesterRing:      {API, MemberlistKV},
   487  		MemberlistKV:      {API},
   488  		Admin:             {API, Storage},
   489  		Version:           {API, MemberlistKV},
   490  		TenantSettings:    {API, Overrides, Storage},
   491  		AdHocProfiles:     {API, Overrides, Storage},
   492  		EmbeddedGrafana:   {API},
   493  		FeatureFlags:      {API},
   494  	}
   495  
   496  	if f.Cfg.V2 {
   497  		v2Modules := map[string][]string{
   498  			SegmentWriter:       {Overrides, API, MemberlistKV, Storage, UsageReport, MetastoreClient},
   499  			Metastore:           {Overrides, API, MetastoreClient, Storage, PlacementManager},
   500  			MetastoreAdmin:      {API, MetastoreClient},
   501  			CompactionWorker:    {Overrides, API, Storage, MetastoreClient, RecordingRulesClient},
   502  			QueryBackend:        {Overrides, API, Storage, QueryBackendClient},
   503  			SegmentWriterRing:   {Overrides, API, MemberlistKV},
   504  			SegmentWriterClient: {Overrides, API, SegmentWriterRing, PlacementAgent},
   505  			PlacementAgent:      {Overrides, API, Storage},
   506  			PlacementManager:    {Overrides, API, Storage},
   507  			Symbolizer:          {Overrides, Storage},
   508  		}
   509  		for k, v := range v2Modules {
   510  			deps[k] = v
   511  		}
   512  
   513  		deps[All] = append(deps[All], SegmentWriter, Metastore, CompactionWorker, QueryBackend)
   514  		deps[QueryFrontend] = append(deps[QueryFrontend], MetastoreClient, QueryBackendClient, Symbolizer)
   515  		deps[Distributor] = append(deps[Distributor], SegmentWriterClient)
   516  		deps[Server] = append(deps[Server], HealthServer)
   517  		deps[Admin] = append(deps[Admin], MetastoreAdmin)
   518  
   519  		mm.RegisterModule(SegmentWriter, f.initSegmentWriter)
   520  		mm.RegisterModule(Metastore, f.initMetastore)
   521  		mm.RegisterModule(CompactionWorker, f.initCompactionWorker)
   522  		mm.RegisterModule(QueryBackend, f.initQueryBackend)
   523  		mm.RegisterModule(Symbolizer, f.initSymbolizer)
   524  
   525  		mm.RegisterModule(SegmentWriterRing, f.initSegmentWriterRing, modules.UserInvisibleModule)
   526  		mm.RegisterModule(SegmentWriterClient, f.initSegmentWriterClient, modules.UserInvisibleModule)
   527  		mm.RegisterModule(MetastoreClient, f.initMetastoreClient, modules.UserInvisibleModule)
   528  		mm.RegisterModule(MetastoreAdmin, f.initMetastoreAdmin, modules.UserInvisibleModule)
   529  		mm.RegisterModule(QueryBackendClient, f.initQueryBackendClient, modules.UserInvisibleModule)
   530  		mm.RegisterModule(PlacementAgent, f.initPlacementAgent, modules.UserInvisibleModule)
   531  		mm.RegisterModule(PlacementManager, f.initPlacementManager, modules.UserInvisibleModule)
   532  		mm.RegisterModule(HealthServer, f.initHealthServer, modules.UserInvisibleModule)
   533  		mm.RegisterModule(RecordingRulesClient, f.initRecordingRulesClient, modules.UserInvisibleModule)
   534  	}
   535  
   536  	for mod, targets := range deps {
   537  		if err := mm.AddDependency(mod, targets...); err != nil {
   538  			return err
   539  		}
   540  	}
   541  
   542  	f.deps = deps
   543  	f.ModuleManager = mm
   544  
   545  	return nil
   546  }
   547  
   548  // made here https://patorjk.com/software/taag/#p=display&f=Doom&t=grafana%20pyroscope
   549  // also needed to replace all ` with '
   550  var banner = `
   551                   / _|
   552    __ _ _ __ __ _| |_ __ _ _ __   __ _   _ __  _   _ _ __ ___  ___  ___ ___  _ __   ___
   553   / _' | '__/ _' |  _/ _' | '_ \ / _' | | '_ \| | | | '__/ _ \/ __|/ __/ _ \| '_ \ / _ \
   554  | (_| | | | (_| | || (_| | | | | (_| | | |_) | |_| | | | (_) \__ \ (_| (_) | |_) |  __/
   555   \__, |_|  \__,_|_| \__,_|_| |_|\__,_| | .__/ \__, |_|  \___/|___/\___\___/| .__/ \___|
   556    __/ |                                | |     __/ |                       | |
   557   |___/                                 |_|    |___/                        |_|
   558   `
   559  
   560  func (f *Pyroscope) Run() error {
   561  	if f.Cfg.ShowBanner {
   562  		_ = cli.GradientBanner(banner, os.Stderr)
   563  	}
   564  
   565  	serviceMap, err := f.ModuleManager.InitModuleServices(f.Cfg.Target...)
   566  	if err != nil {
   567  		return err
   568  	}
   569  
   570  	f.serviceMap = serviceMap
   571  	var servs []services.Service
   572  	for _, s := range serviceMap {
   573  		servs = append(servs, s)
   574  	}
   575  
   576  	sm, err := services.NewManager(servs...)
   577  	if err != nil {
   578  		return err
   579  	}
   580  	f.serviceManager = sm
   581  
   582  	f.API.RegisterReadyHandler(f.readyHandler(sm))
   583  	RegisterHealthServer(f.Server.HTTP, grpcutil.WithManager(sm))
   584  	healthy := func() {
   585  		level.Info(f.logger).Log("msg", "Pyroscope started", "version", version.Info())
   586  		if os.Getenv("PYROSCOPE_PRINT_ROUTES") != "" {
   587  			printRoutes(f.Server.HTTP)
   588  		}
   589  
   590  		// Start profiling when Pyroscope is ready
   591  		if !f.Cfg.SelfProfiling.DisablePush && slices.Contains(f.Cfg.Target, All) {
   592  			_, err := pyroscope.Start(pyroscope.Config{
   593  				ApplicationName: "pyroscope",
   594  				ServerAddress:   fmt.Sprintf("http://%s:%d", "localhost", f.Cfg.Server.HTTPListenPort),
   595  				Tags: map[string]string{
   596  					"hostname":           os.Getenv("HOSTNAME"),
   597  					"target":             "all",
   598  					"service_git_ref":    serviceGitRef(),
   599  					"service_repository": "https://github.com/grafana/pyroscope",
   600  				},
   601  				ProfileTypes: []pyroscope.ProfileType{
   602  					pyroscope.ProfileCPU,
   603  					pyroscope.ProfileAllocObjects,
   604  					pyroscope.ProfileAllocSpace,
   605  					pyroscope.ProfileInuseObjects,
   606  					pyroscope.ProfileInuseSpace,
   607  					pyroscope.ProfileGoroutines,
   608  					pyroscope.ProfileMutexCount,
   609  					pyroscope.ProfileMutexDuration,
   610  					pyroscope.ProfileBlockCount,
   611  					pyroscope.ProfileBlockDuration,
   612  				},
   613  			})
   614  			if err != nil {
   615  				level.Warn(f.logger).Log("msg", "failed to start pyroscope", "err", err)
   616  			}
   617  		}
   618  	}
   619  
   620  	// only query-frontend, query-backend, querier and target all should register the UI
   621  	if slices.Contains(f.Cfg.Target, All) ||
   622  		slices.Contains(f.Cfg.Target, QueryFrontend) ||
   623  		slices.Contains(f.Cfg.Target, QueryBackend) ||
   624  		slices.Contains(f.Cfg.Target, Querier) {
   625  
   626  		if err = f.API.RegisterCatchAll(); err != nil {
   627  			return err
   628  		}
   629  	} else {
   630  		f.API.RegisterRedirectToAdmin()
   631  	}
   632  
   633  	serviceFailed := func(service services.Service) {
   634  		// if any service fails, stop entire Pyroscope
   635  		sm.StopAsync()
   636  
   637  		// let's find out which module failed
   638  		for m, s := range serviceMap {
   639  			if s == service {
   640  				if service.FailureCase() == modules.ErrStopProcess {
   641  					level.Info(f.logger).
   642  						Log("msg", "received stop signal via return error", "module", m, "error", service.FailureCase())
   643  				} else {
   644  					level.Error(f.logger).Log("msg", "module failed", "module", m, "error", service.FailureCase())
   645  				}
   646  				return
   647  			}
   648  		}
   649  
   650  		level.Error(f.logger).Log("msg", "module failed", "module", "unknown", "error", service.FailureCase())
   651  	}
   652  
   653  	sm.AddListener(services.NewManagerListener(healthy, f.stopped, serviceFailed))
   654  
   655  	// Setup signal handler. If signal arrives, we stop the manager, which stops all the services.
   656  	f.SignalHandler = signals.NewHandler(f.Server.Log)
   657  	go func() {
   658  		f.SignalHandler.Loop()
   659  		if f.Cfg.ShutdownDelay > 0 {
   660  			level.Info(f.logger).Log("msg", "shutdown delayed", "delay", f.Cfg.ShutdownDelay)
   661  			time.Sleep(f.Cfg.ShutdownDelay)
   662  		}
   663  		sm.StopAsync()
   664  	}()
   665  
   666  	// Start all services. This can really only fail if some service is already
   667  	// in other state than New, which should not be the case.
   668  	err = sm.StartAsync(context.Background())
   669  	if err == nil {
   670  		// Wait until service manager stops. It can stop in two ways:
   671  		// 1) Signal is received and manager is stopped.
   672  		// 2) Any service fails.
   673  		err = sm.AwaitStopped(context.Background())
   674  	}
   675  	if f.versions != nil {
   676  		f.versions.Shutdown()
   677  	}
   678  	// If there is no error yet (= service manager started and then stopped without problems),
   679  	// but any service failed, report that failure as an error to caller.
   680  	if err == nil {
   681  		if failed := sm.ServicesByState()[services.Failed]; len(failed) > 0 {
   682  			for _, f := range failed {
   683  				if f.FailureCase() != modules.ErrStopProcess {
   684  					// Details were reported via failure listener before
   685  					err = errors.New("failed services")
   686  					break
   687  				}
   688  			}
   689  		}
   690  	}
   691  
   692  	return err
   693  }
   694  
   695  func (f *Pyroscope) readyHandler(sm *services.Manager) http.HandlerFunc {
   696  	return func(w http.ResponseWriter, r *http.Request) {
   697  		if !sm.IsHealthy() {
   698  			msg := bytes.Buffer{}
   699  			msg.WriteString("Some services are not Running:\n")
   700  
   701  			byState := map[services.State][]string{}
   702  			for name, svc := range f.serviceMap {
   703  				state := svc.State()
   704  				byState[state] = append(byState[state], name)
   705  			}
   706  
   707  			states := lo.Keys(byState)
   708  			sort.Slice(states, func(i, j int) bool { return states[i] < states[j] })
   709  
   710  			for _, st := range states {
   711  				sort.Strings(byState[st])
   712  				msg.WriteString(fmt.Sprintf("%v: %v\n", st, byState[st]))
   713  			}
   714  
   715  			http.Error(w, msg.String(), http.StatusServiceUnavailable)
   716  			return
   717  		}
   718  
   719  		if f.metastore != nil {
   720  			if err := f.metastore.CheckReady(r.Context()); err != nil {
   721  				http.Error(w, "Metastore not ready: "+err.Error(), http.StatusServiceUnavailable)
   722  				return
   723  			}
   724  		}
   725  
   726  		if f.ingester != nil {
   727  			if err := f.ingester.CheckReady(r.Context()); err != nil {
   728  				http.Error(w, "Ingester not ready: "+err.Error(), http.StatusServiceUnavailable)
   729  				return
   730  			}
   731  		}
   732  
   733  		if f.segmentWriter != nil {
   734  			if err := f.segmentWriter.CheckReady(r.Context()); err != nil {
   735  				http.Error(w, "Segment Writer not ready: "+err.Error(), http.StatusServiceUnavailable)
   736  				return
   737  			}
   738  		}
   739  
   740  		if f.frontend != nil {
   741  			if err := f.frontend.CheckReady(r.Context()); err != nil {
   742  				http.Error(w, "Query Frontend not ready: "+err.Error(), http.StatusServiceUnavailable)
   743  				return
   744  			}
   745  		}
   746  
   747  		util.WriteTextResponse(w, "ready")
   748  	}
   749  }
   750  
   751  func (f *Pyroscope) Stop() func(context.Context) error {
   752  	if f.serviceManager == nil {
   753  		return func(context.Context) error { return nil }
   754  	}
   755  	f.serviceManager.StopAsync()
   756  	return f.serviceManager.AwaitStopped
   757  }
   758  
   759  func (f *Pyroscope) stopped() {
   760  	level.Info(f.logger).Log("msg", "Pyroscope stopped")
   761  	if f.tracer != nil {
   762  		if err := f.tracer.Close(); err != nil {
   763  			level.Error(f.logger).Log("msg", "error closing tracing", "err", err)
   764  		}
   765  	}
   766  	if err := f.logger.w.Close(); err != nil {
   767  		_, _ = fmt.Fprintf(os.Stderr, "error closing log writer: %v\n", err)
   768  	}
   769  }
   770  
   771  func initLogger(logFormat string, logLevel dslog.Level) *logger {
   772  	w := util.NewAsyncWriter(os.Stderr, // Flush after:
   773  		256<<10, 20, // 256KiB buffer is full (keep 20 buffers).
   774  		1<<10, // 1K writes or 100ms.
   775  		100*time.Millisecond,
   776  	)
   777  
   778  	// Use UTC timestamps and skip 5 stack frames.
   779  	l := dslog.NewGoKitWithWriter(logFormat, w)
   780  	l = log.With(l, "ts", log.DefaultTimestampUTC, "caller", spanlogger.Caller(5))
   781  
   782  	// Must put the level filter last for efficiency.
   783  	l = level.NewFilter(l, logLevel.Option)
   784  
   785  	return &logger{w: w, Logger: l}
   786  }
   787  
   788  type logger struct {
   789  	w io.WriteCloser
   790  	log.Logger
   791  }
   792  
   793  func (f *Pyroscope) initAPI() (services.Service, error) {
   794  	a, err := api.New(f.Cfg.API, f.Server, f.grpcGatewayMux, f.Server.Log)
   795  	if err != nil {
   796  		return nil, err
   797  	}
   798  	f.API = a
   799  
   800  	if err := f.API.RegisterAPI(f.statusService()); err != nil {
   801  		return nil, err
   802  	}
   803  
   804  	return nil, nil
   805  }
   806  
   807  func (f *Pyroscope) initVersion() (services.Service, error) {
   808  	var err error
   809  	f.versions, err = apiversion.New(f.Cfg.Distributor.DistributorRing, f.logger, f.reg)
   810  	if err != nil {
   811  		return nil, err
   812  	}
   813  	f.API.RegisterVersion(f.versions)
   814  	return f.versions, nil
   815  }
   816  
   817  func printRoutes(r *mux.Router) {
   818  	err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
   819  		path, err := route.GetPathRegexp()
   820  		if err != nil {
   821  			fmt.Printf("failed to get path regexp %s\n", err)
   822  			return nil
   823  		}
   824  		method, err := route.GetMethods()
   825  		if err != nil {
   826  			method = []string{"*"}
   827  		}
   828  		fmt.Printf("%s %s\n", strings.Join(method, ","), path)
   829  		return nil
   830  	})
   831  	if err != nil {
   832  		fmt.Printf("failed to walk routes %s\n", err)
   833  	}
   834  }
   835  
   836  // serviceGitRef attempts to find the git revision of the service. Default to HEAD.
   837  func serviceGitRef() string {
   838  	if version.Revision != "" {
   839  		return version.Revision
   840  	}
   841  	buildInfo, ok := debug.ReadBuildInfo()
   842  	if ok {
   843  		for _, setting := range buildInfo.Settings {
   844  			if setting.Key == "vcs.revision" {
   845  				return setting.Value
   846  			}
   847  		}
   848  	}
   849  	return "HEAD"
   850  }