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:19 UTC

[skywalking-go] branch agent-core created (now 4a5c397)

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

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


      at 4a5c397  Implement the Agent core

This branch includes the following new commits:

     new 4a5c397  Implement the Agent core

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by li...@apache.org.
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()
+}