feat: add toolkit logging impl (#202)
diff --git a/.github/workflows/plugin-tests.yaml b/.github/workflows/plugin-tests.yaml
index 46c29dc..7329382 100644
--- a/.github/workflows/plugin-tests.yaml
+++ b/.github/workflows/plugin-tests.yaml
@@ -92,6 +92,7 @@
           - grpc
           - irisv12
           - trace-activation
+          - logging-activation
           - fasthttp
           - discard-reporter
           - fiber
diff --git a/CHANGES.md b/CHANGES.md
index 88de924..3b552a6 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@
 #### Features
 
 * support attaching events to span in the toolkit.
+* support record log in the toolkit.
 
 #### Plugins
 
diff --git a/go.work b/go.work
index 87ebfb8..3d617e9 100644
--- a/go.work
+++ b/go.work
@@ -19,7 +19,7 @@
 	./plugins/mux
 	./plugins/grpc
 	./plugins/irisv12
-	./plugins/trace-activation
+	./plugins/toolkit-activation
 	./plugins/fasthttp
 	./plugins/fiber
 	./plugins/echov4
@@ -63,6 +63,7 @@
 	./test/plugins/scenarios/pulsar
 	./test/plugins/scenarios/segmentio-kafka
 	./test/plugins/scenarios/go-elasticsearchv8
+	./test/plugins/scenarios/logging-activation
 
 	./tools/go-agent
 
diff --git a/plugins/core/logreport.go b/plugins/core/logreport.go
index 361b462..cb09112 100644
--- a/plugins/core/logreport.go
+++ b/plugins/core/logreport.go
@@ -18,8 +18,11 @@
 package core
 
 import (
+	"fmt"
 	"time"
 
+	"github.com/apache/skywalking-go/plugins/core/operator"
+
 	commonv3 "skywalking.apache.org/repo/goapi/collect/common/v3"
 	logv3 "skywalking.apache.org/repo/goapi/collect/logging/v3"
 )
@@ -37,6 +40,8 @@
 	GetEndPointName() string
 }
 
