Skip to content

Commit

Permalink
configurable metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
akvlad committed Oct 10, 2024
1 parent e9bce2f commit 1cf5002
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 18 deletions.
12 changes: 12 additions & 0 deletions receiver/pyroscopereceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ Implements the Pyroscope ingest protocol and conveys the accepted profiles as Op

- `protocols`: sets the application layer protocols that the receiver will serve. See [Supported Protocols](#supported-protocols). Default is http/s on 0.0.0.0:8062 with max request body size of: 5e6 + 1e6.
- `timeout`: sets the server reponse timeout. Default is 10 seconds.
- `metrics`: configures the metrics collection for the Pyroscope receiver.
- `enable`: enables or disables metrics collection. Default is true.
- `exclude_labels`: a list of label names to exclude from the metrics. Available labels are:
- `service`: name of the service provided the pprof request
- `type`: type of pprof request (jfr or pprof)
- `encoding`: not used, empty
- `error_code`: http error code response for http request count
- `status_code`: http response status code for http request count
- `exclude_metrics`: a list of metric names to exclude from collection. Available metrics are:
- `http_request_total`: Pyroscope receiver http request count.
- `request_body_uncompressed_size_bytes`: Pyroscope receiver uncompressed request body size in bytes.
- `parsed_body_uncompressed_size_bytes`: Pyroscope receiver uncompressed parsed body size in bytes.

## Example

Expand Down
8 changes: 8 additions & 0 deletions receiver/pyroscopereceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ type Protocols struct {
HTTP *confighttp.ServerConfig `mapstructure:"http"`
}

type MetricsConfig struct {
Enable bool `mapstructure:"enable" default:"true"`
ExcludeLabels []string `mapstructure:"exclude_labels"`
ExcludeMetrics []string `mapstructure:"exclude_metrics"`
}

// Represents the receiver config within the collector's config.yaml
type Config struct {
Protocols Protocols `mapstructure:"protocols"`

// Cofigures timeout for synchronous request handling by the receiver server
Timeout time.Duration `mapstructure:"timeout"`

Metrics MetricsConfig `mapstructure:"metrics"`
}

var _ component.Config = (*Config)(nil)
Expand Down
15 changes: 11 additions & 4 deletions receiver/pyroscopereceiver/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pyroscopereceiver

import (
"fmt"
"slices"

"go.opentelemetry.io/otel/metric"
)
Expand All @@ -14,22 +15,28 @@ var (
otelcolReceiverPyroscopeParsedBodyUncompressedSizeBytes metric.Int64Histogram
)

func initMetrics(meter metric.Meter) error {
func initMetrics(meter metric.Meter, conf *Config) error {
var err error
if otelcolReceiverPyroscopeHttpRequestTotal, err = meter.Int64Counter(
if !conf.Metrics.Enable || slices.Contains(conf.Metrics.ExcludeMetrics, "http_request_total") {
otelcolReceiverPyroscopeHttpRequestTotal = nil
} else if otelcolReceiverPyroscopeHttpRequestTotal, err = meter.Int64Counter(
fmt.Sprint(prefix, "http_request_total"),
metric.WithDescription("Pyroscope receiver http request count"),
); err != nil {
return err
}
if otelcolReceiverPyroscopeRequestBodyUncompressedSizeBytes, err = meter.Int64Histogram(
if !conf.Metrics.Enable || slices.Contains(conf.Metrics.ExcludeMetrics, "request_body_uncompressed_size_bytes") {
otelcolReceiverPyroscopeRequestBodyUncompressedSizeBytes = nil
} else if otelcolReceiverPyroscopeRequestBodyUncompressedSizeBytes, err = meter.Int64Histogram(
fmt.Sprint(prefix, "request_body_uncompressed_size_bytes"),
metric.WithDescription("Pyroscope receiver uncompressed request body size in bytes"),
metric.WithExplicitBucketBoundaries(0, 1024, 4096, 16384, 32768, 65536, 131072, 262144, 524288, 1048576),
); err != nil {
return err
}
if otelcolReceiverPyroscopeParsedBodyUncompressedSizeBytes, err = meter.Int64Histogram(
if !conf.Metrics.Enable || slices.Contains(conf.Metrics.ExcludeMetrics, "parsed_body_uncompressed_size_bytes") {
otelcolReceiverPyroscopeParsedBodyUncompressedSizeBytes = nil
} else if otelcolReceiverPyroscopeParsedBodyUncompressedSizeBytes, err = meter.Int64Histogram(
fmt.Sprint(prefix, "parsed_body_uncompressed_size_bytes"),
metric.WithDescription("Pyroscope receiver uncompressed parsed body size in bytes"),
metric.WithExplicitBucketBoundaries(0, 1024, 4096, 16384, 32768, 65536, 131072, 262144, 524288, 1048576),
Expand Down
66 changes: 52 additions & 14 deletions receiver/pyroscopereceiver/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net"
"net/http"
"net/url"
"slices"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -117,7 +118,7 @@ func newPyroscopeReceiver(cfg *Config, consumer consumer.Logs, set *receiver.Set
r.mux.HandleFunc(ingestPath, func(resp http.ResponseWriter, req *http.Request) {
r.httpHandlerIngest(resp, req)
})
if err := initMetrics(r.meter); err != nil {
if err := initMetrics(r.meter, cfg); err != nil {
r.logger.Error(fmt.Sprintf("failed to init metrics: %s", err.Error()))
return r, err
}
Expand Down Expand Up @@ -176,15 +177,23 @@ func (r *pyroscopeReceiver) handle(ctx context.Context, resp http.ResponseWriter
r.handleError(ctx, resp, "text/plain", http.StatusInternalServerError, err.Error(), pm.name, errorCodeError)
return
}

otelcolReceiverPyroscopeHttpRequestTotal.Add(ctx, 1, metric.WithAttributeSet(*newOtelcolAttrSetHttp(pm.name, errorCodeSuccess, http.StatusNoContent)))
if otelcolReceiverPyroscopeHttpRequestTotal != nil {
otelcolReceiverPyroscopeHttpRequestTotal.Add(
ctx, 1,
metric.WithAttributeSet(*r.newOtelcolAttrSetHttp(pm.name, errorCodeSuccess, http.StatusNoContent)),
)
}
writeResponseNoContent(resp)
}()
return c
}

func (recv *pyroscopeReceiver) handleError(ctx context.Context, resp http.ResponseWriter, contentType string, statusCode int, msg string, service string, errorCode string) {
otelcolReceiverPyroscopeHttpRequestTotal.Add(ctx, 1, metric.WithAttributeSet(*newOtelcolAttrSetHttp(service, errorCode, statusCode)))
if otelcolReceiverPyroscopeHttpRequestTotal != nil {
otelcolReceiverPyroscopeHttpRequestTotal.Add(ctx, 1,
metric.WithAttributeSet(*recv.newOtelcolAttrSetHttp(service, errorCode, statusCode)))
}

recv.logger.Error(msg)
writeResponse(resp, "text/plain", statusCode, []byte(msg))
}
Expand Down Expand Up @@ -240,12 +249,19 @@ func readParams(qs *url.Values) (params, error) {
return p, nil
}

func newOtelcolAttrSetHttp(service string, errorCode string, statusCode int) *attribute.Set {
s := attribute.NewSet(
attribute.KeyValue{Key: keyService, Value: attribute.StringValue(service)},
attribute.KeyValue{Key: "error_code", Value: attribute.StringValue(errorCode)},
attribute.KeyValue{Key: "status_code", Value: attribute.IntValue(statusCode)},
)
func (r *pyroscopeReceiver) newOtelcolAttrSetHttp(service string, errorCode string, statusCode int) *attribute.Set {
keyValues := []attribute.KeyValue{
{Key: keyService, Value: attribute.StringValue(service)},
{Key: "error_code", Value: attribute.StringValue(errorCode)},
{Key: "status_code", Value: attribute.IntValue(statusCode)},
}
for i := len(keyValues) - 1; i >= 0; i-- {
if slices.Contains(r.cfg.Metrics.ExcludeLabels, string(keyValues[i].Key)) {
keyValues[i] = keyValues[len(keyValues)-1]
keyValues = keyValues[:len(keyValues)-1]
}
}
s := attribute.NewSet(keyValues...)
return &s
}

Expand Down Expand Up @@ -334,7 +350,9 @@ func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request,

r.logger.Debug("received profiles", zap.String("url_query", req.URL.RawQuery))
qs := req.URL.Query()
format := formatPprof
if tmp, ok = qs["format"]; ok && (tmp[0] == "jfr") {
format = formatJfr
p = jfrparser.NewJfrPprofParser()
} else if tmp, ok = qs["spyName"]; ok && (tmp[0] == "nodespy") {
p = nodeparser.NewNodePprofParser()
Expand All @@ -358,7 +376,11 @@ func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request,
return logs, fmt.Errorf("failed to decompress body: %w", err)
}
// TODO: try measure compressed size
otelcolReceiverPyroscopeRequestBodyUncompressedSizeBytes.Record(ctx, int64(buf.Len()), metric.WithAttributeSet(*newOtelcolAttrSetPayloadSizeBytes(formatJfr, "")))
if otelcolReceiverPyroscopeRequestBodyUncompressedSizeBytes != nil {
otelcolReceiverPyroscopeRequestBodyUncompressedSizeBytes.Record(ctx, int64(buf.Len()),
metric.WithAttributeSet(*r.newOtelcolAttrSetPayloadSizeBytes(pm.name, format, "")))
}

resetHeaders(req)

md := profile_types.Metadata{SampleRateHertz: 0}
Expand Down Expand Up @@ -421,16 +443,32 @@ func (r *pyroscopeReceiver) readProfiles(ctx context.Context, req *http.Request,
)
}
// sz may be 0 and it will be recorded
otelcolReceiverPyroscopeParsedBodyUncompressedSizeBytes.Record(ctx, int64(sz), metric.WithAttributeSet(*newOtelcolAttrSetPayloadSizeBytes(formatPprof, "")))
if otelcolReceiverPyroscopeParsedBodyUncompressedSizeBytes != nil {
otelcolReceiverPyroscopeParsedBodyUncompressedSizeBytes.Record(ctx, int64(sz),
metric.WithAttributeSet(*r.newOtelcolAttrSetPayloadSizeBytes(pm.name, formatPprof, "")))
}

return logs, nil
}

func ns(sec uint64) uint64 {
return sec * 1e9
}

func newOtelcolAttrSetPayloadSizeBytes(typ string, encoding string) *attribute.Set {
s := attribute.NewSet(attribute.KeyValue{Key: "type", Value: attribute.StringValue(typ)}, attribute.KeyValue{Key: "encoding", Value: attribute.StringValue(encoding)})
func (r *pyroscopeReceiver) newOtelcolAttrSetPayloadSizeBytes(service string, typ string,
encoding string) *attribute.Set {
keyValues := []attribute.KeyValue{
{Key: keyService, Value: attribute.StringValue(service)},
{Key: "type", Value: attribute.StringValue(typ)},
{Key: "encoding", Value: attribute.StringValue(encoding)},
}
for i := len(keyValues) - 1; i >= 0; i-- {
if slices.Contains(r.cfg.Metrics.ExcludeLabels, string(keyValues[i].Key)) {
keyValues[i] = keyValues[len(keyValues)-1]
keyValues = keyValues[:len(keyValues)-1]
}
}
s := attribute.NewSet(keyValues...)
return &s
}

Expand Down

0 comments on commit 1cf5002

Please sign in to comment.