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 }