Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 38886d0

Browse files
committedNov 8, 2024
parse labels & inject tenant
1 parent 4fc51e8 commit 38886d0

File tree

5 files changed

+372
-47
lines changed

5 files changed

+372
-47
lines changed
 

‎pkg/api/api.go

-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import (
3535
"github.com/grafana/pyroscope/api/gen/proto/go/vcs/v1/vcsv1connect"
3636
"github.com/grafana/pyroscope/api/gen/proto/go/version/v1/versionv1connect"
3737
"github.com/grafana/pyroscope/api/openapiv2"
38-
pprofileotlp "github.com/grafana/pyroscope/api/otlp/collector/profiles/v1experimental"
3938
"github.com/grafana/pyroscope/pkg/adhocprofiles"
4039
connectapi "github.com/grafana/pyroscope/pkg/api/connect"
4140
"github.com/grafana/pyroscope/pkg/compactor"
@@ -230,8 +229,6 @@ func (a *API) RegisterDistributor(d *distributor.Distributor) {
230229
{Desc: "Ring status", Path: "/distributor/ring"},
231230
})
232231

233-
pprofileotlp.RegisterProfilesServiceServer(a.server.GRPCOnHTTPServer, otlpHandler)
234-
235232
a.RegisterRoute("/opentelemetry.proto.collector.profiles.v1experimental.ProfilesService/Export", otlpHandler, true, true, "POST")
236233

237234
// TODO(@petethepig): implement http/protobuf and http/json support

‎pkg/ingester/otlp/convert.go

-7
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,6 @@ func ConvertOtelToGoogle(src *otelProfile.Profile) *googleProfile.Profile {
9292
dst.DefaultSampleType = int64(len(dst.StringTable) - 2)
9393
}
9494

95-
//b, _ := json.MarshalIndent(src, "", " ")
96-
//fmt.Println("src:")
97-
//_, _ = fmt.Fprintln(os.Stdout, string(b))
98-
//b, _ = json.MarshalIndent(dst, "", " ")
99-
//fmt.Println("dst:")
100-
//_, _ = fmt.Fprintln(os.Stdout, string(b))
101-
10295
return dst
10396
}
10497

‎pkg/ingester/otlp/ingest_handler.go

+126-27
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ import (
1010

1111
"connectrpc.com/connect"
1212
"github.com/go-kit/log"
13+
"github.com/go-kit/log/level"
1314
"github.com/google/uuid"
1415
"google.golang.org/grpc"
1516

17+
"github.com/grafana/dskit/user"
1618
pushv1 "github.com/grafana/pyroscope/api/gen/proto/go/push/v1"
1719
typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
1820
pprofileotlp "github.com/grafana/pyroscope/api/otlp/collector/profiles/v1experimental"
19-
"github.com/grafana/pyroscope/pkg/tenant"
21+
v1 "github.com/grafana/pyroscope/api/otlp/common/v1"
22+
"github.com/grafana/pyroscope/api/otlp/profiles/v1experimental"
2023
)
2124

