From 43e2705eb92ccc7eceeb699f385babe8ca45a42b Mon Sep 17 00:00:00 2001 From: zheyu Date: Tue, 10 May 2022 14:09:42 +0800 Subject: [PATCH 1/6] otel trace (#1774) --- common/constant/key.go | 2 + filter/otel/trace/attachment.go | 88 ++++ filter/otel/trace/attachment_test.go | 131 ++++++ filter/otel/trace/doc.go | 20 + filter/otel/trace/filter.go | 141 ++++++ filter/otel/trace/filter_test.go | 631 +++++++++++++++++++++++++++ filter/otel/trace/semconv.go | 34 ++ filter/otel/trace/version.go | 28 ++ filter/otel/trace/version_test.go | 59 +++ go.mod | 2 + go.sum | 10 + imports/imports.go | 1 + 12 files changed, 1147 insertions(+) create mode 100644 filter/otel/trace/attachment.go create mode 100644 filter/otel/trace/attachment_test.go create mode 100644 filter/otel/trace/doc.go create mode 100644 filter/otel/trace/filter.go create mode 100644 filter/otel/trace/filter_test.go create mode 100644 filter/otel/trace/semconv.go create mode 100644 filter/otel/trace/version.go create mode 100644 filter/otel/trace/version_test.go diff --git a/common/constant/key.go b/common/constant/key.go index 9949f427b9..6bb6365df3 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -93,6 +93,8 @@ const ( TpsLimitFilterKey = "tps" TracingFilterKey = "tracing" XdsCircuitBreakerKey = "xds_circuit_reaker" + OTELServerTraceKey = "otelServerTrace" + OTELClientTraceKey = "otelClientTrace" ) const ( diff --git a/filter/otel/trace/attachment.go b/filter/otel/trace/attachment.go new file mode 100644 index 0000000000..7d51ce8b40 --- /dev/null +++ b/filter/otel/trace/attachment.go @@ -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. + */ + +package trace + +import ( + "context" +) + +import ( + "go.opentelemetry.io/otel/baggage" + + "go.opentelemetry.io/otel/propagation" + + "go.opentelemetry.io/otel/trace" +) + +type metadataSupplier struct { + metadata *map[string]interface{} +} + +var _ propagation.TextMapCarrier = &metadataSupplier{} + +func (s *metadataSupplier) Get(key string) string { + if s.metadata == nil { + return "" + } + item, ok := (*s.metadata)[key] + if !ok { + return "" + } + value, ok := item.([]string) + if !ok { + return "" + } + if len(value) == 0 { + return "" + } + return value[0] +} + +func (s *metadataSupplier) Set(key string, value string) { + if s.metadata == nil { + s.metadata = &map[string]interface{}{} + } + (*s.metadata)[key] = value +} + +func (s *metadataSupplier) Keys() []string { + out := make([]string, 0, len(*s.metadata)) + for key := range *s.metadata { + out = append(out, key) + } + return out +} + +// Inject injects correlation context and span context into the dubbo +// metadata object. This function is meant to be used on outgoing +// requests. +func Inject(ctx context.Context, metadata *map[string]interface{}, propagators propagation.TextMapPropagator) { + propagators.Inject(ctx, &metadataSupplier{ + metadata: metadata, + }) +} + +// Extract returns the correlation context and span context that +// another service encoded in the dubbo metadata object with Inject. +// This function is meant to be used on incoming requests. +func Extract(ctx context.Context, metadata *map[string]interface{}, propagators propagation.TextMapPropagator) (baggage.Baggage, trace.SpanContext) { + ctx = propagators.Extract(ctx, &metadataSupplier{ + metadata: metadata, + }) + return baggage.FromContext(ctx), trace.SpanContextFromContext(ctx) +} diff --git a/filter/otel/trace/attachment_test.go b/filter/otel/trace/attachment_test.go new file mode 100644 index 0000000000..cf037b30d7 --- /dev/null +++ b/filter/otel/trace/attachment_test.go @@ -0,0 +1,131 @@ +/* + * 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 trace + +import ( + "reflect" + "testing" +) + +func Test_metadataSupplier_Keys(t *testing.T) { + tests := []struct { + name string + metadata *map[string]interface{} + want []string + }{ + { + name: "test", + metadata: &map[string]interface{}{ + "key1": nil, + }, + want: []string{"key1"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &metadataSupplier{ + metadata: tt.metadata, + } + if got := s.Keys(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Keys() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_metadataSupplier_Set(t *testing.T) { + tests := []struct { + name string + metadata *map[string]interface{} + key string + value string + }{ + { + name: "test", + metadata: nil, + key: "key", + value: "value", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &metadataSupplier{ + metadata: tt.metadata, + } + s.Set(tt.key, tt.value) + }) + } +} + +func Test_metadataSupplier_Get(t *testing.T) { + tests := []struct { + name string + metadata *map[string]interface{} + key string + want string + }{ + { + name: "nil metadata", + metadata: nil, + key: "key", + want: "", + }, + { + name: "not exist", + metadata: &map[string]interface{}{ + "k": nil, + }, + key: "key", + want: "", + }, + { + name: "nil slice", + metadata: &map[string]interface{}{ + "key": nil, + }, + key: "key", + want: "", + }, + { + name: "empty slice", + metadata: &map[string]interface{}{ + "key": []string{}, + }, + key: "key", + want: "", + }, + { + name: "test", + metadata: &map[string]interface{}{ + "key": []string{"test"}, + }, + key: "key", + want: "test", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &metadataSupplier{ + metadata: tt.metadata, + } + if got := s.Get(tt.key); got != tt.want { + t.Errorf("Get() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/filter/otel/trace/doc.go b/filter/otel/trace/doc.go new file mode 100644 index 0000000000..c3c1ce3579 --- /dev/null +++ b/filter/otel/trace/doc.go @@ -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. + */ + +// Package trace instruments dubbogo with open-telemetry +// (https://github.com/open-telemetry/opentelemetry-go). +package trace diff --git a/filter/otel/trace/filter.go b/filter/otel/trace/filter.go new file mode 100644 index 0000000000..951faec57c --- /dev/null +++ b/filter/otel/trace/filter.go @@ -0,0 +1,141 @@ +/* + * 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 trace + +import ( + "context" +) + +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/propagation" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "go.opentelemetry.io/otel/trace" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/common/constant" + "dubbo.apache.org/dubbo-go/v3/common/extension" + "dubbo.apache.org/dubbo-go/v3/filter" + "dubbo.apache.org/dubbo-go/v3/protocol" +) + +const ( + instrumentationName = "dubbo.apache.org/dubbo-go/v3/oteldubbo" +) + +func init() { + extension.SetFilter(constant.OTELServerTraceKey, func() filter.Filter { + return &otelServerFilter{ + Propagators: otel.GetTextMapPropagator(), + TracerProvider: otel.GetTracerProvider(), + } + }) + extension.SetFilter(constant.OTELClientTraceKey, func() filter.Filter { + return &otelClientFilter{ + Propagators: otel.GetTextMapPropagator(), + TracerProvider: otel.GetTracerProvider(), + } + }) +} + +type otelServerFilter struct { + Propagators propagation.TextMapPropagator + TracerProvider trace.TracerProvider +} + +func (f *otelServerFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, protocol protocol.Invocation) protocol.Result { + return result +} + +func (f *otelServerFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + attachments := invocation.Attachments() + bags, spanCtx := Extract(ctx, &attachments, f.Propagators) + ctx = baggage.ContextWithBaggage(ctx, bags) + + tracer := f.TracerProvider.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(SemVersion()), + ) + + ctx, span := tracer.Start( + trace.ContextWithRemoteSpanContext(ctx, spanCtx), + invocation.ActualMethodName(), + trace.WithSpanKind(trace.SpanKindServer), + trace.WithAttributes( + RPCSystemDubbo, + semconv.RPCServiceKey.String(invoker.GetURL().ServiceKey()), + semconv.RPCMethodKey.String(invocation.MethodName()), + ), + ) + defer span.End() + + result := invoker.Invoke(ctx, invocation) + + if result.Error() != nil { + span.SetStatus(codes.Error, result.Error().Error()) + } else { + span.SetStatus(codes.Ok, codes.Ok.String()) + } + return result +} + +type otelClientFilter struct { + Propagators propagation.TextMapPropagator + TracerProvider trace.TracerProvider +} + +func (f *otelClientFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, protocol protocol.Invocation) protocol.Result { + return result +} + +func (f *otelClientFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + tracer := f.TracerProvider.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(SemVersion()), + ) + + var span trace.Span + ctx, span = tracer.Start( + ctx, + invocation.ActualMethodName(), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes( + RPCSystemDubbo, + semconv.RPCServiceKey.String(invoker.GetURL().ServiceKey()), + semconv.RPCMethodKey.String(invocation.MethodName()), + ), + ) + defer span.End() + + attachments := make(map[string]interface{}) + Inject(ctx, &(attachments), f.Propagators) + for k, v := range attachments { + invocation.SetAttachment(k, v) + } + result := invoker.Invoke(ctx, invocation) + + if result.Error() != nil { + span.SetStatus(codes.Error, result.Error().Error()) + } else { + span.SetStatus(codes.Ok, codes.Ok.String()) + } + return result +} diff --git a/filter/otel/trace/filter_test.go b/filter/otel/trace/filter_test.go new file mode 100644 index 0000000000..d0e666ef05 --- /dev/null +++ b/filter/otel/trace/filter_test.go @@ -0,0 +1,631 @@ +/* + * 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 trace + +import ( + "context" + "reflect" + "testing" +) + +import ( + "github.com/golang/mock/gomock" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/common" + "dubbo.apache.org/dubbo-go/v3/protocol" +) + +type fields struct { + Propagators propagation.TextMapPropagator + TracerProvider trace.TracerProvider +} +type args struct { + ctx context.Context + result protocol.Result + invoker protocol.Invoker + protocol protocol.Invocation + invocation protocol.Invocation +} + +// MockInvocation is a mock of Invocation interface +type MockInvocation struct { + ctrl *gomock.Controller + recorder *MockInvocationMockRecorder +} + +// MockInvocationMockRecorder is the mock recorder for MockInvocation +type MockInvocationMockRecorder struct { + mock *MockInvocation +} + +// NewMockInvocation creates a new mock instance +func NewMockInvocation(ctrl *gomock.Controller) *MockInvocation { + mock := &MockInvocation{ctrl: ctrl} + mock.recorder = &MockInvocationMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockInvocation) EXPECT() *MockInvocationMockRecorder { + return m.recorder +} + +// MethodName mocks base method +func (m *MockInvocation) MethodName() string { + ret := m.ctrl.Call(m, "MethodName") + ret0, _ := ret[0].(string) + return ret0 +} + +// MethodName indicates an expected call of MethodName +func (mr *MockInvocationMockRecorder) MethodName() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MethodName", reflect.TypeOf((*MockInvocation)(nil).MethodName)) +} + +// ActualMethodName mocks base method +func (m *MockInvocation) ActualMethodName() string { + ret := m.ctrl.Call(m, "ActualMethodName") + ret0, _ := ret[0].(string) + return ret0 +} + +// ActualMethodName indicates an expected call of ActualMethodName +func (mr *MockInvocationMockRecorder) ActualMethodName() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActualMethodName", reflect.TypeOf((*MockInvocation)(nil).ActualMethodName)) +} + +// ParameterTypeNames mocks base method +func (m *MockInvocation) ParameterTypeNames() []string { + ret := m.ctrl.Call(m, "ParameterTypeNames") + ret0, _ := ret[0].([]string) + return ret0 +} + +// ParameterTypeNames indicates an expected call of ParameterTypeNames +func (mr *MockInvocationMockRecorder) ParameterTypeNames() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParameterTypeNames", reflect.TypeOf((*MockInvocation)(nil).ParameterTypeNames)) +} + +// ParameterTypes mocks base method +func (m *MockInvocation) ParameterTypes() []reflect.Type { + ret := m.ctrl.Call(m, "ParameterTypes") + ret0, _ := ret[0].([]reflect.Type) + return ret0 +} + +// ParameterTypes indicates an expected call of ParameterTypes +func (mr *MockInvocationMockRecorder) ParameterTypes() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParameterTypes", reflect.TypeOf((*MockInvocation)(nil).ParameterTypes)) +} + +// ParameterValues mocks base method +func (m *MockInvocation) ParameterValues() []reflect.Value { + ret := m.ctrl.Call(m, "ParameterValues") + ret0, _ := ret[0].([]reflect.Value) + return ret0 +} + +// ParameterValues indicates an expected call of ParameterValues +func (mr *MockInvocationMockRecorder) ParameterValues() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParameterValues", reflect.TypeOf((*MockInvocation)(nil).ParameterValues)) +} + +// Arguments mocks base method +func (m *MockInvocation) Arguments() []interface{} { + ret := m.ctrl.Call(m, "Arguments") + ret0, _ := ret[0].([]interface{}) + return ret0 +} + +// Arguments indicates an expected call of Arguments +func (mr *MockInvocationMockRecorder) Arguments() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Arguments", reflect.TypeOf((*MockInvocation)(nil).Arguments)) +} + +// Reply mocks base method +func (m *MockInvocation) Reply() interface{} { + ret := m.ctrl.Call(m, "Reply") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// Reply indicates an expected call of Reply +func (mr *MockInvocationMockRecorder) Reply() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reply", reflect.TypeOf((*MockInvocation)(nil).Reply)) +} + +// Invoker mocks base method +func (m *MockInvocation) Invoker() protocol.Invoker { + ret := m.ctrl.Call(m, "Invoker") + ret0, _ := ret[0].(protocol.Invoker) + return ret0 +} + +// Invoker indicates an expected call of Invoker +func (mr *MockInvocationMockRecorder) Invoker() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invoker", reflect.TypeOf((*MockInvocation)(nil).Invoker)) +} + +// IsGenericInvocation mocks base method +func (m *MockInvocation) IsGenericInvocation() bool { + ret := m.ctrl.Call(m, "IsGenericInvocation") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsGenericInvocation indicates an expected call of IsGenericInvocation +func (mr *MockInvocationMockRecorder) IsGenericInvocation() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsGenericInvocation", reflect.TypeOf((*MockInvocation)(nil).IsGenericInvocation)) +} + +// Attachments mocks base method +func (m *MockInvocation) Attachments() map[string]interface{} { + ret := m.ctrl.Call(m, "Attachments") + ret0, _ := ret[0].(map[string]interface{}) + return ret0 +} + +// Attachments indicates an expected call of Attachments +func (mr *MockInvocationMockRecorder) Attachments() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attachments", reflect.TypeOf((*MockInvocation)(nil).Attachments)) +} + +// SetAttachment mocks base method +func (m *MockInvocation) SetAttachment(key string, value interface{}) { + m.ctrl.Call(m, "SetAttachment", key, value) +} + +// SetAttachment indicates an expected call of SetAttachment +func (mr *MockInvocationMockRecorder) SetAttachment(key, value interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAttachment", reflect.TypeOf((*MockInvocation)(nil).SetAttachment), key, value) +} + +// GetAttachment mocks base method +func (m *MockInvocation) GetAttachment(key string) (string, bool) { + ret := m.ctrl.Call(m, "GetAttachment", key) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetAttachment indicates an expected call of GetAttachment +func (mr *MockInvocationMockRecorder) GetAttachment(key interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttachment", reflect.TypeOf((*MockInvocation)(nil).GetAttachment), key) +} + +// GetAttachmentInterface mocks base method +func (m *MockInvocation) GetAttachmentInterface(arg0 string) interface{} { + ret := m.ctrl.Call(m, "GetAttachmentInterface", arg0) + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// GetAttachmentInterface indicates an expected call of GetAttachmentInterface +func (mr *MockInvocationMockRecorder) GetAttachmentInterface(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttachmentInterface", reflect.TypeOf((*MockInvocation)(nil).GetAttachmentInterface), arg0) +} + +// GetAttachmentWithDefaultValue mocks base method +func (m *MockInvocation) GetAttachmentWithDefaultValue(key, defaultValue string) string { + ret := m.ctrl.Call(m, "GetAttachmentWithDefaultValue", key, defaultValue) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetAttachmentWithDefaultValue indicates an expected call of GetAttachmentWithDefaultValue +func (mr *MockInvocationMockRecorder) GetAttachmentWithDefaultValue(key, defaultValue interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttachmentWithDefaultValue", reflect.TypeOf((*MockInvocation)(nil).GetAttachmentWithDefaultValue), key, defaultValue) +} + +// GetAttachmentAsContext mocks base method +func (m *MockInvocation) GetAttachmentAsContext() context.Context { + ret := m.ctrl.Call(m, "GetAttachmentAsContext") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// GetAttachmentAsContext indicates an expected call of GetAttachmentAsContext +func (mr *MockInvocationMockRecorder) GetAttachmentAsContext() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttachmentAsContext", reflect.TypeOf((*MockInvocation)(nil).GetAttachmentAsContext)) +} + +// Attributes mocks base method +func (m *MockInvocation) Attributes() map[string]interface{} { + ret := m.ctrl.Call(m, "Attributes") + ret0, _ := ret[0].(map[string]interface{}) + return ret0 +} + +// Attributes indicates an expected call of Attributes +func (mr *MockInvocationMockRecorder) Attributes() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attributes", reflect.TypeOf((*MockInvocation)(nil).Attributes)) +} + +// SetAttribute mocks base method +func (m *MockInvocation) SetAttribute(key string, value interface{}) { + m.ctrl.Call(m, "SetAttribute", key, value) +} + +// SetAttribute indicates an expected call of SetAttribute +func (mr *MockInvocationMockRecorder) SetAttribute(key, value interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAttribute", reflect.TypeOf((*MockInvocation)(nil).SetAttribute), key, value) +} + +// GetAttribute mocks base method +func (m *MockInvocation) GetAttribute(key string) (interface{}, bool) { + ret := m.ctrl.Call(m, "GetAttribute", key) + ret0, _ := ret[0].(interface{}) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetAttribute indicates an expected call of GetAttribute +func (mr *MockInvocationMockRecorder) GetAttribute(key interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttribute", reflect.TypeOf((*MockInvocation)(nil).GetAttribute), key) +} + +// GetAttributeWithDefaultValue mocks base method +func (m *MockInvocation) GetAttributeWithDefaultValue(key string, defaultValue interface{}) interface{} { + ret := m.ctrl.Call(m, "GetAttributeWithDefaultValue", key, defaultValue) + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// GetAttributeWithDefaultValue indicates an expected call of GetAttributeWithDefaultValue +func (mr *MockInvocationMockRecorder) GetAttributeWithDefaultValue(key, defaultValue interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttributeWithDefaultValue", reflect.TypeOf((*MockInvocation)(nil).GetAttributeWithDefaultValue), key, defaultValue) +} + +// MockResult is a mock of Result interface +type MockResult struct { + ctrl *gomock.Controller + recorder *MockResultMockRecorder +} + +// MockResultMockRecorder is the mock recorder for MockResult +type MockResultMockRecorder struct { + mock *MockResult +} + +// NewMockResult creates a new mock instance +func NewMockResult(ctrl *gomock.Controller) *MockResult { + mock := &MockResult{ctrl: ctrl} + mock.recorder = &MockResultMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockResult) EXPECT() *MockResultMockRecorder { + return m.recorder +} + +// SetError mocks base method +func (m *MockResult) SetError(arg0 error) { + m.ctrl.Call(m, "SetError", arg0) +} + +// SetError indicates an expected call of SetError +func (mr *MockResultMockRecorder) SetError(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetError", reflect.TypeOf((*MockResult)(nil).SetError), arg0) +} + +// Error mocks base method +func (m *MockResult) Error() error { + ret := m.ctrl.Call(m, "Error") + ret0, _ := ret[0].(error) + return ret0 +} + +// Error indicates an expected call of Error +func (mr *MockResultMockRecorder) Error() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockResult)(nil).Error)) +} + +// SetResult mocks base method +func (m *MockResult) SetResult(arg0 interface{}) { + m.ctrl.Call(m, "SetResult", arg0) +} + +// SetResult indicates an expected call of SetResult +func (mr *MockResultMockRecorder) SetResult(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetResult", reflect.TypeOf((*MockResult)(nil).SetResult), arg0) +} + +// Result mocks base method +func (m *MockResult) Result() interface{} { + ret := m.ctrl.Call(m, "Result") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// Result indicates an expected call of Result +func (mr *MockResultMockRecorder) Result() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Result", reflect.TypeOf((*MockResult)(nil).Result)) +} + +// SetAttachments mocks base method +func (m *MockResult) SetAttachments(arg0 map[string]interface{}) { + m.ctrl.Call(m, "SetAttachments", arg0) +} + +// SetAttachments indicates an expected call of SetAttachments +func (mr *MockResultMockRecorder) SetAttachments(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAttachments", reflect.TypeOf((*MockResult)(nil).SetAttachments), arg0) +} + +// Attachments mocks base method +func (m *MockResult) Attachments() map[string]interface{} { + ret := m.ctrl.Call(m, "Attachments") + ret0, _ := ret[0].(map[string]interface{}) + return ret0 +} + +// Attachments indicates an expected call of Attachments +func (mr *MockResultMockRecorder) Attachments() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attachments", reflect.TypeOf((*MockResult)(nil).Attachments)) +} + +// AddAttachment mocks base method +func (m *MockResult) AddAttachment(arg0 string, arg1 interface{}) { + m.ctrl.Call(m, "AddAttachment", arg0, arg1) +} + +// AddAttachment indicates an expected call of AddAttachment +func (mr *MockResultMockRecorder) AddAttachment(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddAttachment", reflect.TypeOf((*MockResult)(nil).AddAttachment), arg0, arg1) +} + +// Attachment mocks base method +func (m *MockResult) Attachment(arg0 string, arg1 interface{}) interface{} { + ret := m.ctrl.Call(m, "Attachment", arg0, arg1) + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// Attachment indicates an expected call of Attachment +func (mr *MockResultMockRecorder) Attachment(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attachment", reflect.TypeOf((*MockResult)(nil).Attachment), arg0, arg1) +} + +// MockInvoker is a mock of Invoker interface +type MockInvoker struct { + ctrl *gomock.Controller + recorder *MockInvokerMockRecorder +} + +// MockInvokerMockRecorder is the mock recorder for MockInvoker +type MockInvokerMockRecorder struct { + mock *MockInvoker +} + +// NewMockInvoker creates a new mock instance +func NewMockInvoker(ctrl *gomock.Controller) *MockInvoker { + mock := &MockInvoker{ctrl: ctrl} + mock.recorder = &MockInvokerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockInvoker) EXPECT() *MockInvokerMockRecorder { + return m.recorder +} + +// GetURL mocks base method +func (m *MockInvoker) GetURL() *common.URL { + ret := m.ctrl.Call(m, "GetURL") + ret0, _ := ret[0].(*common.URL) + return ret0 +} + +// GetURL indicates an expected call of GetURL +func (mr *MockInvokerMockRecorder) GetURL() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetURL", reflect.TypeOf((*MockInvoker)(nil).GetURL)) +} + +// IsAvailable mocks base method +func (m *MockInvoker) IsAvailable() bool { + ret := m.ctrl.Call(m, "IsAvailable") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsAvailable indicates an expected call of IsAvailable +func (mr *MockInvokerMockRecorder) IsAvailable() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsAvailable", reflect.TypeOf((*MockInvoker)(nil).IsAvailable)) +} + +// Destroy mocks base method +func (m *MockInvoker) Destroy() { + m.ctrl.Call(m, "Destroy") +} + +// Destroy indicates an expected call of Destroy +func (mr *MockInvokerMockRecorder) Destroy() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Destroy", reflect.TypeOf((*MockInvoker)(nil).Destroy)) +} + +// Invoke mocks base method +func (m *MockInvoker) Invoke(arg0 context.Context, arg1 protocol.Invocation) protocol.Result { + ret := m.ctrl.Call(m, "Invoke", arg0, arg1) + ret0, _ := ret[0].(protocol.Result) + return ret0 +} + +// Invoke indicates an expected call of Invoke +func (mr *MockInvokerMockRecorder) Invoke(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invoke", reflect.TypeOf((*MockInvoker)(nil).Invoke), arg0, arg1) +} + +func getFields() fields { + return fields{ + Propagators: otel.GetTextMapPropagator(), + TracerProvider: otel.GetTracerProvider(), + } +} + +func Test_otelServerFilter_OnResponse(t *testing.T) { + tests := []struct { + name string + fields fields + args args + want protocol.Result + }{ + { + name: "test", + fields: getFields(), + args: args{}, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &otelServerFilter{ + Propagators: tt.fields.Propagators, + TracerProvider: tt.fields.TracerProvider, + } + if got := f.OnResponse(tt.args.ctx, tt.args.result, tt.args.invoker, tt.args.protocol); !reflect.DeepEqual(got, tt.want) { + t.Errorf("OnResponse() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_otelClientFilter_OnResponse(t *testing.T) { + tests := []struct { + name string + fields fields + args args + want protocol.Result + }{ + { + name: "test", + fields: getFields(), + args: args{}, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &otelClientFilter{ + Propagators: tt.fields.Propagators, + TracerProvider: tt.fields.TracerProvider, + } + if got := f.OnResponse(tt.args.ctx, tt.args.result, tt.args.invoker, tt.args.protocol); !reflect.DeepEqual(got, tt.want) { + t.Errorf("OnResponse() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_otelServerFilter_Invoke(t *testing.T) { + ctrl := gomock.NewController(t) + + result := NewMockResult(ctrl) + result.EXPECT().Error().Return(nil).AnyTimes() + + invoker := NewMockInvoker(ctrl) + invoker.EXPECT().GetURL().Return(&common.URL{}).AnyTimes() + invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(result).AnyTimes() + + invocation := NewMockInvocation(ctrl) + invocation.EXPECT().ActualMethodName().Return("oteldubbogo").AnyTimes() + invocation.EXPECT().MethodName().Return("otel").AnyTimes() + invocation.EXPECT().SetAttachment(gomock.Any(), gomock.Any()).Return().AnyTimes() + invocation.EXPECT().Attachments().Return(map[string]interface{}{}).AnyTimes() + + tests := []struct { + name string + fields fields + args args + want protocol.Result + }{ + { + name: "test", + fields: getFields(), + args: args{ + ctx: context.Background(), + invoker: invoker, + invocation: invocation, + }, + want: result, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &otelServerFilter{ + Propagators: tt.fields.Propagators, + TracerProvider: tt.fields.TracerProvider, + } + if got := f.Invoke(tt.args.ctx, tt.args.invoker, tt.args.invocation); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Invoke() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_otelClientFilter_Invoke(t *testing.T) { + ctrl := gomock.NewController(t) + + result := NewMockResult(ctrl) + result.EXPECT().Error().Return(nil).AnyTimes() + + invoker := NewMockInvoker(ctrl) + invoker.EXPECT().GetURL().Return(&common.URL{}).AnyTimes() + invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(result).AnyTimes() + + invocation := NewMockInvocation(ctrl) + invocation.EXPECT().ActualMethodName().Return("oteldubbogo").AnyTimes() + invocation.EXPECT().MethodName().Return("otel").AnyTimes() + invocation.EXPECT().SetAttachment(gomock.Any(), gomock.Any()).Return().AnyTimes() + invocation.EXPECT().Attachments().Return(map[string]interface{}{}).AnyTimes() + + tests := []struct { + name string + fields fields + args args + want protocol.Result + }{ + { + name: "test", + fields: getFields(), + args: args{ + ctx: context.Background(), + invoker: invoker, + invocation: invocation, + }, + want: result, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &otelClientFilter{ + Propagators: tt.fields.Propagators, + TracerProvider: tt.fields.TracerProvider, + } + if got := f.Invoke(tt.args.ctx, tt.args.invoker, tt.args.invocation); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Invoke() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/filter/otel/trace/semconv.go b/filter/otel/trace/semconv.go new file mode 100644 index 0000000000..16619498f6 --- /dev/null +++ b/filter/otel/trace/semconv.go @@ -0,0 +1,34 @@ +/* + * 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 trace + +import ( + "go.opentelemetry.io/otel/attribute" + + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" +) + +var ( + RPCNameKey = attribute.Key("name") + RPCMessageTypeKey = attribute.Key("message.type") + RPCMessageIDKey = attribute.Key("message.id") + RPCSystemDubbo = semconv.RPCSystemKey.String("apache_dubbo") + RPCNameMessage = RPCNameKey.String("message") + RPCMessageTypeSent = RPCMessageTypeKey.String("SENT") + RPCMessageTypeReceived = RPCMessageTypeKey.String("RECEIVED") +) diff --git a/filter/otel/trace/version.go b/filter/otel/trace/version.go new file mode 100644 index 0000000000..25eb3d2f66 --- /dev/null +++ b/filter/otel/trace/version.go @@ -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. + */ + +package trace + +// Version is the current release version of the dubbogo instrumentation. +func Version() string { + return "3.0.0" +} + +// SemVersion is the semantic version to be supplied to tracer/meter creation. +func SemVersion() string { + return "semver:" + Version() +} diff --git a/filter/otel/trace/version_test.go b/filter/otel/trace/version_test.go new file mode 100644 index 0000000000..a01bb477f9 --- /dev/null +++ b/filter/otel/trace/version_test.go @@ -0,0 +1,59 @@ +/* + * 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 trace + +import ( + "testing" +) + +type filed struct { + name string + want string +} + +func TestSemVersion(t *testing.T) { + tests := []filed{ + { + name: "test", + want: "semver:3.0.0", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := SemVersion(); got != tt.want { + t.Errorf("SemVersion() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestVersion(t *testing.T) { + tests := []filed{ + { + name: "test", + want: "3.0.0", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Version(); got != tt.want { + t.Errorf("Version() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/go.mod b/go.mod index 683d103328..fa4a50fb76 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,8 @@ require ( github.com/zouyx/agollo/v3 v3.4.5 go.etcd.io/etcd/api/v3 v3.5.4 go.etcd.io/etcd/client/v3 v3.5.4 + go.opentelemetry.io/otel v1.7.0 + go.opentelemetry.io/otel/trace v1.7.0 go.uber.org/atomic v1.9.0 go.uber.org/zap v1.21.0 google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 diff --git a/go.sum b/go.sum index 066e4a7615..50cb60e585 100644 --- a/go.sum +++ b/go.sum @@ -249,6 +249,11 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -337,6 +342,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -794,6 +800,10 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= diff --git a/imports/imports.go b/imports/imports.go index 4fce97f77c..3ac1d74abc 100644 --- a/imports/imports.go +++ b/imports/imports.go @@ -46,6 +46,7 @@ import ( _ "dubbo.apache.org/dubbo-go/v3/filter/graceful_shutdown" _ "dubbo.apache.org/dubbo-go/v3/filter/hystrix" _ "dubbo.apache.org/dubbo-go/v3/filter/metrics" + _ "dubbo.apache.org/dubbo-go/v3/filter/otel/trace" _ "dubbo.apache.org/dubbo-go/v3/filter/seata" _ "dubbo.apache.org/dubbo-go/v3/filter/sentinel" _ "dubbo.apache.org/dubbo-go/v3/filter/token" From 5e12eb3a405abd815adf557db87a72557848a70c Mon Sep 17 00:00:00 2001 From: zheyu Date: Mon, 16 May 2022 16:38:20 +0800 Subject: [PATCH 2/6] metdata use map (#1774) --- filter/otel/trace/attachment.go | 16 ++++++++-------- filter/otel/trace/attachment_test.go | 16 ++++++++-------- filter/otel/trace/filter.go | 9 ++++++--- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/filter/otel/trace/attachment.go b/filter/otel/trace/attachment.go index 7d51ce8b40..d9b0992369 100644 --- a/filter/otel/trace/attachment.go +++ b/filter/otel/trace/attachment.go @@ -30,7 +30,7 @@ import ( ) type metadataSupplier struct { - metadata *map[string]interface{} + metadata map[string]interface{} } var _ propagation.TextMapCarrier = &metadataSupplier{} @@ -39,7 +39,7 @@ func (s *metadataSupplier) Get(key string) string { if s.metadata == nil { return "" } - item, ok := (*s.metadata)[key] + item, ok := s.metadata[key] if !ok { return "" } @@ -55,14 +55,14 @@ func (s *metadataSupplier) Get(key string) string { func (s *metadataSupplier) Set(key string, value string) { if s.metadata == nil { - s.metadata = &map[string]interface{}{} + s.metadata = map[string]interface{}{} } - (*s.metadata)[key] = value + s.metadata[key] = value } func (s *metadataSupplier) Keys() []string { - out := make([]string, 0, len(*s.metadata)) - for key := range *s.metadata { + out := make([]string, 0, len(s.metadata)) + for key := range s.metadata { out = append(out, key) } return out @@ -71,7 +71,7 @@ func (s *metadataSupplier) Keys() []string { // Inject injects correlation context and span context into the dubbo // metadata object. This function is meant to be used on outgoing // requests. -func Inject(ctx context.Context, metadata *map[string]interface{}, propagators propagation.TextMapPropagator) { +func Inject(ctx context.Context, metadata map[string]interface{}, propagators propagation.TextMapPropagator) { propagators.Inject(ctx, &metadataSupplier{ metadata: metadata, }) @@ -80,7 +80,7 @@ func Inject(ctx context.Context, metadata *map[string]interface{}, propagators p // Extract returns the correlation context and span context that // another service encoded in the dubbo metadata object with Inject. // This function is meant to be used on incoming requests. -func Extract(ctx context.Context, metadata *map[string]interface{}, propagators propagation.TextMapPropagator) (baggage.Baggage, trace.SpanContext) { +func Extract(ctx context.Context, metadata map[string]interface{}, propagators propagation.TextMapPropagator) (baggage.Baggage, trace.SpanContext) { ctx = propagators.Extract(ctx, &metadataSupplier{ metadata: metadata, }) diff --git a/filter/otel/trace/attachment_test.go b/filter/otel/trace/attachment_test.go index cf037b30d7..e20078a8c3 100644 --- a/filter/otel/trace/attachment_test.go +++ b/filter/otel/trace/attachment_test.go @@ -25,12 +25,12 @@ import ( func Test_metadataSupplier_Keys(t *testing.T) { tests := []struct { name string - metadata *map[string]interface{} + metadata map[string]interface{} want []string }{ { name: "test", - metadata: &map[string]interface{}{ + metadata: map[string]interface{}{ "key1": nil, }, want: []string{"key1"}, @@ -51,7 +51,7 @@ func Test_metadataSupplier_Keys(t *testing.T) { func Test_metadataSupplier_Set(t *testing.T) { tests := []struct { name string - metadata *map[string]interface{} + metadata map[string]interface{} key string value string }{ @@ -75,7 +75,7 @@ func Test_metadataSupplier_Set(t *testing.T) { func Test_metadataSupplier_Get(t *testing.T) { tests := []struct { name string - metadata *map[string]interface{} + metadata map[string]interface{} key string want string }{ @@ -87,7 +87,7 @@ func Test_metadataSupplier_Get(t *testing.T) { }, { name: "not exist", - metadata: &map[string]interface{}{ + metadata: map[string]interface{}{ "k": nil, }, key: "key", @@ -95,7 +95,7 @@ func Test_metadataSupplier_Get(t *testing.T) { }, { name: "nil slice", - metadata: &map[string]interface{}{ + metadata: map[string]interface{}{ "key": nil, }, key: "key", @@ -103,7 +103,7 @@ func Test_metadataSupplier_Get(t *testing.T) { }, { name: "empty slice", - metadata: &map[string]interface{}{ + metadata: map[string]interface{}{ "key": []string{}, }, key: "key", @@ -111,7 +111,7 @@ func Test_metadataSupplier_Get(t *testing.T) { }, { name: "test", - metadata: &map[string]interface{}{ + metadata: map[string]interface{}{ "key": []string{"test"}, }, key: "key", diff --git a/filter/otel/trace/filter.go b/filter/otel/trace/filter.go index 951faec57c..bf3da78258 100644 --- a/filter/otel/trace/filter.go +++ b/filter/otel/trace/filter.go @@ -67,7 +67,7 @@ func (f *otelServerFilter) OnResponse(ctx context.Context, result protocol.Resul func (f *otelServerFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { attachments := invocation.Attachments() - bags, spanCtx := Extract(ctx, &attachments, f.Propagators) + bags, spanCtx := Extract(ctx, attachments, f.Propagators) ctx = baggage.ContextWithBaggage(ctx, bags) tracer := f.TracerProvider.Tracer( @@ -125,8 +125,11 @@ func (f *otelClientFilter) Invoke(ctx context.Context, invoker protocol.Invoker, ) defer span.End() - attachments := make(map[string]interface{}) - Inject(ctx, &(attachments), f.Propagators) + attachments := invocation.Attachments() + if attachments == nil { + attachments = map[string]interface{}{} + } + Inject(ctx, attachments, f.Propagators) for k, v := range attachments { invocation.SetAttachment(k, v) } From bda51cee6da224b39c4f56043592d53780b98ab9 Mon Sep 17 00:00:00 2001 From: pherzheyu <99853473+pherzheyu@users.noreply.github.com> Date: Tue, 17 May 2022 17:41:32 +0800 Subject: [PATCH 3/6] metadata update --- filter/otel/trace/attachment.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/filter/otel/trace/attachment.go b/filter/otel/trace/attachment.go index d9b0992369..6d84eff267 100644 --- a/filter/otel/trace/attachment.go +++ b/filter/otel/trace/attachment.go @@ -39,18 +39,14 @@ func (s *metadataSupplier) Get(key string) string { if s.metadata == nil { return "" } - item, ok := s.metadata[key] + item, ok := s.metadata[key].([]string) if !ok { return "" } - value, ok := item.([]string) - if !ok { - return "" - } - if len(value) == 0 { + if len(item) == 0 { return "" } - return value[0] + return item[0] } func (s *metadataSupplier) Set(key string, value string) { From 1d4bc7125d6e50574f7fe2ef4d506f80891b929e Mon Sep 17 00:00:00 2001 From: pherzheyu <99853473+pherzheyu@users.noreply.github.com> Date: Tue, 17 May 2022 17:42:32 +0800 Subject: [PATCH 4/6] otel version use dubbo version --- filter/otel/trace/version.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/filter/otel/trace/version.go b/filter/otel/trace/version.go index 25eb3d2f66..d266fcee4a 100644 --- a/filter/otel/trace/version.go +++ b/filter/otel/trace/version.go @@ -17,9 +17,13 @@ package trace +import ( + "dubbo.apache.org/dubbo-go/v3/common/constant" +) + // Version is the current release version of the dubbogo instrumentation. func Version() string { - return "3.0.0" + return constant.Version } // SemVersion is the semantic version to be supplied to tracer/meter creation. From 8424de38ba5c28da26491b1f89a6e8dea2f9ff8c Mon Sep 17 00:00:00 2001 From: pherzheyu <99853473+pherzheyu@users.noreply.github.com> Date: Tue, 17 May 2022 17:43:51 +0800 Subject: [PATCH 5/6] update version ut --- filter/otel/trace/version_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/filter/otel/trace/version_test.go b/filter/otel/trace/version_test.go index a01bb477f9..1d1b541a36 100644 --- a/filter/otel/trace/version_test.go +++ b/filter/otel/trace/version_test.go @@ -21,6 +21,10 @@ import ( "testing" ) +import ( + "dubbo.apache.org/dubbo-go/v3/common/constant" +) + type filed struct { name string want string @@ -30,7 +34,7 @@ func TestSemVersion(t *testing.T) { tests := []filed{ { name: "test", - want: "semver:3.0.0", + want: "semver:" + constant.Version, }, } for _, tt := range tests { From 794059abaeef3249170a338f6dbf123b9ddb07f7 Mon Sep 17 00:00:00 2001 From: pherzheyu <99853473+pherzheyu@users.noreply.github.com> Date: Tue, 17 May 2022 17:57:52 +0800 Subject: [PATCH 6/6] update version ut --- filter/otel/trace/version_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter/otel/trace/version_test.go b/filter/otel/trace/version_test.go index 1d1b541a36..332be01ba3 100644 --- a/filter/otel/trace/version_test.go +++ b/filter/otel/trace/version_test.go @@ -50,7 +50,7 @@ func TestVersion(t *testing.T) { tests := []filed{ { name: "test", - want: "3.0.0", + want: constant.Version, }, } for _, tt := range tests {