You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by li...@apache.org on 2023/06/14 03:12:31 UTC
[skywalking-go] branch main updated: Support add configuration for plugin (#60)
This is an automated email from the ASF dual-hosted git repository.
liuhan 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 a0d17df Support add configuration for plugin (#60)
a0d17df is described below
commit a0d17df3e4ed70ee5035c0e2c789c778db07e214
Author: mrproliu <74...@qq.com>
AuthorDate: Wed Jun 14 03:12:26 2023 +0000
Support add configuration for plugin (#60)
---
CHANGES.md | 2 +
docs/en/agent/plugin-configurations.md | 5 +
.../development-guide.md | 44 ++++
docs/menu.yml | 2 +
plugins/core/context.go | 1 +
plugins/core/operator/common.go | 1 +
plugins/core/operator/tools.go | 5 +
.../directive.go => plugins/core/tools/os.go | 24 +-
plugins/core/tools/strconv.go | 64 ++++++
plugins/core/tracer.go | 6 +
plugins/core/tracer_tools.go | 23 ++
plugins/core/tracing/span.go | 1 +
plugins/{core/operator/tools.go => http/config.go} | 7 +-
plugins/http/server_intercepter.go | 5 +
test/plugins/scenarios/http/bin/startup.sh | 2 +
test/plugins/scenarios/http/config/excepted.yml | 2 +
test/plugins/scenarios/http/main.go | 2 +-
tools/go-agent/config/agent.default.yaml | 6 +-
tools/go-agent/config/loader.go | 47 +++-
tools/go-agent/instrument/agentcore/instrument.go | 8 +
tools/go-agent/instrument/consts/directive.go | 3 +
tools/go-agent/instrument/consts/operator.go | 15 +-
.../go-agent/instrument/plugins/enhance_config.go | 244 +++++++++++++++++++++
.../instrument/plugins/enhance_instance.go | 4 +
.../go-agent/instrument/plugins/enhance_method.go | 4 +
tools/go-agent/instrument/plugins/instrument.go | 117 ++++++++--
.../go-agent/instrument/plugins/rewrite/context.go | 37 ++--
tools/go-agent/instrument/plugins/rewrite/func.go | 13 +-
tools/go-agent/instrument/plugins/rewrite/type.go | 5 +-
tools/go-agent/instrument/runtime/instrument.go | 72 ++++--
.../plugins/rewrite => tools}/directive.go | 24 +-
31 files changed, 695 insertions(+), 100 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index d95cbac..64a3520 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,9 +7,11 @@ Release Notes.
#### Features
* Enhance the plugin rewrite ability to support `switch` and `if/else` in the plugin codes.
* Support inject the skywalking-go into project through agent.
+* Support add configuration for plugin.
#### Plugins
* Support [go-redis](https://github.com/redis/go-redis) v9 redis client framework.
+* Support collecting [Native HTTP](https://pkg.go.dev/net/http) URI parameter on server side.
#### Documentation
diff --git a/docs/en/agent/plugin-configurations.md b/docs/en/agent/plugin-configurations.md
new file mode 100644
index 0000000..0b872ac
--- /dev/null
+++ b/docs/en/agent/plugin-configurations.md
@@ -0,0 +1,5 @@
+# Plugin Configurations
+
+| key | environment key | default value | description |
+|--------------------------------|-------------------------------------------------------|---------------|----------------------------------------------------------------|
+| http.server_collect_parameters | SW_AGENT_PLUGIN_CONFIG_HTTP_SERVER_COLLECT_PARAMETERS | false | Collect the parameters of the HTTP request on the server side. |
diff --git a/docs/en/development-and-contribution/development-guide.md b/docs/en/development-and-contribution/development-guide.md
index 6538377..4f70d24 100644
--- a/docs/en/development-and-contribution/development-guide.md
+++ b/docs/en/development-and-contribution/development-guide.md
@@ -246,6 +246,50 @@ place your interceptor code in the relative location within the plugin. **The Ag
For example, if you want to intercept a method in `github.com/gin-gonic/gin/render`, create a **render** directory in the root of your plugin, and **put the interceptor inside it**.
This ensures that the interceptor is properly included during the copy operation and can be correctly applied to the target package.
+### Plugin Configuration
+
+Plugin configuration is used to add custom configuration parameters to a specified plugin.
+When users specify configuration items, the plugin can dynamically adapt the content needed in the plugin according to the user's configuration items.
+
+#### Declaration
+
+Please declare the configuration file you need in the package you want to use.
+Declare it using `var`, and add the `//skywalking:config` directive to specify that this variable requires dynamic updating.
+
+By default, the configuration item belongs to the configuration of the current plugin.
+For example, if the name of my current plugin is `gin`, then this configuration item is under the `gin` plugin.
+Of course, you can also change it to the `http` plugin to reference the configuration information of the relevant plugin,
+in which case you need to specify it as `//skywalking:config http`.
+
+#### Item
+
+Each configuration item needs to add a `config` tag. This is used to specify the name of the current configuration content.
+By default, it would lowercase all letters and add an `_` identifier before each uppercase letter.
+
+Currently, it supports basic data types and struct types, and it also supports obtaining data values through environment variables.
+
+#### Demo
+
+For example, I have declared the following configuration item:
+
+```go
+//skywalking:config http
+var config struct {
+ ServerCollectParameters bool `config:"server_collect_parameters"`
+
+ Client struct{
+ CollectParameters bool `config:"collect_parameters"`
+ } `config:"client"`
+}
+```
+
+In the above example, I created a plugin configuration for `http`, which includes two configuration items.
+
+1. `config.ServerCollectParameters`: Its configuration is located at `http.server_collect_parameters`.
+2. `config.Client.CollectParameter`: Its configuration is located at `http.client.collect_parameter`.
+
+When the plugin needs to be used, it can be accessed directly by reading the config configuration.
+
## Agent API
The Agent API is used when a method is intercepted and interacts with the Agent Core.
diff --git a/docs/menu.yml b/docs/menu.yml
index 7a0058b..bccbc34 100644
--- a/docs/menu.yml
+++ b/docs/menu.yml
@@ -33,6 +33,8 @@ catalog:
path: /en/advanced-features/logging-setup
- name: Plugin Exclusion
path: /en/advanced-features/plugin-exclusion
+ - name: Plugin Configuration
+ path: /en/agent/plugin-configurations
- name: Supported Libraries
path: /en/agent/support-plugins
- name: Performance Test
diff --git a/plugins/core/context.go b/plugins/core/context.go
index 383afa0..03ec73b 100644
--- a/plugins/core/context.go
+++ b/plugins/core/context.go
@@ -24,6 +24,7 @@ var (
SetGLS = func(interface{}) {}
SetGlobalOperator = func(interface{}) {}
GetGlobalOperator = func() interface{} { return nil }
+ GetInitNotify = func() []func() { return nil }
)
type ContextSnapshoter interface {
diff --git a/plugins/core/operator/common.go b/plugins/core/operator/common.go
index ac504fc..56f2a1c 100644
--- a/plugins/core/operator/common.go
+++ b/plugins/core/operator/common.go
@@ -18,6 +18,7 @@
package operator
var GetOperator = func() Operator { return nil }
+var AppendInitNotify = func(func()) {}
type Operator interface {
Tracing() interface{} // to TracingOperator
diff --git a/plugins/core/operator/tools.go b/plugins/core/operator/tools.go
index 0c668fd..73b0101 100644
--- a/plugins/core/operator/tools.go
+++ b/plugins/core/operator/tools.go
@@ -19,4 +19,9 @@ package operator
type ToolsOperator interface {
ReflectGetValue(instance interface{}, filters []interface{}) interface{}
+ GetEnvValue(key string) string
+ ParseFloat(val string, bitSize int) (float64, error)
+ ParseBool(val string) bool
+ ParseInt(val string, base, bitSize int) (int64, error)
+ Atoi(val string) (int, error)
}
diff --git a/tools/go-agent/instrument/plugins/rewrite/directive.go b/plugins/core/tools/os.go
similarity index 72%
copy from tools/go-agent/instrument/plugins/rewrite/directive.go
copy to plugins/core/tools/os.go
index a1557ee..ef43fc5 100644
--- a/tools/go-agent/instrument/plugins/rewrite/directive.go
+++ b/plugins/core/tools/os.go
@@ -15,22 +15,18 @@
// specific language governing permissions and limitations
// under the License.
-package rewrite
+package tools
-import (
- "github.com/apache/skywalking-go/tools/go-agent/instrument/consts"
+import "github.com/apache/skywalking-go/plugins/core/operator"
- "github.com/dave/dst"
-)
-
-func ContainsPublicDirective(desc *dst.NodeDecs) bool {
- if desc == nil {
- return false
+// GetEnvValue get environment value from key
+func GetEnvValue(key string) string {
+ if key == "" {
+ return ""
}
- for _, s := range desc.Start.All() {
- if s == consts.DirectivePublic {
- return true
- }
+ op := operator.GetOperator()
+ if op == nil {
+ return ""
}
- return false
+ return op.Tools().(operator.ToolsOperator).GetEnvValue(key)
}
diff --git a/plugins/core/tools/strconv.go b/plugins/core/tools/strconv.go
new file mode 100644
index 0000000..13210a7
--- /dev/null
+++ b/plugins/core/tools/strconv.go
@@ -0,0 +1,64 @@
+// 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 tools
+
+import "github.com/apache/skywalking-go/plugins/core/operator"
+
+func ParseFloat(val string, bitSize int) (float64, error) {
+ if val == "" || bitSize == 0 {
+ return 0, nil
+ }
+ op := operator.GetOperator()
+ if op == nil {
+ return 0, nil
+ }
+ return op.Tools().(operator.ToolsOperator).ParseFloat(val, bitSize)
+}
+
+func ParseBool(val string) bool {
+ if val == "" {
+ return false
+ }
+ op := operator.GetOperator()
+ if op == nil {
+ return false
+ }
+ return op.Tools().(operator.ToolsOperator).ParseBool(val)
+}
+
+func ParseInt(val string, base, bitSize int) (int64, error) {
+ if val == "" || base == 0 || bitSize == 0 {
+ return 0, nil
+ }
+ op := operator.GetOperator()
+ if op == nil {
+ return 0, nil
+ }
+ return op.Tools().(operator.ToolsOperator).ParseInt(val, base, bitSize)
+}
+
+func Atoi(s string) (int, error) {
+ if s == "" {
+ return 0, nil
+ }
+ op := operator.GetOperator()
+ if op == nil {
+ return 0, nil
+ }
+ return op.Tools().(operator.ToolsOperator).Atoi(s)
+}
diff --git a/plugins/core/tracer.go b/plugins/core/tracer.go
index f58e86d..82bf720 100644
--- a/plugins/core/tracer.go
+++ b/plugins/core/tracer.go
@@ -57,6 +57,12 @@ func (t *Tracer) Init(entity *reporter.Entity, rep reporter.Reporter, samp Sampl
}
t.Reporter.Boot(entity, t.cdsWatchers)
t.initFlag = 1
+ // notify the tracer been init success
+ if len(GetInitNotify()) > 0 {
+ for _, fun := range GetInitNotify() {
+ fun()
+ }
+ }
return nil
}
diff --git a/plugins/core/tracer_tools.go b/plugins/core/tracer_tools.go
index 8619c62..b4b55cc 100644
--- a/plugins/core/tracer_tools.go
+++ b/plugins/core/tracer_tools.go
@@ -18,7 +18,10 @@
package core
import (
+ "os"
"reflect"
+ "strconv"
+ "strings"
"unsafe"
)
@@ -80,6 +83,26 @@ func (t *TracerTools) ReflectGetValue(instance interface{}, filterOpts []interfa
return nil
}
+func (t *TracerTools) GetEnvValue(key string) string {
+ return os.Getenv(key)
+}
+
+func (t *TracerTools) ParseFloat(val string, bitSize int) (float64, error) {
+ return strconv.ParseFloat(val, bitSize)
+}
+
+func (t *TracerTools) ParseBool(val string) bool {
+ return strings.EqualFold(val, "true")
+}
+
+func (t *TracerTools) ParseInt(val string, base, bitSize int) (int64, error) {
+ return strconv.ParseInt(val, base, bitSize)
+}
+
+func (t *TracerTools) Atoi(val string) (int, error) {
+ return strconv.Atoi(val)
+}
+
func (t *TracerTools) checkFieldSupport(field reflect.Value, instanceField *reflect.StructField, filter *ReflectFieldFilter) bool {
if filter.name != "" {
if instanceField.Name != filter.name {
diff --git a/plugins/core/tracing/span.go b/plugins/core/tracing/span.go
index 82e5279..1cc0a86 100644
--- a/plugins/core/tracing/span.go
+++ b/plugins/core/tracing/span.go
@@ -52,6 +52,7 @@ const (
TagURL = "url"
TagStatusCode = "status_code"
TagHTTPMethod = "http.method"
+ TagHTTPParams = "http.params"
TagDBType = "db.type"
TagDBInstance = "db.instance"
TagDBStatement = "db.statement"
diff --git a/plugins/core/operator/tools.go b/plugins/http/config.go
similarity index 86%
copy from plugins/core/operator/tools.go
copy to plugins/http/config.go
index 0c668fd..0d159ab 100644
--- a/plugins/core/operator/tools.go
+++ b/plugins/http/config.go
@@ -15,8 +15,9 @@
// specific language governing permissions and limitations
// under the License.
-package operator
+package http
-type ToolsOperator interface {
- ReflectGetValue(instance interface{}, filters []interface{}) interface{}
+//skywalking:config http
+var config struct {
+ ServerCollectParameters bool `config:"server_collect_parameters"`
}
diff --git a/plugins/http/server_intercepter.go b/plugins/http/server_intercepter.go
index e1f9ac8..912f3c9 100644
--- a/plugins/http/server_intercepter.go
+++ b/plugins/http/server_intercepter.go
@@ -41,6 +41,11 @@ func (h *ServerInterceptor) BeforeInvoke(invocation operator.Invocation) error {
if err != nil {
return err
}
+
+ if config.ServerCollectParameters && request.URL != nil {
+ s.Tag(tracing.TagHTTPParams, request.URL.RawQuery)
+ }
+
writer := invocation.Args()[0].(http.ResponseWriter)
invocation.ChangeArg(0, &writerWrapper{ResponseWriter: writer, statusCode: http.StatusOK})
invocation.SetContext(s)
diff --git a/test/plugins/scenarios/http/bin/startup.sh b/test/plugins/scenarios/http/bin/startup.sh
index f7c49fe..fa90e06 100755
--- a/test/plugins/scenarios/http/bin/startup.sh
+++ b/test/plugins/scenarios/http/bin/startup.sh
@@ -19,4 +19,6 @@
home="$(cd "$(dirname $0)"; pwd)"
go build ${GO_BUILD_OPTS} -o http
+export SW_AGENT_PLUGIN_CONFIG_HTTP_SERVER_COLLECT_PARAMETERS=true
+
./http
\ No newline at end of file
diff --git a/test/plugins/scenarios/http/config/excepted.yml b/test/plugins/scenarios/http/config/excepted.yml
index 2bb131e..2569a5f 100644
--- a/test/plugins/scenarios/http/config/excepted.yml
+++ b/test/plugins/scenarios/http/config/excepted.yml
@@ -34,6 +34,7 @@ segmentItems:
tags:
- {key: http.method, value: GET}
- {key: url, value: 'localhost:8080/provider'}
+ - {key: http.params, value: 'test=1'}
- {key: status_code, value: '200'}
refs:
- {parentEndpoint: 'GET:/consumer', networkAddress: 'localhost:8080', refType: CrossProcess,
@@ -70,6 +71,7 @@ segmentItems:
tags:
- {key: http.method, value: GET}
- {key: url, value: 'service:8080/consumer'}
+ - {key: http.params, value: ''}
- {key: status_code, value: '200'}
meterItems: []
logItems: []
\ No newline at end of file
diff --git a/test/plugins/scenarios/http/main.go b/test/plugins/scenarios/http/main.go
index 0af2f76..b59ef37 100644
--- a/test/plugins/scenarios/http/main.go
+++ b/test/plugins/scenarios/http/main.go
@@ -30,7 +30,7 @@ func providerHandler(w http.ResponseWriter, r *http.Request) {
}
func consumerHandler(w http.ResponseWriter, r *http.Request) {
- resp, err := http.Get("http://localhost:8080/provider")
+ resp, err := http.Get("http://localhost:8080/provider?test=1")
if err != nil {
log.Printf("request provider error: %v", err)
w.WriteHeader(http.StatusInternalServerError)
diff --git a/tools/go-agent/config/agent.default.yaml b/tools/go-agent/config/agent.default.yaml
index 55cf652..45d26af 100644
--- a/tools/go-agent/config/agent.default.yaml
+++ b/tools/go-agent/config/agent.default.yaml
@@ -52,4 +52,8 @@ log:
plugin:
# List the names of excluded plugins, multiple plugin names should be splitted by ","
# NOTE: This parameter only takes effect during the compilation phase.
- excluded: ${SW_AGENT_PLUGIN_EXCLUDES:}
\ No newline at end of file
+ excluded: ${SW_AGENT_PLUGIN_EXCLUDES:}
+ config:
+ http:
+ # Collect the parameters of the HTTP request on the server side
+ server_collect_parameters: ${SW_AGENT_PLUGIN_CONFIG_HTTP_SERVER_COLLECT_PARAMETERS:false}
\ No newline at end of file
diff --git a/tools/go-agent/config/loader.go b/tools/go-agent/config/loader.go
index 226187a..1ba7250 100644
--- a/tools/go-agent/config/loader.go
+++ b/tools/go-agent/config/loader.go
@@ -70,7 +70,8 @@ type GRPCReporter struct {
}
type Plugin struct {
- Excluded StringValue `yaml:"excluded"`
+ Config PluginConfig `yaml:"config"`
+ Excluded StringValue `yaml:"excluded"`
}
func LoadConfig(path string) error {
@@ -104,6 +105,43 @@ func GetConfig() *Config {
return config
}
+type PluginConfig struct {
+ data map[string]interface{}
+}
+
+func (c *PluginConfig) UnmarshalYAML(value *yaml.Node) error {
+ result := make(map[string]interface{})
+ if err := value.Decode(&result); err != nil {
+ return err
+ }
+ c.data = result
+ return nil
+}
+
+func (c *PluginConfig) ParseToStringValue(paths ...string) *StringValue {
+ if len(paths) == 0 {
+ return nil
+ }
+ res := c.data[paths[0]]
+ for i := 1; i < len(paths); i++ {
+ if res == nil {
+ return nil
+ }
+ current, ok := res.(map[string]interface{})
+ if !ok {
+ panic("cannot identity the path: %s" + strings.Join(paths, "."))
+ }
+ res = current[paths[i]]
+ }
+ if res == nil {
+ panic("the value of path is not found: " + strings.Join(paths, "."))
+ }
+
+ v := &StringValue{}
+ v.UnmarshalString(fmt.Sprintf("%v", res))
+ return v
+}
+
type StringValue struct {
EnvKey string
Default string
@@ -114,16 +152,19 @@ func (s *StringValue) UnmarshalYAML(value *yaml.Node) error {
if e := value.Decode(&val); e != nil {
return e
}
+ s.UnmarshalString(val)
+ return nil
+}
+func (s *StringValue) UnmarshalString(val string) {
groups := EnvRegularRegex.FindStringSubmatch(val)
if len(groups) == 0 {
s.Default = val
- return nil
+ return
}
s.EnvKey = groups[1]
s.Default = groups[2]
- return nil
}
func (s *StringValue) ToGoStringValue() string {
diff --git a/tools/go-agent/instrument/agentcore/instrument.go b/tools/go-agent/instrument/agentcore/instrument.go
index 615d1bd..6065e5a 100644
--- a/tools/go-agent/instrument/agentcore/instrument.go
+++ b/tools/go-agent/instrument/agentcore/instrument.go
@@ -180,6 +180,9 @@ var {{.GetGlobalOperatorLinkMethod}} func() interface{}
//go:linkname {{.GetGoroutineIDLinkMethod}} {{.GetGoroutineIDLinkMethod}}
var {{.GetGoroutineIDLinkMethod}} func() int64
+//go:linkname {{.GetInitNotifyLinkMethod}} {{.GetInitNotifyLinkMethod}}
+var {{.GetInitNotifyLinkMethod}} func() []func()
+
func init() {
if {{.TLSGetLinkMethod}} != nil && {{.TLSSetLinkMethod}} != nil {
GetGLS = {{.TLSGetLinkMethod}}
@@ -193,6 +196,9 @@ func init() {
GetGlobalOperator = {{.GetGlobalOperatorLinkMethod}}
SetGlobalOperator(newTracer()) // setting the global tracer when init the agent core
}
+ if {{.GetInitNotifyLinkMethod}} != nil {
+ GetInitNotify = {{.GetInitNotifyLinkMethod}}
+ }
}
`, struct {
TLSGetLinkMethod string
@@ -200,11 +206,13 @@ func init() {
SetGlobalOperatorLinkMethod string
GetGlobalOperatorLinkMethod string
GetGoroutineIDLinkMethod string
+ GetInitNotifyLinkMethod string
}{
TLSGetLinkMethod: consts.TLSGetMethodName,
TLSSetLinkMethod: consts.TLSSetMethodName,
SetGlobalOperatorLinkMethod: consts.GlobalTracerSetMethodName,
GetGlobalOperatorLinkMethod: consts.GlobalTracerGetMethodName,
GetGoroutineIDLinkMethod: consts.CurrentGoroutineIDGetMethodName,
+ GetInitNotifyLinkMethod: consts.GlobalTracerInitGetNotifyMethodName,
}))
}
diff --git a/tools/go-agent/instrument/consts/directive.go b/tools/go-agent/instrument/consts/directive.go
index eac333d..18e3257 100644
--- a/tools/go-agent/instrument/consts/directive.go
+++ b/tools/go-agent/instrument/consts/directive.go
@@ -30,4 +30,7 @@ var (
// DirecitveNoCopy is the directive for define current go file would not copy to the framework
DirecitveNoCopy = "//skywalking:nocopy"
+
+ // DirectiveConfig is the directive for define the config variable
+ DirectiveConfig = "//skywalking:config"
)
diff --git a/tools/go-agent/instrument/consts/operator.go b/tools/go-agent/instrument/consts/operator.go
index 2b4ffc6..a7c2eba 100644
--- a/tools/go-agent/instrument/consts/operator.go
+++ b/tools/go-agent/instrument/consts/operator.go
@@ -18,15 +18,18 @@
package consts
const (
- GlobalTracerFieldName = "globalSkyWalkingOperator"
- GlobalLoggerFieldName = "globalSkyWalkingLogger"
+ GlobalTracerFieldName = "globalSkyWalkingOperator"
+ GlobalLoggerFieldName = "globalSkyWalkingLogger"
+ GlobalTracerInitNotifyFieldName = "globalSkyWalkingInitNotify"
GlobalTracerSnapshotInterface = "skywalkingGoroutineSnapshotCreator"
- GlobalTracerSetMethodName = "_skywalking_set_global_operator"
- GlobalTracerGetMethodName = "_skywalking_get_global_operator"
- GlobalLoggerSetMethodName = "_skywalking_set_global_logger"
- GlobalLoggerGetMethodName = "_skywalking_get_global_logger"
+ GlobalTracerSetMethodName = "_skywalking_set_global_operator"
+ GlobalTracerGetMethodName = "_skywalking_get_global_operator"
+ GlobalLoggerSetMethodName = "_skywalking_set_global_logger"
+ GlobalLoggerGetMethodName = "_skywalking_get_global_logger"
+ GlobalTracerInitAppendNotifyMethodName = "_skywalking_global_init_append_notify"
+ GlobalTracerInitGetNotifyMethodName = "_skywalking_global_init_get_notify"
CurrentGoroutineIDGetMethodName = "_skywalking_get_goid"
)
diff --git a/tools/go-agent/instrument/plugins/enhance_config.go b/tools/go-agent/instrument/plugins/enhance_config.go
new file mode 100644
index 0000000..d336692
--- /dev/null
+++ b/tools/go-agent/instrument/plugins/enhance_config.go
@@ -0,0 +1,244 @@
+// 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 plugins
+
+import (
+ "fmt"
+ "go/token"
+ "reflect"
+ "strings"
+ "unicode"
+
+ "github.com/apache/skywalking-go/plugins/core/instrument"
+ "github.com/apache/skywalking-go/tools/go-agent/config"
+ "github.com/apache/skywalking-go/tools/go-agent/instrument/consts"
+ "github.com/apache/skywalking-go/tools/go-agent/tools"
+
+ "github.com/dave/dst"
+)
+
+var (
+ configFieldKeyTag = "config"
+ toolsImports = "github.com/apache/skywalking-go/plugins/core/tools"
+)
+
+type ConfigEnhance struct {
+ VarSpec *dst.ValueSpec
+ ConfigPrefix string
+ VarName string
+ Fields []*ConfigField
+ GenerateConfigFunName string
+}
+
+func NewConfigEnhance(varSpec *dst.ValueSpec, node dst.Node, inst instrument.Instrument) (*ConfigEnhance, error) {
+ configDirective := tools.FindDirective(node, consts.DirectiveConfig)
+ if configDirective == "" {
+ return nil, fmt.Errorf("cannot find the config directive")
+ }
+ enhance := &ConfigEnhance{VarSpec: varSpec}
+ info := strings.SplitN(configDirective, " ", 2)
+ if len(info) == 2 {
+ enhance.ConfigPrefix = info[1]
+ } else {
+ // default using the plugin name as the prefix
+ enhance.ConfigPrefix = inst.Name()
+ }
+ enhance.VarName = varSpec.Names[0].Name
+
+ varType, ok := varSpec.Type.(*dst.StructType)
+ if !ok {
+ return nil, fmt.Errorf("the config type of %s under %s plugin must be a structure", varSpec.Names[0].Name, inst.Name())
+ }
+ fs, err := NewConfigFields(varType)
+ if err != nil {
+ return nil, fmt.Errorf("analyzing the config %s under %s plugin failed: %v", varSpec.Names[0].Name, inst.Name(), err)
+ }
+ enhance.Fields = fs
+
+ enhance.GenerateConfigFunName = fmt.Sprintf("initConfig%s", enhance.VarName)
+ return enhance, nil
+}
+
+func (e *ConfigEnhance) PackageName() string {
+ return ""
+}
+
+func (e *ConfigEnhance) BuildImports(decl *dst.GenDecl) {
+ for _, spec := range decl.Specs {
+ imp, ok := spec.(*dst.ImportSpec)
+ if !ok {
+ continue
+ }
+ if imp.Path.Value == fmt.Sprintf("%q", toolsImports) {
+ return
+ }
+ }
+ decl.Specs = append(decl.Specs, &dst.ImportSpec{
+ Path: &dst.BasicLit{
+ Kind: token.STRING,
+ Value: fmt.Sprintf("%q", toolsImports),
+ },
+ })
+}
+
+func (e *ConfigEnhance) BuildForDelegator() []dst.Decl {
+ fun := &dst.FuncDecl{
+ Name: dst.NewIdent(e.GenerateConfigFunName),
+ Type: &dst.FuncType{},
+ Body: &dst.BlockStmt{},
+ }
+ for _, field := range e.Fields {
+ fun.Body.List = append(fun.Body.List, field.GenerateAssignFieldValue(e.VarName, []string{}, []string{e.ConfigPrefix})...)
+ }
+ return []dst.Decl{fun}
+}
+
+func (e *ConfigEnhance) ReplaceFileContent(path, content string) string {
+ return content
+}
+
+func (e *ConfigEnhance) InitFunctions() []*EnhanceInitFunction {
+ return []*EnhanceInitFunction{NewEnhanceInitFunction(e.GenerateConfigFunName, true)}
+}
+
+type ConfigField struct {
+ Name string
+ Type string
+ Key string
+ ChildFields []*ConfigField
+}
+
+func NewConfigFields(structType *dst.StructType) ([]*ConfigField, error) {
+ if structType.Fields == nil || len(structType.Fields.List) == 0 {
+ return nil, fmt.Errorf("the config structure must have at least one field")
+ }
+ fields := make([]*ConfigField, 0, len(structType.Fields.List))
+ for _, field := range structType.Fields.List {
+ configField, err := NewConfigField(field)
+ if err != nil {
+ return nil, err
+ }
+ fields = append(fields, configField)
+ }
+
+ return fields, nil
+}
+
+func NewConfigField(f *dst.Field) (*ConfigField, error) {
+ if len(f.Names) == 0 {
+ return nil, fmt.Errorf("the config structure must have named field")
+ }
+ conf := &ConfigField{
+ Name: f.Names[0].Name,
+ }
+ switch t := f.Type.(type) {
+ case *dst.Ident:
+ conf.Type = t.Name
+ case *dst.StructType:
+ fs, err := NewConfigFields(t)
+ if err != nil {
+ return nil, err
+ }
+ conf.ChildFields = fs
+ default:
+ return nil, fmt.Errorf("the config structure field %s type %T is not supported", conf.Name, t)
+ }
+ conf.initFlags(f.Tag)
+ return conf, nil
+}
+
+func (f *ConfigField) initFlags(flagLit *dst.BasicLit) {
+ if flagLit == nil {
+ f.Key = f.generateDefaultKey(f.Name)
+ return
+ }
+
+ tag := reflect.StructTag(flagLit.Value)
+ value, ok := tag.Lookup(configFieldKeyTag)
+ if ok {
+ f.Key = value
+ return
+ }
+
+ f.Key = f.generateDefaultKey(f.Name)
+}
+
+func (f *ConfigField) generateDefaultKey(keyName string) string {
+ var result []rune
+ for i, r := range keyName {
+ if unicode.IsUpper(r) {
+ if i != 0 {
+ result = append(result, '_')
+ }
+ result = append(result, []rune(strings.ToLower(string(r)))...)
+ } else {
+ result = append(result, r)
+ }
+ }
+ return string(result)
+}
+
+func (f *ConfigField) GenerateAssignFieldValue(varName string, field, path []string) []dst.Stmt {
+ field = append(field, f.Name)
+ path = append(path, f.Key)
+ if len(f.ChildFields) > 0 {
+ result := make([]dst.Stmt, 0)
+ for _, child := range f.ChildFields {
+ result = append(result, child.GenerateAssignFieldValue(varName, field, path)...)
+ }
+ return result
+ }
+ fieldKeyPathStr := strings.Join(path, ".")
+ fieldPathStr := strings.Join(field, ".")
+ pluginConfig := config.GetConfig().Plugin.Config.ParseToStringValue(path...)
+ if pluginConfig == nil {
+ panic(fmt.Errorf("cannot find the config %s", fieldKeyPathStr))
+ }
+ resultType := f.Type
+ getFromEnvStr := ""
+ if pluginConfig.EnvKey != "" {
+ getFromEnvStr = fmt.Sprintf("if v := tools.GetEnvValue(%q); v != \"\" { result = v };", pluginConfig.EnvKey)
+ }
+ parseResStr := ""
+ parseErrorMessage := "cannot parse the config " + fieldKeyPathStr + ": err.Error()"
+ switch f.Type {
+ case "string":
+ parseResStr = "return result"
+ case "bool":
+ parseResStr = "return tools.ParseBool(result)"
+ case "int":
+ parseResStr = "if v, err := tools.Atoi(result); err != nil { panic(" + parseErrorMessage + ") } else { return v }"
+ case "int16":
+ parseResStr = "if v, err := tools.ParseInt(result, 10, 16); err != nil { panic(" + parseErrorMessage + ") } else { return v }"
+ case "int32":
+ parseResStr = "if v, err := tools.ParseInt(result, 10, 32); err != nil { panic(" + parseErrorMessage + ") } else { return v }"
+ case "int64":
+ parseResStr = "if v, err := tools.ParseInt(result, 10, 64); err != nil { panic(" + parseErrorMessage + ") } else { return v }"
+ case "float32":
+ parseResStr = "if v, err := tools.ParseFloat(result, 32); err != nil { panic(" + parseErrorMessage + ") } else { return v }"
+ case "float64":
+ parseResStr = "if v, err := tools.ParseFloat(result, 64); err != nil { panic(" + parseErrorMessage + ") } else { return v }"
+ case "float":
+ parseResStr = "if v, err := tools.ParseFloat(result, 64); err != nil { panic(" + parseErrorMessage + ") } else { return v }"
+ default:
+ panic("unsupported config type " + f.Type)
+ }
+ stmtStr := fmt.Sprintf("%s.%s = func () %s { result := %q; %s%s }()",
+ varName, fieldPathStr, resultType, pluginConfig.Default, getFromEnvStr, parseResStr)
+ return tools.GoStringToStats(stmtStr)
+}
diff --git a/tools/go-agent/instrument/plugins/enhance_instance.go b/tools/go-agent/instrument/plugins/enhance_instance.go
index 887c630..1b31f77 100644
--- a/tools/go-agent/instrument/plugins/enhance_instance.go
+++ b/tools/go-agent/instrument/plugins/enhance_instance.go
@@ -115,3 +115,7 @@ func (i *InstanceEnhance) BuildForDelegator() []dst.Decl {
func (i *InstanceEnhance) ReplaceFileContent(path, content string) string {
return content
}
+
+func (i *InstanceEnhance) InitFunctions() []*EnhanceInitFunction {
+ return nil
+}
diff --git a/tools/go-agent/instrument/plugins/enhance_method.go b/tools/go-agent/instrument/plugins/enhance_method.go
index 59b1acd..4fdf38a 100644
--- a/tools/go-agent/instrument/plugins/enhance_method.go
+++ b/tools/go-agent/instrument/plugins/enhance_method.go
@@ -228,3 +228,7 @@ func (m *MethodEnhance) ReplaceFileContent(path, content string) string {
}
return content
}
+
+func (m *MethodEnhance) InitFunctions() []*EnhanceInitFunction {
+ return nil
+}
diff --git a/tools/go-agent/instrument/plugins/instrument.go b/tools/go-agent/instrument/plugins/instrument.go
index 1eb0c37..b60aad8 100644
--- a/tools/go-agent/instrument/plugins/instrument.go
+++ b/tools/go-agent/instrument/plugins/instrument.go
@@ -21,12 +21,15 @@ import (
"bytes"
"embed"
"fmt"
+ "go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"strings"
+ "github.com/pkg/errors"
+
"github.com/apache/skywalking-go/plugins/core"
"github.com/apache/skywalking-go/plugins/core/instrument"
"github.com/apache/skywalking-go/tools/go-agent/config"
@@ -36,6 +39,7 @@ import (
"github.com/apache/skywalking-go/tools/go-agent/tools"
"github.com/dave/dst"
+ "github.com/dave/dst/decorator"
"github.com/dave/dst/dstutil"
"github.com/sirupsen/logrus"
@@ -68,6 +72,16 @@ type Enhance interface {
BuildImports(decl *dst.GenDecl)
BuildForDelegator() []dst.Decl
ReplaceFileContent(path, content string) string
+ InitFunctions() []*EnhanceInitFunction
+}
+
+type EnhanceInitFunction struct {
+ Name string
+ AfterCoreInit bool
+}
+
+func NewEnhanceInitFunction(funcName string, afterCoreInit bool) *EnhanceInitFunction {
+ return &EnhanceInitFunction{Name: funcName, AfterCoreInit: afterCoreInit}
}
func (i *Instrument) CouldHandle(opts *api.CompileOptions) bool {
@@ -183,17 +197,18 @@ func (i *Instrument) WriteExtraFiles(basePath string) ([]string, error) {
}
i.extraFilesWrote = true
- packageName := i.enhancements[0].PackageName()
+ packageName := ""
+ for _, e := range i.enhancements {
+ if e.PackageName() != "" {
+ packageName = e.PackageName()
+ break
+ }
+ }
context := rewrite.NewContext(i.compileOpts.Package, packageName)
var results = make([]string, 0)
- // write delegator file
var files []string
var err error
- if files, err = i.writeDelegatorFile(context, basePath); err != nil {
- return nil, err
- }
- results = append(results, files...)
// copy basic support files(operators)
if files, err = i.copyOperatorsFS(context, basePath, packageName); err != nil {
@@ -206,6 +221,12 @@ func (i *Instrument) WriteExtraFiles(basePath string) ([]string, error) {
return nil, err
}
results = append(results, files...)
+
+ // write delegator file
+ if files, err = i.writeDelegatorFile(context, basePath); err != nil {
+ return nil, err
+ }
+ results = append(results, files...)
return results, nil
}
@@ -242,7 +263,7 @@ func (i *Instrument) copyFrameworkFS(context *rewrite.Context, compilePkgFullPat
if err1 != nil {
return nil, err1
}
- if shouldContinue, err1 := i.fileShouldBeIgnore(context, readFile); err1 != nil {
+ if shouldContinue, err1 := i.processDirectiveInFile(context, readFile); err1 != nil {
return nil, err1
} else if shouldContinue {
continue
@@ -258,7 +279,7 @@ func (i *Instrument) copyFrameworkFS(context *rewrite.Context, compilePkgFullPat
return rewrited, nil
}
-func (i *Instrument) fileShouldBeIgnore(ctx *rewrite.Context, fileContent []byte) (bool, error) {
+func (i *Instrument) processDirectiveInFile(ctx *rewrite.Context, fileContent []byte) (bool, error) {
// ignore nocopy files
if bytes.Contains(fileContent, []byte(consts.DirecitveNoCopy)) {
return true, nil
@@ -271,9 +292,42 @@ func (i *Instrument) fileShouldBeIgnore(ctx *rewrite.Context, fileContent []byte
}
return true, nil
}
+ // if the file contains the plugin config, then generate the configuration
+ if bytes.Contains(fileContent, []byte(consts.DirectiveConfig)) {
+ if err := i.processPluginConfig(fileContent); err != nil {
+ return false, err
+ }
+ return false, nil
+ }
return false, nil
}
+func (i *Instrument) processPluginConfig(fileContent []byte) error {
+ configFile, err := decorator.ParseFile(nil, "config.go", fileContent, parser.ParseComments)
+ if err != nil {
+ return err
+ }
+ for _, decl := range configFile.Decls {
+ genDecl, ok := decl.(*dst.GenDecl)
+ // if not var declaration or not contains config directive, then ignore
+ if !ok || genDecl.Tok != token.VAR || !tools.ContainsDirective(genDecl, consts.DirectiveConfig) {
+ continue
+ }
+ for _, spec := range genDecl.Specs {
+ valueSpec, ok := spec.(*dst.ValueSpec)
+ if !ok {
+ return errors.New(fmt.Sprintf("invalid type of spec for config: %T", spec))
+ }
+ enhance, err := NewConfigEnhance(valueSpec, genDecl, i.realInst)
+ if err != nil {
+ return err
+ }
+ i.enhancements = append(i.enhancements, enhance)
+ }
+ }
+ return nil
+}
+
func (i *Instrument) copyOperatorsFS(context *rewrite.Context, baseDir, packageName string) ([]string, error) {
result := make([]string, 0)
var debugBaseDir string
@@ -323,6 +377,9 @@ import (
//go:linkname {{.OperatorGetLinkMethod}} {{.OperatorGetLinkMethod}}
var {{.OperatorGetLinkMethod}} func() interface{}
+//go:linkname {{.OperatorAppendInitNotifyLinkMethod}} {{.OperatorAppendInitNotifyLinkMethod}}
+var {{.OperatorAppendInitNotifyLinkMethod}} func(func())
+
func init() {
if {{.OperatorGetLinkMethod}} != nil {
{{.OperatorGetRealMethod}} = func() {{.OperatorTypeName}} {
@@ -336,17 +393,24 @@ func init() {
return nil
}
}
+ if {{.OperatorAppendInitNotifyLinkMethod}} != nil {
+ {{.OperatorAppendInitNotifyRealMethod}} = {{.OperatorAppendInitNotifyLinkMethod}}
+ }
}
`, struct {
- PackageName string
- OperatorGetLinkMethod string
- OperatorGetRealMethod string
- OperatorTypeName string
+ PackageName string
+ OperatorGetLinkMethod string
+ OperatorGetRealMethod string
+ OperatorTypeName string
+ OperatorAppendInitNotifyLinkMethod string
+ OperatorAppendInitNotifyRealMethod string
}{
- PackageName: packageName,
- OperatorGetLinkMethod: consts.GlobalTracerGetMethodName,
- OperatorGetRealMethod: rewrite.GlobalOperatorRealGetMethodName,
- OperatorTypeName: rewrite.GlobalOperatorTypeName,
+ PackageName: packageName,
+ OperatorGetLinkMethod: consts.GlobalTracerGetMethodName,
+ OperatorGetRealMethod: rewrite.GlobalOperatorRealGetMethodName,
+ OperatorTypeName: rewrite.GlobalOperatorTypeName,
+ OperatorAppendInitNotifyLinkMethod: consts.GlobalTracerInitAppendNotifyMethodName,
+ OperatorAppendInitNotifyRealMethod: rewrite.GlobalOperatorRealAppendTracerInitNotify,
}),
})
if err != nil {
@@ -368,6 +432,27 @@ func (i *Instrument) writeDelegatorFile(ctx *rewrite.Context, basePath string) (
}
file.Decls = append(file.Decls, importsHeader)
+ // append init function
+ initFunc := &dst.FuncDecl{
+ Name: dst.NewIdent("init"),
+ Type: &dst.FuncType{},
+ Body: &dst.BlockStmt{},
+ }
+ for _, enhance := range i.enhancements {
+ funcs := enhance.InitFunctions()
+ for _, fun := range funcs {
+ if fun.AfterCoreInit {
+ initFunc.Body.List = append(initFunc.Body.List, &dst.ExprStmt{X: &dst.CallExpr{
+ Fun: dst.NewIdent(rewrite.GlobalOperatorRealAppendTracerInitNotify),
+ Args: []dst.Expr{dst.NewIdent(fun.Name)},
+ }})
+ } else {
+ initFunc.Body.List = append(initFunc.Body.List, &dst.ExprStmt{X: &dst.CallExpr{Fun: dst.NewIdent(fun.Name)}})
+ }
+ }
+ }
+ file.Decls = append(file.Decls, initFunc)
+
// append other decls
for _, enhance := range i.enhancements {
file.Decls = append(file.Decls, enhance.BuildForDelegator()...)
diff --git a/tools/go-agent/instrument/plugins/rewrite/context.go b/tools/go-agent/instrument/plugins/rewrite/context.go
index c8dd04a..3bd866f 100644
--- a/tools/go-agent/instrument/plugins/rewrite/context.go
+++ b/tools/go-agent/instrument/plugins/rewrite/context.go
@@ -91,9 +91,9 @@ func (c *Context) IncludeNativeOrReferenceGenerateFiles(content string) error {
dstutil.Apply(parseFile, func(cursor *dstutil.Cursor) bool {
switch n := cursor.Node().(type) {
case *dst.TypeSpec:
- c.analyzeNativeOrReferenceFields(cursor.Node(), cursor.Parent().Decorations(), n.Name.Name)
+ c.analyzeNativeOrReferenceFields(cursor.Node(), cursor.Parent(), n.Name.Name)
case *dst.FuncDecl:
- c.analyzeNativeOrReferenceFields(cursor.Node(), n.Decorations(), n.Name.Name)
+ c.analyzeNativeOrReferenceFields(cursor.Node(), cursor.Node(), n.Name.Name)
}
return true
}, func(cursor *dstutil.Cursor) bool {
@@ -102,40 +102,31 @@ func (c *Context) IncludeNativeOrReferenceGenerateFiles(content string) error {
return nil
}
-func (c *Context) analyzeNativeOrReferenceFields(node dst.Node, decorations *dst.NodeDecs, currentDeclareName string) {
- allComments := decorations.Start.All()
- for _, comment := range allComments {
- if name, typeName, nativeDirective := c.analyzeNativeTypeDirective(comment); nativeDirective {
- c.rewriteMapping.addNativeTypeMapping(currentDeclareName, name, typeName)
- break
- }
- if name, typeName, referenceGenerate := c.analyzeReferenceGenerateDirective(comment); referenceGenerate {
- c.rewriteMapping.addReferenceGenerateMapping(node, currentDeclareName, name, typeName)
- break
- }
+func (c *Context) analyzeNativeOrReferenceFields(node, decorationNode dst.Node, currentDeclareName string) {
+ if native := tools.FindDirective(decorationNode, consts.DirectiveNative); native != "" {
+ name, typeName := c.analyzeNativeTypeDirective(native)
+ c.rewriteMapping.addNativeTypeMapping(currentDeclareName, name, typeName)
+ }
+ if reference := tools.FindDirective(decorationNode, consts.DirectiveReferenceGenerate); reference != "" {
+ name, typeName := c.analyzeReferenceGenerateDirective(reference)
+ c.rewriteMapping.addReferenceGenerateMapping(node, currentDeclareName, name, typeName)
}
}
-func (c *Context) analyzeNativeTypeDirective(comment string) (packageName, typeName string, isNativeDirective bool) {
- if !strings.HasPrefix(comment, consts.DirectiveNative) {
- return "", "", false
- }
+func (c *Context) analyzeNativeTypeDirective(comment string) (packageName, typeName string) {
info := strings.SplitN(comment, " ", 3)
if len(info) != 3 {
panic(fmt.Sprintf("failure to parse the skywalking:native directive: %s", comment))
}
- return info[1], info[2], true
+ return info[1], info[2]
}
-func (c *Context) analyzeReferenceGenerateDirective(comment string) (packageName, typeName string, isNativeDirective bool) {
- if !strings.HasPrefix(comment, consts.DirectiveReferenceGenerate) {
- return "", "", false
- }
+func (c *Context) analyzeReferenceGenerateDirective(comment string) (packageName, typeName string) {
info := strings.SplitN(comment, " ", 3)
if len(info) != 3 {
panic(fmt.Sprintf("failure to parse the skywalking:native directive: %s", comment))
}
- return info[1], info[2], true
+ return info[1], info[2]
}
func (c *Context) enhanceVarNameWhenRewrite(fieldType dst.Expr) (oldName, replacedName string) {
diff --git a/tools/go-agent/instrument/plugins/rewrite/func.go b/tools/go-agent/instrument/plugins/rewrite/func.go
index 4ba8926..fe3a366 100644
--- a/tools/go-agent/instrument/plugins/rewrite/func.go
+++ b/tools/go-agent/instrument/plugins/rewrite/func.go
@@ -25,6 +25,7 @@ import (
"github.com/dave/dst"
"github.com/dave/dst/dstutil"
+ "github.com/apache/skywalking-go/tools/go-agent/instrument/consts"
"github.com/apache/skywalking-go/tools/go-agent/tools"
)
@@ -32,16 +33,24 @@ var (
GlobalOperatorRealSetMethodName = VarPrefix + "OperatorSetOperator"
GlobalOperatorRealGetMethodName = VarPrefix + "OperatorGetOperator"
+ GlobalOperatorRealAppendTracerInitNotify = VarPrefix + "OperatorAppendInitNotify"
+
GlobalOperatorTypeName = TypePrefix + "OperatorOperator"
)
+func (c *Context) isInitFunc(funcDecl *dst.FuncDecl) bool {
+ return funcDecl.Name.Name == "init" &&
+ (funcDecl.Type.Params == nil || len(funcDecl.Type.Params.List) == 0) &&
+ (funcDecl.Type.Results == nil || len(funcDecl.Type.Results.List) == 0)
+}
+
func (c *Context) Func(funcDecl *dst.FuncDecl, cursor *dstutil.Cursor) {
// only the static method needs rewrite
if funcDecl.Recv == nil {
// if the method name is generated, then ignore to enhance(for adapter)
- if !strings.HasPrefix(funcDecl.Name.Name, GenerateMethodPrefix) {
+ if !strings.HasPrefix(funcDecl.Name.Name, GenerateMethodPrefix) && !c.isInitFunc(funcDecl) {
prefix := StaticMethodPrefix
- if ContainsPublicDirective(funcDecl.Decorations()) {
+ if tools.ContainsDirective(funcDecl, consts.DirectivePublic) {
prefix = c.titleCase.String(GenerateMethodPrefix)
}
funcDecl.Name = dst.NewIdent(fmt.Sprintf("%s%s%s", prefix, c.currentPackageTitle, funcDecl.Name.Name))
diff --git a/tools/go-agent/instrument/plugins/rewrite/type.go b/tools/go-agent/instrument/plugins/rewrite/type.go
index 570b5b5..5ff9563 100644
--- a/tools/go-agent/instrument/plugins/rewrite/type.go
+++ b/tools/go-agent/instrument/plugins/rewrite/type.go
@@ -20,6 +20,9 @@ package rewrite
import (
"fmt"
+ "github.com/apache/skywalking-go/tools/go-agent/instrument/consts"
+ "github.com/apache/skywalking-go/tools/go-agent/tools"
+
"github.com/dave/dst"
)
@@ -62,7 +65,7 @@ func (c *Context) Type(tp *dst.TypeSpec, parent dst.Node, onlyName bool) {
func (c *Context) generateTypePrefix(parent dst.Node) string {
prefix := TypePrefix
- if parent == nil || !ContainsPublicDirective(parent.Decorations()) {
+ if parent == nil || !tools.ContainsDirective(parent, consts.DirectivePublic) {
return prefix
}
return c.titleCase.String(TypePrefix)
diff --git a/tools/go-agent/instrument/runtime/instrument.go b/tools/go-agent/instrument/runtime/instrument.go
index dcdbde2..9c56380 100644
--- a/tools/go-agent/instrument/runtime/instrument.go
+++ b/tools/go-agent/instrument/runtime/instrument.go
@@ -111,6 +111,8 @@ var {{.GlobalTracerFieldName}} interface{}
var {{.GlobalLoggerFieldName}} interface{}
+var {{.GlobalTracerInitNotifyFieldName}} = make([]func(), 0)
+
//go:linkname {{.TLSGetMethod}} {{.TLSGetMethod}}
var {{.TLSGetMethod}} = _skywalking_tls_get_impl
@@ -132,6 +134,12 @@ var {{.GlobalLoggerGetMethodName}} = _skywalking_global_logger_get_impl
//go:linkname {{.GoroutineIDGetterMethodName}} {{.GoroutineIDGetterMethodName}}
var {{.GoroutineIDGetterMethodName}} = _skywalking_get_goid_impl
+//go:linkname {{.GlobalTracerInitNotifyMethodName}} {{.GlobalTracerInitNotifyMethodName}}
+var {{.GlobalTracerInitNotifyMethodName}} = _skywalking_global_tracer_init_notify_impl
+
+//go:linkname {{.GlobalTracerInitNotifyGetMethodName}} {{.GlobalTracerInitNotifyGetMethodName}}
+var {{.GlobalTracerInitNotifyGetMethodName}} = _skywalking_global_tracer_init_get_notify_impl
+
//go:nosplit
func _skywalking_get_goid_impl() int64 {
return {{.GoroutineIDCaster}}
@@ -167,6 +175,16 @@ func _skywalking_global_logger_get_impl() interface{} {
return {{.GlobalLoggerFieldName}}
}
+//go:nosplit
+func _skywalking_global_tracer_init_notify_impl(fun func()) {
+ {{.GlobalTracerInitNotifyFieldName}} = append({{.GlobalTracerInitNotifyFieldName}}, fun)
+}
+
+//go:nosplit
+func _skywalking_global_tracer_init_get_notify_impl() []func() {
+ return {{.GlobalTracerInitNotifyFieldName}}
+}
+
type ContextSnapshoter interface {
TakeSnapShot(val interface{}) interface{}
}
@@ -181,31 +199,37 @@ func goroutineChange(tls interface{}) interface{} {
return tls
}
`, struct {
- TLSFiledName string
- TLSGetMethod string
- TLSSetMethod string
- GlobalTracerFieldName string
- GlobalTracerSnapshotInterface string
- GlobalOperatorSetMethodName string
- GlobalOperatorGetMethodName string
- GlobalLoggerFieldName string
- GlobalLoggerSetMethodName string
- GlobalLoggerGetMethodName string
- GoroutineIDGetterMethodName string
- GoroutineIDCaster string
+ TLSFiledName string
+ TLSGetMethod string
+ TLSSetMethod string
+ GlobalTracerFieldName string
+ GlobalTracerSnapshotInterface string
+ GlobalOperatorSetMethodName string
+ GlobalOperatorGetMethodName string
+ GlobalLoggerFieldName string
+ GlobalLoggerSetMethodName string
+ GlobalLoggerGetMethodName string
+ GoroutineIDGetterMethodName string
+ GoroutineIDCaster string
+ GlobalTracerInitNotifyFieldName string
+ GlobalTracerInitNotifyMethodName string
+ GlobalTracerInitNotifyGetMethodName string
}{
- TLSFiledName: consts.TLSFieldName,
- TLSGetMethod: consts.TLSGetMethodName,
- TLSSetMethod: consts.TLSSetMethodName,
- GlobalTracerFieldName: consts.GlobalTracerFieldName,
- GlobalTracerSnapshotInterface: consts.GlobalTracerSnapshotInterface,
- GlobalOperatorSetMethodName: consts.GlobalTracerSetMethodName,
- GlobalOperatorGetMethodName: consts.GlobalTracerGetMethodName,
- GlobalLoggerFieldName: consts.GlobalLoggerFieldName,
- GlobalLoggerSetMethodName: consts.GlobalLoggerSetMethodName,
- GlobalLoggerGetMethodName: consts.GlobalLoggerGetMethodName,
- GoroutineIDGetterMethodName: consts.CurrentGoroutineIDGetMethodName,
- GoroutineIDCaster: r.generateCastGoID("getg().m.curg.goid"),
+ TLSFiledName: consts.TLSFieldName,
+ TLSGetMethod: consts.TLSGetMethodName,
+ TLSSetMethod: consts.TLSSetMethodName,
+ GlobalTracerFieldName: consts.GlobalTracerFieldName,
+ GlobalTracerSnapshotInterface: consts.GlobalTracerSnapshotInterface,
+ GlobalOperatorSetMethodName: consts.GlobalTracerSetMethodName,
+ GlobalOperatorGetMethodName: consts.GlobalTracerGetMethodName,
+ GlobalLoggerFieldName: consts.GlobalLoggerFieldName,
+ GlobalLoggerSetMethodName: consts.GlobalLoggerSetMethodName,
+ GlobalLoggerGetMethodName: consts.GlobalLoggerGetMethodName,
+ GoroutineIDGetterMethodName: consts.CurrentGoroutineIDGetMethodName,
+ GoroutineIDCaster: r.generateCastGoID("getg().m.curg.goid"),
+ GlobalTracerInitNotifyFieldName: consts.GlobalTracerInitNotifyFieldName,
+ GlobalTracerInitNotifyMethodName: consts.GlobalTracerInitAppendNotifyMethodName,
+ GlobalTracerInitNotifyGetMethodName: consts.GlobalTracerInitGetNotifyMethodName,
}),
})
}
diff --git a/tools/go-agent/instrument/plugins/rewrite/directive.go b/tools/go-agent/tools/directive.go
similarity index 65%
rename from tools/go-agent/instrument/plugins/rewrite/directive.go
rename to tools/go-agent/tools/directive.go
index a1557ee..c05eb66 100644
--- a/tools/go-agent/instrument/plugins/rewrite/directive.go
+++ b/tools/go-agent/tools/directive.go
@@ -15,22 +15,34 @@
// specific language governing permissions and limitations
// under the License.
-package rewrite
+package tools
import (
- "github.com/apache/skywalking-go/tools/go-agent/instrument/consts"
+ "strings"
"github.com/dave/dst"
)
-func ContainsPublicDirective(desc *dst.NodeDecs) bool {
- if desc == nil {
+func ContainsDirective(desc dst.Node, directive string) bool {
+ if desc == nil || desc.Decorations() == nil {
return false
}
- for _, s := range desc.Start.All() {
- if s == consts.DirectivePublic {
+ for _, s := range desc.Decorations().Start.All() {
+ if strings.HasPrefix(s, directive) {
return true
}
}
return false
}
+
+func FindDirective(desc dst.Node, directive string) string {
+ if desc == nil || desc.Decorations() == nil {
+ return ""
+ }
+ for _, s := range desc.Decorations().Start.All() {
+ if strings.HasPrefix(s, directive) {
+ return s
+ }
+ }
+ return ""
+}