+var noopContext = &NoopSpan{}
+
 func (t *Tracer) ReportLog(ctx, timeObj interface{}, level, msg string, labels map[string]string) {
 	tracingContext, ok := ctx.(logTracingContext)
 	if !ok || tracingContext == nil {
@@ -46,7 +51,12 @@
 	if entity == nil {
 		return
 	}
-	timeData := timeObj.(time.Time)
+	timeData, ok := timeObj.(time.Time)
+	if !ok {
+		// as a fallback strategy to solve some plugins that
+		// cannot be introduced into the standard library
+		timeData = time.Now()
+	}
 
 	tags := &logv3.LogTags{
 		Data: []*commonv3.KeyStringValuePair{
@@ -62,7 +72,6 @@
 			Value: v,
 		})
 	}
-
 	logData := &logv3.LogData{
 		Timestamp:       Millisecond(timeData),
 		Service:         tracingContext.GetServiceName(),
@@ -85,3 +94,81 @@
 
 	t.Reporter.SendLog(logData)
 }
+
+func (t *Tracer) GetLogContext(withEndpoint bool) interface{} {
+	var (
+		serviceName  string
+		instanceName string
+		endpoint     string
+
+		activeSpan TracingSpan = noopContext
+	)
+
+	if s, ok := t.ActiveSpan().(TracingSpan); ok && s != nil {
+		activeSpan = s
+		if withEndpoint {
+			endpoint = findEndpointNameBySpan(s)
+		}
+	}
+	entity := t.Entity()
+	if e, ok := entity.(operator.Entity); ok && e != nil {
+		serviceName, instanceName = e.GetServiceName(), e.GetInstanceName()
+	}
+	return &SkyWalkingLogContext{
+		ServiceName:    serviceName,
+		InstanceName:   instanceName,
+		TraceID:        activeSpan.GetTraceID(),
+		TraceSegmentID: activeSpan.GetSegmentID(),
+		SpanID:         activeSpan.GetSpanID(),
+		EndPoint:       endpoint,
+	}
+}
+
+func findEndpointNameBySpan(s TracingSpan) string {
+	tmp := s
+	for tmp != nil {
+		if name := tmp.GetOperationName(); name != "" {
+			return name
+		}
+		tmp = tmp.ParentSpan()
+	}
+	return ""
+}
+
+type SkyWalkingLogContext struct {
+	ServiceName    string
+	InstanceName   string
+	TraceID        string
+	EndPoint       string
+	TraceSegmentID string
+	SpanID         int32
+}
+
+func (s *SkyWalkingLogContext) GetServiceName() string {
+	return s.ServiceName
+}
+
+func (s *SkyWalkingLogContext) GetInstanceName() string {
+	return s.InstanceName
+}
+
+func (s *SkyWalkingLogContext) GetTraceID() string {
+	return s.TraceID
+}
+
+func (s *SkyWalkingLogContext) GetTraceSegmentID() string {
+	return s.TraceSegmentID
+}
+
+func (s *SkyWalkingLogContext) GetSpanID() int32 {
+	return s.SpanID
+}
+
+func (s *SkyWalkingLogContext) GetEndPointName() string {
+	return s.EndPoint
+}
+
+func (s *SkyWalkingLogContext) String() string {
+	return fmt.Sprintf("[%s,%s,%s,%s,%d]", s.ServiceName, s.InstanceName,
+		s.TraceID, s.TraceSegmentID, s.SpanID)
+}
diff --git a/plugins/core/logreport_test.go b/plugins/core/logreport_test.go
new file mode 100644
index 0000000..2a0df3c
--- /dev/null
+++ b/plugins/core/logreport_test.go
@@ -0,0 +1,80 @@
+// 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"
+	"testing"
+
+	"github.com/apache/skywalking-go/plugins/core/reporter"
+
+	"github.com/stretchr/testify/assert"
+)
+
+type ExtractorWrapper struct {
+	F func(headerKey string) (string, error)
+}
+
+func (e *ExtractorWrapper) Fun() func(headerKey string) (string, error) {
+	return e.F
+}
+
+func TestGetLogContext(t *testing.T) {
+	defer ResetTracingContext()
+	serviceName := "test-service"
+	serviceInstanceName := "test-instance"
+	Tracing.ServiceEntity = &reporter.Entity{ServiceName: serviceName, ServiceInstanceName: serviceInstanceName}
+	s, err := Tracing.CreateEntrySpan("/test", &ExtractorWrapper{
+		F: func(headerKey string) (string, error) {
+			return "", nil
+		},
+	})
+	assert.Nil(t, err, "err should be nil")
+	assert.NotNil(t, s, "span cannot be nil")
+	context := Tracing.GetLogContext(true)
+	assert.NotNil(t, context, "context cannot be nil")
+	rootSpan, ok := s.(*RootSegmentSpan)
+	assert.True(t, ok, "span should be root span")
+	swCtx, ok := context.(*SkyWalkingLogContext)
+	assert.True(t, ok)
+	assert.NotNil(t, swCtx, "skywalkingContext cannot be nil")
+	assert.Equal(t, serviceName, swCtx.ServiceName, "service name should be equal")
+	assert.Equal(t, serviceInstanceName, serviceInstanceName, "service instance name should be equal")
+	assert.Equal(t, "/test", swCtx.GetEndPointName(), "endpoint name should be equal")
+	assert.Equal(t, rootSpan.Context().GetTraceID(), swCtx.TraceID, "trace id should be equal")
+	assert.Equal(t, rootSpan.Context().GetSegmentID(), swCtx.TraceSegmentID, "trace segment id should be equal")
+	assert.Equal(t, rootSpan.Context().GetSpanID(), swCtx.SpanID, "span id should be equal")
+	assert.NotEqualf(t, "", swCtx.String(), "context string should not be empty")
+	rootSpan.End()
+}
+
+func TestGetLogContextString(t *testing.T) {
+	defer ResetTracingContext()
+	s, err := Tracing.CreateLocalSpan("/test")
+	assert.Nil(t, err, "err should be nil")
+	assert.NotNil(t, s, "span cannot be nil")
+	context := Tracing.GetLogContext(false)
+	assert.NotNil(t, context, "context cannot be nil")
+	stringCtx, ok := context.(fmt.Stringer)
+	assert.True(t, ok)
+	assert.NotNil(t, stringCtx, "stringCtx cannot be nil")
+	assert.NotEqualf(t, "", stringCtx.String(), "context string should not be empty")
+	rootSpan, ok := s.(*RootSegmentSpan)
+	assert.True(t, ok, "span should be root span")
+	rootSpan.End()
+}
diff --git a/plugins/core/operator/logger.go b/plugins/core/operator/logger.go
index b7b0aef..b61c059 100644
--- a/plugins/core/operator/logger.go
+++ b/plugins/core/operator/logger.go
@@ -30,4 +30,5 @@
 
 type LogReporter interface {
 	ReportLog(ctx, time interface{}, level, msg string, labels map[string]string)
+	GetLogContext(withEndpoint bool) interface{}
 }
