Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 13 additions & 0 deletions internal/test/integration/configs/obi-config-no-route-lc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
routes:
ignored_patterns:
- /metrics
unmatched: low-cardinality
max_path_segment_cardinality: 3
otel_metrics_export:
endpoint: http://otelcol:4318
otel_traces_export:
endpoint: http://jaeger:4318
attributes:
select:
"*":
include: ["*"]
47 changes: 47 additions & 0 deletions internal/test/integration/red_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,42 @@ func testREDMetricsForHTTPLibraryNoRoute(t *testing.T, url, svcName string) {
require.Empty(t, results)
}

func testREDMetricsForHTTPLibraryNoRouteLowCardinality(t *testing.T, url, svcName string) {
validNames := []string{"user", "customer", "test", "option", "metric"}

// Call 3 times the instrumented service, forcing it to:
// - take at least 30ms to respond
// - returning a 404 code
for i := 0; i < 3; i++ {
for _, s := range validNames {
ti.DoHTTPGet(t, url+"/api/"+s+"?delay=30ms&status=404", 404)
}
}

// Eventually, Prometheus would make this query visible
pq := prom.Client{HostPort: prometheusHostPort}
var results []prom.Result
test.Eventually(t, testTimeout, func(t require.TestingT) {
var err error
results, err = pq.Query(`http_server_request_duration_seconds_count{` +
`http_request_method="GET",` +
`http_response_status_code="404",` +
`service_namespace="integration-test",` +
`service_name="` + svcName + `",` +
`http_route="/api/*"}`)
require.NoError(t, err)
// check duration_count has 3 calls and all the arguments
enoughPromResults(t, results)
val := totalPromCount(t, results)
assert.LessOrEqual(t, 3, val)
if len(results) > 0 {
res := results[0]
addr := res.Metric["client_address"]
assert.NotNil(t, addr)
}
})
}

