github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/agent/profiler/profiler.go (about)

     1  // Package profiler is a public API golang apps should use to send data to pyroscope server. It is intentionally separate from the rest of the code.
     2  //
     3  //	The idea is that this API won't change much over time, while all the other code will.
     4  package profiler
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"github.com/pyroscope-io/pyroscope/pkg/agent/log"
    10  	"os"
    11  	"runtime/pprof"
    12  	"time"
    13  
    14  	"github.com/pyroscope-io/pyroscope/pkg/agent"
    15  	"github.com/pyroscope-io/pyroscope/pkg/agent/spy"
    16  	"github.com/pyroscope-io/pyroscope/pkg/agent/types"
    17  	"github.com/pyroscope-io/pyroscope/pkg/agent/upstream/remote"
    18  )
    19  
    20  type ProfileType = spy.ProfileType
    21  
    22  var (
    23  	ProfileCPU          = spy.ProfileCPU
    24  	ProfileAllocObjects = spy.ProfileAllocObjects
    25  	ProfileAllocSpace   = spy.ProfileAllocSpace
    26  	ProfileInuseObjects = spy.ProfileInuseObjects
    27  	ProfileInuseSpace   = spy.ProfileInuseSpace
    28  )
    29  
    30  type Config struct {
    31  	ApplicationName string // e.g backend.purchases
    32  	Tags            map[string]string
    33  	ServerAddress   string // e.g http://pyroscope.services.internal:4040
    34  	AuthToken       string // specify this token when using pyroscope cloud
    35  	SampleRate      uint32
    36  	Logger          log.Logger
    37  	ProfileTypes    []ProfileType
    38  	DisableGCRuns   bool // this will disable automatic runtime.GC runs
    39  }
    40  
    41  type Profiler struct {
    42  	session *agent.ProfileSession
    43  }
    44  
    45  // Start starts continuously profiling go code
    46  func Start(cfg Config) (*Profiler, error) {
    47  	if len(cfg.ProfileTypes) == 0 {
    48  		cfg.ProfileTypes = types.DefaultProfileTypes
    49  	}
    50  	if cfg.SampleRate == 0 {
    51  		cfg.SampleRate = types.DefaultSampleRate
    52  	}
    53  	if cfg.Logger == nil {
    54  		cfg.Logger = &log.NoopLogger{}
    55  	}
    56  
    57  	// Override the address to use when the environment variable is defined.
    58  	// This is useful to support adhoc push ingestion.
    59  	if address, ok := os.LookupEnv("PYROSCOPE_ADHOC_SERVER_ADDRESS"); ok {
    60  		cfg.ServerAddress = address
    61  	}
    62  
    63  	rc := remote.RemoteConfig{
    64  		AuthToken:              cfg.AuthToken,
    65  		UpstreamAddress:        cfg.ServerAddress,
    66  		UpstreamThreads:        4,
    67  		UpstreamRequestTimeout: 30 * time.Second,
    68  	}
    69  	upstream, err := remote.New(rc, cfg.Logger)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	sc := agent.SessionConfig{
    75  		Upstream:         upstream,
    76  		Logger:           cfg.Logger,
    77  		AppName:          cfg.ApplicationName,
    78  		Tags:             cfg.Tags,
    79  		ProfilingTypes:   cfg.ProfileTypes,
    80  		DisableGCRuns:    cfg.DisableGCRuns,
    81  		SpyName:          types.GoSpy,
    82  		SampleRate:       cfg.SampleRate,
    83  		UploadRate:       10 * time.Second,
    84  		Pid:              0,
    85  		WithSubprocesses: false,
    86  	}
    87  	session, err := agent.NewSession(sc)
    88  	if err != nil {
    89  		return nil, fmt.Errorf("new session: %w", err)
    90  	}
    91  	upstream.Start()
    92  	if err = session.Start(); err != nil {
    93  		return nil, fmt.Errorf("start session: %w", err)
    94  	}
    95  
    96  	return &Profiler{session: session}, nil
    97  }
    98  
    99  // Stop stops continuous profiling session
   100  func (p *Profiler) Stop() error {
   101  	p.session.Stop()
   102  	// FIXME(abeaumont): call upstream.Stop()
   103  	return nil
   104  }
   105  
   106  type LabelSet = pprof.LabelSet
   107  
   108  var Labels = pprof.Labels
   109  
   110  func TagWrapper(ctx context.Context, labels LabelSet, cb func(context.Context)) {
   111  	pprof.Do(ctx, labels, func(c context.Context) { cb(c) })
   112  }