Skip to content

Commit 7afc304

Browse files
authored
Merge pull request #114 from metrico/feature/support-binary-profiles
Support binary octet/binary content type for profile ingestor
2 parents 283b4e8 + f7a8879 commit 7afc304

File tree

1 file changed

+53
-15
lines changed

1 file changed

+53
-15
lines changed

receiver/pyroscopereceiver/receiver.go

+53-15
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,44 @@ func (r *pyroscopeReceiver) Push(ctx context.Context, req *connect.Request[pushv
324324
return connect.NewResponse(&pushv1.PushResponse{}), nil
325325
}
326326

327+
func (r *pyroscopeReceiver) getProfilesBuff(req *http.Request) (*bytes.Buffer, error) {
328+
var err error
329+
var buf *bytes.Buffer
330+
defer func() {
331+
if err != nil && buf != nil {
332+
releaseBuf(r.uncompressedBufPool, buf)
333+
}
334+
}()
335+
contentType := ""
336+
if len(req.Header["Content-Type"]) > 0 {
337+
contentType = req.Header["Content-Type"][0]
338+
}
339+
if strings.HasPrefix(contentType, "multipart/form-data") {
340+
var f multipart.File
341+
f, err = r.openMultipart(req)
342+
if err != nil {
343+
return nil, err
344+
}
345+
defer f.Close()
346+
347+
buf = acquireBuf(r.uncompressedBufPool)
348+
err = r.decompressor.Decompress(f, compress.Gzip, buf)
349+
if err != nil {
350+
return nil, fmt.Errorf("failed to decompress body: %w", err)
351+
}
352+
return buf, nil
353+
}
354+
if strings.HasPrefix(contentType, "binary/octet-stream") {
355+
buf = acquireBuf(r.uncompressedBufPool)
356+
_, err = io.Copy(buf, req.Body)
357+
if err != nil {
358+
return buf, fmt.Errorf("failed to read body: %w", err)
359+
}
360+
return buf, nil
361+
}
362+
return nil, fmt.Errorf("unsupported content type: %s", contentType)
363+
}
364+
327365
func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request, pm params) (plog.Logs, error) {
328366
var (
329367
tmp []string
@@ -342,21 +380,14 @@ func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request,
342380
p = pprofparser.NewPprofParser()
343381
}
344382
// support only multipart/form-data
345-
f, err := r.openMultipart(req)
383+
buf, err := r.getProfilesBuff(req)
346384
if err != nil {
347385
return logs, err
348386
}
349-
defer f.Close()
350-
351-
buf := acquireBuf(r.uncompressedBufPool)
352387
defer func() {
353388
releaseBuf(r.uncompressedBufPool, buf)
354389
}()
355390

356-
err = r.decompressor.Decompress(f, compress.Gzip, buf)
357-
if err != nil {
358-
return logs, fmt.Errorf("failed to decompress body: %w", err)
359-
}
360391
// TODO: try measure compressed size
361392
otelcolReceiverPyroscopeRequestBodyUncompressedSizeBytes.Record(ctx, int64(buf.Len()), metric.WithAttributeSet(*newOtelcolAttrSetPayloadSizeBytes(pm.name, formatJfr, "")))
362393
resetHeaders(req)
@@ -384,15 +415,13 @@ func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request,
384415
record := rs.AppendEmpty()
385416
if tmp, ok = qs["format"]; ok && (tmp[0] == "jfr") {
386417
timestampNs = ns(pm.start)
387-
durationNs = pm.end - pm.start
388-
durationNs = ns(durationNs)
418+
durationNs = ns(pm.end) - ns(pm.start)
389419
} else if tmp, ok = qs["spyName"]; ok && (tmp[0] == "nodespy") {
390420
timestampNs = uint64(pr.TimeStampNao)
391421
durationNs = uint64(pr.DurationNano)
392422
} else {
393-
timestampNs = pm.start
394-
durationNs = pm.end - pm.start
395-
durationNs = ns(durationNs)
423+
timestampNs = ns(pm.start)
424+
durationNs = ns(pm.end) - ns(pm.start)
396425
}
397426
record.SetTimestamp(pcommon.Timestamp(timestampNs))
398427
m := record.Attributes()
@@ -409,7 +438,7 @@ func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request,
409438
postProcessProf(pr.Profile, &m)
410439
record.Body().SetEmptyBytes().FromRaw(pr.Payload.Bytes())
411440
sz += pr.Payload.Len()
412-
r.logger.Debug(
441+
r.logger.Info(
413442
fmt.Sprintf("parsed profile %d", i),
414443
zap.Uint64("timestamp_ns", timestampNs),
415444
zap.String("type", pr.Type.Type),
@@ -427,7 +456,16 @@ func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request,
427456
}
428457

429458
func ns(sec uint64) uint64 {
430-
return sec * 1e9
459+
if sec < 10000000000000 {
460+
return sec * 1e9
461+
}
462+
if sec < 10000000000000000 {
463+
return sec * 1e6
464+
}
465+
if sec < 10000000000000000000 {
466+
return sec * 1e3
467+
}
468+
return sec
431469
}
432470

433471
func newOtelcolAttrSetPayloadSizeBytes(service string, typ string, encoding string) *attribute.Set {

0 commit comments

Comments
 (0)