You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by xu...@apache.org on 2026/05/05 10:50:14 UTC

(dubbo-go-samples) branch main updated: fix: ensure client spans are exported reliably and complete OTLP HTTP tracing sample #1077 (#1092)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 7bebeb66 fix: ensure client spans are exported reliably and complete OTLP HTTP tracing sample #1077 (#1092)
7bebeb66 is described below

commit 7bebeb666056c9071250a1a45665dc6d7a87a606
Author: nagisa-kunhah <61...@users.noreply.github.com>
AuthorDate: Tue May 5 18:50:08 2026 +0800

    fix: ensure client spans are exported reliably and complete OTLP HTTP tracing sample #1077 (#1092)
    
    * fix: otel example
    
    * fix: md
    
    * optimize code
    
    * fix: lint
    
    * fix: lint
    
    * fix: format
    
    * fix: add Apache license header to go-client main.go
    
    * add ;integrate test, fix generic fail
    
    * fix: ci
---
 book-flight-ai-agent/go-server/tools/tool_base.go  |   2 +-
 generic/go-client/cmd/client.go                    |  21 +++-
 otel/tracing/jaeger/go-client/cmd/main.go          |   7 ++
 otel/tracing/otlp_http_exporter/README.md          |   8 +-
 otel/tracing/otlp_http_exporter/README_CN.md       |   8 +-
 .../otlp_http_exporter/go-client/cmd/main.go       | 116 +++++++++++++++++++++
 .../otlp_http_exporter/go-server/cmd/main.go       |  47 +++++++--
 start_integrate_test.sh                            |   7 +-
 8 files changed, 196 insertions(+), 20 deletions(-)

diff --git a/book-flight-ai-agent/go-server/tools/tool_base.go b/book-flight-ai-agent/go-server/tools/tool_base.go
index 3442f4be..c652b4f0 100644
--- a/book-flight-ai-agent/go-server/tools/tool_base.go
+++ b/book-flight-ai-agent/go-server/tools/tool_base.go
@@ -61,7 +61,7 @@ func scanStructKeys(obj interface{}) string {
 	}
 
 	t := reflect.TypeOf(obj)
