Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Auto-label profiles based on request baggage #99

Merged
merged 19 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GO_VERSION_PRE20 := $(shell go version | awk '{print $$3}' | awk -F '.' '{print ($$1 == "go1" && int($$2) < 20)}')
TEST_PACKAGES := ./... ./godeltaprof/compat/... ./godeltaprof/...
TEST_PACKAGES := ./... ./godeltaprof/compat/... ./godeltaprof/... ./contrib/...

.PHONY: test
test:
Expand All @@ -12,6 +12,7 @@ go/mod:
GO111MODULE=on go mod tidy
cd godeltaprof/compat/ && GO111MODULE=on go mod download
cd godeltaprof/compat/ && GO111MODULE=on go mod tidy
cd godeltaprof/ && GO111MODULE=on go mod download
cd godeltaprof/ && GO111MODULE=on go mod download
cd godeltaprof/ && GO111MODULE=on go mod tidy

cd contrib/ && GO111MODULE=on go mod download
cd contrib/ && GO111MODULE=on go mod tidy
17 changes: 17 additions & 0 deletions contrib/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module github.com/grafana/pyroscope-go/contrib
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we go further and put k6 stuff into a submodule? contrib would be just a container for integration sub-modules.

Otherwise, once we add another integration, its dependencies would be added to the k6 package, and vice-versa

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! I agree with this move! I was attempting to do that last Friday but go work sync was fighting with me. It kept trying to update the go version of other modules.

I'll continue to work on this though and get it addressed before merging.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I got this fixed in https://github.com/grafana/pyroscope-go/pull/99/files/cbda95a5bdb83f75d53d811f4b921385e53e6ed0..016a1717a57831f63723fc46f4298531c17ac3f5. A couple things to note:

  • I marked the k6 package as experimental by moving it to the x/k6 submodule because this API may change or be removed entirely at any time.
  • I had to downgrade the go.opentelemetry.io/otel package from 1.27.0 to 1.17.0 to make the submodule compatible with go versions <1.21.


go 1.22.3

require (
github.com/grafana/pyroscope-go v1.1.1
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/otel v1.27.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.6 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
18 changes: 18 additions & 0 deletions contrib/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/grafana/pyroscope-go v1.1.1 h1:PQoUU9oWtO3ve/fgIiklYuGilvsm8qaGhlY4Vw6MAcQ=
github.com/grafana/pyroscope-go v1.1.1/go.mod h1:Mw26jU7jsL/KStNSGGuuVYdUq7Qghem5P8aXYXSXG88=
github.com/grafana/pyroscope-go/godeltaprof v0.1.6 h1:nEdZ8louGAplSvIJi1HVp7kWvFvdiiYg3COLlTwJiFo=
github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE=
github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
67 changes: 67 additions & 0 deletions contrib/k6/baggage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package k6

import (
"context"
"net/http"
"strings"

"github.com/grafana/pyroscope-go"
"go.opentelemetry.io/otel/baggage"
)

// LabelsFromBaggageHandler is a middleware that will extract key-value pairs
// from the request baggage and make them profiling labels.
func LabelsFromBaggageHandler(handler http.Handler) http.Handler {
lh := &labelHandler{
innerHandler: handler,
}

return lh
}

type labelHandler struct {
innerHandler http.Handler
}

func (lh *labelHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
labels := getBaggageLabels(r)
if labels == nil {
lh.innerHandler.ServeHTTP(w, r)
return
}

pyroscope.TagWrapper(r.Context(), *labels, func(ctx context.Context) {
lh.innerHandler.ServeHTTP(w, r.WithContext(ctx))
})
bryanhuhta marked this conversation as resolved.
Show resolved Hide resolved
}

// getBaggageLabels applies filters and transformations to request baggage and
// returns the resulting LabelSet.
func getBaggageLabels(r *http.Request) *pyroscope.LabelSet {
b, err := baggage.Parse(r.Header.Get("Baggage"))
if err != nil {
return nil
}

labels := baggageToLabels(b)
return &labels
}

// baggageToLabels converts request baggage to a LabelSet.
func baggageToLabels(b baggage.Baggage) pyroscope.LabelSet {
labelPairs := make([]string, 0, len(b.Members())*2)
for _, m := range b.Members() {
if m.Value() == "" {
continue
}

if !strings.HasPrefix(m.Key(), "k6.") {
continue
}
bryanhuhta marked this conversation as resolved.
Show resolved Hide resolved

key := strings.ReplaceAll(m.Key(), ".", "_")
labelPairs = append(labelPairs, key, m.Value())
}

return pyroscope.Labels(labelPairs...)
}
82 changes: 82 additions & 0 deletions contrib/k6/baggage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package k6

import (
"context"
"net/http"
"net/http/httptest"
"runtime/pprof"
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/baggage"
)

func Test_getBaggageLabels(t *testing.T) {
t.Run("empty values are skipped", func(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com", nil)
req = testRequestWithBaggage(t, req, map[string]string{
"blank": "",
})

labelSet := getBaggageLabels(req)
gotLabels := testPprofLabelsToMap(t, *labelSet)

expectedLabels := map[string]string{}
require.Equal(t, expectedLabels, gotLabels)
})

t.Run("with K6Options", func(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com", nil)
req = testRequestWithBaggage(t, req, map[string]string{
"k6.test_run_id": "123",
"not_k6.some_other_key": "value",
})

labelSet := getBaggageLabels(req)
gotLabels := testPprofLabelsToMap(t, *labelSet)

expectedLabels := map[string]string{
"k6_test_run_id": "123",
}
require.Equal(t, expectedLabels, gotLabels)
})

t.Run("does not allocate with failure to parse baggage", func(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com", nil)
req.Header.Add("Baggage", "invalid")

labelSet := getBaggageLabels(req)
require.Nil(t, labelSet)
})
}

func testRequestWithBaggage(t *testing.T, req *http.Request, bag map[string]string) *http.Request {
t.Helper()

members := []baggage.Member{}
for k, v := range bag {
member, err := baggage.NewMember(k, v)
require.NoError(t, err)

members = append(members, member)
}

b, err := baggage.New(members...)
require.NoError(t, err)

req.Header.Add("Baggage", b.String())
return req
}

func testPprofLabelsToMap(t *testing.T, labelSet pprof.LabelSet) map[string]string {
t.Helper()

gotLabels := map[string]string{}
ctx := pprof.WithLabels(context.Background(), labelSet)
pprof.ForLabels(ctx, func(key, value string) bool {
gotLabels[key] = value
return true
})

return gotLabels
}
3 changes: 2 additions & 1 deletion go.work
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
go 1.19
go 1.22.3

use (
.
contrib
godeltaprof
godeltaprof/compat
)
17 changes: 17 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3I
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab h1:BA4a7pe6ZTd9F8kXETBoijjFJ/ntaa//1wiH9BZu4zU=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand All @@ -20,8 +30,15 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
Expand Down
2 changes: 1 addition & 1 deletion godeltaprof/compat/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.18
require (
github.com/google/pprof v0.0.0-20231127191134-f3a68a39ae15
github.com/grafana/pyroscope-go/godeltaprof v0.1.5
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
golang.org/x/tools v0.16.0
)

Expand Down
4 changes: 2 additions & 2 deletions godeltaprof/compat/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.5 h1:gkFVqihFRL1Nro2FCC0u6mW47j
github.com/grafana/pyroscope-go/godeltaprof v0.1.5/go.mod h1:1HSPtjU8vLG0jE9JrTdzjgFqdJ/VgN7fvxBNq3luJko=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
Expand Down
Loading