From 2a4bde005358b620208aca2e87f26b0a1b389d58 Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Wed, 16 Nov 2022 13:28:06 +0800 Subject: [PATCH 01/12] [otelgrpc] refactor otelgrpc to use grpc.StatsHandler Signed-off-by: Ziqi Zhao --- CHANGELOG.md | 1 + .../google.golang.org/grpc/otelgrpc/README.md | 19 + .../grpc/otelgrpc/stats_handler.go | 187 ++++++ .../otelgrpc/test/grpc_stats_handler_test.go | 581 ++++++++++++++++++ 4 files changed, 788 insertions(+) create mode 100644 instrumentation/google.golang.org/grpc/otelgrpc/README.md create mode 100644 instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go create mode 100644 instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f820a5d037..00192d22a0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add `NewMiddleware` function in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#2964) - Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108) - The `go.opentelemetry.io/contrib/exporters/autoexport` package to provide configuration of trace exporters with useful defaults and envar support. (#2753) +- [otelgrpc] implement `grpc.StatsHandler` for grpc instrumentation. (#3002) ### Fixed diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/README.md b/instrumentation/google.golang.org/grpc/otelgrpc/README.md new file mode 100644 index 00000000000..bac7c1e7c95 --- /dev/null +++ b/instrumentation/google.golang.org/grpc/otelgrpc/README.md @@ -0,0 +1,19 @@ +# How to usage + +This library is the instrumentation library for `google.golang.org/grpc` + +For now you can instrument your program which use `google.golang.org/grpc` in two ways: + +- by gRPC Interceptors +- [experimental] by gPRC `stats.Handler` + +You can see the example of both ways in directory `./example` + +Although the implementation `stats.Handler` in experimental stage, we strongly still recommand you to use `stats.Handler`, mainly for two reasons: +- **Functional advantages**: `stats.Handler`` has more information for user to build more flexible and granular metric, for example + - multiple different types of represent "data length": In [InPayLoad](https://pkg.go.dev/google.golang.org/grpc/stats#InPayload), there exists `Length`, `CompressedLength`, `WireLength` to denote the size of uncompressed, compressed payload data, with or without framing data. But in Interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. [#3168](https://github.com/open-telemetry/opentelemetry-go-contrib/pull/3168) + - more accurate timestamp: `InPayload.RecvTime` and `OutPayload.SentTime` records more accurate timestamp that server got and sent the message, the timestamp recorded by interceptors depends on the location of this interceptors in the total interceptor chain. + - some other use cases: for example [catch failure of decoding message](https://github.com/open-telemetry/opentelemetry-go-contrib/issues/197#issuecomment-668377700) +- **Performance advantages**: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. + +You should also **notice** that: **Do not use both two ways in the meantime!** If so, you will get duplicated spans and the parent/child relationships between spans will also be broken. diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go new file mode 100644 index 00000000000..7b447aced28 --- /dev/null +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -0,0 +1,187 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed 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 otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + +import ( + "context" + "sync/atomic" + + grpc_codes "google.golang.org/grpc/codes" + "google.golang.org/grpc/stats" + "google.golang.org/grpc/status" + + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal" + "go.opentelemetry.io/otel/codes" + semconv "go.opentelemetry.io/otel/semconv/v1.12.0" + "go.opentelemetry.io/otel/trace" +) + +type gRPCContextKey struct{} + +type gRPCContext struct { + messagesReceived int64 + messagesSent int64 +} + +// NewServerHandler creates a stats.Handler for gRPC server. +func NewServerHandler(opts ...Option) stats.Handler { + h := &serverHandler{ + config: newConfig(opts), + } + + h.tracer = h.config.TracerProvider.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(SemVersion()), + ) + return h +} + +type serverHandler struct { + *config + tracer trace.Tracer +} + +// TagRPC can attach some information to the given context. +func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { + ctx = extract(ctx, h.config.Propagators) + + name, attrs := internal.ParseFullMethod(info.FullMethodName) + attrs = append(attrs, RPCSystemGRPC) + ctx, _ = h.tracer.Start( + trace.ContextWithRemoteSpanContext(ctx, trace.SpanContextFromContext(ctx)), + name, + trace.WithSpanKind(trace.SpanKindServer), + trace.WithAttributes(attrs...), + ) + + gctx := gRPCContext{} + return context.WithValue(ctx, gRPCContextKey{}, &gctx) +} + +// HandleRPC processes the RPC stats. +func (h *serverHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { + handleRPC(ctx, rs) +} + +// TagConn can attach some information to the given context. +func (h *serverHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context { + span := trace.SpanFromContext(ctx) + attrs := peerAttr(peerFromCtx(ctx)) + span.SetAttributes(attrs...) + return ctx +} + +// HandleConn processes the Conn stats. +func (h *serverHandler) HandleConn(ctx context.Context, info stats.ConnStats) { +} + +// NewClientHandler creates a stats.Handler for gRPC client. +func NewClientHandler(opts ...Option) stats.Handler { + h := &clientHandler{ + config: newConfig(opts), + } + + h.tracer = h.config.TracerProvider.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(SemVersion()), + ) + + return h +} + +type clientHandler struct { + *config + tracer trace.Tracer +} + +// TagRPC can attach some information to the given context. +func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { + name, attrs := internal.ParseFullMethod(info.FullMethodName) + attrs = append(attrs, RPCSystemGRPC) + ctx, _ = h.tracer.Start( + ctx, + name, + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(attrs...), + ) + + gctx := gRPCContext{} + + return inject(context.WithValue(ctx, gRPCContextKey{}, &gctx), h.config.Propagators) +} + +// HandleRPC processes the RPC stats. +func (h *clientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { + handleRPC(ctx, rs) +} + +// TagConn can attach some information to the given context. +func (h *clientHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) context.Context { + span := trace.SpanFromContext(ctx) + attrs := peerAttr(cti.RemoteAddr.String()) + span.SetAttributes(attrs...) + return ctx +} + +// HandleConn processes the Conn stats. +func (h *clientHandler) HandleConn(context.Context, stats.ConnStats) { + // no-op +} + +func handleRPC(ctx context.Context, rs stats.RPCStats) { + span := trace.SpanFromContext(ctx) + gctx, _ := ctx.Value(gRPCContextKey{}).(*gRPCContext) + var messageId int64 + + switch rs := rs.(type) { + case *stats.Begin: + case *stats.InPayload: + if gctx != nil { + messageId = atomic.AddInt64(&gctx.messagesReceived, 1) + } + span.AddEvent("message", + trace.WithAttributes( + semconv.MessageTypeReceived, + semconv.MessageIDKey.Int64(messageId), + semconv.MessageCompressedSizeKey.Int(rs.CompressedLength), + semconv.MessageUncompressedSizeKey.Int(rs.Length), + ), + ) + case *stats.OutPayload: + if gctx != nil { + messageId = atomic.AddInt64(&gctx.messagesSent, 1) + } + + span.AddEvent("message", + trace.WithAttributes( + semconv.MessageTypeSent, + semconv.MessageIDKey.Int64(messageId), + semconv.MessageCompressedSizeKey.Int(rs.CompressedLength), + semconv.MessageUncompressedSizeKey.Int(rs.Length), + ), + ) + case *stats.End: + if rs.Error != nil { + s, _ := status.FromError(rs.Error) + span.SetStatus(codes.Error, s.Message()) + span.SetAttributes(statusCodeAttr(s.Code())) + } else { + span.SetAttributes(statusCodeAttr(grpc_codes.OK)) + } + span.End() + default: + return + } +} diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go new file mode 100644 index 00000000000..b6afd1a118d --- /dev/null +++ b/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go @@ -0,0 +1,581 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed 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 test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" +) + +func TestStatsHandler(t *testing.T) { + clientSR := tracetest.NewSpanRecorder() + clientTP := trace.NewTracerProvider(trace.WithSpanProcessor(clientSR)) + + serverSR := tracetest.NewSpanRecorder() + serverTP := trace.NewTracerProvider(trace.WithSpanProcessor(serverSR)) + + assert.NoError(t, doCalls( + []grpc.DialOption{ + grpc.WithStatsHandler(otelgrpc.NewClientHandler(otelgrpc.WithTracerProvider(clientTP))), + }, + []grpc.ServerOption{ + grpc.StatsHandler(otelgrpc.NewServerHandler(otelgrpc.WithTracerProvider(serverTP))), + }, + )) + + t.Run("ClientSpans", func(t *testing.T) { + checkClientSpans(t, clientSR.Ended()) + }) + + t.Run("ServerSpans", func(t *testing.T) { + checkServerSpans(t, serverSR.Ended()) + }) +} + +func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan) { + require.Len(t, spans, 5) + + emptySpan := spans[0] + assert.False(t, emptySpan.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/EmptyCall", emptySpan.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(0), + otelgrpc.RPCMessageUncompressedSizeKey.Int(0), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(0), + otelgrpc.RPCMessageUncompressedSizeKey.Int(0), + }, + }, + }, emptySpan.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("EmptyCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, emptySpan.Attributes()) + + largeSpan := spans[1] + assert.False(t, largeSpan.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/UnaryCall", largeSpan.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(271840), + otelgrpc.RPCMessageUncompressedSizeKey.Int(271840), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(314167), + otelgrpc.RPCMessageUncompressedSizeKey.Int(314167), + }, + }, + }, largeSpan.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("UnaryCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, largeSpan.Attributes()) + + streamInput := spans[2] + assert.False(t, streamInput.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/StreamingInputCall", streamInput.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(27190), + otelgrpc.RPCMessageUncompressedSizeKey.Int(27190), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(12), + otelgrpc.RPCMessageUncompressedSizeKey.Int(12), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(1834), + otelgrpc.RPCMessageUncompressedSizeKey.Int(1834), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(45912), + otelgrpc.RPCMessageUncompressedSizeKey.Int(45912), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(4), + otelgrpc.RPCMessageUncompressedSizeKey.Int(4), + }, + }, + // client does not record an event for the server response. + }, streamInput.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("StreamingInputCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, streamInput.Attributes()) + + streamOutput := spans[3] + assert.False(t, streamOutput.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/StreamingOutputCall", streamOutput.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(21), + otelgrpc.RPCMessageUncompressedSizeKey.Int(21), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(31423), + otelgrpc.RPCMessageUncompressedSizeKey.Int(31423), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(13), + otelgrpc.RPCMessageUncompressedSizeKey.Int(13), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(2659), + otelgrpc.RPCMessageUncompressedSizeKey.Int(2659), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(58987), + otelgrpc.RPCMessageUncompressedSizeKey.Int(58987), + }, + }, + }, streamOutput.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("StreamingOutputCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, streamOutput.Attributes()) + + pingPong := spans[4] + assert.False(t, pingPong.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/FullDuplexCall", pingPong.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(27196), + otelgrpc.RPCMessageUncompressedSizeKey.Int(27196), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(31423), + otelgrpc.RPCMessageUncompressedSizeKey.Int(31423), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(16), + otelgrpc.RPCMessageUncompressedSizeKey.Int(16), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(13), + otelgrpc.RPCMessageUncompressedSizeKey.Int(13), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(1839), + otelgrpc.RPCMessageUncompressedSizeKey.Int(1839), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(2659), + otelgrpc.RPCMessageUncompressedSizeKey.Int(2659), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(45918), + otelgrpc.RPCMessageUncompressedSizeKey.Int(45918), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(58987), + otelgrpc.RPCMessageUncompressedSizeKey.Int(58987), + }, + }, + }, pingPong.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("FullDuplexCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, pingPong.Attributes()) +} + +func checkServerSpans(t *testing.T, spans []trace.ReadOnlySpan) { + require.Len(t, spans, 5) + + emptySpan := spans[0] + assert.False(t, emptySpan.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/EmptyCall", emptySpan.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(0), + otelgrpc.RPCMessageUncompressedSizeKey.Int(0), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(0), + otelgrpc.RPCMessageUncompressedSizeKey.Int(0), + }, + }, + }, emptySpan.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("EmptyCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, emptySpan.Attributes()) + + largeSpan := spans[1] + assert.False(t, largeSpan.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/UnaryCall", largeSpan.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageCompressedSizeKey.Int(271840), + otelgrpc.RPCMessageUncompressedSizeKey.Int(271840), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageCompressedSizeKey.Int(314167), + otelgrpc.RPCMessageUncompressedSizeKey.Int(314167), + }, + }, + }, largeSpan.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("UnaryCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, largeSpan.Attributes()) + + streamInput := spans[2] + assert.False(t, streamInput.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/StreamingInputCall", streamInput.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(27190), + otelgrpc.RPCMessageUncompressedSizeKey.Int(27190), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(12), + otelgrpc.RPCMessageUncompressedSizeKey.Int(12), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(1834), + otelgrpc.RPCMessageUncompressedSizeKey.Int(1834), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(45912), + otelgrpc.RPCMessageUncompressedSizeKey.Int(45912), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(4), + otelgrpc.RPCMessageUncompressedSizeKey.Int(4), + }, + }, + // client does not record an event for the server response. + }, streamInput.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("StreamingInputCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, streamInput.Attributes()) + + streamOutput := spans[3] + assert.False(t, streamOutput.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/StreamingOutputCall", streamOutput.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(21), + otelgrpc.RPCMessageUncompressedSizeKey.Int(21), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(31423), + otelgrpc.RPCMessageUncompressedSizeKey.Int(31423), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(13), + otelgrpc.RPCMessageUncompressedSizeKey.Int(13), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(2659), + otelgrpc.RPCMessageUncompressedSizeKey.Int(2659), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(58987), + otelgrpc.RPCMessageUncompressedSizeKey.Int(58987), + }, + }, + }, streamOutput.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("StreamingOutputCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, streamOutput.Attributes()) + + pingPong := spans[4] + assert.False(t, pingPong.EndTime().IsZero()) + assert.Equal(t, "grpc.testing.TestService/FullDuplexCall", pingPong.Name()) + assertEvents(t, []trace.Event{ + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(27196), + otelgrpc.RPCMessageUncompressedSizeKey.Int(27196), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(1), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(31423), + otelgrpc.RPCMessageUncompressedSizeKey.Int(31423), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(16), + otelgrpc.RPCMessageUncompressedSizeKey.Int(16), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(2), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(13), + otelgrpc.RPCMessageUncompressedSizeKey.Int(13), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(1839), + otelgrpc.RPCMessageUncompressedSizeKey.Int(1839), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(3), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(2659), + otelgrpc.RPCMessageUncompressedSizeKey.Int(2659), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("RECEIVED"), + otelgrpc.RPCMessageCompressedSizeKey.Int(45918), + otelgrpc.RPCMessageUncompressedSizeKey.Int(45918), + }, + }, + { + Name: "message", + Attributes: []attribute.KeyValue{ + otelgrpc.RPCMessageIDKey.Int(4), + otelgrpc.RPCMessageTypeKey.String("SENT"), + otelgrpc.RPCMessageCompressedSizeKey.Int(58987), + otelgrpc.RPCMessageUncompressedSizeKey.Int(58987), + }, + }, + }, pingPong.Events()) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.RPCMethodKey.String("FullDuplexCall"), + semconv.RPCServiceKey.String("grpc.testing.TestService"), + otelgrpc.RPCSystemGRPC, + otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)), + }, pingPong.Attributes()) +} From 7567e0dbed7323e84b853a44fb0cc9d0fc9088fa Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Wed, 26 Jul 2023 23:55:13 +0800 Subject: [PATCH 02/12] Update instrumentation/google.golang.org/grpc/otelgrpc/README.md Co-authored-by: David Ashpole --- instrumentation/google.golang.org/grpc/otelgrpc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/README.md b/instrumentation/google.golang.org/grpc/otelgrpc/README.md index bac7c1e7c95..b44d3fc6128 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/README.md +++ b/instrumentation/google.golang.org/grpc/otelgrpc/README.md @@ -1,4 +1,4 @@ -# How to usage +# gRPC instrumentation This library is the instrumentation library for `google.golang.org/grpc` From 92110c00d5517132f142593ee7c6db865d78182f Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Wed, 26 Jul 2023 23:55:47 +0800 Subject: [PATCH 03/12] Update instrumentation/google.golang.org/grpc/otelgrpc/README.md Co-authored-by: David Ashpole --- instrumentation/google.golang.org/grpc/otelgrpc/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/README.md b/instrumentation/google.golang.org/grpc/otelgrpc/README.md index b44d3fc6128..db8cd09267a 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/README.md +++ b/instrumentation/google.golang.org/grpc/otelgrpc/README.md @@ -9,6 +9,8 @@ For now you can instrument your program which use `google.golang.org/grpc` in tw You can see the example of both ways in directory `./example` +**notice**: **Do not use both interceptors and stats handlers at the same time!** If so, you will get duplicated spans and the parent/child relationships between spans will also be broken. + Although the implementation `stats.Handler` in experimental stage, we strongly still recommand you to use `stats.Handler`, mainly for two reasons: - **Functional advantages**: `stats.Handler`` has more information for user to build more flexible and granular metric, for example - multiple different types of represent "data length": In [InPayLoad](https://pkg.go.dev/google.golang.org/grpc/stats#InPayload), there exists `Length`, `CompressedLength`, `WireLength` to denote the size of uncompressed, compressed payload data, with or without framing data. But in Interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. [#3168](https://github.com/open-telemetry/opentelemetry-go-contrib/pull/3168) From afebd44e1ddf1f31ba411a957ce6ff0b99905041 Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Wed, 26 Jul 2023 23:55:56 +0800 Subject: [PATCH 04/12] Update instrumentation/google.golang.org/grpc/otelgrpc/README.md Co-authored-by: David Ashpole --- instrumentation/google.golang.org/grpc/otelgrpc/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/README.md b/instrumentation/google.golang.org/grpc/otelgrpc/README.md index db8cd09267a..ee67e5c4d1f 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/README.md +++ b/instrumentation/google.golang.org/grpc/otelgrpc/README.md @@ -18,4 +18,3 @@ Although the implementation `stats.Handler` in experimental stage, we strongly s - some other use cases: for example [catch failure of decoding message](https://github.com/open-telemetry/opentelemetry-go-contrib/issues/197#issuecomment-668377700) - **Performance advantages**: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. -You should also **notice** that: **Do not use both two ways in the meantime!** If so, you will get duplicated spans and the parent/child relationships between spans will also be broken. From 14680393d29a650a0ba23e551827aa1584bcfb0d Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Wed, 30 Aug 2023 12:31:04 +0800 Subject: [PATCH 05/12] Update instrumentation/google.golang.org/grpc/otelgrpc/README.md Co-authored-by: Mikhail Mazurskiy <126021+ash2k@users.noreply.github.com> --- instrumentation/google.golang.org/grpc/otelgrpc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/README.md b/instrumentation/google.golang.org/grpc/otelgrpc/README.md index ee67e5c4d1f..a0c0873bbf8 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/README.md +++ b/instrumentation/google.golang.org/grpc/otelgrpc/README.md @@ -12,7 +12,7 @@ You can see the example of both ways in directory `./example` **notice**: **Do not use both interceptors and stats handlers at the same time!** If so, you will get duplicated spans and the parent/child relationships between spans will also be broken. Although the implementation `stats.Handler` in experimental stage, we strongly still recommand you to use `stats.Handler`, mainly for two reasons: -- **Functional advantages**: `stats.Handler`` has more information for user to build more flexible and granular metric, for example +- **Functional advantages**: `stats.Handler` has more information for user to build more flexible and granular metric, for example - multiple different types of represent "data length": In [InPayLoad](https://pkg.go.dev/google.golang.org/grpc/stats#InPayload), there exists `Length`, `CompressedLength`, `WireLength` to denote the size of uncompressed, compressed payload data, with or without framing data. But in Interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. [#3168](https://github.com/open-telemetry/opentelemetry-go-contrib/pull/3168) - more accurate timestamp: `InPayload.RecvTime` and `OutPayload.SentTime` records more accurate timestamp that server got and sent the message, the timestamp recorded by interceptors depends on the location of this interceptors in the total interceptor chain. - some other use cases: for example [catch failure of decoding message](https://github.com/open-telemetry/opentelemetry-go-contrib/issues/197#issuecomment-668377700) From 7c9c665a86f709d7b7d2f2af3dd483d634a9e9e9 Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Wed, 6 Sep 2023 23:43:19 +0800 Subject: [PATCH 06/12] move doc to doc.go Signed-off-by: Ziqi Zhao --- .../grpc/otelgrpc/{README.md => doc.go} | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) rename instrumentation/google.golang.org/grpc/otelgrpc/{README.md => doc.go} (70%) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/README.md b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go similarity index 70% rename from instrumentation/google.golang.org/grpc/otelgrpc/README.md rename to instrumentation/google.golang.org/grpc/otelgrpc/doc.go index a0c0873bbf8..4231b95ee75 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/README.md +++ b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go @@ -1,11 +1,24 @@ -# gRPC instrumentation +// Copyright The OpenTelemetry Authors +// +// Licensed 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. -This library is the instrumentation library for `google.golang.org/grpc` +/* +Package otelgrpc is the instrumentation library for `google.golang.org/grpc` For now you can instrument your program which use `google.golang.org/grpc` in two ways: - by gRPC Interceptors -- [experimental] by gPRC `stats.Handler` +- by gRPC `stats.Handler` You can see the example of both ways in directory `./example` @@ -16,5 +29,7 @@ Although the implementation `stats.Handler` in experimental stage, we strongly s - multiple different types of represent "data length": In [InPayLoad](https://pkg.go.dev/google.golang.org/grpc/stats#InPayload), there exists `Length`, `CompressedLength`, `WireLength` to denote the size of uncompressed, compressed payload data, with or without framing data. But in Interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. [#3168](https://github.com/open-telemetry/opentelemetry-go-contrib/pull/3168) - more accurate timestamp: `InPayload.RecvTime` and `OutPayload.SentTime` records more accurate timestamp that server got and sent the message, the timestamp recorded by interceptors depends on the location of this interceptors in the total interceptor chain. - some other use cases: for example [catch failure of decoding message](https://github.com/open-telemetry/opentelemetry-go-contrib/issues/197#issuecomment-668377700) -- **Performance advantages**: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. +- **Performance advantages**: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. +*/ +package otelgrpc From 7d2436420a1b30063469673dfe97fd43be865fdd Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Thu, 7 Sep 2023 07:00:30 +0800 Subject: [PATCH 07/12] fix failed lint check Signed-off-by: Ziqi Zhao --- instrumentation/google.golang.org/grpc/otelgrpc/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go index 4231b95ee75..e2fcbd48238 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go @@ -32,4 +32,4 @@ Although the implementation `stats.Handler` in experimental stage, we strongly s - **Performance advantages**: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. */ -package otelgrpc +package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" From 87dbe3869a612084b2d2ea4cca0c347fdff8ecf4 Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Thu, 7 Sep 2023 17:45:42 +0800 Subject: [PATCH 08/12] format doc.go Signed-off-by: Ziqi Zhao --- .../google.golang.org/grpc/otelgrpc/doc.go | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go index e2fcbd48238..bd31286b48b 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go @@ -13,23 +13,33 @@ // limitations under the License. /* -Package otelgrpc is the instrumentation library for `google.golang.org/grpc` +Package otelgrpc is the instrumentation library for [google.golang.org/grpc] -For now you can instrument your program which use `google.golang.org/grpc` in two ways: +For now you can instrument your program which use [google.golang.org/grpc] in two ways: -- by gRPC Interceptors -- by gRPC `stats.Handler` + - by [grpc.UnaryClientInterceptor], [grpc.UnaryServerInterceptor], [grpc.StreamClientInterceptor], [grpc.StreamServerInterceptor] + - by [stats.Handler] -You can see the example of both ways in directory `./example` +Notice: Do not use both interceptors and [stats.Handler] at the same time! If so, you will get duplicated spans and the parent/child relationships between spans will also be broken. -**notice**: **Do not use both interceptors and stats handlers at the same time!** If so, you will get duplicated spans and the parent/child relationships between spans will also be broken. +We strongly still recommand you to use [stats.Handler], mainly for two reasons: -Although the implementation `stats.Handler` in experimental stage, we strongly still recommand you to use `stats.Handler`, mainly for two reasons: -- **Functional advantages**: `stats.Handler` has more information for user to build more flexible and granular metric, for example - - multiple different types of represent "data length": In [InPayLoad](https://pkg.go.dev/google.golang.org/grpc/stats#InPayload), there exists `Length`, `CompressedLength`, `WireLength` to denote the size of uncompressed, compressed payload data, with or without framing data. But in Interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. [#3168](https://github.com/open-telemetry/opentelemetry-go-contrib/pull/3168) - - more accurate timestamp: `InPayload.RecvTime` and `OutPayload.SentTime` records more accurate timestamp that server got and sent the message, the timestamp recorded by interceptors depends on the location of this interceptors in the total interceptor chain. - - some other use cases: for example [catch failure of decoding message](https://github.com/open-telemetry/opentelemetry-go-contrib/issues/197#issuecomment-668377700) +Functional advantages: [stats.Handler] has more information for user to build more flexible and granular metric, for example -- **Performance advantages**: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. + - multiple different types of represent "data length": In [stats.InPayLoad], there exists "Length", "CompressedLength", "WireLength" to denote the size of uncompressed, compressed payload data, with or without framing data. But in interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. + + - more accurate timestamp: [stats.InPayLoad]'s "RecvTime" and [stats.OutPayLoad]'s "SentTime" records more accurate timestamp that server got and sent the message, the timestamp recorded by interceptors depends on the location of this interceptors in the total interceptor chain. + + - some other use cases: for example, catch failure of decoding message. + +Performance advantages: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. + +[stats.Handler]: https://pkg.go.dev/google.golang.org/grpc/stats#Handler +[stats.InPayLoad]: https://pkg.go.dev/google.golang.org/grpc/stats#OutPayLoad +[stats.OutPayLoad]: https://pkg.go.dev/google.golang.org/grpc/stats#InPayload +[grpc.UnaryClientInterceptor]: https://pkg.go.dev/google.golang.org/grpc#UnaryClientInterceptor +[grpc.UnaryServerInterceptor]: https://pkg.go.dev/google.golang.org/grpc#UnaryServerInterceptor +[grpc.StreamClientInterceptor]: https://pkg.go.dev/google.golang.org/grpc#StreamClientInterceptor +[grpc.StreamServerInterceptor]: https://pkg.go.dev/google.golang.org/grpc#StreamServerInterceptor */ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" From 3258d1d01e34aa20fc8d56fbfb5df068c05b1958 Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Thu, 7 Sep 2023 17:54:43 +0800 Subject: [PATCH 09/12] Update instrumentation/google.golang.org/grpc/otelgrpc/doc.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robert Pająk --- instrumentation/google.golang.org/grpc/otelgrpc/doc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go index bd31286b48b..1e7a6cf2d59 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go @@ -35,8 +35,8 @@ Functional advantages: [stats.Handler] has more information for user to build mo Performance advantages: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. [stats.Handler]: https://pkg.go.dev/google.golang.org/grpc/stats#Handler -[stats.InPayLoad]: https://pkg.go.dev/google.golang.org/grpc/stats#OutPayLoad -[stats.OutPayLoad]: https://pkg.go.dev/google.golang.org/grpc/stats#InPayload +[stats.InPayload]: https://pkg.go.dev/google.golang.org/grpc/stats#OutPayload +[stats.OutPayload]: https://pkg.go.dev/google.golang.org/grpc/stats#InPayload [grpc.UnaryClientInterceptor]: https://pkg.go.dev/google.golang.org/grpc#UnaryClientInterceptor [grpc.UnaryServerInterceptor]: https://pkg.go.dev/google.golang.org/grpc#UnaryServerInterceptor [grpc.StreamClientInterceptor]: https://pkg.go.dev/google.golang.org/grpc#StreamClientInterceptor From 0213f6bca8a5b0769da1c81a369a91fe3f9d92cc Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Thu, 7 Sep 2023 17:56:08 +0800 Subject: [PATCH 10/12] fix typo Signed-off-by: Ziqi Zhao --- instrumentation/google.golang.org/grpc/otelgrpc/doc.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go index 1e7a6cf2d59..a993e0fc921 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/doc.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/doc.go @@ -26,20 +26,20 @@ We strongly still recommand you to use [stats.Handler], mainly for two reasons: Functional advantages: [stats.Handler] has more information for user to build more flexible and granular metric, for example - - multiple different types of represent "data length": In [stats.InPayLoad], there exists "Length", "CompressedLength", "WireLength" to denote the size of uncompressed, compressed payload data, with or without framing data. But in interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. + - multiple different types of represent "data length": In [stats.InPayload], there exists "Length", "CompressedLength", "WireLength" to denote the size of uncompressed, compressed payload data, with or without framing data. But in interceptors, we can only got uncompressed data, and this feature is also removed due to performance problem. - - more accurate timestamp: [stats.InPayLoad]'s "RecvTime" and [stats.OutPayLoad]'s "SentTime" records more accurate timestamp that server got and sent the message, the timestamp recorded by interceptors depends on the location of this interceptors in the total interceptor chain. + - more accurate timestamp: [stats.InPayload]'s "RecvTime" and [stats.OutPayload]'s "SentTime" records more accurate timestamp that server got and sent the message, the timestamp recorded by interceptors depends on the location of this interceptors in the total interceptor chain. - some other use cases: for example, catch failure of decoding message. Performance advantages: If too many interceptors are registered in a service, the interceptor chain can become too long, which increases the latency and processing time of the entire RPC call. [stats.Handler]: https://pkg.go.dev/google.golang.org/grpc/stats#Handler -[stats.InPayload]: https://pkg.go.dev/google.golang.org/grpc/stats#OutPayload -[stats.OutPayload]: https://pkg.go.dev/google.golang.org/grpc/stats#InPayload [grpc.UnaryClientInterceptor]: https://pkg.go.dev/google.golang.org/grpc#UnaryClientInterceptor [grpc.UnaryServerInterceptor]: https://pkg.go.dev/google.golang.org/grpc#UnaryServerInterceptor [grpc.StreamClientInterceptor]: https://pkg.go.dev/google.golang.org/grpc#StreamClientInterceptor [grpc.StreamServerInterceptor]: https://pkg.go.dev/google.golang.org/grpc#StreamServerInterceptor +[stats.OutPayload]: https://pkg.go.dev/google.golang.org/grpc/stats#OutPayload +[stats.InPayload]: https://pkg.go.dev/google.golang.org/grpc/stats#InPayload */ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" From 494eb368a0be8fdafefa19f8c0715b7b983c2a7f Mon Sep 17 00:00:00 2001 From: Ziqi Zhao Date: Mon, 18 Sep 2023 19:34:35 +0800 Subject: [PATCH 11/12] Update instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robert Pająk --- .../google.golang.org/grpc/otelgrpc/stats_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index 7b447aced28..c64a53443bc 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -24,7 +24,7 @@ import ( "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal" "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.12.0" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "go.opentelemetry.io/otel/trace" ) From 256b144fa21f19699a0a05c54f393fc747d98752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Mon, 18 Sep 2023 15:15:13 +0200 Subject: [PATCH 12/12] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b98ab03f54..0b149fa66d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108) - Set the description for the `rpc.server.duration` metric in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#4302) +- Add `NewServerHandler` and `NewClientHandler` that return a `grpc.StatsHandler` used for gRPC instrumentation in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#3002) ## [1.19.0/0.44.0/0.13.0] - 2023-09-12 @@ -47,7 +48,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add `NewMiddleware` function in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#2964) - The `go.opentelemetry.io/contrib/exporters/autoexport` package to provide configuration of trace exporters with useful defaults and environment variable support. (#2753, #4100, #4130, #4132, #4134) - `WithRouteTag` in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` adds HTTP route attribute to metrics. (#615) -- [otelgrpc] implement `grpc.StatsHandler` for grpc instrumentation. (#3002) - Add `WithSpanOptions` option in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#3768) - Add testing support for Go 1.21. (#4233) - Add `WithFilter` option to `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux`. (#4230)