diff --git a/plugins/trace-activation/go.mod b/plugins/toolkit-activation/go.mod
similarity index 100%
rename from plugins/trace-activation/go.mod
rename to plugins/toolkit-activation/go.mod
diff --git a/plugins/trace-activation/go.sum b/plugins/toolkit-activation/go.sum
similarity index 100%
rename from plugins/trace-activation/go.sum
rename to plugins/toolkit-activation/go.sum
diff --git a/plugins/trace-activation/instrument.go b/plugins/toolkit-activation/instrument.go
similarity index 82%
rename from plugins/trace-activation/instrument.go
rename to plugins/toolkit-activation/instrument.go
index 42d7c40..095e523 100644
--- a/plugins/trace-activation/instrument.go
+++ b/plugins/toolkit-activation/instrument.go
@@ -35,7 +35,7 @@
 }
 
 func (i *Instrument) Name() string {
-	return "trace-activation"
+	return "toolkit-activation"
 }
 
 func (i *Instrument) BasePackage() string {
@@ -47,6 +47,17 @@
 }
 
 func (i *Instrument) Points() []*instrument.Point {
+	var instPoints []*instrument.Point
+	// append toolkit/trace related enhancements Point
+	instPoints = append(instPoints, tracePoint()...)
+
+	// append toolkit/logging related enhancements Point
+	instPoints = append(instPoints, loggingPoint()...)
+
+	return instPoints
+}
+
+func tracePoint() []*instrument.Point {
 	return []*instrument.Point{
 		{
 			PackagePath: "trace", At: instrument.NewStructEnhance("SpanRef"),
@@ -138,6 +149,27 @@
 	}
 }
 
+func loggingPoint() []*instrument.Point {
+	return []*instrument.Point{
+		{
+			PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Debug"),
+			Interceptor: "DebugEntryInterceptor",
+		},
+		{
+			PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Info"),
+			Interceptor: "InfoEntryInterceptor",
+		},
+		{
+			PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Warn"),
+			Interceptor: "WarnEntryInterceptor",
+		},
+		{
+			PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Error"),
+			Interceptor: "ErrorEntryInterceptor",
+		},
+	}
+}
+
 func (i *Instrument) FS() *embed.FS {
 	return &fs
 }
diff --git a/toolkit/log/api.go b/plugins/toolkit-activation/logging/debug_entry_intercepter.go
similarity index 68%
copy from toolkit/log/api.go
copy to plugins/toolkit-activation/logging/debug_entry_intercepter.go
index 909c5eb..42c015b 100644
--- a/toolkit/log/api.go
+++ b/plugins/toolkit-activation/logging/debug_entry_intercepter.go
@@ -15,23 +15,19 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package logging
 
-// Debug logs a message at DebugLevel
-func Debug(msg string, keyValues ...string) {
+import (
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
 
+type DebugEntryInterceptor struct{}
+
+func (h *DebugEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error {
+	sendLogEntry(debugLevel, invocation.Args()...)
+	return nil
 }
 
-// Info logs a message at InfoLevel
-func Info(msg string, keyValues ...string) {
-
-}
-
-// Warn logs a message at DebugLevel
-func Warn(msg string, keyValues ...string) {
-
-}
-
-// Error logs a message at ErrorLevel
-func Error(msg string, keyValues ...string) {
+func (h *DebugEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error {
+	return nil
 }
diff --git a/toolkit/log/api.go b/plugins/toolkit-activation/logging/error_entry_intercepter.go
similarity index 68%
copy from toolkit/log/api.go
copy to plugins/toolkit-activation/logging/error_entry_intercepter.go
index 909c5eb..84cad3e 100644
--- a/toolkit/log/api.go
+++ b/plugins/toolkit-activation/logging/error_entry_intercepter.go
@@ -15,23 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package logging
 
-// Debug logs a message at DebugLevel
-func Debug(msg string, keyValues ...string) {
+import "github.com/apache/skywalking-go/plugins/core/operator"
 
+type ErrorEntryInterceptor struct{}
+
+func (h *ErrorEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error {
+	sendLogEntry(errorLevel, invocation.Args()...)
+	return nil
 }
 
-// Info logs a message at InfoLevel
-func Info(msg string, keyValues ...string) {
-
-}
-
-// Warn logs a message at DebugLevel
-func Warn(msg string, keyValues ...string) {
-
-}
-
-// Error logs a message at ErrorLevel
-func Error(msg string, keyValues ...string) {
+func (h *ErrorEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error {
+	return nil
 }
diff --git a/toolkit/log/api.go b/plugins/toolkit-activation/logging/info_entry_intercepter.go
similarity index 69%
copy from toolkit/log/api.go
copy to plugins/toolkit-activation/logging/info_entry_intercepter.go
index 909c5eb..2ef6056 100644
--- a/toolkit/log/api.go
+++ b/plugins/toolkit-activation/logging/info_entry_intercepter.go
@@ -15,23 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package logging
 
-// Debug logs a message at DebugLevel
-func Debug(msg string, keyValues ...string) {
+import "github.com/apache/skywalking-go/plugins/core/operator"
 
+type InfoEntryInterceptor struct{}
+
+func (h *InfoEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error {
+	sendLogEntry(infoLevel, invocation.Args()...)
+	return nil
 }
 
-// Info logs a message at InfoLevel
-func Info(msg string, keyValues ...string) {
-
-}
-
-// Warn logs a message at DebugLevel
-func Warn(msg string, keyValues ...string) {
-
-}
-
-// Error logs a message at ErrorLevel
-func Error(msg string, keyValues ...string) {
+func (h *InfoEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error {
+	return nil
 }
diff --git a/plugins/toolkit-activation/logging/send_entry.go b/plugins/toolkit-activation/logging/send_entry.go
new file mode 100644
index 0000000..ed08936
--- /dev/null
+++ b/plugins/toolkit-activation/logging/send_entry.go
@@ -0,0 +1,60 @@
+// 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 logging
+
+import (
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
+
+const (
+	debugLevel = "debug"
+	infoLevel  = "info"
+	warnLevel  = "warn"
+	errorLevel = "error"
+)
+
+func sendLogEntry(level string, args ...interface{}) {
+	if len(args) == 0 {
+		return
+	}
+	logReporter, ok := operator.GetOperator().LogReporter().(operator.LogReporter)
+	if !ok || logReporter == nil {
+		return
+	}
+
+	msg := args[0].(string)
+	labels := parseLabels(args[1])
+	logReporter.ReportLog(logReporter.GetLogContext(true), args[1], level, msg, labels)
+}
+
+// parseLabels parses multiple args into a map of labels
+func parseLabels(args interface{}) map[string]string {
+	keyValues, ok := args.([]string)
+	if !ok || len(keyValues) < 2 {
+		return nil
+	}
+
+	ret := make(map[string]string)
+	for i := 0; i < len(keyValues); i += 2 {
+		v1 := keyValues[i]
+		v2 := keyValues[i+1]
+		ret[v1] = v2
+	}
+
+	return ret
+}
diff --git a/toolkit/log/api.go b/plugins/toolkit-activation/logging/warn_entry_intercepter.go
similarity index 69%
copy from toolkit/log/api.go
copy to plugins/toolkit-activation/logging/warn_entry_intercepter.go
index 909c5eb..28f736f 100644
--- a/toolkit/log/api.go
+++ b/plugins/toolkit-activation/logging/warn_entry_intercepter.go
@@ -15,23 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package logging
 
-// Debug logs a message at DebugLevel
-func Debug(msg string, keyValues ...string) {
+import "github.com/apache/skywalking-go/plugins/core/operator"
 
+type WarnEntryInterceptor struct{}
+
+func (h *WarnEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error {
+	sendLogEntry(warnLevel, invocation.Args()...)
+	return nil
 }
 
-// Info logs a message at InfoLevel
-func Info(msg string, keyValues ...string) {
-
-}
-
-// Warn logs a message at DebugLevel
-func Warn(msg string, keyValues ...string) {
-
-}
-
-// Error logs a message at ErrorLevel
-func Error(msg string, keyValues ...string) {
+func (h *WarnEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error {
+	return nil
 }
diff --git a/plugins/trace-activation/trace/add_event_intercepter.go b/plugins/toolkit-activation/trace/add_event_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/add_event_intercepter.go
rename to plugins/toolkit-activation/trace/add_event_intercepter.go
diff --git a/plugins/trace-activation/trace/add_log_intercepter.go b/plugins/toolkit-activation/trace/add_log_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/add_log_intercepter.go
rename to plugins/toolkit-activation/trace/add_log_intercepter.go
diff --git a/plugins/trace-activation/trace/async_add_event_intercepter.go b/plugins/toolkit-activation/trace/async_add_event_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/async_add_event_intercepter.go
rename to plugins/toolkit-activation/trace/async_add_event_intercepter.go
diff --git a/plugins/trace-activation/trace/async_finish_intercepter.go b/plugins/toolkit-activation/trace/async_finish_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/async_finish_intercepter.go
rename to plugins/toolkit-activation/trace/async_finish_intercepter.go
diff --git a/plugins/trace-activation/trace/async_log_intercepter.go b/plugins/toolkit-activation/trace/async_log_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/async_log_intercepter.go
rename to plugins/toolkit-activation/trace/async_log_intercepter.go
diff --git a/plugins/trace-activation/trace/async_prepare_intercepter.go b/plugins/toolkit-activation/trace/async_prepare_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/async_prepare_intercepter.go
rename to plugins/toolkit-activation/trace/async_prepare_intercepter.go
diff --git a/plugins/trace-activation/trace/async_tag_intercepter.go b/plugins/toolkit-activation/trace/async_tag_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/async_tag_intercepter.go
rename to plugins/toolkit-activation/trace/async_tag_intercepter.go
diff --git a/plugins/trace-activation/trace/capture_intercepter.go b/plugins/toolkit-activation/trace/capture_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/capture_intercepter.go
rename to plugins/toolkit-activation/trace/capture_intercepter.go
diff --git a/plugins/trace-activation/trace/consts.go b/plugins/toolkit-activation/trace/consts.go
similarity index 100%
rename from plugins/trace-activation/trace/consts.go
rename to plugins/toolkit-activation/trace/consts.go
diff --git a/plugins/trace-activation/trace/continue_intercepter.go b/plugins/toolkit-activation/trace/continue_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/continue_intercepter.go
rename to plugins/toolkit-activation/trace/continue_intercepter.go
diff --git a/plugins/trace-activation/trace/correlation_get_intercepter.go b/plugins/toolkit-activation/trace/correlation_get_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/correlation_get_intercepter.go
rename to plugins/toolkit-activation/trace/correlation_get_intercepter.go
diff --git a/plugins/trace-activation/trace/correlation_set_intercepter.go b/plugins/toolkit-activation/trace/correlation_set_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/correlation_set_intercepter.go
rename to plugins/toolkit-activation/trace/correlation_set_intercepter.go
diff --git a/plugins/trace-activation/trace/create_entryspan_intercepter.go b/plugins/toolkit-activation/trace/create_entryspan_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/create_entryspan_intercepter.go
rename to plugins/toolkit-activation/trace/create_entryspan_intercepter.go
diff --git a/plugins/trace-activation/trace/create_exitspan_intercepter.go b/plugins/toolkit-activation/trace/create_exitspan_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/create_exitspan_intercepter.go
rename to plugins/toolkit-activation/trace/create_exitspan_intercepter.go
diff --git a/plugins/trace-activation/trace/create_localspan_intercepter.go b/plugins/toolkit-activation/trace/create_localspan_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/create_localspan_intercepter.go
rename to plugins/toolkit-activation/trace/create_localspan_intercepter.go
diff --git a/plugins/trace-activation/trace/get_segmentid_intercepter.go b/plugins/toolkit-activation/trace/get_segmentid_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/get_segmentid_intercepter.go
rename to plugins/toolkit-activation/trace/get_segmentid_intercepter.go
diff --git a/plugins/trace-activation/trace/get_spanid_intercepter.go b/plugins/toolkit-activation/trace/get_spanid_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/get_spanid_intercepter.go
rename to plugins/toolkit-activation/trace/get_spanid_intercepter.go
diff --git a/plugins/trace-activation/trace/get_traceid_intercepter.go b/plugins/toolkit-activation/trace/get_traceid_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/get_traceid_intercepter.go
rename to plugins/toolkit-activation/trace/get_traceid_intercepter.go
diff --git a/plugins/trace-activation/trace/set_component_interceptor.go b/plugins/toolkit-activation/trace/set_component_interceptor.go
similarity index 100%
rename from plugins/trace-activation/trace/set_component_interceptor.go
rename to plugins/toolkit-activation/trace/set_component_interceptor.go
diff --git a/plugins/trace-activation/trace/set_name_intercepter.go b/plugins/toolkit-activation/trace/set_name_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/set_name_intercepter.go
rename to plugins/toolkit-activation/trace/set_name_intercepter.go
diff --git a/plugins/trace-activation/trace/set_tag_intercepter.go b/plugins/toolkit-activation/trace/set_tag_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/set_tag_intercepter.go
rename to plugins/toolkit-activation/trace/set_tag_intercepter.go
diff --git a/plugins/trace-activation/trace/stopspan_intercepter.go b/plugins/toolkit-activation/trace/stopspan_intercepter.go
similarity index 100%
rename from plugins/trace-activation/trace/stopspan_intercepter.go
rename to plugins/toolkit-activation/trace/stopspan_intercepter.go
diff --git a/test/plugins/scenarios/logging-activation/bin/startup.sh b/test/plugins/scenarios/logging-activation/bin/startup.sh
new file mode 100644
index 0000000..f1e0727
--- /dev/null
+++ b/test/plugins/scenarios/logging-activation/bin/startup.sh
@@ -0,0 +1,20 @@
+# 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.
+
+home="$(cd "$(dirname $0)"; pwd)"
+go build ${GO_BUILD_OPTS} -o logging-activation
+
+./logging-activation
\ No newline at end of file
diff --git a/test/plugins/scenarios/logging-activation/config/excepted.yml b/test/plugins/scenarios/logging-activation/config/excepted.yml
new file mode 100644
index 0000000..ee673f8
--- /dev/null
+++ b/test/plugins/scenarios/logging-activation/config/excepted.yml
@@ -0,0 +1,88 @@
+# 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.
+
+segmentItems: []
+meterItems: []
+logItems:
+  - serviceName: logging-activation
+    logSize: ge 4
+    logs:
+      - timestamp: nq 0
+        endpoint: GET:/provider
+        body:
+          type: TEXT
+          content:
+            text: this is debug msg
+        traceContext:
+          traceId: not null
+          traceSegmentId: not null
+          spanId: 0
+        tags:
+          data:
+            - key: LEVEL
+              value: debug
+            - key: foo1
+              value: bar1
+        layer: GENERAL
+      - timestamp: nq 0
+        endpoint: GET:/consumer
+        body:
+          type: TEXT
+          content:
+            text: this is info msg
+        traceContext:
+          traceId: not null
+          traceSegmentId: not null
+          spanId: 0
+        tags:
+          data:
+            - key: LEVEL
+              value: info
+            - key: foo2
+              value: bar2
+      - timestamp: nq 0
+        endpoint: GET:/consumer
+        body:
+          type: TEXT
+          content:
+            text: this is warn msg
+        traceContext:
+          traceId: not null
+          traceSegmentId: not null
+          spanId: 0
+        tags:
+          data:
+            - key: LEVEL
+              value: warn
+            - key: foo3
+              value: bar3
+      - timestamp: nq 0
+        endpoint: GET:/consumer
+        body:
+          type: TEXT
+          content:
+            text: this is error msg
+        traceContext:
+          traceId: not null
+          traceSegmentId: not null
+          spanId: 0
+        tags:
+          data:
+            - key: LEVEL
+              value: error
+            - key: foo4
+              value: bar4
+        layer: GENERAL
diff --git a/test/plugins/scenarios/logging-activation/go.mod b/test/plugins/scenarios/logging-activation/go.mod
new file mode 100644
index 0000000..48ce6ab
--- /dev/null
+++ b/test/plugins/scenarios/logging-activation/go.mod
@@ -0,0 +1,3 @@
+module test/plugins/scenarios/logging-activation
+
+go 1.19
diff --git a/test/plugins/scenarios/logging-activation/main.go b/test/plugins/scenarios/logging-activation/main.go
new file mode 100644
index 0000000..39ca1df
--- /dev/null
+++ b/test/plugins/scenarios/logging-activation/main.go
@@ -0,0 +1,60 @@
+// 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 (
+	"io"
+	"net/http"
+
+	_ "github.com/apache/skywalking-go"
+	"github.com/apache/skywalking-go/toolkit/logging"
+)
+
+func providerHandler(w http.ResponseWriter, r *http.Request) {
+	logging.Debug("this is debug msg", "foo1", "bar1")
+	_, _ = w.Write([]byte("success"))
+}
+
+func consumerHandler(w http.ResponseWriter, r *http.Request) {
+	resp, err := http.Get("http://localhost:8080/provider?test=1")
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+	defer resp.Body.Close()
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+	logging.Info("this is info msg", "foo2", "bar2")
+	logging.Warn("this is warn msg", "foo3", "bar3")
+	logging.Error("this is error msg", "foo4", "bar4")
+	_, _ = w.Write(body)
+}
+
+func main() {
+	http.HandleFunc("/provider", providerHandler)
+	http.HandleFunc("/consumer", consumerHandler)
+
+	http.HandleFunc("/health", func(writer http.ResponseWriter, request *http.Request) {
+		writer.WriteHeader(http.StatusOK)
+	})
+
+	_ = http.ListenAndServe(":8080", nil)
+}
diff --git a/test/plugins/scenarios/logging-activation/plugin.yml b/test/plugins/scenarios/logging-activation/plugin.yml
new file mode 100644
index 0000000..f8316f3
--- /dev/null
+++ b/test/plugins/scenarios/logging-activation/plugin.yml
@@ -0,0 +1,28 @@
+# 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.
+
+entry-service: http://${HTTP_HOST}:${HTTP_PORT}/consumer
+health-checker: http://${HTTP_HOST}:${HTTP_PORT}/health
+start-script: ./bin/startup.sh
+framework: go
+export-port: 8080
+support-version:
+  - go: 1.19
+  - go: 1.20
+  - go: 1.21
+  - go: 1.22
+  - go: 1.23
+toolkit: true
diff --git a/toolkit/log/api.go b/toolkit/logging/api.go
similarity index 98%
rename from toolkit/log/api.go
rename to toolkit/logging/api.go
index 909c5eb..366c9cc 100644
--- a/toolkit/log/api.go
+++ b/toolkit/logging/api.go
@@ -15,7 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package log
+package logging
 
 // Debug logs a message at DebugLevel
 func Debug(msg string, keyValues ...string) {
diff --git a/tools/go-agent/instrument/logger/context.go b/tools/go-agent/instrument/logger/context.go
index 25defd3..10072ac 100644
--- a/tools/go-agent/instrument/logger/context.go
+++ b/tools/go-agent/instrument/logger/context.go
@@ -17,7 +17,9 @@
 
 package logger
 
-import "fmt"
+import (
+	"fmt"
+)
 
 var GetOperator = func() Operator { return nil }
 var ChangeLogger = func(v interface{}) {}
@@ -43,6 +45,11 @@
 	GetParentSpan() interface{}
 }
 
+type LogReporter interface {
+	ReportLog(ctx, time interface{}, level, msg string, labels map[string]string)
+	GetLogContext(withEndpoint bool) interface{}
+}
+
 type Entity interface {
 	GetServiceName() string
 	GetInstanceName() string
@@ -71,91 +78,20 @@
 	return ""
 }
 
-type SkyWalkingLogContext struct {
-	ServiceName    string
-	InstanceName   string
-	TraceID        string
-	EndPoint       string
-	TraceSegmentID string
-	SpanID         int32
-}
-
-func (s *SkyWalkingLogContext) GetServiceName() string {
-	return s.ServiceName
-}
-
-func (s *SkyWalkingLogContext) GetInstanceName() string {
-	return s.InstanceName
-}
-
-func (s *SkyWalkingLogContext) GetTraceID() string {
-	return s.TraceID
-}
-
-func (s *SkyWalkingLogContext) GetTraceSegmentID() string {
-	return s.TraceSegmentID
-}
-
-func (s *SkyWalkingLogContext) GetSpanID() int32 {
-	return s.SpanID
-}
-
-func (s *SkyWalkingLogContext) GetEndPointName() string {
-	return s.EndPoint
-}
-
-var noopContext = &NoopSpan{}
-
-func GetLogContext(withEndpoint bool) *SkyWalkingLogContext {
-	operator := GetOperator()
-	var activeSpan TracingSpan = noopContext
-	var serviceName, instanceName, endpoint string
-	if operator != nil {
-		tracingOperator := operator.Tracing().(TracingOperator)
-		if s, ok := tracingOperator.ActiveSpan().(TracingSpan); ok && s != nil {
-			activeSpan = s
-			if withEndpoint {
-				endpoint = findEndpointNameBySpan(s)
-			}
-		}
-		entity := operator.Entity()
-		if entity != nil {
-			if e, ok := entity.(Entity); ok && e != nil {
-				serviceName, instanceName = e.GetServiceName(), e.GetInstanceName()
-			}
-		}
+func GetLogContext(withEndpoint bool) interface{} {
+	report, ok := GetOperator().LogReporter().(LogReporter)
+	if !ok || report == nil {
+		return nil
 	}
-	return &SkyWalkingLogContext{
-		ServiceName:    serviceName,
-		InstanceName:   instanceName,
-		TraceID:        activeSpan.GetTraceID(),
-		TraceSegmentID: activeSpan.GetSegmentID(),
-		SpanID:         activeSpan.GetSpanID(),
-		EndPoint:       endpoint,
-	}
-}
 
-func findEndpointNameBySpan(s TracingSpan) string {
-	tmp := s
-	for tmp != nil {
-		if name := tmp.GetEndPointName(); name != "" {
-			return name
-		}
-		parent := tmp.GetParentSpan()
-		if parentTmp, ok := parent.(TracingSpan); ok && parentTmp != nil {
-			tmp = parentTmp
-		} else {
-			tmp = nil
-		}
-	}
-	return ""
+	return report.GetLogContext(withEndpoint)
 }
 
 func GetLogContextString() string {
-	return GetLogContext(false).String()
-}
+	stringer, ok := GetLogContext(false).(fmt.Stringer)
+	if !ok {
+		return ""
+	}
 
-func (s *SkyWalkingLogContext) String() string {
-	return fmt.Sprintf("[%s,%s,%s,%s,%d]", s.ServiceName, s.InstanceName,
-		s.TraceID, s.TraceSegmentID, s.SpanID)
+	return stringer.String()
 }
diff --git a/tools/go-agent/instrument/logger/context_test.go b/tools/go-agent/instrument/logger/context_test.go
deleted file mode 100644
index 6469cd4..0000000
--- a/tools/go-agent/instrument/logger/context_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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 logger
-
-import (
-	"testing"
-
-	"github.com/apache/skywalking-go/plugins/core"
-	"github.com/apache/skywalking-go/plugins/core/reporter"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func init() {
-	GetOperator = func() Operator {
-		return core.Tracing
-	}
-}
-
-type ExtractorWrapper struct {
-	F func(headerKey string) (string, error)
-}
-
-func (e *ExtractorWrapper) Fun() func(headerKey string) (string, error) {
-	return e.F
-}
-
-func TestGetLogContext(t *testing.T) {
-	serviceName := "test-service"
-	serviceInstanceName := "test-instance"
-	core.Tracing.ServiceEntity = &reporter.Entity{ServiceName: serviceName, ServiceInstanceName: serviceInstanceName}
-	s, err := core.Tracing.CreateEntrySpan("/test", &ExtractorWrapper{
-		F: func(headerKey string) (string, error) {
-			return "", nil
-		},
-	})
-	assert.Nil(t, err, "err should be nil")
-	assert.NotNil(t, s, "span cannot be nil")
-	context := GetLogContext(true)
-	assert.NotNil(t, context, "context cannot be nil")
-	rootSpan, ok := s.(*core.RootSegmentSpan)
-	assert.True(t, ok, "span should be root span")
-	assert.Equal(t, serviceName, context.ServiceName, "service name should be equal")
-	assert.Equal(t, serviceInstanceName, serviceInstanceName, "service instance name should be equal")
-	assert.Equal(t, "/test", context.GetEndPointName(), "endpoint name should be equal")
-	assert.Equal(t, rootSpan.Context().GetTraceID(), context.TraceID, "trace id should be equal")
-	assert.Equal(t, rootSpan.Context().GetSegmentID(), context.TraceSegmentID, "trace segment id should be equal")
-	assert.Equal(t, rootSpan.Context().GetSpanID(), context.SpanID, "span id should be equal")
-	assert.NotEqualf(t, "", context.String(), "context string should not be empty")
-	rootSpan.End()
-}
-
-func TestGetLogContextString(t *testing.T) {
-	s, err := core.Tracing.CreateLocalSpan("/test")
-	assert.Nil(t, err, "err should be nil")
-	assert.NotNil(t, s, "span cannot be nil")
-	contextString := GetLogContextString()
-	assert.NotEqualf(t, "", contextString, "context string should not be empty")
-	rootSpan, ok := s.(*core.RootSegmentSpan)
-	assert.True(t, ok, "span should be root span")
-	rootSpan.End()
-}
diff --git a/tools/go-agent/instrument/logger/frameworks/api.go b/tools/go-agent/instrument/logger/frameworks/api.go
index c6c9a65..9b6be07 100644
--- a/tools/go-agent/instrument/logger/frameworks/api.go
+++ b/tools/go-agent/instrument/logger/frameworks/api.go
@@ -19,7 +19,6 @@
 
 import (
 	"embed"
-	"fmt"
 	"time"
 
 	"github.com/apache/skywalking-go/plugins/core/operator"
@@ -52,7 +51,7 @@
 var LogTracingContextKey = "SW_CTX"
 
 // GetLogContext get the tracing context
-var GetLogContext = func(withEndpoint bool) fmt.Stringer {
+var GetLogContext = func(withEndpoint bool) interface{} {
 	return nil
 }
 
diff --git a/tools/go-agent/instrument/logger/frameworks/logrus_format.go b/tools/go-agent/instrument/logger/frameworks/logrus_format.go
index dc6294f..653919c 100644
--- a/tools/go-agent/instrument/logger/frameworks/logrus_format.go
+++ b/tools/go-agent/instrument/logger/frameworks/logrus_format.go
@@ -48,7 +48,9 @@
 		if ctx == nil {
 			return format.Base.Format(entry)
 		}
-		logContext = ctx
+		if stringer, ok := ctx.(fmt.Stringer); ok {
+			logContext = stringer
+		}
 		labels := make(map[string]string, len(keys))
 		for _, key := range keys {
 			for k, v := range entry.Data {
diff --git a/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl b/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl
index ccacac3..9b17f80 100644
--- a/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl
+++ b/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl
@@ -27,7 +27,9 @@
 
 type logReporter interface {
     ReportLog(ctx, time interface{}, level, msg string, labels map[string]string)
+    GetLogContext(withEndpoint bool) interface{}
 }
+
 var {{.LogReportFuncName}} = func(ctx interface{}, time time.Time, level, msg string, labels map[string]string) {
     op := {{.GetOperatorMethodName}}()
     if op == nil {
diff --git a/tools/go-agent/instrument/logger/frameworks/zap_root.go b/tools/go-agent/instrument/logger/frameworks/zap_root.go
index aca4583..30e2357 100644
--- a/tools/go-agent/instrument/logger/frameworks/zap_root.go
+++ b/tools/go-agent/instrument/logger/frameworks/zap_root.go
@@ -39,8 +39,10 @@
 	var getEndpoint = LogReporterEnable
 	if LogReporterEnable || LogTracingContextEnable {
 		ctx := GetLogContext(getEndpoint)
-		f := zap.String(LogTracingContextKey, ctx.String())
-		return ctx, &f
+		if c, ok := ctx.(fmt.Stringer); ok {
+			f := zap.String(LogTracingContextKey, c.String())
+			return ctx, &f
+		}
 	}
 	return nil, nil
 }