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

Add host.name to process metrics #973

Merged
merged 3 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 8 additions & 1 deletion pkg/internal/export/attributes/attr_defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,15 @@ func getDefinitions(groups AttrGroups) map[Section]AttrReportGroup {
},
}

// TODO: populate it with host resource attributes in https://opentelemetry.io/docs/specs/semconv/resource/host/
var hostAttributes = AttrReportGroup{
Attributes: map[attr.Name]Default{
attr.HostName: true,
},
}

var processAttributes = AttrReportGroup{
SubGroups: []*AttrReportGroup{&appKubeAttributes},
SubGroups: []*AttrReportGroup{&appKubeAttributes, &hostAttributes},
Attributes: map[attr.Name]Default{
attr.ProcCommand: true,
attr.ProcCPUState: true,
Expand Down
6 changes: 4 additions & 2 deletions pkg/internal/export/attributes/names/attrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const (
)

// Beyla-specific network attributes
var (
const (
BeylaIP = Name("beyla.ip")
Transport = Name("transport")
SrcAddress = Name("src.address")
Expand Down Expand Up @@ -117,7 +117,7 @@ const (
)

// other beyla-specific attributes
var (
const (
// TargetInstance is a Prometheus-only attribute.
// It will expose the process hostname-pid (or K8s Pod).
// It is advised for users that to use relabeling rules to
Expand All @@ -131,6 +131,8 @@ var (
// attributes, which can't be enabled/disabled by the users
ServiceName = Name(semconv.ServiceNameKey)
ServiceNamespace = Name(semconv.ServiceNamespaceKey)

HostName = Name(semconv.HostNameKey)
)

// traces related attributes
Expand Down
11 changes: 6 additions & 5 deletions pkg/internal/export/prom/prom_proc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/grafana/beyla/pkg/internal/export/otel"
"github.com/grafana/beyla/pkg/internal/infraolly/process"
"github.com/grafana/beyla/pkg/internal/pipe/global"
"github.com/grafana/beyla/pkg/internal/svc"
)

func TestProcPrometheusEndpoint_AggregatedMetrics(t *testing.T) {
Expand Down Expand Up @@ -53,13 +54,13 @@ func TestProcPrometheusEndpoint_AggregatedMetrics(t *testing.T) {

// WHEN it receives process metrics
metrics <- []*process.Status{
{Command: "foo",
{Service: &svc.ID{}, Command: "foo",
CPUUtilisationWait: 3, CPUUtilisationSystem: 2, CPUUtilisationUser: 1,
CPUTimeUserDelta: 30, CPUTimeWaitDelta: 20, CPUTimeSystemDelta: 10,
IOReadBytesDelta: 123, IOWriteBytesDelta: 456,
NetRcvBytesDelta: 12, NetTxBytesDelta: 34,
},
{Command: "bar",
{Service: &svc.ID{}, Command: "bar",
CPUUtilisationWait: 31, CPUUtilisationSystem: 21, CPUUtilisationUser: 11,
CPUTimeUserDelta: 301, CPUTimeWaitDelta: 201, CPUTimeSystemDelta: 101,
IOReadBytesDelta: 321, IOWriteBytesDelta: 654,
Expand All @@ -82,7 +83,7 @@ func TestProcPrometheusEndpoint_AggregatedMetrics(t *testing.T) {

// AND WHEN new metrics are received
metrics <- []*process.Status{
{Command: "foo",
{Service: &svc.ID{}, Command: "foo",
CPUUtilisationWait: 4, CPUUtilisationSystem: 1, CPUUtilisationUser: 2,
CPUTimeUserDelta: 3, CPUTimeWaitDelta: 2, CPUTimeSystemDelta: 1,
IOReadBytesDelta: 31, IOWriteBytesDelta: 10,
Expand Down Expand Up @@ -140,7 +141,7 @@ func TestProcPrometheusEndpoint_DisaggregatedMetrics(t *testing.T) {

// WHEN it receives process metrics
metrics <- []*process.Status{
{Command: "foo",
{Service: &svc.ID{}, Command: "foo",
CPUUtilisationWait: 3, CPUUtilisationSystem: 2, CPUUtilisationUser: 1,
CPUTimeUserDelta: 30, CPUTimeWaitDelta: 20, CPUTimeSystemDelta: 10,
IOReadBytesDelta: 123, IOWriteBytesDelta: 456,
Expand All @@ -165,7 +166,7 @@ func TestProcPrometheusEndpoint_DisaggregatedMetrics(t *testing.T) {

// AND WHEN new metrics are received
metrics <- []*process.Status{
{Command: "foo",
{Service: &svc.ID{}, Command: "foo",
CPUUtilisationWait: 4, CPUUtilisationSystem: 1, CPUUtilisationUser: 2,
CPUTimeUserDelta: 3, CPUTimeWaitDelta: 2, CPUTimeSystemDelta: 1,
IOReadBytesDelta: 3, IOWriteBytesDelta: 2,
Expand Down
4 changes: 4 additions & 0 deletions pkg/internal/infraolly/process/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ func NewStatus(pid int32, svcID *svc.ID) *Status {
func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue], bool) {
var g attributes.Getter[*Status, attribute.KeyValue]
switch name {
case attr.HostName:
g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.HostName).String(s.Service.HostName) }
case attr.ProcCommand:
g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.Command) }
case attr.ProcCommandLine:
Expand Down Expand Up @@ -111,6 +113,8 @@ func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue]
func PromGetters(name attr.Name) (attributes.Getter[*Status, string], bool) {
var g attributes.Getter[*Status, string]
switch name {
case attr.HostName:
g = func(s *Status) string { return s.Service.HostName }
case attr.ProcCommand:
g = func(s *Status) string { return s.Command }
case attr.ProcCommandLine:
Expand Down
4 changes: 4 additions & 0 deletions pkg/internal/svc/svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ type ID struct {
// It is stored here at process discovery time, because it might differ form the
// UserPID and HostPID fields of the request.PidInfo struct.
ProcPID int32

// HostName running the process. It will default to the Beyla host and will be overridden
// by other metadata if available (e.g., Pod Name, Node Name, etc...)
HostName string
}

func (i *ID) String() string {
Expand Down
1 change: 1 addition & 0 deletions pkg/internal/traces/read_decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func hostNamePIDDecorator(cfg *InstanceIDConfig) decorator {
}
spans[i].ServiceID.Instance = instanceID
spans[i].ServiceID.UID = svc.UID(instanceID)
spans[i].ServiceID.HostName = fullHostName
}
}
}
11 changes: 9 additions & 2 deletions pkg/internal/traces/read_decorator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package traces

import (
"context"
"os"
"testing"
"time"

Expand Down Expand Up @@ -66,9 +67,15 @@ func TestReadDecorator(t *testing.T) {
{Path: "/bar", Pid: request.PidInfo{HostPID: 1234}},
}
outSpans := testutil.ReadChannel(t, decoratedOutput, testTimeout)
expectedHostName := tc.cfg.InstanceID.OverrideHostname
if expectedHostName == "" {
expectedHostName, _ = os.Hostname()
}
assert.Equal(t, []request.Span{
{ServiceID: svc.ID{Instance: tc.expectedID, UID: tc.expectedUID}, Path: "/foo", Pid: request.PidInfo{HostPID: 1234}},
{ServiceID: svc.ID{Instance: tc.expectedID, UID: tc.expectedUID}, Path: "/bar", Pid: request.PidInfo{HostPID: 1234}},
{ServiceID: svc.ID{Instance: tc.expectedID, UID: tc.expectedUID, HostName: expectedHostName},
Path: "/foo", Pid: request.PidInfo{HostPID: 1234}},
{ServiceID: svc.ID{Instance: tc.expectedID, UID: tc.expectedUID, HostName: expectedHostName},
Path: "/bar", Pid: request.PidInfo{HostPID: 1234}},
}, outSpans)
})
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/transform/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,6 @@ func appendMetadata(span *request.Span, info *kube.PodInfo) {
span.ServiceID.Metadata[attr.Name(owner.LabelName)] = owner.Name
owner = owner.Owner
}
// override hostname by the Pod name
span.ServiceID.HostName = info.Name
}
24 changes: 15 additions & 9 deletions test/integration/k8s/common/k8s_metrics_testfuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,23 @@ func FeatureGRPCMetricsDecoration(manifest string) features.Feature {
).Feature()
}

func FeatureProcessMetricsDecoration() features.Feature {
func FeatureProcessMetricsDecoration(overrideProperties map[string]string) features.Feature {
properties := map[string]string{
"k8s_namespace_name": "^default$",
"k8s_node_name": ".+-control-plane$",
"k8s_pod_name": "^testserver-.*",
"k8s_pod_uid": UUIDRegex,
"k8s_pod_start_time": TimeRegex,
"k8s_deployment_name": "^testserver$",
"k8s_replicaset_name": "^testserver-",
}
for k, v := range overrideProperties {
properties[k] = v
}
return features.New("Decoration of process metrics").
Assess("all the process metrics from currently instrumented services are properly decorated",
testMetricsDecoration(processMetrics, `{k8s_pod_name=~"testserver-.*"}`, map[string]string{
"k8s_namespace_name": "^default$",
"k8s_node_name": ".+-control-plane$",
"k8s_pod_uid": UUIDRegex,
"k8s_pod_start_time": TimeRegex,
"k8s_deployment_name": "^testserver$",
"k8s_replicaset_name": "^testserver-",
})).Feature()
testMetricsDecoration(processMetrics, `{k8s_pod_name=~"`+properties["k8s_pod_name"]+`"}`, properties),
).Feature()
}

func testMetricsDecoration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func TestMain(m *testing.M) {
kube.LocalImage("jaegertracing/all-in-one:latest"),
kube.Deploy(k8s.PathManifests+"/01-volumes.yml"),
kube.Deploy(k8s.PathManifests+"/01-serviceaccount.yml"),
kube.Deploy(k8s.PathManifests+"/02-prometheus-otelscrape.yml"),
kube.Deploy(k8s.PathManifests+"/03-otelcol.yml"),
kube.Deploy(k8s.PathManifests+"/04-jaeger.yml"),
kube.Deploy(k8s.PathManifests+"/05-uninstrumented-service.yml"),
Expand Down
21 changes: 21 additions & 0 deletions test/integration/k8s/daemonset/k8s_daemonset_z_metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//go:build integration_k8s

package otel

import (
"testing"

k8s "github.com/grafana/beyla/test/integration/k8s/common"
)

// to find process information in the prometheus database,
// we need to make sure that this test is executed after the
// tests in k8s_daemonsset_traces_test.go file
func TestProcessMetrics(t *testing.T) {
cluster.TestEnv().Test(t, k8s.FeatureProcessMetricsDecoration(map[string]string{
"k8s_deployment_name": "^otherinstance$",
"k8s_replicaset_name": "^otherinstance-.*",
"k8s_pod_name": "^otherinstance-.*",
"host_name": "^otherinstance-.*",
}))
}
15 changes: 15 additions & 0 deletions test/integration/k8s/manifests/06-beyla-daemonset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ data:
attributes:
kubernetes:
enable: true
select:
process_cpu_time:
include: ["*"]
process_cpu_utilization:
include: ["*"]
process_memory_usage:
include: ["*"]
process_memory_virtual:
include: ["*"]
process_disk_io:
include: ["*"]
process_network_io:
include: ["*"]
print_traces: true
log_level: debug
discovery:
Expand Down Expand Up @@ -82,3 +95,5 @@ spec:
value: "true"
- name: BEYLA_OTEL_METRICS_TTL
value: "30m0s"
- name: BEYLA_OTEL_METRICS_FEATURES
value: "application,application_process"
2 changes: 1 addition & 1 deletion test/integration/k8s/otel/k8s_otel_metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ func TestOTEL_MetricsDecoration_GRPC(t *testing.T) {
}

func TestOTEL_ProcessMetrics(t *testing.T) {
cluster.TestEnv().Test(t, k8s.FeatureProcessMetricsDecoration())
cluster.TestEnv().Test(t, k8s.FeatureProcessMetricsDecoration(nil))
}
2 changes: 1 addition & 1 deletion test/integration/k8s/prom/k8s_prom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ func TestPrometheus_MetricsDecoration_GRPC(t *testing.T) {
}

func TestPrometheus_ProcessMetrics(t *testing.T) {
cluster.TestEnv().Test(t, k8s.FeatureProcessMetricsDecoration())
cluster.TestEnv().Test(t, k8s.FeatureProcessMetricsDecoration(nil))
}
Loading