From 9edca3d6126ac07eaeefc5976de1f9b9cddd69c8 Mon Sep 17 00:00:00 2001 From: huawei Date: Thu, 6 Aug 2020 23:46:23 +0800 Subject: [PATCH] Add trace ignore (#59) --- docs/EnvVars.md | 2 + skywalking/config.py | 3 ++ skywalking/trace/context.py | 9 ++++ skywalking/utils/ant_matcher.py | 90 +++++++++++++++++++++++++++++++++ tests/test_ant_matcher.py | 86 +++++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 skywalking/utils/ant_matcher.py create mode 100644 tests/test_ant_matcher.py diff --git a/docs/EnvVars.md b/docs/EnvVars.md index 7f1714d..babd9b1 100644 --- a/docs/EnvVars.md +++ b/docs/EnvVars.md @@ -17,3 +17,5 @@ Environment Variable | Description | Default | `SW_HTTP_PARAMS_LENGTH_THRESHOLD`| When `COLLECT_HTTP_PARAMS` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete parameters, NB. this config item is added for the sake of performance. | `1024` | | `SW_CORRELATION_ELEMENT_MAX_NUMBER`|Max element count of the correlation context.| `3` | | `SW_CORRELATION_VALUE_MAX_LENGTH`| Max value length of correlation context element.| `128` | +| `SW_TRACE_IGNORE`| This config item controls that whether the trace should be ignore | `false` | +| `SW_TRACE_IGNORE_PATH`| You can setup multiple URL path patterns, The endpoints match these patterns wouldn't be traced. the current matching rules follow Ant Path match style , like /path/*, /path/**, /path/?.| `''` | diff --git a/skywalking/config.py b/skywalking/config.py index cc2544e..fe73943 100644 --- a/skywalking/config.py +++ b/skywalking/config.py @@ -38,6 +38,9 @@ django_collect_http_params = True if os.getenv('SW_DJANGO_COLLECT_HTTP_PARAMS') os.getenv('SW_DJANGO_COLLECT_HTTP_PARAMS') == 'True' else False # type: bool correlation_element_max_number = int(os.getenv('SW_CORRELATION_ELEMENT_MAX_NUMBER') or '3') # type: int correlation_value_max_length = int(os.getenv('SW_CORRELATION_VALUE_MAX_LENGTH') or '128') # type: int +trace_ignore = True if os.getenv('SW_TRACE_IGNORE') and \ + os.getenv('SW_TRACE_IGNORE') == 'True' else False # type: bool +trace_ignore_path = (os.getenv('SW_TRACE_IGNORE_PATH') or '').split(',') # type: List[str] def init( diff --git a/skywalking/trace/context.py b/skywalking/trace/context.py index 23cdbc6..39b80ef 100644 --- a/skywalking/trace/context.py +++ b/skywalking/trace/context.py @@ -25,6 +25,7 @@ from skywalking.trace.carrier import Carrier from skywalking.trace.segment import Segment, SegmentRef from skywalking.trace.snapshot import Snapshot from skywalking.trace.span import Span, Kind, NoopSpan, EntrySpan, ExitSpan +from skywalking.utils.ant_matcher import fast_path_match from skywalking.utils.counter import Counter logger = logging.getLogger(__name__) @@ -98,6 +99,14 @@ class SpanContext(object): context=NoopContext(), kind=kind, ) + if config.trace_ignore: + for pattern in config.trace_ignore_path: + if fast_path_match(pattern, op): + return NoopSpan( + context=NoopContext(), + kind=kind, + ) + return None def start(self, span: Span): diff --git a/skywalking/utils/ant_matcher.py b/skywalking/utils/ant_matcher.py new file mode 100644 index 0000000..41e004a --- /dev/null +++ b/skywalking/utils/ant_matcher.py @@ -0,0 +1,90 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +def fast_path_match(pattern: str, path: str): + return normal_match(pattern, 0, path, 0) + + +def normal_match(pat: str, p: int, var: str, s: int) -> bool: + while p < len(pat): + pc = pat[p] + sc = safe_char_at(var, s) + + if pc == '*': + p += 1 + + if safe_char_at(pat, p) == '*': + p += 1 + + return multi_wildcard_match(pat, p, var, s) + else: + return wildcard_match(pat, p, var, s) + + if (pc == '?' and sc != '0' and sc != '/') or pc == sc: + s += 1 + p += 1 + continue + + return False + + return s == len(var) + + +def wildcard_match(pat: str, p: int, var: str, s: int) -> bool: + pc = safe_char_at(pat, p) + + while True: + sc = safe_char_at(var, s) + + if sc == '/': + + if pc == sc: + return normal_match(pat, p + 1, var, s + 1) + + return False + + if normal_match(pat, p, var, s) is False: + if s >= len(var): + return False + + s += 1 + continue + + return True + + +def multi_wildcard_match(pat: str, p: int, var: str, s: int) -> bool: + if p >= len(pat) and s < len(var): + return var[len(var) - 1] != '/' + + while True: + if not normal_match(pat, p, var, s): + if s >= len(var): + return False + + s += 1 + continue + + return True + + +def safe_char_at(value: str, index: int) -> str: + if index >= len(value): + return '0' + + return value[index] diff --git a/tests/test_ant_matcher.py b/tests/test_ant_matcher.py new file mode 100644 index 0000000..de64c4d --- /dev/null +++ b/tests/test_ant_matcher.py @@ -0,0 +1,86 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import unittest + +from skywalking.utils.ant_matcher import fast_path_match + + +class TestFastPathMatch(unittest.TestCase): + def test_match(self): + patten = "/eureka/*" + path = "/eureka/apps" + self.assertTrue(fast_path_match(patten, path)) + path = "/eureka/" + self.assertTrue(fast_path_match(patten, path)) + path = "/eureka/apps/" + self.assertFalse(fast_path_match(patten, path)) + + patten = "/eureka/*/" + path = "/eureka/apps/" + self.assertTrue(fast_path_match(patten, path)) + path = "/eureka/" + self.assertFalse(fast_path_match(patten, path)) + path = "/eureka/apps/list" + self.assertFalse(fast_path_match(patten, path)) + + patten = "/eureka/**" + path = "/eureka/" + self.assertTrue(fast_path_match(patten, path)) + path = "/eureka/apps/test" + self.assertTrue(fast_path_match(patten, path)) + path = "/eureka/apps/test/" + self.assertFalse(fast_path_match(patten, path)) + + patten = "eureka/apps/?" + path = "eureka/apps/list" + self.assertFalse(fast_path_match(patten, path)) + path = "eureka/apps/" + self.assertFalse(fast_path_match(patten, path)) + path = "eureka/apps/a" + self.assertTrue(fast_path_match(patten, path)) + + patten = "eureka/**/lists" + path = "eureka/apps/lists" + self.assertTrue(fast_path_match(patten, path)) + path = "eureka/apps/test/lists" + self.assertTrue(fast_path_match(patten, path)) + path = "eureka/apps/test/" + self.assertFalse(fast_path_match(patten, path)) + path = "eureka/apps/test" + self.assertFalse(fast_path_match(patten, path)) + + patten = "eureka/**/test/**" + path = "eureka/apps/test/list" + self.assertTrue(fast_path_match(patten, path)) + path = "eureka/apps/foo/test/list/bar" + self.assertTrue(fast_path_match(patten, path)) + path = "eureka/apps/foo/test/list/bar/" + self.assertFalse(fast_path_match(patten, path)) + path = "eureka/apps/test/list" + self.assertTrue(fast_path_match(patten, path)) + path = "eureka/test/list" + self.assertFalse(fast_path_match(patten, path)) + + patten = "/eureka/**/b/**/*.txt" + path = "/eureka/a/aa/aaa/b/bb/bbb/xxxxxx.txt" + self.assertTrue(fast_path_match(patten, path)) + path = "/eureka/a/aa/aaa/b/bb/bbb/xxxxxx" + self.assertFalse(fast_path_match(patten, path)) + + +if __name__ == '__main__': + unittest.main() -- GitLab