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 ""
+}