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/04/12 16:04:20 UTC

[skywalking-go] 01/01: Implement the Agent core

This is an automated email from the ASF dual-hosted git repository.

liuhan pushed a commit to branch agent-core
in repository https://gitbox.apache.org/repos/asf/skywalking-go.git

commit 4a5c397b67e5c708301b9dbf2729b44d29ff6bae
Author: Mrproliu <74...@qq.com>
AuthorDate: Thu Apr 13 00:04:05 2023 +0800

    Implement the Agent core
---
 .github/workflows/skywalking-go.yaml               |  29 +-
 .licenserc.yaml                                    |  39 +++
 Makefile                                           |  76 +++++
 api.go                                             |  18 +
 go.mod                                             |  16 +
 imports.go                                         |  23 ++
 plugins/core/context.go                            |  81 +++++
 plugins/core/go.mod                                |  26 ++
 plugins/core/intercepter.go                        |  38 +++
 plugins/core/propagating.go                        | 246 ++++++++++++++
 plugins/core/sampler.go                            | 156 +++++++++
 plugins/core/sampler_test.go                       | 101 ++++++
 plugins/core/span.go                               |  74 +++++
 plugins/core/span_default.go                       | 133 ++++++++
 plugins/core/span_noop.go                          |  83 +++++
 plugins/core/span_tracing.go                       | 361 +++++++++++++++++++++
 plugins/core/test_base.go                          |  61 ++++
 plugins/core/tool.go                               |  44 +++
 plugins/core/tracer.go                             |  42 +++
 plugins/core/tracer_state.go                       |  41 +++
 plugins/core/tracing/api.go                        | 252 ++++++++++++++
 plugins/core/tracing/api_test.go                   | 330 +++++++++++++++++++
 reporter/config_discovery.go                       |  81 +++++
 reporter/doc.go                                    |  21 ++
 reporter/grpc.go                                   | 306 +++++++++++++++++
 reporter/grpc_opts.go                              |  22 ++
 reporter/reporter.go                               |  94 ++++++
 skylog/logger.go                                   |  37 +++
 tools/go-agent-enhance/cmd/helper.go               |  40 +++
 tools/go-agent-enhance/cmd/main.go                 |  73 +++++
 tools/go-agent-enhance/go.mod                      |  11 +
 tools/go-agent-enhance/go.sum                      |  21 ++
 tools/go-agent-enhance/instrument/api/flags.go     |  33 ++
 .../go-agent-enhance/instrument/api/instrument.go  |  31 ++
 tools/go-agent-enhance/instrument/instrument.go    | 188 +++++++++++
 .../instrument/runtime/instrument.go               | 136 ++++++++
 tools/go-agent-enhance/tools/enhancement.go        | 102 ++++++
 tools/go-agent-enhance/tools/files.go              |  36 ++
 tools/go-agent-enhance/tools/flags.go              | 102 ++++++
 tools/go-agent-enhance/tools/logger.go             |  36 ++
 tools/go-agent-enhance/tools/templetes.go          |  36 ++
 41 files changed, 3675 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/skywalking-go.yaml b/.github/workflows/skywalking-go.yaml
index 134fe38..ac8bb26 100644
--- a/.github/workflows/skywalking-go.yaml
+++ b/.github/workflows/skywalking-go.yaml
@@ -20,11 +20,38 @@ on:
   pull_request:
 
 jobs:
+  build:
+    name: Build and Test
+    runs-on: ubuntu-latest
+    timeout-minutes: 60
+    steps:
+      - name: Set up Go 1.18
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.18
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@v2
+        with:
+          submodules: true
+      - name: Check License
+        uses: apache/skywalking-eyes@9bd5feb86b5817aa6072b008f9866a2c3bbc8587
+      - name: Get dependencies
+        run: make deps
+      - name: Test
+        run: make test
+      - name: Lint
+        run: make lint
+
   required:
     if: always()
     name: Required
+    needs:
+      - build
     runs-on: ubuntu-latest
     timeout-minutes: 10
     steps:
       - name: Merge Requirement
-        run: echo "success"
\ No newline at end of file
+        run: |
+          if [[ ${{ needs.build.result }} != 'success' ]]; then
+            exit -1
+          fi
\ No newline at end of file
diff --git a/.licenserc.yaml b/.licenserc.yaml
new file mode 100644
index 0000000..1a02b30
--- /dev/null
+++ b/.licenserc.yaml
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+header:
+  license:
+    spdx-id: Apache-2.0
+    copyright-owner: Apache Software Foundation
+
+  paths-ignore:
+    - '**/*.md'
+    - 'licenses'
+    - '**/go.mod'
+    - '**/go.sum'
+    - 'LICENSE'
+    - 'NOTICE'
+    - '.gitignore'
+    - 'dist/LICENSE.tpl'
+
+  comment: on-failure
+
+dependency:
+  files:
+    - go.mod
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..901d68f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,76 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SH = sh
+GO = go
+GIT = git
+GO_PATH = $$($(GO) env GOPATH)
+GO_BUILD = $(GO) build
+GO_GET = $(GO) get
+GO_LINT = $(GO_PATH)/bin/golangci-lint
+
+GO_TEST = $(GO) test
+GO_TEST_LDFLAGS =
+
+REPODIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))/
+
+SHELL = /bin/bash
+
+deps:
+	$(GO_GET) -v -t -d ./...
+
+linter:
+	$(GO_LINT) version || curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GO_PATH)/bin v1.50.0
+
+.PHONY: test
+test:
+	echo "mode: atomic" > ${REPODIR}/coverage.txt;
+	@for dir in $$(find . -name go.mod -exec dirname {} \; ); do \
+		cd $$dir; \
+		go test -v -coverprofile=module_coverage.txt -covermode=atomic ./...; \
+		test_status=$$?; \
+		if [ -f module_coverage.txt ]; then \
+			tail -n +2 module_coverage.txt >> ${REPODIR}/coverage.txt; \
+			rm module_coverage.txt; \
+		fi; \
+		cd ${REPODIR}; \
+		if [ $$test_status -ne 0 ]; then \
+			echo "Error occurred during go test, exiting..."; \
+			exit $$test_status; \
+		fi; \
+	done
+
+.PHONY: lint
+lint: linter
+	@for dir in $$(find . -name go.mod -exec dirname {} \; ); do \
+		echo "Linting $$dir"; \
+		(cd $$dir && $(GO_LINT) run -v --timeout 5m ./...); \
+		if [ $$? -ne 0 ]; then \
+			exit 1; \
+		fi; \
+	done
+	$(GO_LINT) run -v --timeout 5m ./...
+
+.PHONY: check
+check:
+	$(GO) mod tidy > /dev/null
+	@if [ ! -z "`git status -s`" ]; then \
+		echo "Following files are not consistent with CI:"; \
+		git status -s; \
+		git diff; \
+		exit 1; \
+	fi
\ No newline at end of file
diff --git a/api.go b/api.go
new file mode 100644
index 0000000..2881d22
--- /dev/null
+++ b/api.go
@@ -0,0 +1,18 @@
+// 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 main
diff --git a/go.mod b/go.mod
index 830b713..79c92f5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,19 @@
 module github.com/apache/skywalking-go
 
 go 1.18
