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::"