func testREDMetricsHTTPNoRoute(t *testing.T) {
for _, testCaseURL := range []string{
instrumentedServiceGorillaURL,
Expand All @@ -892,6 +928,17 @@ func testREDMetricsHTTPNoRoute(t *testing.T) {
}
}

func testREDMetricsHTTPNoRouteLowCardinality(t *testing.T) {
for _, testCaseURL := range []string{
instrumentedServiceStdURL,
} {
t.Run(testCaseURL, func(t *testing.T) {
waitForTestComponents(t, testCaseURL)
testREDMetricsForHTTPLibraryNoRouteLowCardinality(t, testCaseURL, "testserver")
})
}
}

func testREDMetricsUnsupportedHTTP(t *testing.T) {
for _, testCaseURL := range []string{
instrumentedServiceStdURL,
Expand Down
10 changes: 10 additions & 0 deletions internal/test/integration/suites_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,16 @@ func TestSuiteNoRoutes(t *testing.T) {
require.NoError(t, compose.Close())
}

func TestSuiteNoRoutesLowCardinality(t *testing.T) {
compose, err := docker.ComposeSuite("docker-compose.yml", path.Join(pathOutput, "test-suite-no-routes-low-cardinality.log"))
require.NoError(t, err)

compose.Env = append(compose.Env, "INSTRUMENTER_CONFIG_SUFFIX=-no-route-lc")
require.NoError(t, compose.Up())
t.Run("RED metrics", testREDMetricsHTTPNoRouteLowCardinality)
require.NoError(t, compose.Close())
}

func TestSuite_Elixir(t *testing.T) {
compose, err := docker.ComposeSuite("docker-compose-elixir.yml", path.Join(pathOutput, "test-suite-elixir.log"))
require.NoError(t, err)
Expand Down
3 changes: 2 additions & 1 deletion internal/tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import (
_ "github.com/google/go-licenses/v2"
_ "github.com/grafana/go-offsets-tracker/cmd/go-offsets-tracker"
_ "github.com/onsi/ginkgo/v2/ginkgo"
_ "go.opentelemetry.io/build-tools/multimod"
_ "gotest.tools/gotestsum"
_ "sigs.k8s.io/controller-runtime/tools/setup-envtest"
_ "sigs.k8s.io/kind"

_ "go.opentelemetry.io/build-tools/multimod"
)
2 changes: 2 additions & 0 deletions pkg/appolly/app/svc/svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"go.opentelemetry.io/obi/pkg/appolly/services"
attr "go.opentelemetry.io/obi/pkg/export/attributes/names"
"go.opentelemetry.io/obi/pkg/internal/transform/route"
"go.opentelemetry.io/obi/pkg/internal/transform/route/clusterurl"
)

type InstrumentableType int
Expand Down Expand Up @@ -117,6 +118,7 @@ type Attrs struct {
CustomInRouteMatcher route.Matcher
CustomOutRouteMatcher route.Matcher
HarvestedRouteMatcher route.Matcher
PathTrie *clusterurl.PathTrie
}

func (i *Attrs) GetUID() UID {
Expand Down
9 changes: 5 additions & 4 deletions pkg/appolly/discover/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"go.opentelemetry.io/obi/pkg/internal/testutil"
"go.opentelemetry.io/obi/pkg/obi"
"go.opentelemetry.io/obi/pkg/pipe/msg"
"go.opentelemetry.io/obi/pkg/transform"
)

func testMatch(t *testing.T, m Event[ProcessMatch], name string,
Expand Down Expand Up @@ -551,7 +552,7 @@ func TestCriteriaMatcher_Granular(t *testing.T) {

require.Len(t, planetMatch.Criteria, 2)

planetAttrs := makeServiceAttrs(&planetMatch)
planetAttrs := makeServiceAttrs(&planetMatch, &transform.RoutesConfig{})

assert.True(t, planetAttrs.ExportModes.CanExportTraces())
assert.False(t, planetAttrs.ExportModes.CanExportMetrics())
Expand All @@ -562,7 +563,7 @@ func TestCriteriaMatcher_Granular(t *testing.T) {

require.Len(t, satelliteMatch.Criteria, 2)

satelliteAttrs := makeServiceAttrs(&satelliteMatch)
satelliteAttrs := makeServiceAttrs(&satelliteMatch, &transform.RoutesConfig{})

assert.False(t, satelliteAttrs.ExportModes.CanExportTraces())
assert.False(t, satelliteAttrs.ExportModes.CanExportMetrics())
Expand All @@ -572,7 +573,7 @@ func TestCriteriaMatcher_Granular(t *testing.T) {

require.Len(t, starMatch.Criteria, 2)

starAttrs := makeServiceAttrs(&starMatch)
starAttrs := makeServiceAttrs(&starMatch, &transform.RoutesConfig{})

assert.False(t, starAttrs.ExportModes.CanExportTraces())
assert.True(t, starAttrs.ExportModes.CanExportMetrics())
Expand All @@ -582,7 +583,7 @@ func TestCriteriaMatcher_Granular(t *testing.T) {

require.Len(t, asteroidMatch.Criteria, 2)

asteroidAttrs := makeServiceAttrs(&asteroidMatch)
asteroidAttrs := makeServiceAttrs(&asteroidMatch, &transform.RoutesConfig{})

assert.True(t, asteroidAttrs.ExportModes.CanExportTraces())
assert.True(t, asteroidAttrs.ExportModes.CanExportMetrics())
Expand Down
12 changes: 10 additions & 2 deletions pkg/appolly/discover/typer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import (
"go.opentelemetry.io/obi/pkg/export/imetrics"
"go.opentelemetry.io/obi/pkg/internal/goexec"
"go.opentelemetry.io/obi/pkg/internal/procs"
"go.opentelemetry.io/obi/pkg/internal/transform/route/clusterurl"
"go.opentelemetry.io/obi/pkg/kube"
"go.opentelemetry.io/obi/pkg/obi"
"go.opentelemetry.io/obi/pkg/pipe/msg"
"go.opentelemetry.io/obi/pkg/pipe/swarm"
"go.opentelemetry.io/obi/pkg/pipe/swarm/swarms"
"go.opentelemetry.io/obi/pkg/transform"
)

type instrumentedExecutable struct {
Expand Down Expand Up @@ -87,7 +89,7 @@ func samplerFromConfig(s *services.SamplerConfig) trace.Sampler {
return nil
}

func makeServiceAttrs(processMatch *ProcessMatch) svc.Attrs {
func makeServiceAttrs(processMatch *ProcessMatch, routesCfg *transform.RoutesConfig) svc.Attrs {
var name string
var namespace string
exportModes := services.ExportModeUnset
Expand Down Expand Up @@ -116,6 +118,11 @@ func makeServiceAttrs(processMatch *ProcessMatch) svc.Attrs {
}
}

wildcard := byte('*')
if routesCfg.WildcardChar != "" {
wildcard = routesCfg.WildcardChar[0]
}

s := svc.Attrs{
UID: svc.UID{
Name: name,
Expand All @@ -124,6 +131,7 @@ func makeServiceAttrs(processMatch *ProcessMatch) svc.Attrs {
ProcPID: processMatch.Process.Pid,
ExportModes: exportModes,
Sampler: samplerFromConfig(samplerConfig),
PathTrie: clusterurl.NewPathTrie(routesCfg.MaxPathSegmentCardinality, wildcard),
}

if routesConfig != nil {
Expand All @@ -146,7 +154,7 @@ func (t *typer) FilterClassify(evs []Event[ProcessMatch]) []Event[ebpf.Instrumen
ev := &evs[i]
switch evs[i].Type {
case EventCreated:
svcID := makeServiceAttrs(&ev.Obj)
svcID := makeServiceAttrs(&ev.Obj, t.cfg.Routes)

if elfFile, err := findExecElf(ev.Obj.Process, svcID, t.k8sInformer.IsKubeEnabled()); err != nil {
t.log.Debug("error finding process ELF. Ignoring", "error", err)
Expand Down
5 changes: 3 additions & 2 deletions pkg/appolly/discover/typer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"

"go.opentelemetry.io/obi/pkg/appolly/services"
"go.opentelemetry.io/obi/pkg/transform"
)

type dummyCriterion struct {
Expand Down Expand Up @@ -41,7 +42,7 @@ func TestMakeServiceAttrs(t *testing.T) {
dummyCriterion{name: "svc1", namespace: "ns1", export: services.ExportModeUnset},
},
}
attrs := makeServiceAttrs(proc)
attrs := makeServiceAttrs(proc, &transform.RoutesConfig{})
assert.Equal(t, "svc1", attrs.UID.Name)
assert.Equal(t, "ns1", attrs.UID.Namespace)
assert.Equal(t, int32(1234), attrs.ProcPID)
Expand All @@ -60,7 +61,7 @@ func TestMakeServiceAttrs(t *testing.T) {
dummyCriterion{sampler: sampler, routes: routes},
},
}
attrs2 := makeServiceAttrs(proc2)
attrs2 := makeServiceAttrs(proc2, &transform.RoutesConfig{})
assert.NotNil(t, attrs2.Sampler)
assert.NotNil(t, attrs2.CustomInRouteMatcher)
assert.NotNil(t, attrs2.CustomOutRouteMatcher)
Expand Down
5 changes: 5 additions & 0 deletions pkg/internal/transform/route/clusterurl/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func TestClusterURL(t *testing.T) {
assert.Equal(t, "/*", csf.ClusterURL("/1#"))
assert.Equal(t, "a", csf.ClusterURL("a#"))
assert.Equal(t, "/a/b/c/d/e/f/g/h/i", csf.ClusterURL("/a/b/c/d/e/f/g/h/i/j"))
assert.Equal(t, "/api/user", csf.ClusterURL("/api/user"))
assert.Equal(t, "/api/customer", csf.ClusterURL("/api/customer"))
assert.Equal(t, "/api/test", csf.ClusterURL("/api/test"))
assert.Equal(t, "/api/option", csf.ClusterURL("/api/option"))
assert.Equal(t, "/api/metric", csf.ClusterURL("/api/metric"))
}

func BenchmarkClusterURLWithCache(b *testing.B) {
Expand Down
Loading
Loading