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 2024/09/23 04:26:24 UTC

(skywalking-go) branch main updated: feat: support manual reporting of metrics in toolkit (#205)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0b729dd  feat: support manual reporting of metrics in toolkit (#205)
0b729dd is described below

commit 0b729dde37b3ac0dac3e18fa7f8e65b3d248a87d
Author: Shyunn <sh...@outlook.com>
AuthorDate: Mon Sep 23 12:26:19 2024 +0800

    feat: support manual reporting of metrics in toolkit (#205)
---
 .github/workflows/plugin-tests.yaml                |  1 +
 CHANGES.md                                         |  1 +
 go.work                                            |  1 +
 plugins/toolkit-activation/instrument.go           | 57 ++++++++++++++++
 .../metric/counter_get_intercepter.go              | 32 ++++++---
 .../metric/counter_inc_intercepter.go              | 31 ++++++---
 .../metric/gauge_get_intercepter.go                | 32 ++++++---
 .../metric/histogram_observe_intercepter.go        | 49 +++++---------
 .../histogram_observewithcount_intercepter.go      | 49 +++++---------
 .../metric/meter_opt_intercepter.go                | 26 ++++---
 .../metric/new_counter_intercepter.go              | 61 +++++++----------
 .../metric/new_gauge_intercepter.go                | 62 +++++++----------
 .../metric/new_histogram_intercepter.go            | 62 +++++++----------
 .../scenarios/metric-activation/bin/startup.sh     | 22 ++++++
 .../metric-activation/config/excepted.yml          | 79 ++++++++++++++++++++++
 test/plugins/scenarios/metric-activation/go.mod    |  3 +
 test/plugins/scenarios/metric-activation/main.go   | 64 ++++++++++++++++++
 .../plugins/scenarios/metric-activation/plugin.yml | 28 ++++++++
 .../scenarios/metric-activation/test_service.go    | 57 ++++++++++++++++
 toolkit/metric/api.go                              | 16 ++++-
 toolkit/metric/metrics.go                          | 14 +---
 21 files changed, 514 insertions(+), 233 deletions(-)

diff --git a/.github/workflows/plugin-tests.yaml b/.github/workflows/plugin-tests.yaml
index 27710f1..94293a5 100644
--- a/.github/workflows/plugin-tests.yaml
+++ b/.github/workflows/plugin-tests.yaml
@@ -93,6 +93,7 @@ jobs:
           - irisv12
           - trace-activation
           - logging-activation
+          - metric-activation
           - fasthttp
           - discard-reporter
           - fiber
diff --git a/CHANGES.md b/CHANGES.md
index c84e0d3..a4248df 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,6 +8,7 @@ Release Notes.
 
 * support attaching events to span in the toolkit.
 * support record log in the toolkit.
+* support manually report metrics in the toolkit.
 
 #### Plugins
 * Support [goframev2](https://github.com/gogf/gf) goframev2.
diff --git a/go.work b/go.work
index 05a3142..1dfe2c1 100644
--- a/go.work
+++ b/go.work
@@ -65,6 +65,7 @@ use (
 	./test/plugins/scenarios/segmentio-kafka
 	./test/plugins/scenarios/go-elasticsearchv8
 	./test/plugins/scenarios/logging-activation
+	./test/plugins/scenarios/metric-activation
 	./test/plugins/scenarios/goframe
 
 	./tools/go-agent
diff --git a/plugins/toolkit-activation/instrument.go b/plugins/toolkit-activation/instrument.go
index 095e523..105ed78 100644
--- a/plugins/toolkit-activation/instrument.go
+++ b/plugins/toolkit-activation/instrument.go
@@ -54,9 +54,66 @@ func (i *Instrument) Points() []*instrument.Point {
 	// append toolkit/logging related enhancements Point
 	instPoints = append(instPoints, loggingPoint()...)
 
+	// append toolkit/metric related enhancements Point
+	instPoints = append(instPoints, metricPoint()...)
+
 	return instPoints
 }
 
+func metricPoint() []*instrument.Point {
+	return []*instrument.Point{
+		// Counter metric type related enhancement point
+		{
+			PackagePath: "metric", At: instrument.NewStructEnhance("CounterRef"),
+		},
+		{
+			PackagePath: "metric", At: instrument.NewStaticMethodEnhance("NewCounter"),
+			Interceptor: "NewCounterInterceptor",
+		},
+		{
+			PackagePath: "metric", At: instrument.NewMethodEnhance("*CounterRef", "Get"),
+			Interceptor: "CounterGetInterceptor",
+		},
+		{
+			PackagePath: "metric", At: instrument.NewMethodEnhance("*CounterRef", "Inc"),
+			Interceptor: "CounterIncInterceptor",
+		},
+		// Gauge metric type related enhancement point
+		{
+			PackagePath: "metric", At: instrument.NewStructEnhance("GaugeRef"),
+		},
+		{
+			PackagePath: "metric", At: instrument.NewStaticMethodEnhance("NewGauge"),
+			Interceptor: "NewGaugeInterceptor",
+		},
+		{
+			PackagePath: "metric", At: instrument.NewMethodEnhance("*GaugeRef", "Get"),
+			Interceptor: "GaugeGetInterceptor",
+		},
+		// Histogram metric type related enhancement point
+		{
+			PackagePath: "metric", At: instrument.NewStructEnhance("HistogramRef"),
+		},
+		{
+			PackagePath: "metric", At: instrument.NewStaticMethodEnhance("NewHistogram"),
+			Interceptor: "NewHistogramInterceptor",
+		},
+		{
+			PackagePath: "metric", At: instrument.NewMethodEnhance("*HistogramRef", "Observe"),
+			Interceptor: "HistogramObserveInterceptor",
+		},
+		{
+			PackagePath: "metric", At: instrument.NewMethodEnhance("*HistogramRef", "ObserveWithCount"),
+			Interceptor: "HistogramObserveWithCountInterceptor",
+		},
+		// metric options related enhancement point
+		{
+			PackagePath: "metric", At: instrument.NewStaticMethodEnhance("WithLabels"),
+			Interceptor: "WithLabelsInterceptor",
+		},
+	}
+}
+
 func tracePoint() []*instrument.Point {
 	return []*instrument.Point{
 		{
diff --git a/toolkit/metric/api.go b/plugins/toolkit-activation/metric/counter_get_intercepter.go
similarity index 54%
copy from toolkit/metric/api.go
copy to plugins/toolkit-activation/metric/counter_get_intercepter.go
index daa27e4..ed7415e 100644
--- a/toolkit/metric/api.go
+++ b/plugins/toolkit-activation/metric/counter_get_intercepter.go
@@ -6,7 +6,7 @@
 // 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
+//     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
@@ -17,17 +17,27 @@
 
 package metric
 
-// NewCounter creates a new counter metrics.
-func NewCounter(name string, opt ...MeterOpt) *CounterRef {
-	return &CounterRef{}
-}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
+
+type CounterGetInterceptor struct{}
 
-// NewGauge creates a new gauge metrics.
-func NewGauge(name string, watcher func() float64, opts ...MeterOpt) *GaugeRef {
-	return &GaugeRef{}
+func (h *CounterGetInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
 }
 
-// NewHistogram creates a new histogram metrics.
-func NewHistogram(name string, steps []float64, opts ...MeterOpt) *Histogram {
-	return &Histogram{}
+func (h *CounterGetInterceptor) AfterInvoke(invocation operator.Invocation, _ ...interface{}) error {
+	enhanced, ok := invocation.CallerInstance().(operator.EnhancedInstance)
+	if !ok {
+		return nil
+	}
+
+	counter, ok := enhanced.GetSkyWalkingDynamicField().(metrics.Counter)
+	if ok && counter != nil {
+		val := counter.Get()
+		invocation.DefineReturnValues(val)
+	}
+	return nil
 }
diff --git a/toolkit/metric/api.go b/plugins/toolkit-activation/metric/counter_inc_intercepter.go
similarity index 54%
copy from toolkit/metric/api.go
copy to plugins/toolkit-activation/metric/counter_inc_intercepter.go
index daa27e4..ca7e77a 100644
--- a/toolkit/metric/api.go
+++ b/plugins/toolkit-activation/metric/counter_inc_intercepter.go
@@ -6,7 +6,7 @@
 // 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
+//     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
@@ -17,17 +17,26 @@
 
 package metric
 
-// NewCounter creates a new counter metrics.
-func NewCounter(name string, opt ...MeterOpt) *CounterRef {
-	return &CounterRef{}
-}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
+
+type CounterIncInterceptor struct{}
 
-// NewGauge creates a new gauge metrics.
-func NewGauge(name string, watcher func() float64, opts ...MeterOpt) *GaugeRef {
-	return &GaugeRef{}
+func (h *CounterIncInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
 }
 
-// NewHistogram creates a new histogram metrics.
-func NewHistogram(name string, steps []float64, opts ...MeterOpt) *Histogram {
-	return &Histogram{}
+func (h *CounterIncInterceptor) AfterInvoke(invocation operator.Invocation, _ ...interface{}) error {
+	enhanced, ok := invocation.CallerInstance().(operator.EnhancedInstance)
+	if !ok {
+		return nil
+	}
+
+	counter, ok := enhanced.GetSkyWalkingDynamicField().(metrics.Counter)
+	if ok && counter != nil {
+		counter.Inc(invocation.Args()[0].(float64))
+	}
+	return nil
 }
diff --git a/toolkit/metric/api.go b/plugins/toolkit-activation/metric/gauge_get_intercepter.go
similarity index 54%
copy from toolkit/metric/api.go
copy to plugins/toolkit-activation/metric/gauge_get_intercepter.go
index daa27e4..63d4be6 100644
--- a/toolkit/metric/api.go
+++ b/plugins/toolkit-activation/metric/gauge_get_intercepter.go
@@ -6,7 +6,7 @@
 // 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
+//     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
@@ -17,17 +17,27 @@
 
 package metric
 
-// NewCounter creates a new counter metrics.
-func NewCounter(name string, opt ...MeterOpt) *CounterRef {
-	return &CounterRef{}
-}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
+
+type GaugeGetInterceptor struct{}
 
-// NewGauge creates a new gauge metrics.
-func NewGauge(name string, watcher func() float64, opts ...MeterOpt) *GaugeRef {
-	return &GaugeRef{}
+func (h *GaugeGetInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
 }
 
-// NewHistogram creates a new histogram metrics.
-func NewHistogram(name string, steps []float64, opts ...MeterOpt) *Histogram {
-	return &Histogram{}
+func (h *GaugeGetInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
+	enhanced, ok := invocation.CallerInstance().(operator.EnhancedInstance)
+	if !ok {
+		return nil
+	}
+
+	gauge, ok := enhanced.GetSkyWalkingDynamicField().(metrics.Gauge)
+	if ok && gauge != nil {
+		val := gauge.Get()
+		invocation.DefineReturnValues(val)
+	}
+	return nil
 }
diff --git a/toolkit/metric/metrics.go b/plugins/toolkit-activation/metric/histogram_observe_intercepter.go
similarity index 53%
copy from toolkit/metric/metrics.go
copy to plugins/toolkit-activation/metric/histogram_observe_intercepter.go
index fdf433b..26da9a8 100644
--- a/toolkit/metric/metrics.go
+++ b/plugins/toolkit-activation/metric/histogram_observe_intercepter.go
@@ -17,41 +17,26 @@
 
 package metric
 
-type CounterRef struct{}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
 
-// Get returns the current value of the counter.
-func (c *CounterRef) Get() float64 {
-	return -1
-}
-
-// Inc increments the counter with value.
-func (c *CounterRef) Inc(val float64) {}
-
-type GaugeRef struct {
-}
+type HistogramObserveInterceptor struct{}
 
-// Get returns the current value of the gauge.
-func (g *GaugeRef) Get() float64 {
-	return -1
+func (h *HistogramObserveInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
 }
 
-type Histogram struct {
-}
-
-// Observe find the value associate bucket and add 1.
-func (h *Histogram) Observe(val float64) {
-
-}
-
-// ObserveWithCount find the value associate bucket and add specific count.
-func (h *Histogram) ObserveWithCount(val float64, count int64) {
-
-}
-
-type MeterOpt struct {
-}
+func (h *HistogramObserveInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
+	enhanced, ok := invocation.CallerInstance().(operator.EnhancedInstance)
+	if !ok {
+		return nil
+	}
 
-// WithLabels Add labels for metric
-func WithLabels(key, val string) MeterOpt {
-	return MeterOpt{}
+	histogram, ok := enhanced.GetSkyWalkingDynamicField().(metrics.Histogram)
+	if ok && histogram != nil {
+		histogram.Observe(invocation.Args()[0].(float64))
+	}
+	return nil
 }
diff --git a/toolkit/metric/metrics.go b/plugins/toolkit-activation/metric/histogram_observewithcount_intercepter.go
similarity index 53%
copy from toolkit/metric/metrics.go
copy to plugins/toolkit-activation/metric/histogram_observewithcount_intercepter.go
index fdf433b..8cc1783 100644
--- a/toolkit/metric/metrics.go
+++ b/plugins/toolkit-activation/metric/histogram_observewithcount_intercepter.go
@@ -17,41 +17,26 @@
 
 package metric
 
-type CounterRef struct{}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
 
-// Get returns the current value of the counter.
-func (c *CounterRef) Get() float64 {
-	return -1
-}
-
-// Inc increments the counter with value.
-func (c *CounterRef) Inc(val float64) {}
-
-type GaugeRef struct {
-}
+type HistogramObserveWithCountInterceptor struct{}
 
-// Get returns the current value of the gauge.
-func (g *GaugeRef) Get() float64 {
-	return -1
+func (h *HistogramObserveWithCountInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
 }
 
-type Histogram struct {
-}
-
-// Observe find the value associate bucket and add 1.
-func (h *Histogram) Observe(val float64) {
-
-}
-
-// ObserveWithCount find the value associate bucket and add specific count.
-func (h *Histogram) ObserveWithCount(val float64, count int64) {
-
-}
-
-type MeterOpt struct {
-}
+func (h *HistogramObserveWithCountInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
+	enhanced, ok := invocation.CallerInstance().(operator.EnhancedInstance)
+	if !ok {
+		return nil
+	}
 
-// WithLabels Add labels for metric
-func WithLabels(key, val string) MeterOpt {
-	return MeterOpt{}
+	histogram, ok := enhanced.GetSkyWalkingDynamicField().(metrics.Histogram)
+	if ok && histogram != nil {
+		histogram.ObserveWithCount(invocation.Args()[0].(float64), invocation.Args()[1].(int64))
+	}
+	return nil
 }
diff --git a/toolkit/metric/api.go b/plugins/toolkit-activation/metric/meter_opt_intercepter.go
similarity index 57%
copy from toolkit/metric/api.go
copy to plugins/toolkit-activation/metric/meter_opt_intercepter.go
index daa27e4..a72676e 100644
--- a/toolkit/metric/api.go
+++ b/plugins/toolkit-activation/metric/meter_opt_intercepter.go
@@ -6,7 +6,7 @@
 // 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
+//     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
@@ -17,17 +17,21 @@
 
 package metric
 
-// NewCounter creates a new counter metrics.
-func NewCounter(name string, opt ...MeterOpt) *CounterRef {
-	return &CounterRef{}
-}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+)
+
+type WithLabelsInterceptor struct{}
 
-// NewGauge creates a new gauge metrics.
-func NewGauge(name string, watcher func() float64, opts ...MeterOpt) *GaugeRef {
-	return &GaugeRef{}
+func (h *WithLabelsInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
 }
 
-// NewHistogram creates a new histogram metrics.
-func NewHistogram(name string, steps []float64, opts ...MeterOpt) *Histogram {
-	return &Histogram{}
+func (h *WithLabelsInterceptor) AfterInvoke(invocation operator.Invocation, _ ...interface{}) error {
+	key := invocation.Args()[0].(string)
+	val := invocation.Args()[1].(string)
+	withLabelOpt := metrics.WithLabel(key, val)
+	invocation.DefineReturnValues(withLabelOpt)
+	return nil
 }
diff --git a/toolkit/metric/metrics.go b/plugins/toolkit-activation/metric/new_counter_intercepter.go
similarity index 52%
copy from toolkit/metric/metrics.go
copy to plugins/toolkit-activation/metric/new_counter_intercepter.go
index fdf433b..193a256 100644
--- a/toolkit/metric/metrics.go
+++ b/plugins/toolkit-activation/metric/new_counter_intercepter.go
@@ -17,41 +17,28 @@
 
 package metric
 
-type CounterRef struct{}
-
-// Get returns the current value of the counter.
-func (c *CounterRef) Get() float64 {
-	return -1
-}
-
-// Inc increments the counter with value.
-func (c *CounterRef) Inc(val float64) {}
-
-type GaugeRef struct {
-}
-
-// Get returns the current value of the gauge.
-func (g *GaugeRef) Get() float64 {
-	return -1
-}
-
-type Histogram struct {
-}
-
-// Observe find the value associate bucket and add 1.
-func (h *Histogram) Observe(val float64) {
-
-}
-
-// ObserveWithCount find the value associate bucket and add specific count.
-func (h *Histogram) ObserveWithCount(val float64, count int64) {
-
-}
-
-type MeterOpt struct {
-}
-
-// WithLabels Add labels for metric
-func WithLabels(key, val string) MeterOpt {
-	return MeterOpt{}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+	"github.com/apache/skywalking-go/toolkit/metric"
+)
+
+type NewCounterInterceptor struct{}
+
+func (h *NewCounterInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
+}
+
+func (h *NewCounterInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
+	enhanced := result[0].(operator.EnhancedInstance)
+	metricName := invocation.Args()[0].(string)
+	var opts []metrics.Opt
+	for _, o := range invocation.Args()[1].([]metric.MeterOpt) {
+		opt := o.(metrics.Opt)
+		opts = append(opts, opt)
+	}
+
+	counter := metrics.NewCounter(metricName, opts...)
+	enhanced.SetSkyWalkingDynamicField(counter)
+	return nil
 }
diff --git a/toolkit/metric/metrics.go b/plugins/toolkit-activation/metric/new_gauge_intercepter.go
similarity index 51%
copy from toolkit/metric/metrics.go
copy to plugins/toolkit-activation/metric/new_gauge_intercepter.go
index fdf433b..0a053f6 100644
--- a/toolkit/metric/metrics.go
+++ b/plugins/toolkit-activation/metric/new_gauge_intercepter.go
@@ -17,41 +17,29 @@
 
 package metric
 
-type CounterRef struct{}
-
-// Get returns the current value of the counter.
-func (c *CounterRef) Get() float64 {
-	return -1
-}
-
-// Inc increments the counter with value.
-func (c *CounterRef) Inc(val float64) {}
-
-type GaugeRef struct {
-}
-
-// Get returns the current value of the gauge.
-func (g *GaugeRef) Get() float64 {
-	return -1
-}
-
-type Histogram struct {
-}
-
-// Observe find the value associate bucket and add 1.
-func (h *Histogram) Observe(val float64) {
-
-}
-
-// ObserveWithCount find the value associate bucket and add specific count.
-func (h *Histogram) ObserveWithCount(val float64, count int64) {
-
-}
-
-type MeterOpt struct {
-}
-
-// WithLabels Add labels for metric
-func WithLabels(key, val string) MeterOpt {
-	return MeterOpt{}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+	"github.com/apache/skywalking-go/toolkit/metric"
+)
+
+type NewGaugeInterceptor struct{}
+
+func (h *NewGaugeInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
+}
+
+func (h *NewGaugeInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
+	enhanced := result[0].(operator.EnhancedInstance)
+	metricName := invocation.Args()[0].(string)
+	getterFn := invocation.Args()[1].(func() float64)
+	var opts []metrics.Opt
+	for _, o := range invocation.Args()[2].([]metric.MeterOpt) {
+		opt := o.(metrics.Opt)
+		opts = append(opts, opt)
+	}
+
+	gauge := metrics.NewGauge(metricName, getterFn, opts...)
+	enhanced.SetSkyWalkingDynamicField(gauge)
+	return nil
 }
diff --git a/toolkit/metric/metrics.go b/plugins/toolkit-activation/metric/new_histogram_intercepter.go
similarity index 51%
copy from toolkit/metric/metrics.go
copy to plugins/toolkit-activation/metric/new_histogram_intercepter.go
index fdf433b..f1935a8 100644
--- a/toolkit/metric/metrics.go
+++ b/plugins/toolkit-activation/metric/new_histogram_intercepter.go
@@ -17,41 +17,29 @@
 
 package metric
 
-type CounterRef struct{}
-
-// Get returns the current value of the counter.
-func (c *CounterRef) Get() float64 {
-	return -1
-}
-
-// Inc increments the counter with value.
-func (c *CounterRef) Inc(val float64) {}
-
-type GaugeRef struct {
-}
-
-// Get returns the current value of the gauge.
-func (g *GaugeRef) Get() float64 {
-	return -1
-}
-
-type Histogram struct {
-}
-
-// Observe find the value associate bucket and add 1.
-func (h *Histogram) Observe(val float64) {
-
-}
-
-// ObserveWithCount find the value associate bucket and add specific count.
-func (h *Histogram) ObserveWithCount(val float64, count int64) {
-
-}
-
-type MeterOpt struct {
-}
-
-// WithLabels Add labels for metric
-func WithLabels(key, val string) MeterOpt {
-	return MeterOpt{}
+import (
+	"github.com/apache/skywalking-go/plugins/core/metrics"
+	"github.com/apache/skywalking-go/plugins/core/operator"
+	"github.com/apache/skywalking-go/toolkit/metric"
+)
+
+type NewHistogramInterceptor struct{}
+
+func (h *NewHistogramInterceptor) BeforeInvoke(_ operator.Invocation) error {
+	return nil
+}
+
+func (h *NewHistogramInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error {
+	enhanced := result[0].(operator.EnhancedInstance)
+	metricName := invocation.Args()[0].(string)
+	steps := invocation.Args()[1].([]float64)
+	var opts []metrics.Opt
+	for _, o := range invocation.Args()[2].([]metric.MeterOpt) {
+		opt := o.(metrics.Opt)
+		opts = append(opts, opt)
+	}
+
+	gauge := metrics.NewHistogram(metricName, steps, opts...)
+	enhanced.SetSkyWalkingDynamicField(gauge)
+	return nil
 }
diff --git a/test/plugins/scenarios/metric-activation/bin/startup.sh b/test/plugins/scenarios/metric-activation/bin/startup.sh
new file mode 100644
index 0000000..8e39d23
--- /dev/null
+++ b/test/plugins/scenarios/metric-activation/bin/startup.sh
@@ -0,0 +1,22 @@
+# 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 metric-activation
+
+export SW_AGENT_METER_COLLECT_INTERVAL=1
+
+./metric-activation
\ No newline at end of file
diff --git a/test/plugins/scenarios/metric-activation/config/excepted.yml b/test/plugins/scenarios/metric-activation/config/excepted.yml
new file mode 100644
index 0000000..754d6d9
--- /dev/null
+++ b/test/plugins/scenarios/metric-activation/config/excepted.yml
@@ -0,0 +1,79 @@
+# 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:
+  - serviceName: metric-activation
+    meterSize: ge 4
+    meters:
+      - meterId:
+          name: request_counter
+          tags: []
+        singleValue: ge 1
+      - meterId:
+          name: request_counter_with_label
+          tags:
+            - { name: foo, value: bar }
+        singleValue: ge 1
+      - meterId:
+          name: cpu_usage_gauge
+          tags: []
+        singleValue: ge 50
+      - meterId:
+          name: cpu_usage_gauge_with_label
+          tags:
+            - { name: foo, value: bar }
+        singleValue: ge 30
+      - meterId:
+          name: request_duration
+          tags: []
+        histogramBuckets:
+          - 0.0
+          - 5.0
+          - 20.0
+          - 50.0
+          - 100.0
+          - 500.0
+          - 1000.0
+        histogramValues:
+          - 0
+          - 1
+          - 3
+          - 0
+          - 1
+          - 0
+          - 1
+      - meterId:
+          name: request_duration_with_label
+          tags:
+            - { name: foo, value: bar }
+        histogramBuckets:
+          - 0.0
+          - 5.0
+          - 20.0
+          - 50.0
+          - 100.0
+          - 500.0
+          - 1000.0
+        histogramValues:
+          - 0
+          - 1
+          - 0
+          - 0
+          - 2
+          - 2
+          - 1
+logItems: []
diff --git a/test/plugins/scenarios/metric-activation/go.mod b/test/plugins/scenarios/metric-activation/go.mod
new file mode 100644
index 0000000..bd3ea7b
--- /dev/null
+++ b/test/plugins/scenarios/metric-activation/go.mod
@@ -0,0 +1,3 @@
+module test/plugins/scenarios/metric-activation
+
+go 1.19
diff --git a/test/plugins/scenarios/metric-activation/main.go b/test/plugins/scenarios/metric-activation/main.go
new file mode 100644
index 0000000..f075b3d
--- /dev/null
+++ b/test/plugins/scenarios/metric-activation/main.go
@@ -0,0 +1,64 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package main
+
+import (
+	"io"
+	"log"
+	"net/http"
+	"time"
+
+	_ "github.com/apache/skywalking-go"
+)
+
+func providerHandler(w http.ResponseWriter, r *http.Request) {
+	testCounter()
+	testGauge()
+	testHistogram()
+
+	time.Sleep(2 * time.Second) // make sure the meter already uploaded
+	_, _ = 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 {
+		log.Printf("request provider error: %v", err)
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+	defer resp.Body.Close()
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		log.Print(err)
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+	_, _ = 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/metric-activation/plugin.yml b/test/plugins/scenarios/metric-activation/plugin.yml
new file mode 100644
index 0000000..f8316f3
--- /dev/null
+++ b/test/plugins/scenarios/metric-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/test/plugins/scenarios/metric-activation/test_service.go b/test/plugins/scenarios/metric-activation/test_service.go
new file mode 100644
index 0000000..b83045b
--- /dev/null
+++ b/test/plugins/scenarios/metric-activation/test_service.go
@@ -0,0 +1,57 @@
+// 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/apache/skywalking-go/toolkit/metric"
+)
+
+func testCounter() {
+	requestCounter := metric.NewCounter("request_counter")
+	requestCounter.Inc(1)
+
+	requestCounterWithLabel := metric.NewCounter("request_counter_with_label", metric.WithLabels("foo", "bar"))
+	requestCounterWithLabel.Inc(1)
+}
+
+func testGauge() {
+	metric.NewGauge("cpu_usage_gauge", func() float64 {
+		return 50
+	})
+
+	metric.NewGauge("cpu_usage_gauge_with_label", func() float64 {
+		return 30
+	}, metric.WithLabels("foo", "bar"))
+}
+
+func testHistogram() {
+	steps := []float64{5, 20, 50, 100, 500, 1000}
+	histogram := metric.NewHistogram("request_duration", steps)
+	histogram.Observe(10)
+	histogram.Observe(200)
+	histogram.Observe(1500)
+	histogram.ObserveWithCount(20, 1)
+	histogram.ObserveWithCount(20, 2)
+
+	histogramWithLabel := metric.NewHistogram("request_duration_with_label", steps, metric.WithLabels("foo", "bar"))
+	histogramWithLabel.Observe(10)
+	histogramWithLabel.Observe(200)
+	histogramWithLabel.Observe(1500)
+	histogramWithLabel.ObserveWithCount(100, 1)
+	histogramWithLabel.ObserveWithCount(500, 2)
+}
diff --git a/toolkit/metric/api.go b/toolkit/metric/api.go
index daa27e4..826b8d5 100644
--- a/toolkit/metric/api.go
+++ b/toolkit/metric/api.go
@@ -23,11 +23,21 @@ func NewCounter(name string, opt ...MeterOpt) *CounterRef {
 }
 
 // NewGauge creates a new gauge metrics.
-func NewGauge(name string, watcher func() float64, opts ...MeterOpt) *GaugeRef {
+func NewGauge(name string, getter func() float64, opts ...MeterOpt) *GaugeRef {
 	return &GaugeRef{}
 }
 
 // NewHistogram creates a new histogram metrics.
-func NewHistogram(name string, steps []float64, opts ...MeterOpt) *Histogram {
-	return &Histogram{}
+func NewHistogram(name string, steps []float64, opts ...MeterOpt) *HistogramRef {
+	return &HistogramRef{}
+}
+
+// MeterOpt Defines common options apply for Meter.
+// This is implemented in core/metrics package and converted in interceptors.
+type MeterOpt interface {
+}
+
+// WithLabels Add labels for metric
+func WithLabels(key, val string) MeterOpt {
+	return nil
 }
diff --git a/toolkit/metric/metrics.go b/toolkit/metric/metrics.go
index fdf433b..3dea739 100644
--- a/toolkit/metric/metrics.go
+++ b/toolkit/metric/metrics.go
@@ -35,23 +35,15 @@ func (g *GaugeRef) Get() float64 {
 	return -1
 }
 
-type Histogram struct {
+type HistogramRef struct {
 }
 
 // Observe find the value associate bucket and add 1.
-func (h *Histogram) Observe(val float64) {
+func (h *HistogramRef) Observe(val float64) {
 
 }
 
 // ObserveWithCount find the value associate bucket and add specific count.
-func (h *Histogram) ObserveWithCount(val float64, count int64) {
+func (h *HistogramRef) ObserveWithCount(val float64, count int64) {
 
 }
-
-type MeterOpt struct {
-}
-
-// WithLabels Add labels for metric
-func WithLabels(key, val string) MeterOpt {
-	return MeterOpt{}
-}