+
+require (
+	github.com/google/uuid v1.3.0
+	github.com/pkg/errors v0.9.1
+	google.golang.org/grpc v1.54.0
+	skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a
+)
+
+require (
+	github.com/golang/protobuf v1.5.2 // indirect
+	golang.org/x/net v0.8.0 // indirect
+	golang.org/x/sys v0.6.0 // indirect
+	golang.org/x/text v0.8.0 // indirect
+	google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
+	google.golang.org/protobuf v1.29.0 // indirect
+)
diff --git a/imports.go b/imports.go
new file mode 100644
index 0000000..ae12a13
--- /dev/null
+++ b/imports.go
@@ -0,0 +1,23 @@
+// 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 main
+
+import (
+	_ "github.com/google/uuid"
+	_ "github.com/pkg/errors"
+)
diff --git a/plugins/core/context.go b/plugins/core/context.go
new file mode 100644
index 0000000..87b7f6e
--- /dev/null
+++ b/plugins/core/context.go
@@ -0,0 +1,81 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+var (
+	GetGLS = func() interface{} { return nil }
+	SetGLS = func(interface{}) {}
+)
+
+type TracingContext struct {
+	ActiveSpan Span
+	Runtime    *RuntimeContext
+}
+
+type RuntimeContext struct {
+	data map[string]interface{}
+}
+
+func NewTracingContext() *TracingContext {
+	return &TracingContext{
+		Runtime: &RuntimeContext{
+			data: make(map[string]interface{}),
+		},
+	}
+}
+
+func (r *RuntimeContext) clone() *RuntimeContext {
+	newData := make(map[string]interface{})
+	for k, v := range r.data {
+		newData[k] = v
+	}
+	return &RuntimeContext{
+		data: newData,
+	}
+}
+
+func GetTracingContext() *TracingContext {
+	gls := GetGLS()
+	if gls == nil {
+		return nil
+	}
+	return gls.(*TracingContext)
+}
+
+func (r *RuntimeContext) Get(key string) interface{} {
+	return r.data[key]
+}
+
+func (r *RuntimeContext) Set(key string, value interface{}) {
+	r.data[key] = value
+}
+
+func TaskTracingContextSnapshot(val interface{}) interface{} {
+	if val == nil {
+		return nil
+	}
+	context, ok := val.(*TracingContext)
+	if !ok {
+		return nil
+	}
+	snapshot := newSnapshotSpan(context.ActiveSpan)
+	return &TracingContext{
+		ActiveSpan: snapshot,
+		Runtime:    context.Runtime.clone(),
+	}
+}
diff --git a/plugins/core/go.mod b/plugins/core/go.mod
new file mode 100644
index 0000000..2ade84b
--- /dev/null
+++ b/plugins/core/go.mod
@@ -0,0 +1,26 @@
+module github.com/apache/skywalking-go/plugins/core
+
+go 1.18
+
+require (
+	github.com/apache/skywalking-go v0.0.0-20230411034404-b9270e98036b
+	github.com/google/uuid v1.3.0
+	github.com/pkg/errors v0.9.1
+	github.com/stretchr/testify v1.8.2
+	skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	golang.org/x/net v0.8.0 // indirect
+	golang.org/x/sys v0.6.0 // indirect
+	golang.org/x/text v0.8.0 // indirect
+	google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
+	google.golang.org/grpc v1.54.0 // indirect
+	google.golang.org/protobuf v1.29.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
+
+replace github.com/apache/skywalking-go => ../../
diff --git a/plugins/core/intercepter.go b/plugins/core/intercepter.go
new file mode 100644
index 0000000..8efa903
--- /dev/null
+++ b/plugins/core/intercepter.go
@@ -0,0 +1,38 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+type Invocation struct {
+	CallerInstance interface{}
+	Args           []interface{}
+
+	Continue bool
+	Return   []interface{}
+
+	Context interface{}
+}
+
+type EnhancedInstance interface {
+	GetSkyWalkingDynamicField() interface{}
+	SetSkyWalkingDynamicField(interface{})
+}
+
+type Interceptor interface {
+	BeforeInvoke(invocation *Invocation) error
+	AfterInvoke(invocation *Invocation, result ...interface{}) error
+}
diff --git a/plugins/core/propagating.go b/plugins/core/propagating.go
new file mode 100644
index 0000000..64b3f30
--- /dev/null
+++ b/plugins/core/propagating.go
@@ -0,0 +1,246 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"encoding/base64"
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/pkg/errors"
+)
+
+const (
+	Header                        string = "sw8"
+	HeaderCorrelation             string = "sw8-correlation"
+	headerLen                     int    = 8
+	splitToken                    string = "-"
+	correlationSplitToken         string = ","
+	correlationKeyValueSplitToken string = ":"
+)
+
+var (
+	errEmptyHeader                = errors.New("empty header")
+	errInsufficientHeaderEntities = errors.New("insufficient header entities")
+)
+
+type SpanContext struct {
+	TraceID               string            `json:"trace_id"`
+	ParentSegmentID       string            `json:"parent_segment_id"`
+	ParentService         string            `json:"parent_service"`
+	ParentServiceInstance string            `json:"parent_service_instance"`
+	ParentEndpoint        string            `json:"parent_endpoint"`
+	AddressUsedAtClient   string            `json:"address_used_at_client"`
+	ParentSpanID          int32             `json:"parent_span_id"`
+	Sample                int8              `json:"sample"`
+	Valid                 bool              `json:"valid"`
+	CorrelationContext    map[string]string `json:"correlation_context"`
+}
+
+func (s *SpanContext) GetTraceID() string {
+	return s.TraceID
+}
+
+func (s *SpanContext) GetParentSegmentID() string {
+	return s.ParentSegmentID
+}
+
+func (s *SpanContext) GetParentService() string {
+	return s.ParentService
+}
+
+func (s *SpanContext) GetParentServiceInstance() string {
+	return s.ParentServiceInstance
+}
+
+func (s *SpanContext) GetParentEndpoint() string {
+	return s.ParentEndpoint
+}
+
+func (s *SpanContext) GetAddressUsedAtClient() string {
+	return s.AddressUsedAtClient
+}
+
+func (s *SpanContext) GetParentSpanID() int32 {
+	return s.ParentSpanID
+}
+
+// Decode all SpanContext data from Extractor
+func (s *SpanContext) Decode(extractor func(headerKey string) (string, error)) error {
+	s.Valid = false
+	// sw8
+	err := s.decode(extractor, Header, s.DecodeSW8)
+	if err != nil {
+		return err
+	}
+
+	// correlation
+	err = s.decode(extractor, HeaderCorrelation, s.DecodeSW8Correlation)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Encode all SpanContext data to Injector
+func (s *SpanContext) Encode(injector func(headerKey, headerValue string) error) error {
+	// sw8
+	err := injector(Header, s.EncodeSW8())
+	if err != nil {
+		return err
+	}
+	// correlation
+	err = injector(HeaderCorrelation, s.EncodeSW8Correlation())
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// DecodeSW6 converts string header to SpanContext
+func (s *SpanContext) DecodeSW8(header string) error {
+	if header == "" {
+		return errEmptyHeader
+	}
+	hh := strings.Split(header, splitToken)
+	if len(hh) < headerLen {
+		return errors.WithMessagef(errInsufficientHeaderEntities, "header string: %s", header)
+	}
+	sample, err := strconv.ParseInt(hh[0], 10, 8)
+	if err != nil {
+		return errors.Errorf("str to int8 error %s", hh[0])
+	}
+	s.Sample = int8(sample)
+	s.TraceID, err = decodeBase64(hh[1])
+	if err != nil {
+		return errors.Wrap(err, "trace id parse error")
+	}
+	s.ParentSegmentID, err = decodeBase64(hh[2])
+	if err != nil {
+		return errors.Wrap(err, "parent segment id parse error")
+	}
+	s.ParentSpanID, err = stringConvertInt32(hh[3])
+	if err != nil {
+		return errors.Wrap(err, "parent span id parse error")
+	}
+	s.ParentService, err = decodeBase64(hh[4])
+	if err != nil {
+		return errors.Wrap(err, "parent service parse error")
+	}
+	s.ParentServiceInstance, err = decodeBase64(hh[5])
+	if err != nil {
+		return errors.Wrap(err, "parent service instance parse error")
+	}
+	s.ParentEndpoint, err = decodeBase64(hh[6])
+	if err != nil {
+		return errors.Wrap(err, "parent endpoint parse error")
+	}
+	s.AddressUsedAtClient, err = decodeBase64(hh[7])
+	if err != nil {
+		return errors.Wrap(err, "network address parse error")
+	}
+	s.Valid = true
+	return nil
+}
+
+// EncodeSW6 converts SpanContext to string header
+func (s *SpanContext) EncodeSW8() string {
+	return strings.Join([]string{
+		fmt.Sprint(s.Sample),
+		encodeBase64(s.TraceID),
+		encodeBase64(s.ParentSegmentID),
+		fmt.Sprint(s.ParentSpanID),
+		encodeBase64(s.ParentService),
+		encodeBase64(s.ParentServiceInstance),
+		encodeBase64(s.ParentEndpoint),
+		encodeBase64(s.AddressUsedAtClient),
+	}, "-")
+}
+
+// DecodeSW8Correlation converts correlation string header to SpanContext
+func (s *SpanContext) DecodeSW8Correlation(header string) error {
+	s.CorrelationContext = make(map[string]string)
+	if header == "" {
+		return nil
+	}
+
+	hh := strings.Split(header, correlationSplitToken)
+	for inx := range hh {
+		keyValues := strings.Split(hh[inx], correlationKeyValueSplitToken)
+		if len(keyValues) != 2 {
+			continue
+		}
+		decodedKey, err := decodeBase64(keyValues[0])
+		if err != nil {
+			continue
+		}
+		decodedValue, err := decodeBase64(keyValues[1])
+		if err != nil {
+			continue
+		}
+
+		s.CorrelationContext[decodedKey] = decodedValue
+	}
+	return nil
+}
+
+// EncodeSW8Correlation converts correlation to string header
+func (s *SpanContext) EncodeSW8Correlation() string {
+	if len(s.CorrelationContext) == 0 {
+		return ""
+	}
+
+	content := make([]string, 0, len(s.CorrelationContext))
+	for k, v := range s.CorrelationContext {
+		content = append(content, fmt.Sprintf("%s%s%s", encodeBase64(k), correlationKeyValueSplitToken, encodeBase64(v)))
+	}
+	return strings.Join(content, correlationSplitToken)
+}
+
+func stringConvertInt32(str string) (int32, error) {
+	i, err := strconv.ParseInt(str, 0, 32)
+	return int32(i), err
+}
+
+func decodeBase64(str string) (string, error) {
+	ret, err := base64.StdEncoding.DecodeString(str)
+	if err != nil {
+		return "", err
+	}
+	return string(ret), nil
+}
+
+func encodeBase64(str string) string {
+	return base64.StdEncoding.EncodeToString([]byte(str))
+}
+
+func (s *SpanContext) decode(extractor func(headerKey string) (string, error), headerKey string, decoder func(header string) error) error {
+	val, err := extractor(headerKey)
+	if err != nil {
+		return err
+	}
+	if val == "" {
+		return nil
+	}
+	err = decoder(val)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/plugins/core/sampler.go b/plugins/core/sampler.go
new file mode 100644
index 0000000..539a05b
--- /dev/null
+++ b/plugins/core/sampler.go
@@ -0,0 +1,156 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"fmt"
+	"math/rand"
+	"strconv"
+	"sync"
+	"time"
+
+	"github.com/apache/skywalking-go/reporter"
+)
+
+type Sampler interface {
+	IsSampled(operation string) (sampled bool)
+}
+
+type ConstSampler struct {
+	decision bool
+}
+
+// NewConstSampler creates a ConstSampler.
+func NewConstSampler(sample bool) *ConstSampler {
+	s := &ConstSampler{
+		decision: sample,
+	}
+	return s
+}
+
+// IsSampled implements IsSampled() of Sampler.
+func (s *ConstSampler) IsSampled(operation string) bool {
+	return s.decision
+}
+
+// RandomSampler Use sync.Pool to implement concurrent-safe for randomizer.
+type RandomSampler struct {
+	samplingRate float64
+	threshold    int
+	pool         sync.Pool
+}
+
+// IsSampled implements IsSampled() of Sampler.
+func (s *RandomSampler) IsSampled(_ string) bool {
+	return s.threshold > s.generateRandomNumber()
+}
+
+func (s *RandomSampler) init() {
+	s.threshold = int(s.samplingRate * 100)
+	s.pool.New = s.newRand
+}
+
+func (s *RandomSampler) generateRandomNumber() int {
+	r := s.getRandomizer()
+	defer s.returnRandomizer(r)
+
+	return r.Intn(100)
+}
+
+func (s *RandomSampler) returnRandomizer(r *rand.Rand) {
+	s.pool.Put(r)
+}
+
+func (s *RandomSampler) getRandomizer() *rand.Rand {
+	var r *rand.Rand
+	generator := s.pool.Get()
+	if generator == nil {
+		generator = s.newRand()
+	}
+
+	r, ok := generator.(*rand.Rand)
+	if !ok {
+		r = s.newRand().(*rand.Rand) // it must be *rand.Rand
+	}
+
+	return r
+}
+
+func (s *RandomSampler) newRand() interface{} {
+	return rand.New(rand.NewSource(time.Now().UnixNano()))
+}
+
+func NewRandomSampler(samplingRate float64) *RandomSampler {
+	s := &RandomSampler{
+		samplingRate: samplingRate,
+	}
+	s.init()
+	return s
+}
+
+type DynamicSampler struct {
+	currentRate float64
+	defaultRate float64
+	sampler     Sampler
+}
+
+// IsSampled implements IsSampled() of Sampler.
+func (s *DynamicSampler) IsSampled(operation string) bool {
+	return s.sampler.IsSampled(operation)
+}
+
+func (s *DynamicSampler) Key() string {
+	return "agent.sample_rate"
+}
+
+func (s *DynamicSampler) Notify(eventType reporter.AgentConfigEventType, newValue string) {
+	if eventType == reporter.DELETED {
+		newValue = fmt.Sprintf("%f", s.defaultRate)
+	}
+	samplingRate, err := strconv.ParseFloat(newValue, 64)
+	if err != nil {
+		return
+	}
+
+	// change Sampler
+	var sampler Sampler
+	if samplingRate <= 0 {
+		sampler = NewConstSampler(false)
+	} else if samplingRate >= 1.0 {
+		sampler = NewConstSampler(true)
+	} else {
+		sampler = NewRandomSampler(samplingRate)
+	}
+	s.sampler = sampler
+	s.currentRate = samplingRate
+}
+
+func (s *DynamicSampler) Value() string {
+	return fmt.Sprintf("%f", s.currentRate)
+}
+
+func NewDynamicSampler(samplingRate float64, tracer *Tracer) *DynamicSampler {
+	s := &DynamicSampler{
+		currentRate: samplingRate,
+		defaultRate: samplingRate,
+	}
+	s.Notify(reporter.MODIFY, fmt.Sprintf("%f", samplingRate))
+	// append watcher
+	tracer.cdsWatchers = append(tracer.cdsWatchers, s)
+	return s
+}
diff --git a/plugins/core/sampler_test.go b/plugins/core/sampler_test.go
new file mode 100644
index 0000000..cc93abd
--- /dev/null
+++ b/plugins/core/sampler_test.go
@@ -0,0 +1,101 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"sync"
+	"testing"
+)
+
+var samplerOperationName = "op"
+
+func TestConstSampler_IsSampled(t *testing.T) {
+	sampler := NewConstSampler(true)
+	sampled := sampler.IsSampled(samplerOperationName)
+	if sampled != true {
+		t.Errorf("const sampler should be sampled")
+	}
+	samplerNegative := NewConstSampler(false)
+	sampledNegative := samplerNegative.IsSampled(samplerOperationName)
+	if sampledNegative != false {
+		t.Errorf("const sampler should not be sampled")
+	}
+}
+
+func TestRandomSampler_IsSampled(t *testing.T) {
+	randomSampler := NewRandomSampler(0.5)
+
+	t.Run("threshold need transform", func(t *testing.T) {
+		if randomSampler.threshold != 50 {
+			t.Errorf("threshold should be 50")
+		}
+	})
+
+	// just for test case
+	randomSampler.threshold = 100
+	sampled := randomSampler.IsSampled(samplerOperationName)
+	if sampled != true {
+		t.Errorf("random sampler should be sampled")
+	}
+
+	randomSampler.threshold = 0
+	sampled = randomSampler.IsSampled(samplerOperationName)
+	if sampled != false {
+		t.Errorf("random sampler should not be sampled")
+	}
+}
+
+func TestNewRandomSampler(t *testing.T) {
+	randomSampler := NewRandomSampler(100)
+	sampled := randomSampler.IsSampled(samplerOperationName)
+	if sampled != true {
+		t.Errorf("random sampler should be sampled")
+	}
+}
+
+func TestRandomSampler_getRandomizer(t *testing.T) {
+	t.Run("must not nil", func(t *testing.T) {
+		sampler := &RandomSampler{
+			pool: sync.Pool{},
+		}
+
+		if sampler.getRandomizer() == nil {
+			t.Errorf("randomizer should be nil")
+		}
+	})
+
+	t.Run("must not nil, if got not a *rand.Rand", func(t *testing.T) {
+		sampler := &RandomSampler{
+			pool: sync.Pool{},
+		}
+
+		sampler.pool.Put(&struct{}{})
+		if sampler.getRandomizer() == nil {
+			t.Errorf("randomizer should be nil")
+		}
+	})
+}
+
+func BenchmarkRandomPoolSampler_IsSampled(b *testing.B) {
+	sampler := NewRandomSampler(0.5)
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			sampler.IsSampled(samplerOperationName)
+		}
+	})
+}
diff --git a/plugins/core/span.go b/plugins/core/span.go
new file mode 100644
index 0000000..4d797d5
--- /dev/null
+++ b/plugins/core/span.go
@@ -0,0 +1,74 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"time"
+
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+)
+
+// SpanType is used to identify entry, exit and local
+type SpanType int32
+
+// Tag are supported by sky-walking engine.
+// As default, all Tags will be stored, but these ones have
+// particular meanings.
+type Tag string
+
+const (
+	// SpanTypeEntry is a entry span, eg http server
+	SpanTypeEntry SpanType = 0
+	// SpanTypeExit is a exit span, eg http client
+	SpanTypeExit SpanType = 1
+	// SpanTypeLocal is a local span, eg local method invoke
+	SpanTypeLocal SpanType = 2
+)
+
+const (
+	TagURL             Tag = "url"
+	TagStatusCode      Tag = "status_code"
+	TagHTTPMethod      Tag = "http.method"
+	TagDBType          Tag = "db.type"
+	TagDBInstance      Tag = "db.instance"
+	TagDBStatement     Tag = "db.statement"
+	TagDBSqlParameters Tag = "db.sql.parameters"
+	TagMQQueue         Tag = "mq.queue"
+	TagMQBroker        Tag = "mq.broker"
+	TagMQTopic         Tag = "mq.topic"
+)
+
+// Span interface as commonv3 span specification
+type Span interface {
+	SetOperationName(string)
+	GetOperationName() string
+	SetPeer(string)
+	GetPeer() string
+	SetSpanLayer(agentv3.SpanLayer)
+	GetSpanLayer() agentv3.SpanLayer
+	SetComponent(int32)
+	GetComponent() int32
+	Tag(Tag, string)
+	Log(time.Time, ...string)
+	Error(time.Time, ...string)
+	End()
+	IsEntry() bool
+	IsExit() bool
+	IsValid() bool
+	ParentSpan() Span
+}
diff --git a/plugins/core/span_default.go b/plugins/core/span_default.go
new file mode 100644
index 0000000..ad9db39
--- /dev/null
+++ b/plugins/core/span_default.go
@@ -0,0 +1,133 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"math"
+	"time"
+
+	"github.com/apache/skywalking-go/reporter"
+
+	commonv3 "skywalking.apache.org/repo/goapi/collect/common/v3"
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+)
+
+type DefaultSpan struct {
+	Refs          []reporter.SpanContext
+	tracer        *Tracer
+	StartTime     time.Time
+	EndTime       time.Time
+	OperationName string
+	Peer          string
+	Layer         agentv3.SpanLayer
+	ComponentID   int32
+	Tags          []*commonv3.KeyStringValuePair
+	Logs          []*agentv3.Log
+	IsError       bool
+	SpanType      SpanType
+	Parent        Span
+}
+
+func NewDefaultSpan(tracer *Tracer, parent Span) *DefaultSpan {
+	return &DefaultSpan{
+		tracer:    tracer,
+		StartTime: time.Now(),
+		SpanType:  SpanTypeLocal,
+		Parent:    parent,
+	}
+}
+
+// For Span
+func (ds *DefaultSpan) SetOperationName(name string) {
+	ds.OperationName = name
+}
+
+func (ds *DefaultSpan) GetOperationName() string {
+	return ds.OperationName
+}
+
+func (ds *DefaultSpan) SetPeer(peer string) {
+	ds.Peer = peer
+}
+
+func (ds *DefaultSpan) GetPeer() string {
+	return ds.Peer
+}
+
+func (ds *DefaultSpan) SetSpanLayer(layer agentv3.SpanLayer) {
+	ds.Layer = layer
+}
+
+func (ds *DefaultSpan) GetSpanLayer() agentv3.SpanLayer {
+	return ds.Layer
+}
+
+func (ds *DefaultSpan) SetComponent(componentID int32) {
+	ds.ComponentID = componentID
+}
+
+func (ds *DefaultSpan) GetComponent() int32 {
+	return ds.ComponentID
+}
+
+func (ds *DefaultSpan) Tag(key Tag, value string) {
+	ds.Tags = append(ds.Tags, &commonv3.KeyStringValuePair{Key: string(key), Value: value})
+}
+
+func (ds *DefaultSpan) Log(t time.Time, ll ...string) {
+	data := make([]*commonv3.KeyStringValuePair, 0, int32(math.Ceil(float64(len(ll))/2.0)))
+	var kvp *commonv3.KeyStringValuePair
+	for i, l := range ll {
+		if i%2 == 0 {
+			kvp = &commonv3.KeyStringValuePair{}
+			data = append(data, kvp)
+			kvp.Key = l
+		} else if kvp != nil {
+			kvp.Value = l
+		}
+	}
+	ds.Logs = append(ds.Logs, &agentv3.Log{Time: Millisecond(t), Data: data})
+}
+
+func (ds *DefaultSpan) Error(t time.Time, ll ...string) {
+	ds.IsError = true
+	ds.Log(t, ll...)
+}
+
+func (ds *DefaultSpan) End() {
+	ds.EndTime = time.Now()
+	if ctx := GetTracingContext(); ctx != nil {
+		ctx.ActiveSpan = ds.Parent
+	}
+}
+
+func (ds *DefaultSpan) IsEntry() bool {
+	return ds.SpanType == SpanTypeEntry
+}
+
+func (ds *DefaultSpan) IsExit() bool {
+	return ds.SpanType == SpanTypeExit
+}
+
+func (ds *DefaultSpan) IsValid() bool {
+	return ds.EndTime.IsZero()
+}
+
+func (ds *DefaultSpan) ParentSpan() Span {
+	return ds.Parent
+}
diff --git a/plugins/core/span_noop.go b/plugins/core/span_noop.go
new file mode 100644
index 0000000..9810228
--- /dev/null
+++ b/plugins/core/span_noop.go
@@ -0,0 +1,83 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"time"
+
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+)
+
+type NoopSpan struct {
+}
+
+func (*NoopSpan) SetOperationName(string) {
+}
+
+func (*NoopSpan) GetOperationName() string {
+	return ""
+}
+
+func (*NoopSpan) SetPeer(string) {
+}
+
+func (*NoopSpan) GetPeer() string {
+	return ""
+}
+
+func (*NoopSpan) SetSpanLayer(agentv3.SpanLayer) {
+}
+
+func (*NoopSpan) GetSpanLayer() agentv3.SpanLayer {
+	return 0
+}
+
+func (*NoopSpan) SetComponent(int32) {
+}
+
+func (*NoopSpan) GetComponent() int32 {
+	return 0
+}
+
+func (*NoopSpan) Tag(Tag, string) {
+}
+
+func (*NoopSpan) Log(time.Time, ...string) {
+}
+
+func (*NoopSpan) Error(time.Time, ...string) {
+}
+
+func (*NoopSpan) End() {
+}
+
+func (*NoopSpan) IsEntry() bool {
+	return false
+}
+
+func (*NoopSpan) IsExit() bool {
+	return false
+}
+
+func (*NoopSpan) IsValid() bool {
+	return true
+}
+
+func (n *NoopSpan) ParentSpan() Span {
+	return nil
+}
diff --git a/plugins/core/span_tracing.go b/plugins/core/span_tracing.go
new file mode 100644
index 0000000..f5152b8
--- /dev/null
+++ b/plugins/core/span_tracing.go
@@ -0,0 +1,361 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"fmt"
+	"sync/atomic"
+	"time"
+
+	"github.com/apache/skywalking-go/reporter"
+
+	commonv3 "skywalking.apache.org/repo/goapi/collect/common/v3"
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+)
+
+func NewSegmentSpan(defaultSpan *DefaultSpan, parentSpan SegmentSpan) (s SegmentSpan, err error) {
+	ssi := &SegmentSpanImpl{
+		DefaultSpan: *defaultSpan,
+	}
+	err = ssi.createSegmentContext(parentSpan)
+	if err != nil {
+		return nil, err
+	}
+	if parentSpan == nil || !parentSpan.segmentRegister() {
+		rs := newSegmentRoot(ssi)
+		err = rs.createRootSegmentContext(parentSpan)
+		if err != nil {
+			return nil, err
+		}
+		s = rs
+	} else {
+		s = ssi
+	}
+	return
+}
+
+// SegmentContext is the context in a segment
+type SegmentContext struct {
+	TraceID            string
+	SegmentID          string
+	SpanID             int32
+	ParentSpanID       int32
+	ParentSegmentID    string
+	collect            chan<- reporter.ReportedSpan
+	refNum             *int32
+	spanIDGenerator    *int32
+	FirstSpan          Span `json:"-"`
+	CorrelationContext map[string]string
+}
+
+func (c *SegmentContext) GetTraceID() string {
+	return c.TraceID
+}
+
+func (c *SegmentContext) GetSegmentID() string {
+	return c.SegmentID
+}
+
+func (c *SegmentContext) GetSpanID() int32 {
+	return c.SpanID
+}
+
+func (c *SegmentContext) GetParentSpanID() int32 {
+	return c.ParentSpanID
+}
+
+func (c *SegmentContext) GetParentSegmentID() string {
+	return c.ParentSegmentID
+}
+
+type SegmentSpan interface {
+	Span
+	GetSegmentContext() SegmentContext
+	tracer() *Tracer
+	segmentRegister() bool
+}
+
+type SegmentSpanImpl struct {
+	DefaultSpan
+	SegmentContext
+}
+
+// For Span
+func (s *SegmentSpanImpl) End() {
+	if !s.IsValid() {
+		return
+	}
+	s.DefaultSpan.End()
+	go func() {
+		s.SegmentContext.collect <- s
+	}()
+}
+
+func (s *SegmentSpanImpl) GetDefaultSpan() DefaultSpan {
+	return s.DefaultSpan
+}
+
+// For Reported Span
+
+func (s *SegmentSpanImpl) Context() reporter.SegmentContext {
+	return &s.SegmentContext
+}
+
+func (s *SegmentSpanImpl) Refs() []reporter.SpanContext {
+	return s.DefaultSpan.Refs
+}
+
+func (s *SegmentSpanImpl) StartTime() int64 {
+	return Millisecond(s.DefaultSpan.StartTime)
+}
+
+func (s *SegmentSpanImpl) EndTime() int64 {
+	return Millisecond(s.DefaultSpan.EndTime)
+}
+
+func (s *SegmentSpanImpl) OperationName() string {
+	return s.DefaultSpan.OperationName
+}
+
+func (s *SegmentSpanImpl) Peer() string {
+	return s.DefaultSpan.Peer
+}
+
+func (s *SegmentSpanImpl) SpanType() agentv3.SpanType {
+	return agentv3.SpanType(s.DefaultSpan.SpanType)
+}
+
+func (s *SegmentSpanImpl) SpanLayer() agentv3.SpanLayer {
+	return s.DefaultSpan.Layer
+}
+
+func (s *SegmentSpanImpl) IsError() bool {
+	return s.DefaultSpan.IsError
+}
+
+func (s *SegmentSpanImpl) Tags() []*commonv3.KeyStringValuePair {
+	return s.DefaultSpan.Tags
+}
+
+func (s *SegmentSpanImpl) Logs() []*agentv3.Log {
+	return s.DefaultSpan.Logs
+}
+
+func (s *SegmentSpanImpl) ComponentID() int32 {
+	return s.DefaultSpan.ComponentID
+}
+
+func (s *SegmentSpanImpl) GetSegmentContext() SegmentContext {
+	return s.SegmentContext
+}
+
+func (s *SegmentSpanImpl) tracer() *Tracer {
+	return s.DefaultSpan.tracer
+}
+
+func (s *SegmentSpanImpl) segmentRegister() bool {
+	for {
+		o := atomic.LoadInt32(s.SegmentContext.refNum)
+		if o < 0 {
+			return false
+		}
+		if atomic.CompareAndSwapInt32(s.SegmentContext.refNum, o, o+1) {
+			return true
+		}
+	}
+}
+
+func (s *SegmentSpanImpl) createSegmentContext(parent SegmentSpan) (err error) {
+	if parent == nil {
+		s.SegmentContext = SegmentContext{}
+		if len(s.DefaultSpan.Refs) > 0 {
+			s.TraceID = s.DefaultSpan.Refs[0].GetTraceID()
+			s.CorrelationContext = s.DefaultSpan.Refs[0].(*SpanContext).CorrelationContext
+		} else {
+			s.TraceID, err = GenerateGlobalID()
+			if err != nil {
+				return err
+			}
+			s.CorrelationContext = make(map[string]string)
+		}
+	} else {
+		s.SegmentContext = parent.GetSegmentContext()
+		s.ParentSegmentID = s.GetSegmentID()
+		s.ParentSpanID = s.GetSpanID()
+		s.SpanID = atomic.AddInt32(s.SegmentContext.spanIDGenerator, 1)
+		s.CorrelationContext = parent.GetSegmentContext().CorrelationContext
+	}
+	if s.SegmentContext.FirstSpan == nil {
+		s.SegmentContext.FirstSpan = s
+	}
+	if s.CorrelationContext == nil {
+		s.CorrelationContext = make(map[string]string)
+	}
+	return
+}
+
+type RootSegmentSpan struct {
+	*SegmentSpanImpl
+	notify  <-chan reporter.ReportedSpan
+	segment []reporter.ReportedSpan
+	doneCh  chan int32
+}
+
+func (rs *RootSegmentSpan) End() {
+	if !rs.IsValid() {
+		return
+	}
+	rs.DefaultSpan.End()
+	go func() {
+		rs.doneCh <- atomic.SwapInt32(rs.SegmentContext.refNum, -1)
+	}()
+}
+
+func (rs *RootSegmentSpan) createRootSegmentContext(_ SegmentSpan) (err error) {
+	rs.SegmentID, err = GenerateGlobalID()
+	if err != nil {
+		return err
+	}
+	i := int32(0)
+	rs.spanIDGenerator = &i
+	rs.SpanID = i
+	rs.ParentSpanID = -1
+	return
+}
+
+type snapshotSpan struct {
+	DefaultSpan
+	SegmentContext
+}
+
+func (s *snapshotSpan) End() {
+	panic(fmt.Errorf("cannot End the span in other goroutine"))
+}
+
+func (s *snapshotSpan) SetOperationName(_ string) {
+	panic(fmt.Errorf("cannot update the operation name of span in other goroutine"))
+}
+
+func (s *snapshotSpan) SetSpanLayer(_ agentv3.SpanLayer) {
+	panic(fmt.Errorf("cannot update the layer of span in other goroutine"))
+}
+
+func (s *snapshotSpan) SetComponent(_ int32) {
+	panic(fmt.Errorf("cannot update the compoenent of span in other goroutine"))
+}
+
+func (s *snapshotSpan) Tag(key Tag, value string) {
+	panic(fmt.Errorf("cannot add tag of span in other goroutine"))
+}
+
+func (s *snapshotSpan) Log(_ time.Time, _ ...string) {
+	panic(fmt.Errorf("cannot add log of span in other goroutine"))
+}
+
+func (s *snapshotSpan) Error(_ time.Time, _ ...string) {
+	panic(fmt.Errorf("cannot add error of span in other goroutine"))
+}
+
+func (s *snapshotSpan) GetSegmentContext() SegmentContext {
+	return s.SegmentContext
+}
+
+func (s *snapshotSpan) tracer() *Tracer {
+	return s.DefaultSpan.tracer
+}
+
+func (s *snapshotSpan) segmentRegister() bool {
+	for {
+		o := atomic.LoadInt32(s.SegmentContext.refNum)
+		if o < 0 {
+			return false
+		}
+		if atomic.CompareAndSwapInt32(s.SegmentContext.refNum, o, o+1) {
+			return true
+		}
+	}
+}
+
+func newSegmentRoot(segmentSpan *SegmentSpanImpl) *RootSegmentSpan {
+	s := &RootSegmentSpan{
+		SegmentSpanImpl: segmentSpan,
+	}
+	var init int32
+	s.refNum = &init
+	ch := make(chan reporter.ReportedSpan)
+	s.collect = ch
+	s.notify = ch
+	s.segment = make([]reporter.ReportedSpan, 0, 10)
+	s.doneCh = make(chan int32)
+	go func() {
+		total := -1
+		defer close(ch)
+		defer close(s.doneCh)
+		for {
+			select {
+			case span := <-s.notify:
+				s.segment = append(s.segment, span)
+			case n := <-s.doneCh:
+				total = int(n)
+			}
+			if total == len(s.segment) {
+				break
+			}
+		}
+		s.tracer().Reporter.Send(append(s.segment, s))
+	}()
+	return s
+}
+
+func newSnapshotSpan(current Span) *snapshotSpan {
+	if current == nil {
+		return nil
+	}
+	segmentSpan, ok := current.(SegmentSpan)
+	if !ok {
+		return nil
+	}
+
+	segCtx := segmentSpan.GetSegmentContext()
+	copiedCorrelation := make(map[string]string)
+	for k, v := range segCtx.CorrelationContext {
+		copiedCorrelation[k] = v
+	}
+	s := &snapshotSpan{
+		DefaultSpan: DefaultSpan{
+			OperationName: segmentSpan.GetOperationName(),
+			Refs:          nil,
+			tracer:        segmentSpan.tracer(),
+			Peer:          segmentSpan.GetPeer(),
+		},
+		SegmentContext: SegmentContext{
+			TraceID:            segCtx.GetTraceID(),
+			SegmentID:          segCtx.SegmentID,
+			SpanID:             segCtx.SpanID,
+			ParentSpanID:       segCtx.ParentSpanID,
+			ParentSegmentID:    segCtx.ParentSegmentID,
+			collect:            segCtx.collect,
+			refNum:             segCtx.refNum,
+			spanIDGenerator:    segCtx.spanIDGenerator,
+			FirstSpan:          segCtx.FirstSpan,
+			CorrelationContext: copiedCorrelation,
+		},
+	}
+
+	return s
+}
diff --git a/plugins/core/test_base.go b/plugins/core/test_base.go
new file mode 100644
index 0000000..2825e6c
--- /dev/null
+++ b/plugins/core/test_base.go
@@ -0,0 +1,61 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"github.com/apache/skywalking-go/reporter"
+)
+
+var tlsData interface{}
+
+func init() {
+	SetGLS = func(i interface{}) {
+		tlsData = i
+	}
+	GetGLS = func() interface{} {
+		return tlsData
+	}
+	ResetTracingContext()
+}
+
+func ResetTracingContext() {
+	SetGLS(nil)
+	SetGlobalTracer(&Tracer{initFlag: 1, Sampler: NewConstSampler(true), Reporter: &StoreReporter{}})
+}
+
+func GetReportedSpans() []reporter.ReportedSpan {
+	return GetGlobalTracer().Reporter.(*StoreReporter).Spans
+}
+
+type StoreReporter struct {
+	Spans []reporter.ReportedSpan
+}
+
+func NewStoreReporter() *StoreReporter {
+	return &StoreReporter{}
+}
+
+func (r *StoreReporter) Boot(entity reporter.Entity, cdsWatchers []reporter.AgentConfigChangeWatcher) {
+}
+
+func (r *StoreReporter) Send(spans []reporter.ReportedSpan) {
+	r.Spans = append(r.Spans, spans...)
+}
+
+func (r *StoreReporter) Close() {
+}
diff --git a/plugins/core/tool.go b/plugins/core/tool.go
new file mode 100644
index 0000000..1db067d
--- /dev/null
+++ b/plugins/core/tool.go
@@ -0,0 +1,44 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"strings"
+	"time"
+
+	"github.com/google/uuid"
+)
+
+// Millisecond converts time to unix millisecond
+func Millisecond(t time.Time) int64 {
+	return t.UnixNano() / int64(time.Millisecond)
+}
+
+// UUID generate UUID
+func UUID() (string, error) {
+	id, err := uuid.NewUUID()
+	if err != nil {
+		return "", err
+	}
+	return strings.ReplaceAll(id.String(), "-", ""), nil
+}
+
+// GenerateGlobalID generates global unique id
+func GenerateGlobalID() (globalID string, err error) {
+	return UUID()
+}
diff --git a/plugins/core/tracer.go b/plugins/core/tracer.go
new file mode 100644
index 0000000..9890dfc
--- /dev/null
+++ b/plugins/core/tracer.go
@@ -0,0 +1,42 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import (
+	"github.com/apache/skywalking-go/reporter"
+)
+
+type CorrelationConfig struct {
+	MaxKeyCount  int
+	MaxValueSize int
+}
+
+type Tracer struct {
+	Service  string
+	Instance string
+	Reporter reporter.Reporter
+	// 0 not init 1 init
+	initFlag int32
+	Sampler  Sampler
+	// correlation *CorrelationConfig	// temporarily disable, because haven't been implemented yet
+	cdsWatchers []reporter.AgentConfigChangeWatcher
+}
+
+func (t *Tracer) InitSuccess() bool {
+	return t.initFlag == 1
+}
diff --git a/plugins/core/tracer_state.go b/plugins/core/tracer_state.go
new file mode 100644
index 0000000..f2fd3ff
--- /dev/null
+++ b/plugins/core/tracer_state.go
@@ -0,0 +1,41 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package core
+
+import "sync/atomic"
+
+var (
+	globalTracer = &atomic.Value{}
+)
+
+// SetGlobalTracer registers `tracer` as the global Tracer.
+func SetGlobalTracer(tracer *Tracer) {
+	globalTracer.Store(tracer)
+}
+
+// GetGlobalTracer returns the registered global Tracer.
+// If none is registered then an instance of `nil` is returned.
+func GetGlobalTracer() *Tracer {
+	value := globalTracer.Load()
+	if value != nil {
+		if tracer, ok := value.(*Tracer); ok {
+			return tracer
+		}
+	}
+	return nil
+}
diff --git a/plugins/core/tracing/api.go b/plugins/core/tracing/api.go
new file mode 100644
index 0000000..952eaa6
--- /dev/null
+++ b/plugins/core/tracing/api.go
@@ -0,0 +1,252 @@
+// 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 tracing
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/apache/skywalking-go/plugins/core"
+	"github.com/apache/skywalking-go/reporter"
+
+	"github.com/pkg/errors"
+
+	commonv3 "skywalking.apache.org/repo/goapi/collect/common/v3"
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+)
+
+var (
+	errParameter = fmt.Errorf("parameter are nil")
+)
+
+// Extractor is a tool specification which define how to
+// extract trace parent context from propagation context
+type Extractor func(headerKey string) (string, error)
+
+// Injector is a tool specification which define how to
+// inject trace context into propagation context
+type Injector func(headerKey, headerValue string) error
+
+func CreateEntrySpan(operationName string, extractor Extractor, opts ...SpanOption) (s core.Span, err error) {
+	if operationName == "" || extractor == nil {
+		return nil, errParameter
+	}
+	tracer, ctx, s, noop := createNoop()
+	if noop {
+		return s, nil
+	}
+	defer func() {
+		saveSpanToActiveIfNotError(ctx, s, err)
+	}()
+	var ref = &core.SpanContext{}
+	if err := ref.Decode(extractor); err != nil {
+		return nil, err
+	}
+	if !ref.Valid {
+		ref = nil
+	}
+
+	return createSpan0(tracer, s, append(opts, withRef(ref), withSpanType(core.SpanTypeEntry), withOperationName(operationName))...)
+}
+
+func CreateLocalSpan(operationName string, opts ...SpanOption) (s core.Span, err error) {
+	if operationName == "" {
+		return nil, errParameter
+	}
+	tracer, ctx, s, noop := createNoop()
+	if noop {
+		return s, nil
+	}
+	defer func() {
+		saveSpanToActiveIfNotError(ctx, s, err)
+	}()
+
+	return createSpan0(tracer, s, append(opts, withSpanType(core.SpanTypeLocal), withOperationName(operationName))...)
+}
+
+func CreateExitSpan(operationName, peer string, injector Injector, opts ...SpanOption) (s core.Span, err error) {
+	if operationName == "" || peer == "" || injector == nil {
+		return nil, errParameter
+	}
+	tracer, ctx, s, noop := createNoop()
+	if noop {
+		return s, nil
+	}
+	defer func() {
+		saveSpanToActiveIfNotError(ctx, s, err)
+	}()
+
+	span, err := createSpan0(tracer, s, append(opts, withSpanType(core.SpanTypeExit), withOperationName(operationName), withPeer(peer))...)
+	if err != nil {
+		return nil, err
+	}
+	spanContext := &core.SpanContext{}
+	reportedSpan, ok := span.(core.SegmentSpan)
+	if !ok {
+		return nil, errors.New("span type is wrong")
+	}
+
+	firstSpan := reportedSpan.GetSegmentContext().FirstSpan
+	spanContext.Sample = 1
+	spanContext.TraceID = reportedSpan.GetSegmentContext().TraceID
+	spanContext.ParentSegmentID = reportedSpan.GetSegmentContext().SegmentID
+	spanContext.ParentSpanID = reportedSpan.GetSegmentContext().SpanID
+	spanContext.ParentService = tracer.Service
+	spanContext.ParentServiceInstance = tracer.Instance
+	spanContext.ParentEndpoint = firstSpan.GetOperationName()
+	spanContext.AddressUsedAtClient = peer
+	spanContext.CorrelationContext = reportedSpan.GetSegmentContext().CorrelationContext
+
+	err = spanContext.Encode(injector)
+	if err != nil {
+		return nil, err
+	}
+	return span, nil
+}
+
+// SpanOption allows for functional options to adjust behavior
+// of a Span to be created by CreateLocalSpan
+type SpanOption func(s *core.DefaultSpan)
+
+func WithLayer(layer agentv3.SpanLayer) SpanOption {
+	return func(s *core.DefaultSpan) {
+		s.Layer = layer
+	}
+}
+
+func WithComponent(componentID int32) SpanOption {
+	return func(s *core.DefaultSpan) {
+		s.ComponentID = componentID
+	}
+}
+
+func WithTag(key, value string) SpanOption {
+	return func(s *core.DefaultSpan) {
+		s.Tags = append(s.Tags, &commonv3.KeyStringValuePair{Key: key, Value: value})
+	}
+}
+
+func GetRuntimeContextValue(key string) interface{} {
+	context := core.GetTracingContext()
+	if context == nil {
+		return nil
+	}
+	return context.Runtime.Get(key)
+}
+
+func SetRuntimeContextValue(key string, val interface{}) {
+	context := core.GetTracingContext()
+	if context == nil {
+		context = core.NewTracingContext()
+		core.SetGLS(context)
+	}
+	context.Runtime.Set(key, val)
+}
+
+func createNoop() (*core.Tracer, *core.TracingContext, core.Span, bool) {
+	tracer := core.GetGlobalTracer()
+	if tracer == nil || !tracer.InitSuccess() {
+		return nil, nil, &core.NoopSpan{}, true
+	}
+	ctx := getTracingContext()
+	if ctx != nil {
+		_, ok := ctx.ActiveSpan.(*core.NoopSpan)
+		return tracer, ctx, ctx.ActiveSpan, ok
+	}
+	return tracer, nil, nil, false
+}
+
+func createSpan0(tracer *core.Tracer, parent core.Span, opts ...SpanOption) (s core.Span, err error) {
+	ds := core.NewDefaultSpan(tracer, parent)
+	for _, opt := range opts {
+		opt(ds)
+	}
+	var parentSpan core.SegmentSpan
+	if parent != nil {
+		tmpSpan, ok := parent.(core.SegmentSpan)
+		if ok {
+			parentSpan = tmpSpan
+		}
+	}
+	isForceSample := len(ds.Refs) > 0
+	// Try to sample when it is not force sample
+	if parentSpan == nil && !isForceSample {
+		// Force sample
+		sampled := tracer.Sampler.IsSampled(ds.OperationName)
+		if !sampled {
+			// Filter by sample just return noop span
+			s = &core.NoopSpan{}
+			return s, nil
+		}
+	}
+	s, err = core.NewSegmentSpan(ds, parentSpan)
+	if err != nil {
+		return nil, err
+	}
+	return s, nil
+}
+
+func withSpanType(spanType core.SpanType) SpanOption {
+	return func(span *core.DefaultSpan) {
+		span.SpanType = spanType
+	}
+}
+
+func withOperationName(opName string) SpanOption {
+	return func(span *core.DefaultSpan) {
+		span.OperationName = opName
+	}
+}
+
+func withRef(sc reporter.SpanContext) SpanOption {
+	return func(span *core.DefaultSpan) {
+		if sc == nil {
+			return
+		}
+		v := reflect.ValueOf(sc)
+		if v.Interface() == reflect.Zero(v.Type()).Interface() {
+			return
+		}
+		span.Refs = append(span.Refs, sc)
+	}
+}
+
+func withPeer(peer string) SpanOption {
+	return func(span *core.DefaultSpan) {
+		span.Peer = peer
+	}
+}
+
+func getTracingContext() *core.TracingContext {
+	ctx := core.GetGLS()
+	if ctx == nil {
+		return nil
+	}
+	return ctx.(*core.TracingContext)
+}
+
+func saveSpanToActiveIfNotError(ctx *core.TracingContext, span core.Span, err error) {
+	if err != nil {
+		return
+	}
+	if ctx == nil {
+		ctx = core.NewTracingContext()
+	}
+	ctx.ActiveSpan = span
+	core.SetGLS(ctx)
+}
diff --git a/plugins/core/tracing/api_test.go b/plugins/core/tracing/api_test.go
new file mode 100644
index 0000000..068801c
--- /dev/null
+++ b/plugins/core/tracing/api_test.go
@@ -0,0 +1,330 @@
+// 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 tracing
+
+import (
+	"testing"
+	"time"
+
+	"github.com/apache/skywalking-go/plugins/core"
+
+	"github.com/stretchr/testify/assert"
+
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+)
+
+const (
+	sample                = 1
+	traceID               = "1f2d4bf47bf711eab794acde48001122"
+	parentSegmentID       = "1e7c204a7bf711eab858acde48001122"
+	parentSpanID          = 0
+	parentService         = "service"
+	parentServiceInstance = "instance"
+	parentEndpoint        = "/foo/bar"
+	addressUsedAtClient   = "foo.svc:8787"
+)
+
+var header string
+
+func init() {
+	scx := core.SpanContext{
+		Sample:                sample,
+		TraceID:               traceID,
+		ParentSegmentID:       parentSegmentID,
+		ParentSpanID:          parentSpanID,
+		ParentService:         parentService,
+		ParentServiceInstance: parentServiceInstance,
+		ParentEndpoint:        parentEndpoint,
+		AddressUsedAtClient:   addressUsedAtClient,
+	}
+	header = scx.EncodeSW8()
+}
+
+type spanOperationTestCase struct {
+	operations    []func(existingSpans []core.Span) (core.Span, error)
+	exceptedSpans []struct {
+		spanType         core.SpanType
+		operationName    string
+		parentSpanOpName string
+		peer             string
+	}
+}
+
+func TestCreateSpanInSingleGoroutine(t *testing.T) {
+	defer core.ResetTracingContext()
+	validateSpanOperation(t, []spanOperationTestCase{
+		{
+			operations: []func(existingSpans []core.Span) (core.Span, error){
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateEntrySpan("/entry", func(key string) (string, error) { return "", nil })
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateLocalSpan("/local1")
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateLocalSpan("/local1-1")
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateExitSpan("/local1-1-exit", "localhost:8080", func(key, value string) error { return nil })
+				},
+				func(existingSpans []core.Span) (core.Span, error) { existingSpans[3].End(); return nil, nil },
+				func(existingSpans []core.Span) (core.Span, error) { existingSpans[2].End(); return nil, nil },
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateExitSpan("/local1-exit", "localhost:8080", func(key, value string) error { return nil })
+				},
+				func(existingSpans []core.Span) (core.Span, error) { existingSpans[4].End(); return nil, nil },
+				func(existingSpans []core.Span) (core.Span, error) { existingSpans[1].End(); return nil, nil },
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateExitSpan("/entry-exit", "localhost:8080", func(key, value string) error { return nil })
+				},
+				func(existingSpans []core.Span) (core.Span, error) { existingSpans[5].End(); return nil, nil },
+				func(existingSpans []core.Span) (core.Span, error) { existingSpans[0].End(); return nil, nil },
+			},
+			exceptedSpans: []struct {
+				spanType         core.SpanType
+				operationName    string
+				parentSpanOpName string
+				peer             string
+			}{
+				{core.SpanTypeEntry, "/entry", "", ""},
+				{core.SpanTypeLocal, "/local1", "/entry", ""},
+				{core.SpanTypeLocal, "/local1-1", "/local1", ""},
+				{core.SpanTypeExit, "/local1-1-exit", "/local1-1", "localhost:8080"},
+				{core.SpanTypeExit, "/local1-exit", "/local1", "localhost:8080"},
+				{core.SpanTypeExit, "/entry-exit", "/entry", "localhost:8080"},
+			},
+		},
+	})
+}
+
+func TestCreateSpanInDifferenceGoroutine(t *testing.T) {
+	defer core.ResetTracingContext()
+	validateSpanOperation(t, []spanOperationTestCase{
+		{
+			operations: []func(existingSpans []core.Span) (core.Span, error){
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateEntrySpan("/entry", func(key string) (string, error) { return "", nil })
+				},
+				func(existingSpans []core.Span) (core.Span, error) { // new goroutine
+					core.SetGLS(core.TaskTracingContextSnapshot(core.GetGLS()))
+					return nil, nil
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateLocalSpan("/local")
+				},
+				func(existingSpans []core.Span) (core.Span, error) { // new goroutine
+					core.SetGLS(core.TaskTracingContextSnapshot(core.GetGLS()))
+					return nil, nil
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					return CreateExitSpan("/local-exit", "localhost:8080", func(key, value string) error { return nil })
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					existingSpans[2].End()
+					return nil, nil
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					existingSpans[1].End()
+					return nil, nil
+				},
+				func(existingSpans []core.Span) (core.Span, error) {
+					existingSpans[0].End()
+					return nil, nil
+				},
+			},
+			exceptedSpans: []struct {
+				spanType         core.SpanType
+				operationName    string
+				parentSpanOpName string
+				peer             string
+			}{
+				{core.SpanTypeEntry, "/entry", "", ""},
+				{core.SpanTypeLocal, "/local", "/entry", ""},
+				{core.SpanTypeExit, "/local-exit", "/local", "localhost:8080"},
+			},
+		},
+	})
+}
+
+func TestSpanContextWriting(t *testing.T) {
+	defer core.ResetTracingContext()
+	s, err := CreateEntrySpan("/entry", func(key string) (string, error) { return "", nil })
+	assert.NoError(t, err)
+	s.End()
+	s, err = CreateExitSpan("/exit", "localhost:8080", func(key, value string) error {
+		ctx := core.SpanContext{}
+		if key == core.Header {
+			assert.NoError(t, ctx.DecodeSW8(value))
+		}
+		if key == core.HeaderCorrelation {
+			assert.NoError(t, ctx.DecodeSW8Correlation(value))
+		}
+		return nil
+	})
+	assert.NoError(t, err)
+	s.End()
+}
+
+func TestSpanContextReading(t *testing.T) {
+	defer core.ResetTracingContext()
+	s, err := CreateEntrySpan("/entry", func(key string) (string, error) {
+		if key == core.Header {
+			return header, nil
+		}
+		return "", nil
+	})
+	assert.NoError(t, err)
+	s.End()
+	time.Sleep(time.Millisecond * 50)
+	spans := core.GetReportedSpans()
+	assert.Equal(t, 1, len(spans), "span count not correct")
+	tracingContext := spans[0].Context()
+	assert.Equal(t, traceID, tracingContext.GetTraceID(), "trace id not correct")
+	assert.Equal(t, 1, len(spans[0].Refs()), "refs not correct")
+	refCtx := spans[0].Refs()[0]
+	assert.Equal(t, traceID, refCtx.GetTraceID(), "ref trace id not correct")
+	assert.Equal(t, parentSegmentID, refCtx.GetParentSegmentID(), "ref segment id not correct")
+	assert.Equal(t, parentEndpoint, refCtx.GetParentEndpoint(), "ref endpoint not correct")
+	assert.Equal(t, int32(parentSpanID), refCtx.GetParentSpanID(), "ref span id not correct")
+	assert.Equal(t, parentService, refCtx.GetParentService(), "ref service not correct")
+	assert.Equal(t, parentServiceInstance, refCtx.GetParentServiceInstance(), "ref service instance not correct")
+	assert.Equal(t, parentEndpoint, refCtx.GetParentEndpoint(), "ref endpoint not correct")
+}
+
+func TestSpanOperation(t *testing.T) {
+	defer core.ResetTracingContext()
+	spanCreations := []func(op SpanOption) (core.Span, error){
+		func(op SpanOption) (core.Span, error) {
+			return CreateEntrySpan("test", func(headerKey string) (string, error) {
+				return "", nil
+			}, op)
+		},
+		func(op SpanOption) (core.Span, error) {
+			return CreateLocalSpan("test", op)
+		},
+		func(op SpanOption) (core.Span, error) {
+			return CreateExitSpan("test", "localhost:8080", func(headerKey, headerValue string) error {
+				return nil
+			}, op)
+		},
+	}
+
+	spanOptions := []struct {
+		op       SpanOption
+		validate func(s *core.RootSegmentSpan) bool
+	}{
+		{WithLayer(agentv3.SpanLayer_Http), func(s *core.RootSegmentSpan) bool {
+			return s.DefaultSpan.Layer == agentv3.SpanLayer_Http
+		}},
+		{WithComponent(1), func(s *core.RootSegmentSpan) bool {
+			return s.DefaultSpan.ComponentID == 1
+		}},
+		{WithTag("test", "test1"), func(s *core.RootSegmentSpan) bool {
+			for _, k := range s.DefaultSpan.Tags {
+				if k.Key == "test" {
+					return k.Value == "test1"
+				}
+			}
+			return false
+		}},
+	}
+
+	for createInx, spanCreate := range spanCreations {
+		for _, op := range spanOptions {
+			create, err := spanCreate(op.op)
+			if err != nil {
+				assert.Nil(t, err, "create span error")
+			}
+			span := create.(*core.RootSegmentSpan)
+			assert.Truef(t, op.validate(span), "span validation failed, create index: %d, option index: %d", createInx, op)
+			create.End()
+		}
+	}
+}
+
+func TestRuntimeContext(t *testing.T) {
+	defer core.ResetTracingContext()
+	assert.Nilf(t, GetRuntimeContextValue("test"), "runtime context data should be nil")
+	SetRuntimeContextValue("test", "test")
+	assert.Equal(t, "test", GetRuntimeContextValue("test"), "runtime context data should be \"test\"")
+	// switch to the new goroutine
+	oldGLS := core.GetGLS()
+	core.SetGLS(core.TaskTracingContextSnapshot(oldGLS))
+	assert.Equal(t, "test", GetRuntimeContextValue("test"), "runtime context data should be \"test\"")
+	assert.Nilf(t, GetRuntimeContextValue("test1"), "runtime context data should be nil")
+	SetRuntimeContextValue("test1", "test1")
+	assert.Equal(t, "test1", GetRuntimeContextValue("test1"), "runtime context data should be \"test1\"")
+	// switch back to the old goroutine
+	core.SetGLS(oldGLS)
+	assert.Nilf(t, GetRuntimeContextValue("test1"), "runtime context data should be nil")
+}
+
+func validateSpanOperation(t *testing.T, cases []spanOperationTestCase) {
+	for _, tt := range cases {
+		spans := make([]core.Span, 0)
+		for i, op := range tt.operations {
+			span, err := op(spans)
+			assert.Nilf(t, err, "create span error, operation index: %d", i)
+			time.Sleep(time.Millisecond * 50)
+			if span != nil {
+				spans = append(spans, span)
+			}
+		}
+
+		time.Sleep(time.Millisecond * 100)
+		assert.Equal(t, len(tt.exceptedSpans), len(core.GetReportedSpans()), "span count not equal")
+		for i, exceptedSpan := range tt.exceptedSpans {
+			var span core.DefaultSpan
+			if i == 0 {
+				tmp, ok := core.GetReportedSpans()[len(core.GetReportedSpans())-1-i].(*core.RootSegmentSpan)
+				assert.True(t, ok, "first span is not root segment span")
+				span = tmp.DefaultSpan
+			} else {
+				found := false
+				for _, s := range core.GetReportedSpans() {
+					if s.OperationName() != exceptedSpan.operationName {
+						continue
+					}
+					tmp, ok := s.(*core.SegmentSpanImpl)
+					assert.Truef(t, ok, "span is not segment span, span index: %d", i)
+					span = tmp.DefaultSpan
+					found = true
+					break
+				}
+				assert.Truef(t, found, "span not found, span index: %d, name: %s", i, exceptedSpan.operationName)
+			}
+			assert.Equalf(t, exceptedSpan.spanType, span.SpanType, "span type not equal, span index: %d", i)
+			assert.Equalf(t, exceptedSpan.operationName, span.OperationName, "operation name not equal, span index: %d", i)
+			if exceptedSpan.parentSpanOpName != "" {
+				assert.Equalf(t, exceptedSpan.parentSpanOpName, span.Parent.GetOperationName(), "parent operation name not equal, span index: %d", i)
+			} else {
+				assert.Nilf(t, span.Parent, "parent span not nil, span index: %d", i)
+			}
+			if exceptedSpan.peer != "" {
+				assert.Equalf(t, exceptedSpan.peer, span.Peer, "span peer not equal, span index: %d", i)
+			} else {
+				assert.Truef(t, span.Peer == "", "span peer not empty, span index: %d", i)
+			}
+
+			assert.Greaterf(t, core.Millisecond(span.StartTime), int64(0), "start time not greater than 0, span index: %d", i)
+			assert.Greaterf(t, core.Millisecond(span.EndTime), core.Millisecond(span.StartTime), "end time not greater than 0, span indeX: %d", i)
+		}
+
+		core.GetGlobalTracer().Reporter = core.NewStoreReporter()
+	}
+}
diff --git a/reporter/config_discovery.go b/reporter/config_discovery.go
new file mode 100644
index 0000000..3c1e37a
--- /dev/null
+++ b/reporter/config_discovery.go
@@ -0,0 +1,81 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package reporter
+
+import common "skywalking.apache.org/repo/goapi/collect/common/v3"
+
+type AgentConfigEventType int32
+
+const (
+	MODIFY AgentConfigEventType = iota
+	DELETED
+)
+
+func NewConfigDiscoveryService() *ConfigDiscoveryService {
+	return &ConfigDiscoveryService{}
+}
+
+type ConfigDiscoveryService struct {
+	UUID     string
+	watchers map[string]AgentConfigChangeWatcher
+}
+
+func (s *ConfigDiscoveryService) BindWatchers(watchers []AgentConfigChangeWatcher) {
+	// bind watchers
+	s.watchers = make(map[string]AgentConfigChangeWatcher)
+	for _, watcher := range watchers {
+		s.watchers[watcher.Key()] = watcher
+	}
+}
+
+func (s *ConfigDiscoveryService) HandleCommand(command *common.Command) {
+	var uuid string
+	var newConfigs = make(map[string]*common.KeyStringValuePair)
+	for _, pair := range command.GetArgs() {
+		if pair.Key == "SerialNumber" {
+		} else if pair.Key == "UUID" {
+			uuid = pair.Value
+		} else {
+			newConfigs[pair.Key] = pair
+		}
+	}
+
+	// check same uuid
+	if s.UUID == uuid {
+		return
+	}
+
+	// notify to all watchers
+	for key, watcher := range s.watchers {
+		pair := newConfigs[key]
+		if pair == nil || pair.Value == "" {
+			watcher.Notify(DELETED, "")
+		} else if pair.Value != watcher.Value() {
+			watcher.Notify(MODIFY, pair.Value)
+		}
+	}
+
+	// update uuid
+	s.UUID = uuid
+}
+
+type AgentConfigChangeWatcher interface {
+	Key() string
+	Notify(eventType AgentConfigEventType, newValue string)
+	Value() string
+}
diff --git a/reporter/doc.go b/reporter/doc.go
new file mode 100644
index 0000000..4eeeffe
--- /dev/null
+++ b/reporter/doc.go
@@ -0,0 +1,21 @@
+// 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 reporter holds reporters contain official reporter implementations.
+*/
+package reporter
diff --git a/reporter/grpc.go b/reporter/grpc.go
new file mode 100644
index 0000000..123e4bf
--- /dev/null
+++ b/reporter/grpc.go
@@ -0,0 +1,306 @@
+// 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 reporter
+
+import (
+	"context"
+	"io"
+	"time"
+
+	"google.golang.org/grpc/credentials/insecure"
+
+	"github.com/apache/skywalking-go/skylog"
+
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/connectivity"
+	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/metadata"
+
+	configuration "skywalking.apache.org/repo/goapi/collect/agent/configuration/v3"
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+	managementv3 "skywalking.apache.org/repo/goapi/collect/management/v3"
+)
+
+const (
+	maxSendQueueSize     int32 = 30000
+	defaultCheckInterval       = 20 * time.Second
+	defaultCDSInterval         = 20 * time.Second
+)
+
+// NewGRPCReporter create a new reporter to send data to gRPC oap server. Only one backend address is allowed.
+func NewGRPCReporter(log skylog.Logger, serverAddr string, opts ...GRPCReporterOption) (Reporter, error) {
+	r := &gRPCReporter{
+		logger:        log,
+		sendCh:        make(chan *agentv3.SegmentObject, maxSendQueueSize),
+		checkInterval: defaultCheckInterval,
+		cdsInterval:   defaultCDSInterval, // cds default on
+	}
+	for _, o := range opts {
+		o(r)
+	}
+
+	var credsDialOption grpc.DialOption
+	if r.creds != nil {
+		// use tls
+		credsDialOption = grpc.WithTransportCredentials(r.creds)
+	} else {
+		credsDialOption = grpc.WithTransportCredentials(insecure.NewCredentials())
+	}
+
+	conn, err := grpc.Dial(serverAddr, credsDialOption)
+	if err != nil {
+		return nil, err
+	}
+	r.conn = conn
+	r.traceClient = agentv3.NewTraceSegmentReportServiceClient(r.conn)
+	r.managementClient = managementv3.NewManagementServiceClient(r.conn)
+	if r.cdsInterval > 0 {
+		r.cdsClient = configuration.NewConfigurationDiscoveryServiceClient(r.conn)
+		r.cdsService = NewConfigDiscoveryService()
+	}
+	return r, nil
+}
+
+type gRPCReporter struct {
+	entity           Entity
+	logger           skylog.Logger
+	sendCh           chan *agentv3.SegmentObject
+	conn             *grpc.ClientConn
+	traceClient      agentv3.TraceSegmentReportServiceClient
+	managementClient managementv3.ManagementServiceClient
+	checkInterval    time.Duration
+	cdsInterval      time.Duration
+	cdsService       *ConfigDiscoveryService
+	cdsClient        configuration.ConfigurationDiscoveryServiceClient
+
+	md    metadata.MD
+	creds credentials.TransportCredentials
+
+	// bootFlag is set if Boot be executed
+	bootFlag bool
+}
+
+func (r *gRPCReporter) Boot(entity Entity, cdsWatchers []AgentConfigChangeWatcher) {
+	r.entity = entity
+	r.initSendPipeline()
+	r.check()
+	r.initCDS(cdsWatchers)
+	r.bootFlag = true
+}
+
+func (r *gRPCReporter) Send(spans []ReportedSpan) {
+	spanSize := len(spans)
+	if spanSize < 1 {
+		return
+	}
+	rootSpan := spans[spanSize-1]
+	rootCtx := rootSpan.Context()
+	segmentObject := &agentv3.SegmentObject{
+		TraceId:         rootCtx.GetTraceID(),
+		TraceSegmentId:  rootCtx.GetSegmentID(),
+		Spans:           make([]*agentv3.SpanObject, spanSize),
+		Service:         r.entity.ServiceInstanceName,
+		ServiceInstance: r.entity.ServiceInstanceName,
+	}
+	for i, s := range spans {
+		spanCtx := s.Context()
+		segmentObject.Spans[i] = &agentv3.SpanObject{
+			SpanId:        spanCtx.GetSpanID(),
+			ParentSpanId:  spanCtx.GetParentSpanID(),
+			StartTime:     s.StartTime(),
+			EndTime:       s.EndTime(),
+			OperationName: s.OperationName(),
+			Peer:          s.Peer(),
+			SpanType:      s.SpanType(),
+			SpanLayer:     s.SpanLayer(),
+			ComponentId:   s.ComponentID(),
+			IsError:       s.IsError(),
+			Tags:          s.Tags(),
+			Logs:          s.Logs(),
+		}
+		srr := make([]*agentv3.SegmentReference, 0)
+		if i == (spanSize-1) && spanCtx.GetParentSpanID() > -1 {
+			srr = append(srr, &agentv3.SegmentReference{
+				RefType:               agentv3.RefType_CrossThread,
+				TraceId:               spanCtx.GetTraceID(),
+				ParentTraceSegmentId:  spanCtx.GetParentSegmentID(),
+				ParentSpanId:          spanCtx.GetParentSpanID(),
+				ParentService:         r.entity.ServiceName,
+				ParentServiceInstance: r.entity.ServiceInstanceName,
+			})
+		}
+		if len(s.Refs()) > 0 {
+			for _, tc := range s.Refs() {
+				srr = append(srr, &agentv3.SegmentReference{
+					RefType:                  agentv3.RefType_CrossProcess,
+					TraceId:                  spanCtx.GetTraceID(),
+					ParentTraceSegmentId:     tc.GetParentSegmentID(),
+					ParentSpanId:             tc.GetParentSpanID(),
+					ParentService:            tc.GetParentService(),
+					ParentServiceInstance:    tc.GetParentServiceInstance(),
+					ParentEndpoint:           tc.GetParentEndpoint(),
+					NetworkAddressUsedAtPeer: tc.GetAddressUsedAtClient(),
+				})
+			}
+		}
+		segmentObject.Spans[i].Refs = srr
+	}
+	defer func() {
+		// recover the panic caused by close sendCh
+		if err := recover(); err != nil {
+			r.logger.Errorf("reporter segment err %v", err)
+		}
+	}()
+	select {
+	case r.sendCh <- segmentObject:
+	default:
+		r.logger.Errorf("reach max send buffer")
+	}
+}
+
+func (r *gRPCReporter) Close() {
+	if r.sendCh != nil && r.bootFlag {
+		close(r.sendCh)
+	} else {
+		r.closeGRPCConn()
+	}
+}
+
+func (r *gRPCReporter) closeGRPCConn() {
+	if r.conn != nil {
+		if err := r.conn.Close(); err != nil {
+			r.logger.Error(err)
+		}
+	}
+}
+
+func (r *gRPCReporter) initSendPipeline() {
+	if r.traceClient == nil {
+		return
+	}
+	go func() {
+	StreamLoop:
+		for {
+			stream, err := r.traceClient.Collect(metadata.NewOutgoingContext(context.Background(), r.md))
+			if err != nil {
+				r.logger.Errorf("open stream error %v", err)
+				time.Sleep(5 * time.Second)
+				continue StreamLoop
+			}
+			for s := range r.sendCh {
+				err = stream.Send(s)
+				if err != nil {
+					r.logger.Errorf("send segment error %v", err)
+					r.closeStream(stream)
+					continue StreamLoop
+				}
+			}
+			r.closeStream(stream)
+			r.closeGRPCConn()
+			break
+		}
+	}()
+}
+
+func (r *gRPCReporter) initCDS(cdsWatchers []AgentConfigChangeWatcher) {
+	if r.cdsClient == nil {
+		return
+	}
+
+	// bind watchers
+	r.cdsService.BindWatchers(cdsWatchers)
+
+	// fetch config
+	go func() {
+		for {
+			if r.conn.GetState() == connectivity.Shutdown {
+				break
+			}
+
+			configurations, err := r.cdsClient.FetchConfigurations(context.Background(), &configuration.ConfigurationSyncRequest{
+				Service: r.entity.ServiceName,
+				Uuid:    r.cdsService.UUID,
+			})
+
+			if err != nil {
+				r.logger.Errorf("fetch dynamic configuration error %v", err)
+				time.Sleep(r.cdsInterval)
+				continue
+			}
+
+			if len(configurations.GetCommands()) > 0 && configurations.GetCommands()[0].Command == "ConfigurationDiscoveryCommand" {
+				command := configurations.GetCommands()[0]
+				r.cdsService.HandleCommand(command)
+			}
+
+			time.Sleep(r.cdsInterval)
+		}
+	}()
+}
+
+func (r *gRPCReporter) closeStream(stream agentv3.TraceSegmentReportService_CollectClient) {
+	_, err := stream.CloseAndRecv()
+	if err != nil && err != io.EOF {
+		r.logger.Errorf("send closing error %v", err)
+	}
+}
+
+func (r *gRPCReporter) reportInstanceProperties() (err error) {
+	_, err = r.managementClient.ReportInstanceProperties(metadata.NewOutgoingContext(context.Background(), r.md), &managementv3.InstanceProperties{
+		Service:         r.entity.ServiceName,
+		ServiceInstance: r.entity.ServiceInstanceName,
+		Properties:      r.entity.Props,
+		Layer:           r.entity.Layer,
+	})
+	return err
+}
+
+func (r *gRPCReporter) check() {
+	if r.checkInterval < 0 || r.conn == nil || r.managementClient == nil {
+		return
+	}
+	go func() {
+		instancePropertiesSubmitted := false
+		for {
+			if r.conn.GetState() == connectivity.Shutdown {
+				break
+			}
+
+			if !instancePropertiesSubmitted {
+				err := r.reportInstanceProperties()
+				if err != nil {
+					r.logger.Errorf("report serviceInstance properties error %v", err)
+					time.Sleep(r.checkInterval)
+					continue
+				}
+				instancePropertiesSubmitted = true
+			}
+
+			_, err := r.managementClient.KeepAlive(metadata.NewOutgoingContext(context.Background(), r.md), &managementv3.InstancePingPkg{
+				Service:         r.entity.ServiceName,
+				ServiceInstance: r.entity.ServiceInstanceName,
+				Layer:           r.entity.Layer,
+			})
+
+			if err != nil {
+				r.logger.Errorf("send keep alive signal error %v", err)
+			}
+			time.Sleep(r.checkInterval)
+		}
+	}()
+}
diff --git a/reporter/grpc_opts.go b/reporter/grpc_opts.go
new file mode 100644
index 0000000..74c9db4
--- /dev/null
+++ b/reporter/grpc_opts.go
@@ -0,0 +1,22 @@
+// 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 reporter
+
+// GRPCReporterOption allows for functional options to adjust behavior
+// of a gRPC reporter to be created by NewGRPCReporter
+type GRPCReporterOption func(r *gRPCReporter)
diff --git a/reporter/reporter.go b/reporter/reporter.go
new file mode 100644
index 0000000..679777f
--- /dev/null
+++ b/reporter/reporter.go
@@ -0,0 +1,94 @@
+// 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 reporter
+
+import (
+	"time"
+
+	commonv3 "skywalking.apache.org/repo/goapi/collect/common/v3"
+	agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
+)
+
+// Tag are supported by sky-walking engine.
+// As default, all Tags will be stored, but these ones have
+// particular meanings.
+type Tag string
+
+// Span interface as commonv3 span specification
+type Span interface {
+	SetOperationName(string)
+	GetOperationName() string
+	SetPeer(string)
+	SetSpanLayer(agentv3.SpanLayer)
+	SetComponent(int32)
+	Tag(Tag, string)
+	Log(time.Time, ...string)
+	Error(time.Time, ...string)
+	End()
+	IsEntry() bool
+	IsExit() bool
+	IsValid() bool
+}
+
+// SegmentContext is the context in a segment
+type SegmentContext interface {
+	GetTraceID() string
+	GetSegmentID() string
+	GetSpanID() int32
+	GetParentSpanID() int32
+	GetParentSegmentID() string
+}
+
+// SpanContext defines propagation specification of SkyWalking
+type SpanContext interface {
+	GetTraceID() string
+	GetParentSegmentID() string
+	GetParentService() string
+	GetParentServiceInstance() string
+	GetParentEndpoint() string
+	GetAddressUsedAtClient() string
+	GetParentSpanID() int32
+}
+
+type ReportedSpan interface {
+	Context() SegmentContext
+	Refs() []SpanContext
+	StartTime() int64
+	EndTime() int64
+	OperationName() string
+	Peer() string
+	SpanType() agentv3.SpanType
+	SpanLayer() agentv3.SpanLayer
+	IsError() bool
+	Tags() []*commonv3.KeyStringValuePair
+	Logs() []*agentv3.Log
+	ComponentID() int32
+}
+
+type Entity struct {
+	ServiceName         string
+	ServiceInstanceName string
+	Props               []*commonv3.KeyStringValuePair
+	Layer               string
+}
+
+type Reporter interface {
+	Boot(entity Entity, cdsWatchers []AgentConfigChangeWatcher)
+	Send(spans []ReportedSpan)
+	Close()
+}
diff --git a/skylog/logger.go b/skylog/logger.go
new file mode 100644
index 0000000..80507f1
--- /dev/null
+++ b/skylog/logger.go
@@ -0,0 +1,37 @@
+// 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 skylog
+
+type Logger interface {
+	// Info logs to the INFO log.
+	Info(args ...interface{})
+	// Infof logs to the INFO log.
+	Infof(format string, args ...interface{})
+	// Warn logs to the WARNING and INFO logs.
+	Warn(args ...interface{})
+	// Warnf logs to the WARNING and INFO logs.
+	Warnf(format string, args ...interface{})
+	// Error logs to the ERROR, WARNING, and INFO logs.
+	Error(args ...interface{})
+	// Errorf logs to the ERROR, WARNING, and INFO logs.
+	Errorf(format string, args ...interface{})
+}
+
+// SetLogger for connect the agent with program
+func SetLogger(logger Logger) {
+}
diff --git a/tools/go-agent-enhance/cmd/helper.go b/tools/go-agent-enhance/cmd/helper.go
new file mode 100644
index 0000000..d97e152
--- /dev/null
+++ b/tools/go-agent-enhance/cmd/helper.go
@@ -0,0 +1,40 @@
+// 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 main
+
+import (
+	"fmt"
+	"os"
+)
+
+type EnhancementToolFlags struct {
+	Help bool `skyflag:"-h"`
+}
+
+func PrintUsageWithExit() {
+	fmt.Printf(`Usage: go {build,install} -a [-work] -toolexec "%s" PACKAGE...
+
+The Go-agent-enhance tool is designed for automatic enhancement of Golang programs, 
+providing the capability for Distribute Tracing.
+
+Options:
+		-h
+				Print the usage message.
+`, os.Args[0])
+	os.Exit(1)
+}
diff --git a/tools/go-agent-enhance/cmd/main.go b/tools/go-agent-enhance/cmd/main.go
new file mode 100644
index 0000000..77b3ac1
--- /dev/null
+++ b/tools/go-agent-enhance/cmd/main.go
@@ -0,0 +1,73 @@
+// 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 main
+
+import (
+	"log"
+	"os"
+	"os/exec"
+
+	"github.com/apache/skywalking-go/tools/go-agent-enhance/instrument"
+	"github.com/apache/skywalking-go/tools/go-agent-enhance/instrument/api"
+	"github.com/apache/skywalking-go/tools/go-agent-enhance/tools"
+)
+
+var toolFlags = &EnhancementToolFlags{}
+
+func main() {
+	args := os.Args[1:]
+	// Print usage
+	if err := tools.ParseFlags(toolFlags, args); err != nil || toolFlags.Help {
+		PrintUsageWithExit()
+	}
+
+	// only enhance the "compile" phase
+	cmdName := tools.ParseProxyCommandName(args)
+	if cmdName != "compile" {
+		executeDelegateCommand(args)
+		return
+	}
+
+	// parse the args
+	compileOptions := &api.CompileOptions{}
+	if err := tools.ParseFlags(compileOptions, args); err != nil {
+		executeDelegateCommand(args)
+		return
+	}
+
+	// execute the enhancement
+	args, err := instrument.Execute(compileOptions, args)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// execute the delegate command with updated args
+	executeDelegateCommand(args)
+}
+
+func executeDelegateCommand(args []string) {
+	path := args[0]
+	args = args[1:]
+	cmd := exec.Command(path, args...)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if e := cmd.Run(); e != nil {
+		log.Fatal(e)
+	}
+}
diff --git a/tools/go-agent-enhance/go.mod b/tools/go-agent-enhance/go.mod
new file mode 100644
index 0000000..cd47ea4
--- /dev/null
+++ b/tools/go-agent-enhance/go.mod
@@ -0,0 +1,11 @@
+module github.com/apache/skywalking-go/tools/go-agent-enhance
+
+go 1.18
+
+require (
+	github.com/dave/dst v0.27.2 // indirect
+	github.com/sirupsen/logrus v1.9.0 // indirect
+	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
+	golang.org/x/tools v0.1.12 // indirect
+)
diff --git a/tools/go-agent-enhance/go.sum b/tools/go-agent-enhance/go.sum
new file mode 100644
index 0000000..408aba8
--- /dev/null
+++ b/tools/go-agent-enhance/go.sum
@@ -0,0 +1,21 @@
+github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc=
+github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
+github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/tools/go-agent-enhance/instrument/api/flags.go b/tools/go-agent-enhance/instrument/api/flags.go
new file mode 100644
index 0000000..0a39718
--- /dev/null
+++ b/tools/go-agent-enhance/instrument/api/flags.go
@@ -0,0 +1,33 @@
+// 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 api
+
+import "path/filepath"
+
+type CompileOptions struct {
+	Package string `skyflag:"-p"`
+	Output  string `skyflag:"-o"`
+}
+
+func (c *CompileOptions) IsValid() bool {
+	return c.Package != "" && c.Output != ""
+}
+
+func (c *CompileOptions) CompileBaseDir() string {
+	return filepath.Dir(filepath.Dir(c.Output))
+}
diff --git a/tools/go-agent-enhance/instrument/api/instrument.go b/tools/go-agent-enhance/instrument/api/instrument.go
new file mode 100644
index 0000000..e38427c
--- /dev/null
+++ b/tools/go-agent-enhance/instrument/api/instrument.go
@@ -0,0 +1,31 @@
+// 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 api
+
+import (
+	"github.com/dave/dst/dstutil"
+)
+
+type Instrument interface {
+	// CouldHandle verify current instrument can handle this request
+	CouldHandle(opts *CompileOptions) bool
+	// FilterAndEdit filter the matched data which decode by DST, and edit the data
+	FilterAndEdit(path string, cursor *dstutil.Cursor) bool
+	// WriteExtraFiles customized the extra files when there have instrumented files
+	WriteExtraFiles(dir string) ([]string, error)
+}
diff --git a/tools/go-agent-enhance/instrument/instrument.go b/tools/go-agent-enhance/instrument/instrument.go
new file mode 100644
index 0000000..ad6916c
--- /dev/null
+++ b/tools/go-agent-enhance/instrument/instrument.go
@@ -0,0 +1,188 @@
+// 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 instrument
+
+import (
+	"fmt"
+	"go/parser"
+	"go/printer"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/dave/dst"
+	"github.com/dave/dst/decorator"
+	"github.com/dave/dst/dstutil"
+
+	"github.com/sirupsen/logrus"
+
+	"github.com/apache/skywalking-go/tools/go-agent-enhance/instrument/api"
+	"github.com/apache/skywalking-go/tools/go-agent-enhance/instrument/runtime"
+)
+
+var instruments = []api.Instrument{
+	runtime.NewRuntimeInstrument(),
+}
+
+func Execute(opts *api.CompileOptions, args []string) ([]string, error) {
+	// if the options is invalid, just ignore
+	if !opts.IsValid() {
+		return args, nil
+	}
+
+	// init the logger for the instrument
+	loggerFile, err := initLogger(opts)
+	if err != nil {
+		return nil, err
+	}
+	defer loggerFile.Close()
+
+	return execute0(opts, args)
+}
+
+func execute0(opts *api.CompileOptions, args []string) ([]string, error) {
+	// find the instrument
+	var inst api.Instrument
+	for _, ins := range instruments {
+		if ins.CouldHandle(opts) {
+			inst = ins
+			break
+		}
+	}
+	if inst == nil {
+		return args, nil
+	}
+
+	var buildDir = filepath.Dir(opts.Output)
+
+	// instrument existing files
+	if err := instrumentFiles(buildDir, inst, args); err != nil {
+		return nil, err
+	}
+
+	// write extra files if exist
+	files, err := inst.WriteExtraFiles(buildDir)
+	if err != nil {
+		return nil, err
+	}
+	if len(files) > 0 {
+		args = append(args, files...)
+	}
+
+	return args, nil
+}
+
+func instrumentFiles(buildDir string, inst api.Instrument, args []string) error {
+	// parse files
+	parsedFiles, err := parseFilesInArgs(args)
+	if err != nil {
+		return err
+	}
+
+	// filter and edit the files
+	instrumentedFiles := make([]string, 0)
+	for path, info := range parsedFiles {
+		hasInstruted := false
+		dstutil.Apply(info.dstFile, func(cursor *dstutil.Cursor) bool {
+			if inst.FilterAndEdit(path, cursor) {
+				hasInstruted = true
+			}
+			return true
+		}, func(cursor *dstutil.Cursor) bool {
+			return true
+		})
+
+		if hasInstruted {
+			instrumentedFiles = append(instrumentedFiles, path)
+		}
+	}
+
+	// write instrumented files to the build directory
+	for _, updateFileSrc := range instrumentedFiles {
+		info := parsedFiles[updateFileSrc]
+		filename := filepath.Base(updateFileSrc)
+		dest := filepath.Join(buildDir, filename)
+		output, err := os.Create(dest)
+		if err != nil {
+			return err
+		}
+		if _, err := output.WriteString(fmt.Sprintf("//line %s:1\n", updateFileSrc)); err != nil {
+			return err
+		}
+		if err := writeFile(info.dstFile, output); err != nil {
+			return err
+		}
+		args[info.argsIndex] = dest
+		if err := output.Close(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func parseFilesInArgs(args []string) (map[string]*fileInfo, error) {
+	parsedFiles := make(map[string]*fileInfo)
+	for inx, path := range args {
+		// only process the go file
+		if !strings.HasSuffix(path, ".go") {
+			continue
+		}
+
+		// parse the file
+		file, err := decorator.ParseFile(nil, path, nil, parser.ParseComments)
+		if err != nil {
+			return nil, err
+		}
+
+		parsedFiles[path] = &fileInfo{
+			argsIndex: inx,
+			dstFile:   file,
+		}
+	}
+
+	return parsedFiles, nil
+}
+
+func writeFile(file *dst.File, w io.Writer) error {
+	fset, af, err := decorator.RestoreFile(file)
+	if err != nil {
+		return err
+	}
+	return printer.Fprint(w, fset, af)
+}
+
+func initLogger(opts *api.CompileOptions) (*os.File, error) {
+	logrus.SetFormatter(&logrus.TextFormatter{
+		DisableColors: true,
+		FullTimestamp: true,
+	})
+	file, err := os.OpenFile(filepath.Join(opts.CompileBaseDir(), "instrument.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
+	if err != nil {
+		return nil, err
+	}
+	logrus.SetOutput(file)
+
+	return file, nil
+}
+
+type fileInfo struct {
+	argsIndex int
+	dstFile   *dst.File
+}
diff --git a/tools/go-agent-enhance/instrument/runtime/instrument.go b/tools/go-agent-enhance/instrument/runtime/instrument.go
new file mode 100644
index 0000000..eb57458
--- /dev/null
+++ b/tools/go-agent-enhance/instrument/runtime/instrument.go
@@ -0,0 +1,136 @@
+// 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 runtime
+
+import (
+	"github.com/dave/dst"
+	"github.com/dave/dst/dstutil"
+
+	"github.com/apache/skywalking-go/tools/go-agent-enhance/instrument/api"
+	"github.com/apache/skywalking-go/tools/go-agent-enhance/tools"
+)
+
+var (
+	TLSFieldName              = "skywalking_tls"
+	TLSGetMethodName          = "_skywalking_get_gls"
+	TLSSetMethodName          = "_skywalking_set_gls"
+	TLSTakeSnapshotMethodName = "_skywalking_tls_take_snapshot"
+)
+
+type Instrument struct {
+}
+
+func NewRuntimeInstrument() *Instrument {
+	return &Instrument{}
+}
+
+func (r *Instrument) CouldHandle(opts *api.CompileOptions) bool {
+	return opts.Package == "runtime"
+}
+
+func (r *Instrument) FilterAndEdit(path string, cursor *dstutil.Cursor) bool {
+	switch n := cursor.Node().(type) {
+	case *dst.TypeSpec:
+		if n.Name != nil && n.Name.Name != "g" {
+			return false
+		}
+		st, ok := n.Type.(*dst.StructType)
+		if !ok {
+			return false
+		}
+		// append the tls field
+		st.Fields.List = append(st.Fields.List, &dst.Field{
+			Names: []*dst.Ident{dst.NewIdent(TLSFieldName)},
+			Type:  dst.NewIdent("interface{}"),
+		})
+		tools.LogWithStructEnhance("runtime", "g", TLSFieldName, "tls field")
+		return true
+	case *dst.FuncDecl:
+		if n.Name.Name != "newproc1" {
+			return false
+		}
+		if len(n.Type.Results.List) != 1 {
+			return false
+		}
+		if len(n.Type.Params.List) != 3 {
+			return false
+		}
+		parameters := tools.EnhanceParameterNames(n.Type.Params, false)
+		results := tools.EnhanceParameterNames(n.Type.Results, true)
+
+		tools.InsertStmtsBeforeBody(n.Body, `defer func() {
+	if {{(index .Parameters 1).Name}} != nil && {{(index .Results 0).Name}} != nil && 
+		{{.SnapshotMethod}} != nil && {{(index .Parameters 1).Name}}.{{.TLSField}} != nil {
+		{{(index .Results 0).Name}}.{{.TLSField}} = {{.SnapshotMethod}}({{(index .Parameters 1).Name}}.{{.TLSField}})
+	}
+}()
+`, struct {
+			Parameters     []*tools.ParameterInfo
+			Results        []*tools.ParameterInfo
+			SnapshotMethod string
+			TLSField       string
+		}{
+			Parameters:     parameters,
+			Results:        results,
+			SnapshotMethod: TLSTakeSnapshotMethodName,
+			TLSField:       TLSFieldName,
+		})
+		tools.LogWithMethodEnhance("runtime", "", "newproc1", "support cross goroutine context propagating")
+		return true
+	}
+	return false
+}
+
+func (r *Instrument) WriteExtraFiles(dir string) ([]string, error) {
+	return tools.WriteMultipleFile(dir, map[string]string{
+		"tls.go": tools.ExecuteTemplate(`package runtime
+
+import (
+	_ "unsafe"
+)
+
+//go:linkname {{.TLSTaskSnapshotMethod}} {{.TLSTaskSnapshotMethod}}
+var {{.TLSTaskSnapshotMethod}} func(interface{}) interface{}
+
+//go:linkname {{.TLSGetMethod}} {{.TLSGetMethod}}
+var {{.TLSGetMethod}} = _skywalking_tls_get_impl
+
+//go:linkname {{.TLSSetMethod}} {{.TLSSetMethod}}
+var {{.TLSSetMethod}} = _skywalking_tls_set_impl
+
+//go:nosplit
+func _skywalking_tls_get_impl() interface{} {
+	return getg().m.curg.{{.TLSFiledName}}
+}
+
+//go:nosplit
+func _skywalking_tls_set_impl(v interface{}) {
+	getg().m.curg.{{.TLSFiledName}} = v
+}`, struct {
+			TLSFiledName          string
+			TLSGetMethod          string
+			TLSSetMethod          string
+			TLSTaskSnapshotMethod string
+		}{
+			TLSFiledName:          TLSFieldName,
+			TLSGetMethod:          TLSGetMethodName,
+			TLSSetMethod:          TLSSetMethodName,
+			TLSTaskSnapshotMethod: TLSTakeSnapshotMethodName,
+		}),
+	})
+}
diff --git a/tools/go-agent-enhance/tools/enhancement.go b/tools/go-agent-enhance/tools/enhancement.go
new file mode 100644
index 0000000..d362fba
--- /dev/null
+++ b/tools/go-agent-enhance/tools/enhancement.go
@@ -0,0 +1,102 @@
+// 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 (
+	"fmt"
+	"go/token"
+
+	"github.com/dave/dst"
+	"github.com/dave/dst/decorator"
+)
+
+type ParameterInfo struct {
+	Name                 string
+	Type                 dst.Expr
+	DefaultValueAsString string
+}
+
+// EnhanceParameterNames enhance the parameter names if they are missing
+func EnhanceParameterNames(fields *dst.FieldList, isResult bool) []*ParameterInfo {
+	if fields == nil {
+		return nil
+	}
+	result := make([]*ParameterInfo, 0)
+	for i, f := range fields.List {
+		defineName := fmt.Sprintf("skywalking_param_%d", i)
+		if isResult {
+			defineName = fmt.Sprintf("skywalking_result_%d", i)
+		}
+		if len(f.Names) == 0 {
+			f.Names = []*dst.Ident{{Name: defineName}}
+			result = append(result, newParameterInfo(defineName, f.Type))
+		} else {
+			for _, n := range f.Names {
+				if n.Name == "_" {
+					*n = *dst.NewIdent(defineName)
+					break
+				}
+			}
+
+			for _, n := range f.Names {
+				result = append(result, newParameterInfo(n.Name, f.Type))
+				break
+			}
+		}
+	}
+	return result
+}
+
+func GoStringToStats(goString string) []dst.Stmt {
+	parsed, err := decorator.Parse(fmt.Sprintf(`
+package main
+func main() {
+%s
+}`, goString))
+	if err != nil {
+		panic(fmt.Sprintf("parsing go failure: %v\n%s", err, goString))
+	}
+
+	return parsed.Decls[0].(*dst.FuncDecl).Body.List
+}
+
+func InsertStmtsBeforeBody(body *dst.BlockStmt, tmpl string, data interface{}) {
+	body.List = append(GoStringToStats(ExecuteTemplate(tmpl, data)), body.List...)
+}
+
+func newParameterInfo(name string, tp dst.Expr) *ParameterInfo {
+	result := &ParameterInfo{
+		Name: name,
+		Type: tp,
+	}
+	var defaultNil = "nil"
+	switch n := tp.(type) {
+	case *dst.StarExpr:
+		result.DefaultValueAsString = defaultNil
+	case *dst.UnaryExpr:
+		if n.Op == token.INT || n.Op == token.FLOAT {
+			result.DefaultValueAsString = "0"
+		} else {
+			result.DefaultValueAsString = defaultNil
+		}
+	default:
+		result.DefaultValueAsString = defaultNil
+	}
+
+	return result
+}
diff --git a/tools/go-agent-enhance/tools/files.go b/tools/go-agent-enhance/tools/files.go
new file mode 100644
index 0000000..99ba059
--- /dev/null
+++ b/tools/go-agent-enhance/tools/files.go
@@ -0,0 +1,36 @@
+// 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 (
+	"os"
+	"path/filepath"
+)
+
+func WriteMultipleFile(baseDir string, nameWithData map[string]string) ([]string, error) {
+	paths := make([]string, 0)
+	for name, data := range nameWithData {
+		fileName := filepath.Join(baseDir, name)
+		if err := os.WriteFile(fileName, []byte(data), 0o600); err != nil {
+			return nil, err
+		}
+		paths = append(paths, fileName)
+	}
+
+	return paths, nil
+}
diff --git a/tools/go-agent-enhance/tools/flags.go b/tools/go-agent-enhance/tools/flags.go
new file mode 100644
index 0000000..a23c2d4
--- /dev/null
+++ b/tools/go-agent-enhance/tools/flags.go
@@ -0,0 +1,102 @@
+// 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 (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"strings"
+)
+
+const flagTagKey = "skyflag"
+
+func ParseFlags(result interface{}, args []string) error {
+	if len(args) == 0 {
+		return fmt.Errorf("no args")
+	}
+	flags := parseFlagsFromStruct(result)
+	if len(flags) == 0 {
+		return nil
+	}
+
+	i := 0
+	for i < len(args)-1 {
+		i += parseFlag(flags, args[i], args[i+1])
+	}
+
+	if i < len(args) {
+		parseFlag(flags, args[i], "")
+	}
+	return nil
+}
+
+func ParseProxyCommandName(args []string) string {
+	if len(args) == 0 {
+		return ""
+	}
+
+	cmd := filepath.Base(args[0])
+	if ext := filepath.Ext(cmd); ext != "" {
+		cmd = strings.TrimSuffix(cmd, ext)
+	}
+	return cmd
+}
+
+func parseFlag(flags map[string]reflect.Value, curArg, nextArg string) int {
+	if curArg[0] != '-' {
+		return 1
+	}
+
+	kv := strings.SplitN(curArg, "=", 2)
+	option := kv[0]
+	if v, exist := flags[option]; !exist {
+		if len(kv) == 2 {
+			return 1
+		} else if nextArg == "" || (len(nextArg) > 1 && nextArg[0] != '-') {
+			return 2
+		}
+		return 1
+	} else if len(kv) == 2 {
+		v.SetString(kv[1])
+		return 1
+	} else {
+		switch v.Kind() {
+		case reflect.String:
+			v.SetString(nextArg)
+			return 2
+		case reflect.Bool:
+			v.SetBool(true)
+			return 1
+		}
+		return 1
+	}
+}
+
+func parseFlagsFromStruct(result interface{}) map[string]reflect.Value {
+	e := reflect.ValueOf(result).Elem()
+	typ := e.Type()
+	flagSetValueMap := make(map[string]reflect.Value, e.NumField())
+	for i := 0; i < typ.NumField(); i++ {
+		field := typ.Field(i)
+		if tag, ok := field.Tag.Lookup(flagTagKey); ok {
+			flagSetValueMap[tag] = e.Field(i)
+		}
+	}
+	return flagSetValueMap
+}
diff --git a/tools/go-agent-enhance/tools/logger.go b/tools/go-agent-enhance/tools/logger.go
new file mode 100644
index 0000000..e5b23b1
--- /dev/null
+++ b/tools/go-agent-enhance/tools/logger.go
@@ -0,0 +1,36 @@
+// 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/sirupsen/logrus"
+
+func LogWithMethodEnhance(pkg, receiver, method, detail string) {
+	logrus.WithField("type", "enhance_method").
+		WithField("package", pkg).
+		WithField("receiver", receiver).
+		WithField("method", method).
+		Info(detail)
+}
+
+func LogWithStructEnhance(pkg, structName, field, detail string) {
+	logrus.WithField("type", "enhance_struct").
+		WithField("package", pkg).
+		WithField("struct", structName).
+		WithField("field", field).
+		Info(detail)
+}
diff --git a/tools/go-agent-enhance/tools/templetes.go b/tools/go-agent-enhance/tools/templetes.go
new file mode 100644
index 0000000..bb51f87
--- /dev/null
+++ b/tools/go-agent-enhance/tools/templetes.go
@@ -0,0 +1,36 @@
+// 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 (
+	"bytes"
+	"html/template"
+)
+
+func ExecuteTemplate(tmpl string, data interface{}) string {
+	t, err := template.New("").Parse(tmpl)
+	if err != nil {
+		panic(err)
+	}
+	var buf bytes.Buffer
+	err = t.Execute(&buf, data)
+	if err != nil {
+		panic(err)
+	}
+	return buf.String()
+}