This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/skywalking-go.git
The following commit(s) were added to refs/heads/main by this push: new e427c3e Add support trace ignore (#177) e427c3e is described below commit e427c3e53a7fda57ebd85b9784e58db352d77a53 Author: Starry <codeprince2...@163.com> AuthorDate: Fri Mar 22 09:23:52 2024 +0800 Add support trace ignore (#177) --- CHANGES.md | 5 +- docs/en/agent/tracing-metrics-logging.md | 9 +- plugins/core/tracer.go | 4 +- plugins/core/tracer_ignore.go | 135 ++++++++++++++++++++++ plugins/core/tracer_ignore_test.go | 81 +++++++++++++ plugins/core/tracing.go | 16 +-- tools/go-agent/config/agent.default.yaml | 8 +- tools/go-agent/config/loader.go | 1 + tools/go-agent/instrument/agentcore/instrument.go | 3 +- 9 files changed, 239 insertions(+), 23 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f823c49..fe3da3b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,9 @@ Release Notes. 0.5.0 ------------------ +#### Features +* Add support trace ignore. + #### Plugins * Support [Pulsar](https://github.com/apache/pulsar-client-go) MQ. * Support [Segmentio-Kafka](https://github.com/segmentio/kafka-go) MQ. @@ -111,7 +114,7 @@ Release Notes. * Support [Kratos](github.com/go-kratos/kratos) v2 server and client framework. * Support [Go-Micro](https://github.com/go-micro/go-micro) v4 server and client framework. * Support [GORM](https://github.com/go-gorm/gorm) v2 database client framework. - * Support [MySQL Driver](https://github.com/go-gorm/mysql) detection. +* Support [MySQL Driver](https://github.com/go-gorm/mysql) detection. #### Documentation * Initialize the documentation. diff --git a/docs/en/agent/tracing-metrics-logging.md b/docs/en/agent/tracing-metrics-logging.md index 07ccf8d..ec6e263 100644 --- a/docs/en/agent/tracing-metrics-logging.md +++ b/docs/en/agent/tracing-metrics-logging.md @@ -22,10 +22,11 @@ If you wish to disable a particular plugin to prevent enhancements related to th The basic configuration is as follows: -| Name | Environment Key | Default Value | Description | -|---------------------|------------------------|--------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| -| agent.sampler | SW_AGENT_SAMPLER | 1 | Sampling rate of tracing data, which is a floating-point value that must be between 0 and 1. | -| agent.ignore_suffix | SW_AGENT_IGNORE_SUFFIX | .jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg | If the operation name of the first span is included in this set, this segment should be ignored.(multiple split by ","). | +| Name | Environment Key | Default Value | Description | +|-------------------------|----------------------------|--------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| agent.sampler | SW_AGENT_SAMPLER | 1 | Sampling rate of tracing data, which is a floating-point value that must be between 0 and 1. | +| agent.ignore_suffix | SW_AGENT_IGNORE_SUFFIX | .jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg | If the suffix obtained by splitting the operation name by the last index of "." in this set, this segment should be ignored.(multiple split by ","). | +| agent.trace_ignore_path | SW_AGENT_TRACE_IGNORE_PATH | | If the operation name of the first span is matching, this segment should be ignored.(multiple split by ","). | ## Metrics diff --git a/plugins/core/tracer.go b/plugins/core/tracer.go index e10a56b..53a5eff 100644 --- a/plugins/core/tracer.go +++ b/plugins/core/tracer.go @@ -54,10 +54,11 @@ type Tracer struct { meterMap *sync.Map meterCollectListeners []func() ignoreSuffix []string + traceIgnorePath []string } func (t *Tracer) Init(entity *reporter.Entity, rep reporter.Reporter, samp Sampler, logger operator.LogOperator, - meterCollectSecond int, correlation *CorrelationConfig, ignoreSuffixStr string) error { + meterCollectSecond int, correlation *CorrelationConfig, ignoreSuffixStr string, ignorePath string) error { t.ServiceEntity = entity t.Reporter = rep t.Sampler = samp @@ -69,6 +70,7 @@ func (t *Tracer) Init(entity *reporter.Entity, rep reporter.Reporter, samp Sampl t.initMetricsCollect(meterCollectSecond) t.correlation = correlation t.ignoreSuffix = strings.Split(ignoreSuffixStr, ",") + t.traceIgnorePath = strings.Split(ignorePath, ",") // notify the tracer been init success if len(GetInitNotify()) > 0 { for _, fun := range GetInitNotify() { diff --git a/plugins/core/tracer_ignore.go b/plugins/core/tracer_ignore.go new file mode 100644 index 0000000..ae4212b --- /dev/null +++ b/plugins/core/tracer_ignore.go @@ -0,0 +1,135 @@ +// Licensed to 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. Apache Software Foundation (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. + +package core + +import ( + "strings" +) + +func tracerIgnore(operationName string, ignoreSuffixList, ignorePath []string) bool { + return ignoreSuffix(operationName, ignoreSuffixList) || traceIgnorePath(operationName, ignorePath) +} + +func ignoreSuffix(operationName string, ignoreSuffix []string) bool { + suffixIdx := strings.LastIndex(operationName, ".") + if suffixIdx == -1 { + return false + } + for _, suffix := range ignoreSuffix { + if suffix == operationName[suffixIdx:] { + return true + } + } + return false +} + +func traceIgnorePath(operationName string, ignorePath []string) bool { + for _, pattern := range ignorePath { + if normalMatch(pattern, 0, operationName, 0) { + return true + } + } + return false +} + +// normalMatch determines whether the operation name matches the wildcard pattern. +// The parameters `p` and `s` represent the current index in pattern and operationName respectively. +func normalMatch(pattern string, p int, operationName string, s int) bool { + for p < len(pattern) { + pc := pattern[p] + sc := safeCharAt(operationName, s) + + if pc == '*' { + p++ + if safeCharAt(pattern, p) == '*' { + p++ + return multiWildcardMatch(pattern, p, operationName, s) + } + return wildcardMatch(pattern, p, operationName, s) + } + + if (pc == '?' && sc != 0 && sc != '/') || pc == sc { + s++ + p++ + continue + } + return false + } + return s == len(operationName) +} + +func wildcardMatch(pattern string, p int, operationName string, s int) bool { + pc := safeCharAt(pattern, p) + + if pc == 0 { + for { + sc := safeCharAt(operationName, s) + if sc == 0 { + return true + } + if sc == '/' { + return s == len(operationName)-1 + } + s++ + } + } + + for { + sc := safeCharAt(operationName, s) + if sc == '/' { + if pc == sc { + return normalMatch(pattern, p+1, operationName, s+1) + } + return false + } + if !normalMatch(pattern, p, operationName, s) { + if s >= len(operationName) { + return false + } + s++ + continue + } + return true + } +} + +func multiWildcardMatch(pattern string, p int, operationName string, s int) bool { + switch safeCharAt(pattern, p) { + case 0: + return true + case '/': + p++ + } + for { + if !normalMatch(pattern, p, operationName, s) { + if s >= len(operationName) { + return false + } + s++ + continue + } + return true + } +} + +func safeCharAt(value string, index int) byte { + if index >= len(value) { + return 0 + } + return value[index] +} diff --git a/plugins/core/tracer_ignore_test.go b/plugins/core/tracer_ignore_test.go new file mode 100644 index 0000000..9bd3bf3 --- /dev/null +++ b/plugins/core/tracer_ignore_test.go @@ -0,0 +1,81 @@ +// Licensed to 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. Apache Software Foundation (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. + +package core + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIgnoreSuffix(t *testing.T) { + ignoreSuffixStr := ".jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg" + ignoreSuffixList := strings.Split(ignoreSuffixStr, ",") + assert.True(t, ignoreSuffix("GET:/favicon.ico", ignoreSuffixList)) +} + +func TestTraceIgnorePath(t *testing.T) { + ignorePath := []string{"/health/*"} + assert.False(t, traceIgnorePath("", ignorePath)) + assert.True(t, traceIgnorePath("/health/apps", ignorePath)) + assert.True(t, traceIgnorePath("/health/", ignorePath)) + assert.True(t, traceIgnorePath("/health/apps/", ignorePath)) + + ignorePath = []string{"/health/**"} + assert.True(t, traceIgnorePath("/health/apps/", ignorePath)) + + ignorePath = []string{"/health/?"} + assert.True(t, traceIgnorePath("/health/a", ignorePath)) + assert.False(t, traceIgnorePath("/health/ab", ignorePath)) + + ignorePath = []string{"/health/*/"} + assert.True(t, traceIgnorePath("/health/apps/", ignorePath)) + assert.False(t, traceIgnorePath("/health/", ignorePath)) + assert.False(t, traceIgnorePath("/health/apps/list", ignorePath)) + assert.False(t, traceIgnorePath("/health/test", ignorePath)) + + ignorePath = []string{"/health/**"} + assert.True(t, traceIgnorePath("/health/", ignorePath)) + assert.True(t, traceIgnorePath("/health/apps/test", ignorePath)) + assert.True(t, traceIgnorePath("/health/apps/test/", ignorePath)) + + ignorePath = []string{"health/apps/?"} + assert.False(t, traceIgnorePath("health/apps/list", ignorePath)) + assert.False(t, traceIgnorePath("health/apps/", ignorePath)) + assert.True(t, traceIgnorePath("health/apps/a", ignorePath)) + + ignorePath = []string{"health/**/lists"} + assert.True(t, traceIgnorePath("health/apps/lists", ignorePath)) + assert.True(t, traceIgnorePath("health/apps/test/lists", ignorePath)) + assert.False(t, traceIgnorePath("health/apps/test/", ignorePath)) + assert.False(t, traceIgnorePath("health/apps/test", ignorePath)) + + ignorePath = []string{"health/**/test/**"} + assert.True(t, traceIgnorePath("health/apps/test/list", ignorePath)) + assert.True(t, traceIgnorePath("health/apps/foo/test/list/bar", ignorePath)) + assert.True(t, traceIgnorePath("health/apps/foo/test/list/bar/", ignorePath)) + assert.True(t, traceIgnorePath("health/apps/test/list", ignorePath)) + assert.True(t, traceIgnorePath("health/test/list", ignorePath)) + + ignorePath = []string{"/health/**/b/**/*.txt", "abc/*"} + assert.True(t, traceIgnorePath("/health/a/aa/aaa/b/bb/bbb/xxxxxx.txt", ignorePath)) + assert.False(t, traceIgnorePath("/health/a/aa/aaa/b/bb/bbb/xxxxxx", ignorePath)) + assert.False(t, traceIgnorePath("abc/foo/bar", ignorePath)) + assert.True(t, traceIgnorePath("abc/foo", ignorePath)) +} diff --git a/plugins/core/tracing.go b/plugins/core/tracing.go index 8fac32f..1f73c89 100644 --- a/plugins/core/tracing.go +++ b/plugins/core/tracing.go @@ -20,7 +20,6 @@ package core import ( "reflect" "runtime/debug" - "strings" "github.com/pkg/errors" @@ -230,7 +229,7 @@ func (t *Tracer) createNoop(operationName string) (*TracingContext, TracingSpan, if !t.InitSuccess() || t.Reporter.ConnectionStatus() == reporter.ConnectionStatusDisconnect { return nil, newNoopSpan(), true } - if ignoreSuffixFilter(operationName, t.ignoreSuffix) { + if tracerIgnore(operationName, t.ignoreSuffix, t.traceIgnorePath) { return nil, newNoopSpan(), true } ctx := getTracingContext() @@ -341,16 +340,3 @@ func saveSpanToActiveIfNotError(ctx *TracingContext, span interface{}, err error ctx.SaveActiveSpan(span.(TracingSpan)) SetGLS(ctx) } - -func ignoreSuffixFilter(operationName string, ignoreSuffix []string) bool { - suffixIdx := strings.LastIndex(operationName, ".") - if suffixIdx == -1 { - return false - } - for _, suffix := range ignoreSuffix { - if suffix == operationName[suffixIdx:] { - return true - } - } - return false -} diff --git a/tools/go-agent/config/agent.default.yaml b/tools/go-agent/config/agent.default.yaml index 0af4207..ee1e5cd 100644 --- a/tools/go-agent/config/agent.default.yaml +++ b/tools/go-agent/config/agent.default.yaml @@ -30,8 +30,14 @@ agent: correlation: max_key_count: ${SW_AGENT_CORRELATION_MAX_KEY_COUNT:3} max_value_size: ${SW_AGENT_CORRELATION_MAX_VALUE_SIZE:128} - # If the operation name of the first span is included in this set, this segment should be ignored.(multiple split by ",") + # If the suffix obtained by splitting the operation name by the last index of "." in this set, this segment should be ignored.(multiple split by ","). ignore_suffix: ${SW_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg} + # If the operation name of the first span is matching, this segment should be ignored.(multiple split by ","). + # Matching rules follow Ant Path match style, like /path/*, /path/**, /path/?. + # "/path/*" means matching any path that starts with "/path/". + # "/path/**" means matching any path that starts with "/path/" and includes its subpaths. + # "/path/?" means matching any path that starts with "/path/" and has any single character as a wildcard. + trace_ignore_path: ${SW_AGENT_TRACE_IGNORE_PATH:} reporter: discard: ${SW_AGENT_REPORTER_DISCARD:false} diff --git a/tools/go-agent/config/loader.go b/tools/go-agent/config/loader.go index e42420d..466626a 100644 --- a/tools/go-agent/config/loader.go +++ b/tools/go-agent/config/loader.go @@ -51,6 +51,7 @@ type Agent struct { Meter Meter `yaml:"meter"` Correlation Correlation `yaml:"correlation"` IgnoreSuffix StringValue `yaml:"ignore_suffix"` + TraceIgnorePath StringValue `yaml:"trace_ignore_path"` } type Reporter struct { diff --git a/tools/go-agent/instrument/agentcore/instrument.go b/tools/go-agent/instrument/agentcore/instrument.go index 83dcbb6..b46abec 100644 --- a/tools/go-agent/instrument/agentcore/instrument.go +++ b/tools/go-agent/instrument/agentcore/instrument.go @@ -163,7 +163,8 @@ func (t *Tracer) InitTracer(extend map[string]interface{}) { MaxValueSize : {{.Config.Agent.Correlation.MaxValueSize.ToGoIntValue "loading the agent correlation maxValueSize error"}}, } ignoreSuffixStr := {{.Config.Agent.IgnoreSuffix.ToGoStringValue}} - if err := t.Init(entity, rep, samp, logger, meterCollectInterval, correlation, ignoreSuffixStr); err != nil { + ignorePath := {{.Config.Agent.TraceIgnorePath.ToGoStringValue}} + if err := t.Init(entity, rep, samp, logger, meterCollectInterval, correlation, ignoreSuffixStr, ignorePath); err != nil { t.Log.Errorf("cannot initialize the SkyWalking Tracer: %v", err) } }`, struct {