diff --git a/ddtrace/tracer/log_test.go b/ddtrace/tracer/log_test.go index 566f136d23..cae65b538c 100644 --- a/ddtrace/tracer/log_test.go +++ b/ddtrace/tracer/log_test.go @@ -64,7 +64,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) require.Len(t, tp.Logs(), 2) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"100","sampling_rules":\[{"service":"mysql","name":"","sample_rate":0\.75,"type":"trace\(0\)"}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":false,"Stats":false,"DataStreams":false,"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":true,"metadata":{"version":"v1"}}}`, tp.Logs()[1]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"100","sampling_rules":\[{"service":"mysql","name":"","sample_rate":0\.75,"target_span":"any","type":"trace\(0\)"}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":false,"Stats":false,"DataStreams":false,"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":true,"metadata":{"version":"v1"}}}`, tp.Logs()[1]) }) t.Run("limit", func(t *testing.T) { @@ -96,7 +96,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) require.Len(t, tp.Logs(), 2) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"1000.001","sampling_rules":\[{"service":"mysql","name":"","sample_rate":0\.75,"type":"trace\(0\)"}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":false,"Stats":false,"DataStreams":false,"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"1000.001","sampling_rules":\[{"service":"mysql","name":"","sample_rate":0\.75,"target_span":"any","type":"trace\(0\)"}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":false,"Stats":false,"DataStreams":false,"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) }) t.Run("errors", func(t *testing.T) { @@ -111,7 +111,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) require.Len(t, tp.Logs(), 2) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"100","sampling_rules":\[{"service":"some.service","name":"","sample_rate":0\.234,"type":"trace\(0\)"}\],"sampling_rules_error":"\\n\\tat index 1: rate not provided","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":((true)|(false)),"Stats":((true)|(false)),"DataStreams":((true)|(false)),"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"100","sampling_rules":\[{"service":"some.service","name":"","sample_rate":0\.234,"target_span":"any","type":"trace\(0\)"}\],"sampling_rules_error":"\\n\\tat index 1: rate not provided","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":((true)|(false)),"Stats":((true)|(false)),"DataStreams":((true)|(false)),"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) }) t.Run("lambda", func(t *testing.T) { diff --git a/ddtrace/tracer/rules_sampler.go b/ddtrace/tracer/rules_sampler.go index 486693287d..91be7190d9 100644 --- a/ddtrace/tracer/rules_sampler.go +++ b/ddtrace/tracer/rules_sampler.go @@ -87,6 +87,9 @@ type SamplingRule struct { // match returns true when the span's details match all the expected values in the rule. func (sr *SamplingRule) match(s *span) bool { + if sr.TargetRoot && s.root().SpanID != s.SpanID { + return false + } if sr.Service != nil && !sr.Service.MatchString(s.Service) { return false } else if sr.exactService != "" && sr.exactService != s.Service { @@ -97,6 +100,17 @@ func (sr *SamplingRule) match(s *span) bool { } else if sr.exactName != "" && sr.exactName != s.Name { return false } + if sr.Resource != nil && !sr.Resource.MatchString(s.Resource) { + return false + } + if sr.Tags != nil { + for k, regex := range sr.Tags { + v, ok := s.Meta[k] + if !ok || !regex.MatchString(v) { + return false + } + } + } return true } @@ -643,11 +657,14 @@ func unmarshalSamplingRules(b []byte, spanType SamplingRuleType) ([]SamplingRule // MarshalJSON implements the json.Marshaler interface. func (sr *SamplingRule) MarshalJSON() ([]byte, error) { s := struct { - Service string `json:"service"` - Name string `json:"name"` - Rate float64 `json:"sample_rate"` - Type string `json:"type"` - MaxPerSecond *float64 `json:"max_per_second,omitempty"` + Service string `json:"service"` + Name string `json:"name"` + Resource string `json:"resource,omitempty"` + Rate float64 `json:"sample_rate"` + TargetSpan string `json:"target_span,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + Type string `json:"type"` + MaxPerSecond *float64 `json:"max_per_second,omitempty"` }{} if sr.exactService != "" { s.Service = sr.exactService @@ -659,10 +676,26 @@ func (sr *SamplingRule) MarshalJSON() ([]byte, error) { } else if sr.Name != nil { s.Name = fmt.Sprintf("%s", sr.Name) } - s.Rate = sr.Rate - s.Type = fmt.Sprintf("%v(%d)", sr.ruleType.String(), sr.ruleType) if sr.MaxPerSecond != 0 { s.MaxPerSecond = &sr.MaxPerSecond } + if sr.ruleType == SamplingRuleTrace { + if sr.TargetRoot { + s.TargetSpan = "root" + } else { + s.TargetSpan = "any" + } + } + if sr.Resource != nil { + s.Resource = sr.Resource.String() + } + s.Rate = sr.Rate + s.Type = fmt.Sprintf("%v(%d)", sr.ruleType.String(), sr.ruleType) + s.Tags = make(map[string]string, len(sr.Tags)) + for k, v := range sr.Tags { + if v != nil { + s.Tags[k] = v.String() + } + } return json.Marshal(&s) } diff --git a/ddtrace/tracer/sampler_test.go b/ddtrace/tracer/sampler_test.go index b0986137dc..fee366b9a8 100644 --- a/ddtrace/tracer/sampler_test.go +++ b/ddtrace/tracer/sampler_test.go @@ -6,6 +6,7 @@ package tracer import ( + "context" "fmt" "io" "math" @@ -260,7 +261,7 @@ func TestRuleEnvVars(t *testing.T) { ruleN: 1, }, { - value: `[{"target_span": "root", "sample_rate": 1.0, "tags": {"host":"h-1234"}}]`, + value: `[{"target_span": "any", "sample_rate": 1.0, "tags": {"host":"h-1234"}}]`, ruleN: 1, }, { @@ -443,10 +444,15 @@ func TestRuleEnvVars(t *testing.T) { func TestRulesSampler(t *testing.T) { makeSpan := func(op string, svc string) *span { - return newSpan(op, svc, "", random.Uint64(), random.Uint64(), 0) + s := newSpan(op, svc, "res-10", random.Uint64(), random.Uint64(), 0) + s.setMeta("hostname", "hn-30") + return s } - makeFinishedSpan := func(op string, svc string) *span { - s := newSpan(op, svc, "", random.Uint64(), random.Uint64(), 0) + makeFinishedSpan := func(op, svc, resource string, tags map[string]string) *span { + s := newSpan(op, svc, resource, random.Uint64(), random.Uint64(), 0) + for k, v := range tags { + s.setMeta(k, v) + } s.finished = true return s } @@ -466,6 +472,15 @@ func TestRulesSampler(t *testing.T) { {NameServiceRule("http.request", "test-service", 1.0)}, {{Service: regexp.MustCompile("^test-"), Name: regexp.MustCompile("http\\..*"), Rate: 1.0}}, {ServiceRule("other-service-1", 0.0), ServiceRule("other-service-2", 0.0), ServiceRule("test-service", 1.0)}, + {TagsResourceRule( + map[string]*regexp.Regexp{"hostname": regexp.MustCompile("hn-[0-9]+")}, + "", "", "", 1.0, false)}, + {TagsResourceRule( + map[string]*regexp.Regexp{"hostname": regexp.MustCompile("hn-3*")}, + "res-1*", "", "", 1.0, false)}, + {TagsResourceRule( + map[string]*regexp.Regexp{"hostname": regexp.MustCompile("hn-[0-9]+")}, + "", "", "", 1.0, true)}, } for _, v := range traceRules { t.Run("", func(t *testing.T) { @@ -489,6 +504,13 @@ func TestRulesSampler(t *testing.T) { {{Service: regexp.MustCompile("^toast-"), Name: regexp.MustCompile("http\\..*"), Rate: 1.0}}, {{Service: regexp.MustCompile("^test-"), Name: regexp.MustCompile("grpc\\..*"), Rate: 1.0}}, {ServiceRule("other-service-1", 0.0), ServiceRule("other-service-2", 0.0), ServiceRule("toast-service", 1.0)}, + {TagsResourceRule( + map[string]*regexp.Regexp{"hostname": regexp.MustCompile("hn--1")}, + "", "", "", 1.0, true)}, + {TagsResourceRule( + map[string]*regexp.Regexp{"host": regexp.MustCompile("hn-1")}, + "", "", "", 1.0, true)}, + {TagsResourceRule(nil, "res", "", "", 1.0, true)}, } for _, v := range traceRules { t.Run("", func(t *testing.T) { @@ -502,6 +524,73 @@ func TestRulesSampler(t *testing.T) { } }) + t.Run("matching-target-span", func(t *testing.T) { + + testCase := []struct { + rulesEnv string + rules []SamplingRule + sampleParent bool + sampleChild bool + }{ + { + rules: []SamplingRule{TagsResourceRule(nil, "res-*", "", "", 1.0, true)}, + sampleChild: false, + sampleParent: true, + }, + { + rules: []SamplingRule{TagsResourceRule(nil, "res-*", "", "", 1.0, false)}, + sampleChild: true, + sampleParent: true, + }, + { + sampleParent: true, + sampleChild: true, + rulesEnv: `[{"target_span": "any", "sample_rate": 1.0, "tags": {"hostname":"hn-10"}}]`, + }, + { + sampleParent: true, + sampleChild: true, + rulesEnv: `[{"target_span": "any", "sample_rate": 1.0, "tags": {"hostname":"hn-10"}}]`, + }, + { + // not matching the 'host' tag + sampleParent: false, + sampleChild: false, + rulesEnv: `[{"target_span": "any", "sample_rate": 1.0, "tags": {"host":"hn-10"}}]`, + }, + { + // not matching the '*' tag + sampleParent: false, + sampleChild: false, + rulesEnv: `[{"target_span": "any", "sample_rate": 1.0, "tags": {"*":"hn-10"}}]`, + }, + } + + for _, tt := range testCase { + t.Run("", func(t *testing.T) { + os.Setenv("DD_TRACE_SAMPLING_RULES", tt.rulesEnv) + _, _, _, stop := startTestTracer(t) + defer stop() + opts := []StartSpanOption{ResourceName("res-10"), Tag("hostname", "hn-10")} + + parent, ctx := StartSpanFromContext(context.Background(), "parent", opts...) + defer parent.Finish() + + child, ctx := StartSpanFromContext(ctx, "parent", opts...) + defer child.Finish() + + rt, _, err := samplingRulesFromEnv() + assert.Nil(t, err) + if len(rt) == 0 { + rt = tt.rules + } + rs := newRulesSampler(rt, nil, globalSampleRate()) + assert.Equal(t, tt.sampleParent, rs.SampleTrace(parent.(*span))) + assert.Equal(t, tt.sampleChild, rs.SampleTrace(child.(*span))) + }) + } + }) + t.Run("matching-span-rules-from-env", func(t *testing.T) { defer os.Unsetenv("DD_SPAN_SAMPLING_RULES") for _, tt := range []struct { @@ -525,12 +614,12 @@ func TestRulesSampler(t *testing.T) { spanName: "abcde", }, { - rules: `[{"tags":{"hostname":"hn-3"},"max_per_second":100}]`, + rules: `[{"tags":{"hostname":"hn-3*"},"max_per_second":100}]`, spanSrv: "test-service", spanName: "abcde", }, { - rules: `[{"resource":"res-1","max_per_second":100}]`, + rules: `[{"resource":"res-1*","max_per_second":100}]`, spanSrv: "test-service", spanName: "abcde", }, @@ -542,7 +631,7 @@ func TestRulesSampler(t *testing.T) { assert := assert.New(t) rs := newRulesSampler(nil, rules, globalSampleRate()) - span := makeFinishedSpan(tt.spanName, tt.spanSrv) + span := makeFinishedSpan(tt.spanName, tt.spanSrv, "res-10", map[string]string{"hostname": "hn-30"}) result := rs.SampleSpan(span) assert.True(result) @@ -575,15 +664,25 @@ func TestRulesSampler(t *testing.T) { spanSrv: "test-service", spanName: "abcde", }, + { + rules: `[{"sample_rate": 1.0,"tags":{"hostname":"hn*"}, "max_per_second":100}]`, + spanSrv: "test-service", + spanName: "abcde", + }, + { + rules: `[{"sample_rate": 1.0,"resource":"res*", "tags":{"hostname":"hn*"}, "max_per_second":100}]`, + spanSrv: "test-service", + spanName: "abcde", + }, } { t.Run("", func(t *testing.T) { os.Setenv("DD_SPAN_SAMPLING_RULES", tt.rules) - _, rules, _ := samplingRulesFromEnv() - + _, rules, err := samplingRulesFromEnv() + assert.Nil(t, err) assert := assert.New(t) rs := newRulesSampler(nil, rules, globalSampleRate()) - span := makeFinishedSpan(tt.spanName, tt.spanSrv) + span := makeFinishedSpan(tt.spanName, tt.spanSrv, "res-10", map[string]string{"hostname": "hn-30"}) result := rs.SampleSpan(span) assert.True(result) assert.Contains(span.Metrics, keySpanSamplingMechanism) @@ -599,22 +698,44 @@ func TestRulesSampler(t *testing.T) { rules string spanSrv string spanName string + resName string }{ { //first matching rule takes precedence rules: `[{"name": "abcd?", "sample_rate": 0.0},{"name": "abcd?", "sample_rate": 1.0}]`, spanSrv: "test-service", spanName: "abcdef", + resName: "res-10", }, { rules: `[{"service": "abcd", "sample_rate": 1.0}]`, spanSrv: "xyzabcd", spanName: "abcde", + resName: "res-10", + }, + { + rules: `[{"resource": "rc-100", "sample_rate": 1.0}]`, + spanSrv: "xyzabcd", + spanName: "abcde", + resName: "external_api", + }, + { + rules: `[{"resource": "rc-100", "sample_rate": 1.0}]`, + spanSrv: "xyzabcd", + spanName: "abcde", + resName: "external_api", }, { rules: `[{"service": "?", "sample_rate": 1.0}]`, spanSrv: "test-service", spanName: "abcde", + resName: "res-10", + }, + { + rules: `[{"tags": {"*":"hs-30"}, "sample_rate": 1.0}]`, + spanSrv: "test-service", + spanName: "abcde", + resName: "res-10", }, } { t.Run("", func(t *testing.T) { @@ -624,7 +745,7 @@ func TestRulesSampler(t *testing.T) { assert := assert.New(t) rs := newRulesSampler(nil, rules, globalSampleRate()) - span := makeFinishedSpan(tt.spanName, tt.spanSrv) + span := makeFinishedSpan(tt.spanName, tt.spanSrv, tt.resName, map[string]string{"hostname": "hn-30"}) result := rs.SampleSpan(span) assert.False(result) assert.NotContains(span.Metrics, keySpanSamplingMechanism) @@ -655,7 +776,32 @@ func TestRulesSampler(t *testing.T) { rules: []SamplingRule{SpanNameServiceRule("[a-z]+\\d+", "\\w+", 1.0)}, spanSrv: "test-service", spanName: "abcde123", - }, { + }, + { + rules: []SamplingRule{SpanNameServiceRule(``, "\\w+", 1.0)}, + spanSrv: "test-service", + spanName: "abcde123", + }, + { + rules: []SamplingRule{SpanTagsResourceRule( + map[string]*regexp.Regexp{"host": regexp.MustCompile("hn-1")}, + "", "", "", 1.0)}, + spanSrv: "test-service", + spanName: "abcde123", + }, + { + rules: []SamplingRule{SpanTagsResourceRule(map[string]*regexp.Regexp{"hostname": regexp.MustCompile("hn-100")}, "res-1*", "", "", 1.0)}, + spanSrv: "test-service", + spanName: "abcde123", + }, + { + rules: []SamplingRule{SpanTagsResourceRule( + map[string]*regexp.Regexp{"hostname": regexp.MustCompile("hn-10")}, + "res-100", "", "", 1.0)}, + spanSrv: "test-service", + spanName: "abcde123", + }, + { rules: []SamplingRule{SpanNameServiceRule(``, "\\w+", 1.0)}, spanSrv: "test-service", spanName: "abcde123", @@ -666,7 +812,7 @@ func TestRulesSampler(t *testing.T) { c := newConfig(WithSamplingRules(tt.rules)) rs := newRulesSampler(nil, c.spanRules, globalSampleRate()) - span := makeFinishedSpan(tt.spanName, tt.spanSrv) + span := makeFinishedSpan(tt.spanName, tt.spanSrv, "res-10", map[string]string{"hostname": "hn-30"}) result := rs.SampleSpan(span) assert.False(result) assert.NotContains(span.Metrics, keySpanSamplingMechanism) @@ -1001,29 +1147,33 @@ func TestGlobMatch(t *testing.T) { } func TestSamplingRuleMarshall(t *testing.T) { - for _, tt := range []struct { + for i, tt := range []struct { in SamplingRule out string - // TODO (dianashevchenko) : add test cases for marshalling with tags and resource amd target_span }{ {SamplingRule{nil, nil, 0, 0, nil, nil, false, 0, "srv", "ops", nil}, - `{"service":"srv","name":"ops","sample_rate":0,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), nil, 0, 0, nil, nil, false, 0, "srv", "ops", nil}, - `{"service":"srv","name":"ops","sample_rate":0,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.*"), regexp.MustCompile("ops.[0-9]+]"), 0, 0, nil, nil, false, 0, "", "", nil}, - `{"service":"srv.*","name":"ops.[0-9]+]","sample_rate":0,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), 0.55, 0, nil, nil, false, 0, "", "", nil}, - `{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","sample_rate":0.55,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), 0.55, 0, nil, nil, false, 1, "", "", nil}, - `{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","sample_rate":0.55,"type":"span(1)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), 0.55, 1000, nil, nil, false, 1, "", "", nil}, - `{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","sample_rate":0.55,"type":"span(1)","max_per_second":1000}`}, + `{"service":"srv","name":"ops","sample_rate":0,"target_span":"any","type":"trace(0)"}`}, + {SamplingRule{regexp.MustCompile("srv.[0-9]+"), nil, 0, 0, nil, nil, false, 0, "srv", "ops", nil}, + `{"service":"srv","name":"ops","sample_rate":0,"target_span":"any","type":"trace(0)"}`}, + {SamplingRule{regexp.MustCompile("srv.*"), regexp.MustCompile("ops.[0-9]+"), 0, 0, nil, nil, false, 0, "", "", nil}, + `{"service":"srv.*","name":"ops.[0-9]+","sample_rate":0,"target_span":"any","type":"trace(0)"}`}, + {SamplingRule{regexp.MustCompile("srv.[0-9]+"), regexp.MustCompile("ops.[0-9]+"), 0.55, 0, nil, nil, false, 0, "", "", nil}, + `{"service":"srv.[0-9]+","name":"ops.[0-9]+","sample_rate":0.55,"target_span":"any","type":"trace(0)"}`}, + {SamplingRule{nil, nil, 0.35, 0, regexp.MustCompile("http_get"), nil, false, 0, "srv", "ops", nil}, + `{"service":"srv","name":"ops","resource":"http_get","sample_rate":0.35,"target_span":"any","type":"trace(0)"}`}, + {SamplingRule{nil, nil, 0.35, 0, regexp.MustCompile("http_get"), map[string]*regexp.Regexp{"host": regexp.MustCompile("hn-*")}, false, 0, "srv", "ops", nil}, + `{"service":"srv","name":"ops","resource":"http_get","sample_rate":0.35,"target_span":"any","tags":{"host":"hn-*"},"type":"trace(0)"}`}, + {SamplingRule{regexp.MustCompile("srv.[0-9]+"), regexp.MustCompile("ops.[0-9]+"), 0.55, 0, nil, nil, false, 1, "", "", nil}, + `{"service":"srv.[0-9]+","name":"ops.[0-9]+","sample_rate":0.55,"type":"span(1)"}`}, + {SamplingRule{regexp.MustCompile("srv.[0-9]+"), regexp.MustCompile("ops.[0-9]+"), 0.55, 1000, nil, nil, false, 1, "", "", nil}, + `{"service":"srv.[0-9]+","name":"ops.[0-9]+","sample_rate":0.55,"type":"span(1)","max_per_second":1000}`}, } { m, err := tt.in.MarshalJSON() assert.Nil(t, err) - assert.Equal(t, tt.out, string(m)) + assert.Equal(t, tt.out, string(m), "at %d index", i) } } + func BenchmarkGlobMatchSpan(b *testing.B) { var spans []*span for i := 0; i < 1000; i++ {