-	if t.Kind() == reflect.Ptr {
+	if t.Kind() == reflect.Pointer {
 		// If it is a pointer, get the value it points to
 		t = t.Elem()
 	}
diff --git a/generic/go-client/cmd/client.go b/generic/go-client/cmd/client.go
index 6e2d4dea..e0182ce2 100644
--- a/generic/go-client/cmd/client.go
+++ b/generic/go-client/cmd/client.go
@@ -27,7 +27,6 @@ import (
 	"dubbo.apache.org/dubbo-go/v3"
 	"dubbo.apache.org/dubbo-go/v3/client"
 	"dubbo.apache.org/dubbo-go/v3/common/constant"
-	"dubbo.apache.org/dubbo-go/v3/filter/generic"
 	_ "dubbo.apache.org/dubbo-go/v3/imports"
 
 	hessian "github.com/apache/dubbo-go-hessian2"
@@ -64,11 +63,13 @@ func main() {
 		panic(err)
 	}
 
-	genericService, err := cli.NewGenericService(
+	conn, err := cli.Dial(
 		UserProvider,
 		client.WithURL(DirectServerURL),
 		client.WithVersion(ServiceVersion),
 		client.WithGroup(ServiceGroup),
+		client.WithGeneric(),
+		client.WithSerialization(constant.Hessian2Serialization),
 	)
 	if err != nil {
 		panic(err)
@@ -78,7 +79,7 @@ func main() {
 	logger.Info("Connected to server via direct URL, starting tests...")
 
 	failed := false
-	failed = runGenericTests(genericService) || failed
+	failed = runGenericTests(&genericService{conn: conn}) || failed
 
 	if failed {
 		logger.Errorf("Some generic call tests failed")
@@ -87,7 +88,19 @@ func main() {
 	logger.Info("All generic call tests passed")
 }
 
-func runGenericTests(svc *generic.GenericService) bool {
+type genericService struct {
+	conn *client.Connection
+}
+
+func (svc *genericService) Invoke(ctx context.Context, methodName string, types []string, args []hessian.Object) (any, error) {
+	var result any
+	if err := svc.conn.CallUnary(ctx, []any{methodName, types, args}, &result, constant.Generic); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func runGenericTests(svc *genericService) bool {
 	failed := false
 	ctx := context.Background()
 
diff --git a/otel/tracing/jaeger/go-client/cmd/main.go b/otel/tracing/jaeger/go-client/cmd/main.go
index a980b8a3..1cd63d6d 100644
--- a/otel/tracing/jaeger/go-client/cmd/main.go
+++ b/otel/tracing/jaeger/go-client/cmd/main.go
@@ -28,6 +28,9 @@ import (
 	"dubbo.apache.org/dubbo-go/v3/otel/trace"
 
 	"github.com/dubbogo/gost/log/logger"
+
+	"go.opentelemetry.io/otel"
+	trace2 "go.opentelemetry.io/otel/sdk/trace"
 )
 
 import (
@@ -65,5 +68,9 @@ func main() {
 	if err != nil {
 		logger.Error(err)
 	}
+	if tp, ok := otel.GetTracerProvider().(*trace2.TracerProvider); ok {
+		_ = tp.Shutdown(context.Background())
+	}
+
 	logger.Infof("Greet response: %s", resp)
 }
diff --git a/otel/tracing/otlp_http_exporter/README.md b/otel/tracing/otlp_http_exporter/README.md
index dbb15918..2ed5d43c 100644
--- a/otel/tracing/otlp_http_exporter/README.md
+++ b/otel/tracing/otlp_http_exporter/README.md
@@ -14,17 +14,17 @@ The test exports the tracing data to a local mock http server.
 go run ./go-server/cmd/main.go
 ```
 
-### Run test (client)
+### Run client
 
 ```shell
-go test -tags integration -v ./tests/integration/... 
+go run ./go-client/cmd/main.go
 ```
 
-If test success, you will see the log like below in the server terminal:
+If successful, you will see the log like below in the server terminal:
 
 ```shell
 2025-09-18 16:22:29	INFO	cmd/main.go:127	server count: 3, client count: 3
 ```
 
-If the test fails, you will see the panic in the server terminal.
+If it fails, you will see the panic in the server terminal.
 
diff --git a/otel/tracing/otlp_http_exporter/README_CN.md b/otel/tracing/otlp_http_exporter/README_CN.md
index 59819fcc..dfb95d59 100644
--- a/otel/tracing/otlp_http_exporter/README_CN.md
+++ b/otel/tracing/otlp_http_exporter/README_CN.md
@@ -14,16 +14,16 @@
 go run ./go-server/cmd/main.go
 ```
 
-### 运行测试(客户端)
+### 运行客户端
 
 ```shell
-go test -tags integration -v ./tests/integration/... 
+go run ./go-client/cmd/main.go
 ```
 
-如果测试成功,你会在服务端终端看到类似如下的日志:
+如果运行成功,你会在服务端终端看到类似如下的日志:
 
 ```shell
 2025-09-18 16:22:29	INFO	cmd/main.go:127	server count: 3, client count: 3
 ```
 
-如果测试失败,你会在服务端终端看到 panic。
+如果运行失败,你会在服务端终端看到 panic。
diff --git a/otel/tracing/otlp_http_exporter/go-client/cmd/main.go b/otel/tracing/otlp_http_exporter/go-client/cmd/main.go
new file mode 100644
index 00000000..9722f60c
--- /dev/null
+++ b/otel/tracing/otlp_http_exporter/go-client/cmd/main.go
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package main
+
+import (
+	"context"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3"
+	"dubbo.apache.org/dubbo-go/v3/client"
+	"dubbo.apache.org/dubbo-go/v3/common/constant"
+	_ "dubbo.apache.org/dubbo-go/v3/imports"
+	"dubbo.apache.org/dubbo-go/v3/otel/trace"
+
+	"github.com/dubbogo/gost/log/logger"
+
+	"go.opentelemetry.io/otel"
+	sdktrace "go.opentelemetry.io/otel/sdk/trace"
+)
+
+import (
+	greet "github.com/apache/dubbo-go-samples/otel/tracing/stdout/proto"
+)
+
+func main() {
+	ins, err := dubbo.NewInstance(
+		dubbo.WithName("dubbo_otel_client"),
+		dubbo.WithTracing(
+			trace.WithEnabled(),
+			trace.WithOtlpHttpExporter(),
+			trace.WithW3cPropagator(),
+			trace.WithAlwaysMode(),
+			trace.WithInsecure(),
+			trace.WithEndpoint("127.0.0.1:4318"),
+		),
+	)
+	if err != nil {
+		panic(err)
+	}
+
+	// Triple
+	cli1, err := ins.NewClient(client.WithClientURL("127.0.0.1:20000"))
+	if err != nil {
+		panic(err)
+	}
+	svc, err := greet.NewGreetService(cli1)
+	if err != nil {
+		panic(err)
+	}
+	resp, err := svc.Greet(context.Background(), &greet.GreetRequest{Name: "triple"})
+	if err != nil {
+		logger.Error(err)
+	} else {
+		logger.Infof("Triple response: %s", resp.Greeting)
+	}
+
+	// Dubbo
+	cli2, err := ins.NewClient(
+		client.WithClientURL("127.0.0.1:20001"),
+		client.WithClientProtocolDubbo(),
+		client.WithClientSerialization(constant.Hessian2Serialization),
+	)
+	if err != nil {
+		panic(err)
+	}
+	conn2, err := cli2.Dial("GreetProvider")
+	if err != nil {
+		panic(err)
+	}
+	var dubboResp string
+	if err = conn2.CallUnary(context.Background(), []interface{}{"hello", "new", "dubbo"}, &dubboResp, "SayHello"); err != nil {
+		logger.Error(err)
+	} else {
+		logger.Infof("Dubbo response: %s", dubboResp)
+	}
+
+	// JSONRPC
+	cli3, err := ins.NewClient(
+		client.WithClientURL("127.0.0.1:20002"),
+		client.WithClientProtocolJsonRPC(),
+		client.WithClientSerialization(constant.JSONSerialization),
+	)
+	if err != nil {
+		panic(err)
+	}
+	conn3, err := cli3.Dial("GreetProvider")
+	if err != nil {
+		panic(err)
+	}
+	var jsonrpcResp string
+	if err = conn3.CallUnary(context.Background(), []interface{}{"hello", "new", "jsonrpc"}, &jsonrpcResp, "SayHello"); err != nil {
+		logger.Error(err)
+	} else {
+		logger.Infof("JSONRPC response: %s", jsonrpcResp)
+	}
+
+	if tp, ok := otel.GetTracerProvider().(*sdktrace.TracerProvider); ok {
+		_ = tp.Shutdown(context.Background())
+	}
+}
diff --git a/otel/tracing/otlp_http_exporter/go-server/cmd/main.go b/otel/tracing/otlp_http_exporter/go-server/cmd/main.go
index e7a69fbc..bac55369 100644
--- a/otel/tracing/otlp_http_exporter/go-server/cmd/main.go
+++ b/otel/tracing/otlp_http_exporter/go-server/cmd/main.go
@@ -42,6 +42,7 @@ import (
 	"github.com/golang/protobuf/proto"
 
 	collecttracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
+	tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
 )
 
 import (
@@ -70,6 +71,25 @@ var (
 	errChan            = make(chan error, 6)
 )
 
+func getServiceName(rs *tracepb.ResourceSpans) string {
+	if resource := rs.GetResource(); resource != nil {
+		for _, attr := range resource.GetAttributes() {
+			if attr.GetKey() == "service.name" {
+				return attr.GetValue().GetStringValue()
+			}
+		}
+	}
+	return ""
+}
+
+func getSpanCount(rs *tracepb.ResourceSpans) int {
+	count := 0
+	for _, scopeSpan := range rs.GetScopeSpans() {
+		count += len(scopeSpan.GetSpans())
+	}
+	return count
+}
+
 func mockOtlpReceiver() {
 	mux := http.NewServeMux()
 	mux.HandleFunc("/v1/traces", func(w http.ResponseWriter, r *http.Request) {
@@ -121,15 +141,30 @@ func mockOtlpReceiver() {
 			return
 		}
 
-		reqStr := req.String()
-		switch {
-		case strings.Contains(reqStr, "dubbo_otel_server"):
+		var serverCount, clientCount int
+		for _, resourceSpan := range req.GetResourceSpans() {
+			serviceName := getServiceName(resourceSpan)
+			spanCount := getSpanCount(resourceSpan)
+
+			switch serviceName {
+			case "dubbo_otel_server":
+				serverCount += spanCount
+			case "dubbo_otel_client":
+				clientCount += spanCount
+			}
+		}
+
+		logger.Infof("[receive] server spans: %d, client spans: %d", serverCount, clientCount)
+		for i := 0; i < serverCount; i++ {
 			serverReceivesChan <- true
-		case strings.Contains(reqStr, "dubbo_otel_client"):
+		}
+		for i := 0; i < clientCount; i++ {
 			clientReceivesChan <- true
-		default:
+		}
+
+		if serverCount == 0 && clientCount == 0 {
 			select {
-			case errChan <- errors.New("unknown trace: " + reqStr):
+			case errChan <- errors.New("unknown trace: " + req.String()):
 			default:
 			}
 		}
diff --git a/start_integrate_test.sh b/start_integrate_test.sh
index 1eb5606f..8998862e 100755
--- a/start_integrate_test.sh
+++ b/start_integrate_test.sh
@@ -24,6 +24,7 @@ array+=("helloworld")
 
 # tracing
 array+=("otel/tracing/stdout")
+array+=("otel/tracing/otlp_http_exporter")
 
 # direct
 array+=("direct")
@@ -122,7 +123,11 @@ echo "::endgroup::"
 for t in "${array[@]}"; do
   echo "::group::> start: $t"
   set +e
-  bash ./integrate_test.sh "$t"
+  if [ "$t" = "otel/tracing/otlp_http_exporter" ]; then
+    OTEL_BSP_SCHEDULE_DELAY=100 bash ./integrate_test.sh "$t"
+  else
+    bash ./integrate_test.sh "$t"
+  fi
   result=$?
   set -e
   echo "::endgroup::"