Project: Grafana Pyroscope (Observability and Analysis / Observability)
File: go_pull.md

title: Set up Go profiling in pull mode menuTitle: Set up Go profiling in pull mode description: Learn how to set up Go profiling in pull mode. weight: 10 In pull mode, the collector, whether Grafana Alloy (preferred) or Grafana Agent (legacy), periodically retrieves profiles from Golang applications, specifically targeting the `/debug/pprof/*` endpoints. To set up Golang profiling in pull mode, you need to: Expose pprof endpoints Install a collector, either Grafana Alloy (preferred) or Grafana Agent (legacy) Prepare the collector's configuration file Start the collector Ensure your Golang application exposes pprof endpoints. Get `godeltaprof` package ```bash go get github.com/grafana/pyroscope-go/godeltaprof@latest ``` Import `net/http/pprof` and `godeltaprof/http/pprof` packages at the start of your application. ```go import _ "net/http/pprof" import _ "github.com/grafana/pyroscope-go/godeltaprof/http/pprof" ``` This procedure can be used with either Grafana Alloy or Grafana Agent collector. You can use the sample collector configuration file to send data to Pyroscope. This configuration file works for either Grafana Alloy or Grafana Agent in Flow mode. Grafana Alloy is the preferred collector. To install Alloy, refer to . {{< docs/shared lookup="agent-deprecation.md" source="alloy" version="next" >}} If you are using legacy Grafana Agent Flow, use the documentation to install. In the Grafana Alloy or Grafana Agent Flow configuration file, you need to add at least two blocks: `pyroscope.write` and `pyroscope.scrape`. Add `pyroscope.write` block. ```river pyroscope.write "writejobname" { endpoint { url = "http://localhost:4040" } } ``` Add `pyroscope.scrape` block. ```river pyroscope.scrape "scrapejobname" { targets = [{"address" = "localhost:4040", "servicename" = "exampleservice"}] forwardto = [pyroscope.write.writejob_name.receiver] profiling_config { profile.process_cpu { enabled = true } profile.godeltaprof_memory { enabled = true } profile.memory { // disable memory, use godeltaprof_memory instead enabled = false } profile.godeltaprof_mutex { enabled = true } profile.mutex { // disable mutex, use godeltaprof_mutex instead enabled = false } profile.godeltaprof_block { enabled = true } profile.block { // disable block, use godeltaprof_block instead enabled = false } profile.goroutine { enabled = true } } } ``` Save the changes to the

Start a local Pyroscope instance for testing purposes: ```bash docker run -p 4040:4040 grafana/pyroscope ``` Start the collector: To start Grafana Alloy, replace `configuration.alloy` with your configuration file name: <br> `alloy run --stability.level=public-preview configuration.alloy` The `stability.level` option is required for `pyroscope.scrape`. For more information about `stability.level`, refer to documentation. To start Grafana Agent, replace `configuration.river` with your configuration file name: <br> ` grafana-agent-flow run configuration.river` Open a browser to http://localhost:4040. The page should list profiles. Your Grafana Cloud URL, username, and password can be found on the "Details Page" for Pyroscope from your stack on grafana.com. On this same page, create a token and use it as the Basic authentication password. ```river pyroscope.write "writejobname" { endpoint { url = "<Grafana Cloud URL>" basic_auth { username = "<Grafana Cloud User>" password = "<Grafana Cloud Password>" } } } ``` Select all pods ```river discovery.kubernetes "all_pods" { role = "pod" } ``` Drop not running pods, create `namespace`, `pod`, `node` and `container` labels. Compose `service_name` label based on `namespace` and `container` labels. Select only services matching regex pattern `(ns1/.)|(ns2/container-.0)`. ```river discovery.relabel "specific_pods" { targets = discovery.kubernetes.all_pods.targets rule { action = "drop" regex = "Succeeded|Failed|Completed" sourcelabels = ["metakubernetespodphase"] } rule { action = "replace" sourcelabels = ["metakubernetes_namespace"] target_label = "namespace" } rule { action = "replace" sourcelabels = ["metakubernetespodname"] target_label = "pod" } rule { action = "replace" sourcelabels = ["metakubernetesnodename"] target_label = "node" } rule { action = "replace" sourcelabels = ["metakubernetespodcontainer_name"] target_label = "container" } rule { action = "replace" regex = "(.)@(.)" replacement = "${1}/${2}" separator = "@" sourcelabels = ["metakubernetesnamespace", "metakubernetespodcontainer_name"] targetlabel = "servicename" } rule { action = "keep" regex = "(ns1/.)|(ns2/container-.0)" sourcelabels = ["servicename"] } } ``` Use `discovery.relabel.specific_pods.output` as a target for `pyroscope.scrape` block. ```river pyroscope.scrape "scrapejobname" { targets = discovery.relabel.specific_pods.output ... } ``` If you don't use `http.DefaultServeMux`, you can register `/debug/pprof/*` handlers to your own `http.ServeMux`: ```go var mux *http.ServeMux mux.Handle("/debug/pprof/", http.DefaultServeMux) ``` Or, if you use gorilla/mux: ```go var router *mux.Router router.PathPrefix("/debug/pprof").Handler(http.DefaultServeMux) ``` .