2225
type ingestHandler struct {
@@ -26,7 +29,6 @@ type ingestHandler struct {
2629
handler http.Handler
2730
}
2831

29-
// TODO(@petethepig): split http and grpc
3032
type Handler interface {
3133
http.Handler
3234
pprofileotlp.ProfilesServiceServer
@@ -66,41 +68,45 @@ func (h *ingestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
6668

6769
// TODO(@petethepig): split http and grpc
6870
func (h *ingestHandler) Export(ctx context.Context, er *pprofileotlp.ExportProfilesServiceRequest) (*pprofileotlp.ExportProfilesServiceResponse, error) {
69-
70-
// TODO(@petethepig): make it tenant-aware
71-
ctx = tenant.InjectTenantID(ctx, tenant.DefaultTenantID)
72-
73-
h.log.Log("msg", "Export called")
71+
// Extracts user ID from the request metadata and returns and injects the user ID in the context
72+
_, ctx, err := user.ExtractFromGRPCRequest(ctx)
73+
if err != nil {
74+
level.Error(h.log).Log("msg", "failed to extract tenant ID from GRPC request", "err", err)
75+
return &pprofileotlp.ExportProfilesServiceResponse{}, fmt.Errorf("failed to extract tenant ID from GRPC request: %w", err)
76+
}
7477

7578
rps := er.ResourceProfiles
7679
for i := 0; i < len(rps); i++ {
7780
rp := rps[i]
7881

79-
labelsDst := []*typesv1.LabelPair{}
80-
// TODO(@petethepig): make labels work
81-
labelsDst = append(labelsDst, &typesv1.LabelPair{
82-
Name: "__name__",
83-
Value: "process_cpu",
84-
})
85-
labelsDst = append(labelsDst, &typesv1.LabelPair{
86-
Name: "service_name",
87-
Value: "otlp_test_app4",
88-
})
89-
labelsDst = append(labelsDst, &typesv1.LabelPair{
90-
Name: "__delta__",
91-
Value: "false",
92-
})
93-
labelsDst = append(labelsDst, &typesv1.LabelPair{
94-
Name: "pyroscope_spy",
95-
Value: "unknown",
96-
})
82+
// Get service name
83+
serviceName := getServiceNameFromAttributes(rp.Resource.GetAttributes())
84+
85+
// Start with default labels
86+
labels := getDefaultLabels(serviceName)
87+
88+
// Track processed attribute keys to avoid duplicates across levels
89+
processedKeys := make(map[string]bool)
90+
91+
// Add resource attributes
92+
labels = appendAttributesUnique(labels, rp.Resource.GetAttributes(), processedKeys)
9793

9894
sps := rp.ScopeProfiles
9995
for j := 0; j < len(sps); j++ {
10096
sp := sps[j]
97+
98+
// Add scope attributes
99+
labels = appendAttributesUnique(labels, sp.Scope.GetAttributes(), processedKeys)
100+
101101
for k := 0; k < len(sp.Profiles); k++ {
102102
p := sp.Profiles[k]
103103

104+
// Add profile attributes
105+
labels = appendAttributesUnique(labels, p.GetAttributes(), processedKeys)
106+
107+
// Add profile-specific attributes from samples/attributetable
108+
labels = appendProfileLabels(labels, p.Profile, processedKeys)
109+
104110
pprofBytes, err := OprofToPprof(p.Profile)
105111
if err != nil {
106112
return &pprofileotlp.ExportProfilesServiceResponse{}, fmt.Errorf("failed to convert from OTLP to legacy pprof: %w", err)
@@ -111,7 +117,7 @@ func (h *ingestHandler) Export(ctx context.Context, er *pprofileotlp.ExportProfi
111117
req := &pushv1.PushRequest{
112118
Series: []*pushv1.RawProfileSeries{
113119
{
114-
Labels: labelsDst,
120+
Labels: labels,
115121
Samples: []*pushv1.RawSample{{
116122
RawProfile: pprofBytes,
117123
ID: uuid.New().String(),
@@ -126,8 +132,101 @@ func (h *ingestHandler) Export(ctx context.Context, er *pprofileotlp.ExportProfi
126132
}
127133
}
128134
}
129-
130135
}
131136

132137
return &pprofileotlp.ExportProfilesServiceResponse{}, nil
133138
}
139+
140+
// getServiceNameFromAttributes extracts service name from OTLP resource attributes.
141+
// Returns "unknown" if service name is not found or empty.
142+
func getServiceNameFromAttributes(attrs []v1.KeyValue) string {
143+
for _, attr := range attrs {
144+
if attr.Key == "service.name" {
145+
val := attr.GetValue()
146+
if sv := val.GetStringValue(); sv != "" {
147+
return sv
148+
}
149+
break
150+
}
151+
}
152+
return "unknown"
153+
}
154+
155+
// getDefaultLabels returns the required base labels for Pyroscope profiles
156+
func getDefaultLabels(serviceName string) []*typesv1.LabelPair {
157+
return []*typesv1.LabelPair{
158+
{
159+
Name: "__name__",
160+
Value: "process_cpu",
161+
},
162+
{
163+
Name: "service_name",
164+
Value: serviceName,
165+
},
166+
{
167+
Name: "__delta__",
168+
Value: "false",
169+
},
170+
{
171+
Name: "pyroscope_spy",
172+
Value: "unknown",
173+
},
174+
}
175+
}
176+
177+
func appendAttributesUnique(labels []*typesv1.LabelPair, attrs []v1.KeyValue, processedKeys map[string]bool) []*typesv1.LabelPair {
178+
for _, attr := range attrs {
179+
// Skip if we've already seen this key at any level
180+
if processedKeys[attr.Key] {
181+
continue
182+
}
183+
184+
val := attr.GetValue()
185+
if sv := val.GetStringValue(); sv != "" {
186+
labels = append(labels, &typesv1.LabelPair{
187+
Name: attr.Key,
188+
Value: sv,
189+
})
190+
processedKeys[attr.Key] = true
191+
}
192+
}
193+
return labels
194+
}
195+
196+
func appendProfileLabels(labels []*typesv1.LabelPair, profile *v1experimental.Profile, processedKeys map[string]bool) []*typesv1.LabelPair {
197+
if profile == nil {
198+
return labels
199+
}
200+
201+
// Create mapping of attribute indices to their values
202+
attrMap := make(map[uint64]v1.AnyValue)
203+
for i, attr := range profile.GetAttributeTable() {
204+
val := attr.GetValue()
205+
if val.GetValue() != nil {
206+
attrMap[uint64(i)] = val
207+
}
208+
}
209+
210+
// Process only attributes referenced in samples
211+
for _, sample := range profile.Sample {
212+
for _, attrIdx := range sample.GetAttributes() {
213+
attr := profile.AttributeTable[attrIdx]
214+
// Skip if we've already processed this key at any level
215+
if processedKeys[attr.Key] {
216+
continue
217+
}
218+
219+
if value, exists := attrMap[attrIdx]; exists {
220+
if sv := value.GetStringValue(); sv != "" {
221+
labels = append(labels, &typesv1.LabelPair{
222+
Name: attr.Key,
223+
Value: sv,
224+
})
225+
processedKeys[attr.Key] = true
226+
}
227+
}
228+
}
229+
}
230+
231+
return labels
232+
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.