def test_datadog_sampler_init(): # No args sampler = DatadogSampler() assert sampler.rules == [] assert isinstance(sampler.limiter, RateLimiter) assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT # With rules rule = SamplingRule(sample_rate=1) sampler = DatadogSampler(rules=[rule]) assert sampler.rules == [rule] assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT # With rate limit sampler = DatadogSampler(rate_limit=10) assert sampler.limiter.rate_limit == 10 # Invalid rules for val in (None, True, False, object(), 1, Exception()): with pytest.raises(TypeError): DatadogSampler(rules=[val]) # Ensure rule order rule_1 = SamplingRule(sample_rate=1) rule_2 = SamplingRule(sample_rate=0.5, service='test') rule_3 = SamplingRule(sample_rate=0.25, name='flask.request') sampler = DatadogSampler(rules=[rule_1, rule_2, rule_3]) assert sampler.rules == [rule_1, rule_2, rule_3]
def test_sampling_rule_init_sample_rate(sample_rate, allowed): if allowed: rule = SamplingRule(sample_rate=sample_rate) assert rule.sample_rate == sample_rate else: with pytest.raises(ValueError): SamplingRule(sample_rate=sample_rate)
def test_sampling_rule_sample_rate_1(): tracer = DummyTracer() rule = SamplingRule(sample_rate=1) iterations = int(1e4) assert all( rule.sample(Span(tracer=tracer, name=i)) for i in range(iterations))
def test_sampling_rule_sample_rate_0(): tracer = get_dummy_tracer() rule = SamplingRule(sample_rate=0) iterations = int(1e4) assert sum( rule.sample(Span(tracer=tracer, name=i)) for i in range(iterations)) == 0
def test_datadog_sampler_init(): # No args sampler = DatadogSampler() assert sampler.rules == [] assert isinstance(sampler.limiter, RateLimiter) assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT assert isinstance(sampler.default_sampler, RateByServiceSampler) # With rules rule = SamplingRule(sample_rate=1) sampler = DatadogSampler(rules=[rule]) assert sampler.rules == [rule] assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT assert isinstance(sampler.default_sampler, RateByServiceSampler) # With rate limit sampler = DatadogSampler(rate_limit=10) assert sampler.limiter.rate_limit == 10 assert isinstance(sampler.default_sampler, RateByServiceSampler) # With default_sample_rate sampler = DatadogSampler(default_sample_rate=0.5) assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT assert isinstance(sampler.default_sampler, SamplingRule) assert sampler.default_sampler.sample_rate == 0.5 # From env variables with override_env( dict(DD_TRACE_SAMPLE_RATE="0.5", DD_TRACE_RATE_LIMIT="10")): sampler = DatadogSampler() assert sampler.limiter.rate_limit == 10 assert isinstance(sampler.default_sampler, SamplingRule) assert sampler.default_sampler.sample_rate == 0.5 # Invalid env vars with override_env(dict(DD_TRACE_SAMPLE_RATE="asdf")): with pytest.raises(ValueError): DatadogSampler() # Invalid env vars with override_env(dict(DD_TRACE_RATE_LIMIT="asdf")): with pytest.raises(ValueError): DatadogSampler() # Invalid rules for val in (None, True, False, object(), 1, Exception()): with pytest.raises(TypeError): DatadogSampler(rules=[val]) # Ensure rule order rule_1 = SamplingRule(sample_rate=1) rule_2 = SamplingRule(sample_rate=0.5, service="test") rule_3 = SamplingRule(sample_rate=0.25, name="flask.request") sampler = DatadogSampler(rules=[rule_1, rule_2, rule_3]) assert sampler.rules == [rule_1, rule_2, rule_3]
def test_sampling_rule_sample(sample_rate): rule = SamplingRule(sample_rate=sample_rate) iterations = int(1e4 / sample_rate) sampled = sum(rule.sample(Span(name=str(i))) for i in range(iterations)) # Less than 5% deviation when 'enough' iterations (arbitrary, just check if it converges) deviation = abs(sampled - (iterations * sample_rate)) / (iterations * sample_rate) assert deviation < 0.05, "Deviation {!r} too high with sample_rate {!r} for {} sampled".format( deviation, sample_rate, sampled)
def test_sampling(self, tracer): with tracer.trace("trace1"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1.0) tracer.configure(sampler=sampler, writer=tracer.writer) with tracer.trace("trace2"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=0.000001) tracer.configure(sampler=sampler, writer=tracer.writer) with tracer.trace("trace3"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1, rules=[SamplingRule(1.0)]) tracer.configure(sampler=sampler, writer=tracer.writer) with tracer.trace("trace4"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1, rules=[SamplingRule(0)]) tracer.configure(sampler=sampler, writer=tracer.writer) with tracer.trace("trace5"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1) tracer.configure(sampler=sampler, writer=tracer.writer) with tracer.trace("trace6"): with tracer.trace("child") as span: span.set_tag(MANUAL_DROP_KEY) sampler = DatadogSampler(default_sample_rate=1) tracer.configure(sampler=sampler, writer=tracer.writer) with tracer.trace("trace7"): with tracer.trace("child") as span: span.set_tag(MANUAL_KEEP_KEY) sampler = RateSampler(0.0000000001) tracer.configure(sampler=sampler, writer=tracer.writer) # This trace should not appear in the snapshot with tracer.trace("trace8"): with tracer.trace("child"): pass tracer.shutdown()
def test_datadog_sampler_tracer_rate_limited(dummy_tracer): rule = SamplingRule(sample_rate=1.0, name='test.span') rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) limiter_spy.is_allowed.return_value = False # Have the limiter deny the span sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) dummy_tracer.configure(sampler=sampler_spy) assert dummy_tracer.sampler is sampler_spy with dummy_tracer.trace('test.span') as span: # Assert all of our expected functions were called sampler_spy.sample.assert_called_once_with(span) rule_spy.matches.assert_called_once_with(span) rule_spy.sample.assert_called_once_with(span) limiter_spy.is_allowed.assert_called_once_with() # We know it was not sampled because of our limiter assert span.sampled is False assert span._context.sampling_priority is AUTO_REJECT assert_sampling_decision_tags(span, rule=1.0, limit=None)
def test_datadog_sampler_tracer(dummy_tracer): rule = SamplingRule(sample_rate=1.0, name='test.span') rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) dummy_tracer.configure(sampler=sampler_spy) assert dummy_tracer.sampler is sampler_spy with dummy_tracer.trace('test.span') as span: # Assert all of our expected functions were called sampler_spy.sample.assert_called_once_with(span) rule_spy.matches.assert_called_once_with(span) rule_spy.sample.assert_called_once_with(span) limiter_spy.is_allowed.assert_called_once_with() # We know it was sampled because we have a sample rate of 1.0 assert span.sampled is True assert span._context.sampling_priority is AUTO_KEEP assert_sampling_decision_tags(span, rule=1.0)
def run(self): # Generate random service and operation names for the counts we requested services = [rands() for _ in range(self.num_services)] operation_names = [rands() for _ in range(self.num_operations)] # Generate all possible permutations of service and operation names spans = [ Span(tracer=None, service=service, name=name) for service, name in itertools.product(services, operation_names) ] # Create a single rule to use for all matches # Pick a random service/operation name rule = SamplingRule( service=random.choice(services), name=random.choice(operation_names), sample_rate=1.0, ) def _(loops): for _ in range(loops): for span in iter_n(spans, n=self.num_iterations): rule.matches(span) yield _
def test_datadog_sampler_tracer_rate_0(dummy_tracer): rule = SamplingRule( sample_rate=0, name='test.span') # Sample rate of 0 means never sample rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) # TODO: Remove `priority_sampling=False` when we remove fallback dummy_tracer.configure(sampler=sampler_spy, priority_sampling=False) assert dummy_tracer.sampler is sampler_spy with dummy_tracer.trace('test.span') as span: # Assert all of our expected functions were called sampler_spy.sample.assert_called_once_with(span) rule_spy.matches.assert_called_once_with(span) rule_spy.sample.assert_called_once_with(span) limiter_spy.is_allowed.assert_not_called() # We know it was not sampled because we have a sample rate of 0.0 assert span.sampled is False assert span._context.sampling_priority is AUTO_REJECT assert_sampling_decision_tags(span, rule=0)
def test_datadog_sampler_tracer_child(dummy_tracer): rule = SamplingRule( sample_rate=1.0) # No rules means it gets applied to every span rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) # TODO: Remove `priority_sampling=False` when we remove fallback dummy_tracer.configure(sampler=sampler_spy, priority_sampling=False) assert dummy_tracer.sampler is sampler_spy with dummy_tracer.trace('parent.span') as parent: with dummy_tracer.trace('child.span') as child: # Assert all of our expected functions were called # DEV: `assert_called_once_with` ensures we didn't also call with the child span sampler_spy.sample.assert_called_once_with(parent) rule_spy.matches.assert_called_once_with(parent) rule_spy.sample.assert_called_once_with(parent) limiter_spy.is_allowed.assert_called_once_with() # We know it was sampled because we have a sample rate of 1.0 assert parent.sampled is True assert parent._context.sampling_priority is AUTO_KEEP assert_sampling_decision_tags(parent, rule=1.0) assert child.sampled is True assert child._parent is parent assert child._context.sampling_priority is AUTO_KEEP
def test_datadog_sampler_tracer_start_span(dummy_tracer): rule = SamplingRule( sample_rate=1.0) # No rules means it gets applied to every span rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) # TODO: Remove `priority_sampling=False` when we remove fallback dummy_tracer.configure(sampler=sampler_spy, priority_sampling=False) assert dummy_tracer.sampler is sampler_spy span = dummy_tracer.start_span('test.span') # Assert all of our expected functions were called sampler_spy.sample.assert_called_once_with(span) rule_spy.matches.assert_called_once_with(span) rule_spy.sample.assert_called_once_with(span) limiter_spy.is_allowed.assert_called_once_with() # We know it was sampled because we have a sample rate of 1.0 assert span.sampled is True assert span._context.sampling_priority is AUTO_KEEP assert_sampling_decision_tags(span, rule=1.0)
def test_datadog_sampler_tracer_rate_0(dummy_tracer): rule = SamplingRule( sample_rate=0, name="test.span") # Sample rate of 0 means never sample rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) dummy_tracer.configure(sampler=sampler_spy) assert dummy_tracer.sampler is sampler_spy with dummy_tracer.trace("test.span") as span: # Assert all of our expected functions were called sampler_spy.sample.assert_called_once_with(span) rule_spy.matches.assert_called_once_with(span) rule_spy.sample.assert_called_once_with(span) limiter_spy.is_allowed.assert_not_called() spans = dummy_tracer.pop() assert len(spans) == 1, "Span should have been sampled and written" assert spans[0].get_metric(SAMPLING_PRIORITY_KEY) is AUTO_REJECT assert spans[0].get_metric(SAMPLING_RULE_DECISION) == 0
def test_datadog_sampler_tracer_child(dummy_tracer): rule = SamplingRule( sample_rate=1.0) # No rules means it gets applied to every span rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) dummy_tracer.configure(sampler=sampler_spy) assert dummy_tracer.sampler is sampler_spy with dummy_tracer.trace("parent.span") as parent: with dummy_tracer.trace("child.span"): # Assert all of our expected functions were called # DEV: `assert_called_once_with` ensures we didn't also call with the child span sampler_spy.sample.assert_called_once_with(parent) rule_spy.matches.assert_called_once_with(parent) rule_spy.sample.assert_called_once_with(parent) limiter_spy.is_allowed.assert_called_once_with() spans = dummy_tracer.pop() assert len(spans) == 2, "Trace should have been sampled and written" assert spans[0].get_metric(SAMPLING_PRIORITY_KEY) is AUTO_KEEP assert spans[0].get_metric(SAMPLING_RULE_DECISION) == 1.0
def test_datadog_sampler_tracer_start_span(dummy_tracer): rule = SamplingRule( sample_rate=1.0) # No rules means it gets applied to every span rule_spy = mock.Mock(spec=rule, wraps=rule) rule_spy.sample_rate = rule.sample_rate sampler = DatadogSampler(rules=[rule_spy]) limiter_spy = mock.Mock(spec=sampler.limiter, wraps=sampler.limiter) sampler.limiter = limiter_spy sampler_spy = mock.Mock(spec=sampler, wraps=sampler) dummy_tracer.configure(sampler=sampler_spy) assert dummy_tracer.sampler is sampler_spy span = dummy_tracer.start_span("test.span") span.finish() # Assert all of our expected functions were called sampler_spy.sample.assert_called_once_with(span) rule_spy.matches.assert_called_once_with(span) rule_spy.sample.assert_called_once_with(span) limiter_spy.is_allowed.assert_called_once_with() spans = dummy_tracer.pop() assert len(spans) == 1, "Span should have been sampled and written" assert spans[0].get_metric(SAMPLING_PRIORITY_KEY) is AUTO_KEEP assert spans[0].get_metric(SAMPLING_RULE_DECISION) == 1.0
def test_sampling_rule_matches_exception(): e = Exception("an error occurred") def pattern(prop): raise e rule = SamplingRule(sample_rate=1.0, name=pattern) span = create_span(name="test.span") with mock.patch("ddtrace.sampler.log") as mock_log: assert rule.matches(span) is False mock_log.warning.assert_called_once_with( "%r pattern %r failed with %r", rule, pattern, "test.span", exc_info=True, )
def test_sampling_rule_matches_exception(): e = Exception('an error occurred') def pattern(prop): raise e rule = SamplingRule(sample_rate=1.0, name=pattern) span = create_span(name='test.span') with mock.patch('ddtrace.sampler.log') as mock_log: assert rule.matches(span) is False mock_log.warning.assert_called_once_with( '%r pattern %r failed with %r: %s', rule, pattern, 'test.span', e, )
def reset(): mock_is_allowed.reset_mock() for rule in rules: rule.reset_mock() rule.sample_rate = 0.5 default_rule = SamplingRule(sample_rate=1.0) sampler.default_sampler = mock.Mock(spec=SamplingRule, wraps=default_rule) # Mock has lots of problems with mocking/wrapping over class properties sampler.default_sampler.sample_rate = default_rule.sample_rate
def test_datadog_sampler_tracer_rate_limited(dummy_tracer): rule = SamplingRule(sample_rate=1.0, name="test.span") sampler = DatadogSampler(rules=[rule], rate_limit=0) dummy_tracer.configure(sampler=sampler) with dummy_tracer.trace("test.span"): pass spans = dummy_tracer.pop() assert len(spans) == 1, "Span should have been sampled and written" assert spans[0].get_metric(SAMPLING_PRIORITY_KEY) is USER_REJECT assert_sampling_decision_tags(spans[0], rule=1.0, limit=0.0)
def test_datadog_sampler_tracer_start_span(dummy_tracer): # No rules means it gets applied to every span rule = SamplingRule(sample_rate=1.0) sampler = DatadogSampler(rules=[rule]) dummy_tracer.configure(sampler=sampler) span = dummy_tracer.start_span("test.span") span.finish() spans = dummy_tracer.pop() assert len(spans) == 1, "Span should have been sampled and written" assert spans[0].get_metric(SAMPLING_PRIORITY_KEY) is USER_KEEP assert_sampling_decision_tags(spans[0], rule=1.0, limit=None)
def test_sampling_rule_init(): name_regex = re.compile(r"\.request$") rule = SamplingRule( sample_rate=0.0, # Value service="my-service", # Regex name=name_regex, ) assert rule.sample_rate == 0.0 assert rule.service == "my-service" assert rule.name == name_regex
def test_datadog_sampler_tracer_child(dummy_tracer): # No rules means it gets applied to every span rule = SamplingRule(sample_rate=1.0) sampler = DatadogSampler(rules=[rule]) dummy_tracer.configure(sampler=sampler) with dummy_tracer.trace("parent.span"): with dummy_tracer.trace("child.span"): pass spans = dummy_tracer.pop() assert len(spans) == 2, "Trace should have been sampled and written" assert spans[0].get_metric(SAMPLING_PRIORITY_KEY) is USER_KEEP assert_sampling_decision_tags(spans[0], rule=1.0, limit=None) assert_sampling_decision_tags(spans[1], agent=None, rule=None, limit=None)
def test_sampling_rule_init(): name_regex = re.compile(r'\.request$') def resource_check(resource): return 'healthcheck' in resource rule = SamplingRule( sample_rate=0.0, # Value service='my-service', # Regex name=name_regex, ) assert rule.sample_rate == 0.0 assert rule.service == 'my-service' assert rule.name == name_regex
# Value service='my-service', # Regex name=name_regex, ) assert rule.sample_rate == 0.0 assert rule.service == 'my-service' assert rule.name == name_regex @pytest.mark.parametrize( 'span,rule,expected', [ # DEV: Use sample_rate=1 to ensure SamplingRule._sample always returns True (create_span(name=name), SamplingRule(sample_rate=1, name=pattern), expected) for name, pattern, expected in [ ('test.span', SamplingRule.NO_RULE, True), # DEV: `span.name` cannot be `None` ('test.span', None, False), ('test.span', 'test.span', True), ('test.span', 'test_span', False), ('test.span', re.compile(r'^test\.span$'), True), ('test_span', re.compile(r'^test.span$'), True), ('test.span', re.compile(r'^test_span$'), False), ('test.span', re.compile(r'test'), True), ('test.span', re.compile(r'test\.span|another\.span'), True), ('another.span', re.compile(r'test\.span|another\.span'), True), ('test.span', lambda name: 'span' in name, True), ('test.span', lambda name: 'span' not in name, False), ('test.span', lambda name: 1 / 0, False),
def test_sampling_rule_sample_rate_0(): rule = SamplingRule(sample_rate=0) iterations = int(1e4) assert sum(rule.sample(Span(name=str(i))) for i in range(iterations)) == 0
# Value service="my-service", # Regex name=name_regex, ) assert rule.sample_rate == 0.0 assert rule.service == "my-service" assert rule.name == name_regex @pytest.mark.parametrize( "rule_1,rule_2,expected", [ # Sample rate only (SamplingRule(sample_rate=1.0), SamplingRule(sample_rate=1.0), True), (SamplingRule(sample_rate=0.5), SamplingRule(sample_rate=0.5), True), (SamplingRule(sample_rate=0.0), SamplingRule(sample_rate=0.0), True), (SamplingRule(sample_rate=0.5), SamplingRule(sample_rate=1.0), False), # Sample rate, and service name (SamplingRule(sample_rate=1.0, service="my-svc"), SamplingRule(sample_rate=1.0, service="my-svc"), True), ( SamplingRule(sample_rate=1.0, service=re.compile("my-svc")), SamplingRule(sample_rate=1.0, service=re.compile("my-svc")), True, ), (SamplingRule(sample_rate=1.0, service="my-svc"), SamplingRule(sample_rate=1.0, service="other-svc"), False), (SamplingRule(sample_rate=1.0, service="my-svc"), SamplingRule(sample_rate=0.5, service="my-svc"), False),
def test_sampling(writer, tracer): if writer == "sync": writer = AgentWriter( tracer.writer.agent_url, priority_sampler=tracer.priority_sampler, sync_mode=True, ) # Need to copy the headers which contain the test token to associate # traces with this test case. writer._headers = tracer.writer._headers else: writer = tracer.writer tracer.configure(writer=writer) with tracer.trace("trace1"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1.0) tracer.configure(sampler=sampler, writer=writer) with tracer.trace("trace2"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=0.000001) tracer.configure(sampler=sampler, writer=writer) with tracer.trace("trace3"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1, rules=[SamplingRule(1.0)]) tracer.configure(sampler=sampler, writer=writer) with tracer.trace("trace4"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1, rules=[SamplingRule(0)]) tracer.configure(sampler=sampler, writer=writer) with tracer.trace("trace5"): with tracer.trace("child"): pass sampler = DatadogSampler(default_sample_rate=1) tracer.configure(sampler=sampler, writer=writer) with tracer.trace("trace6"): with tracer.trace("child") as span: span.set_tag(MANUAL_DROP_KEY) sampler = DatadogSampler(default_sample_rate=1) tracer.configure(sampler=sampler, writer=writer) with tracer.trace("trace7"): with tracer.trace("child") as span: span.set_tag(MANUAL_KEEP_KEY) sampler = RateSampler(0.0000000001) tracer.configure(sampler=sampler, writer=writer) # This trace should not appear in the snapshot with tracer.trace("trace8"): with tracer.trace("child"): pass tracer.shutdown()
def test_sampling_rule_init_defaults(): rule = SamplingRule(sample_rate=1.0) assert rule.sample_rate == 1.0 assert rule.service == SamplingRule.NO_RULE assert rule.name == SamplingRule.NO_RULE
# Value service="my-service", # Regex name=name_regex, ) assert rule.sample_rate == 0.0 assert rule.service == "my-service" assert rule.name == name_regex @pytest.mark.parametrize( "span,rule,expected", [ # DEV: Use sample_rate=1 to ensure SamplingRule._sample always returns True (create_span(name=name), SamplingRule(sample_rate=1, name=pattern), expected) for name, pattern, expected in [ ("test.span", SamplingRule.NO_RULE, True), # DEV: `span.name` cannot be `None` ("test.span", None, False), ("test.span", "test.span", True), ("test.span", "test_span", False), ("test.span", re.compile(r"^test\.span$"), True), ("test_span", re.compile(r"^test.span$"), True), ("test.span", re.compile(r"^test_span$"), False), ("test.span", re.compile(r"test"), True), ("test.span", re.compile(r"test\.span|another\.span"), True), ("another.span", re.compile(r"test\.span|another\.span"), True), ("test.span", lambda name: "span" in name, True), ("test.span", lambda name: "span" not in name, False), ("test.span", lambda name: 1 / 0, False),