From b6307a8ba848c7a272d5ddf87ba3c188368b00a0 Mon Sep 17 00:00:00 2001 From: Richard Carson Derr Date: Sun, 17 Dec 2023 13:56:06 -0500 Subject: [PATCH] story(grpc): configure otel (#38) * feat(issue-35): add server side handler for adding otel tracing * fix(issue-35): missed the nil scenario * example(issue-35): created simple grpc example * example(issue-35): make simple grpc port more predictable * fix(issue-35): forgot to update goreleaser image names * example(issue-35): register reflection service so grpc example works with insomnia etc. --- .goreleaser.yaml | 44 +++- example/simple_grpc/Containerfile | 9 + example/simple_grpc/main.go | 71 ++++++ example/simple_grpc/simple_grpc.proto | 22 ++ .../simple_grpc_pb/simple_grpc.pb.go | 215 ++++++++++++++++++ .../simple_grpc_pb/simple_grpc_grpc.pb.go | 105 +++++++++ example/simple_http/Containerfile | 1 + go.mod | 3 +- go.sum | 6 + grpc/grpc.go | 10 +- http/http.go | 2 +- 11 files changed, 478 insertions(+), 10 deletions(-) create mode 100644 example/simple_grpc/Containerfile create mode 100644 example/simple_grpc/main.go create mode 100644 example/simple_grpc/simple_grpc.proto create mode 100644 example/simple_grpc/simple_grpc_pb/simple_grpc.pb.go create mode 100644 example/simple_grpc/simple_grpc_pb/simple_grpc_grpc.pb.go diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 829a161..4a7938c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -19,6 +19,19 @@ builds: main: example/otlp/main.go binary: otlp + - id: simple_grpc + env: + - CGO_ENABLED=0 + goos: + - linux + goarch: + - amd64 + - arm64 + goamd64: + - v3 + main: example/simple_grpc/main.go + binary: simple_grpc + - id: simple_http env: - CGO_ENABLED=0 @@ -53,8 +66,8 @@ dockers: ids: - otlp image_templates: - - "ghcr.io/z5labs/app/example/otlp:latest" - - "ghcr.io/z5labs/app/example/otlp:{{ .Tag }}" + - "ghcr.io/z5labs/bedrock/example/otlp:latest" + - "ghcr.io/z5labs/bedrock/example/otlp:{{ .Tag }}" dockerfile: example/otlp/Containerfile use: docker build_flag_templates: @@ -65,6 +78,25 @@ dockers: - "--label=org.opencontainers.image.version={{.Version}}" - "--platform=linux/amd64" + - id: simple_grpc + goos: linux + goarch: amd64 + goamd64: v3 + ids: + - simple_grpc + image_templates: + - "ghcr.io/z5labs/bedrock/example/simple_grpc:latest" + - "ghcr.io/z5labs/bedrock/example/simple_grpc:{{ .Tag }}" + dockerfile: example/simple_grpc/Containerfile + use: docker + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title={{.ProjectName}}" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + - "--platform=linux/amd64" + - id: simple_http goos: linux goarch: amd64 @@ -72,8 +104,8 @@ dockers: ids: - simple_http image_templates: - - "ghcr.io/z5labs/app/example/simple_http:latest" - - "ghcr.io/z5labs/app/example/simple_http:{{ .Tag }}" + - "ghcr.io/z5labs/bedrock/example/simple_http:latest" + - "ghcr.io/z5labs/bedrock/example/simple_http:{{ .Tag }}" dockerfile: example/simple_http/Containerfile use: docker build_flag_templates: @@ -91,8 +123,8 @@ dockers: ids: - simple_queue image_templates: - - "ghcr.io/z5labs/app/example/simple_queue:latest" - - "ghcr.io/z5labs/app/example/simple_queue:{{ .Tag }}" + - "ghcr.io/z5labs/bedrock/example/simple_queue:latest" + - "ghcr.io/z5labs/bedrock/example/simple_queue:{{ .Tag }}" dockerfile: example/simple_queue/Containerfile use: docker build_flag_templates: diff --git a/example/simple_grpc/Containerfile b/example/simple_grpc/Containerfile new file mode 100644 index 0000000..489d3b0 --- /dev/null +++ b/example/simple_grpc/Containerfile @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Z5Labs and Contributors +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +FROM scratch +EXPOSE 9080 +COPY simple_grpc / +ENTRYPOINT ["/simple_grpc"] \ No newline at end of file diff --git a/example/simple_grpc/main.go b/example/simple_grpc/main.go new file mode 100644 index 0000000..2793a32 --- /dev/null +++ b/example/simple_grpc/main.go @@ -0,0 +1,71 @@ +// Copyright (c) 2023 Z5Labs and Contributors +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +package main + +import ( + "context" + "log/slog" + "os" + + "github.com/z5labs/bedrock" + "github.com/z5labs/bedrock/example/simple_grpc/simple_grpc_pb" + brgrpc "github.com/z5labs/bedrock/grpc" + "github.com/z5labs/bedrock/pkg/health" + "github.com/z5labs/bedrock/pkg/otelconfig" + + "go.opentelemetry.io/otel" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +type simpleService struct { + simple_grpc_pb.UnimplementedSimpleServer +} + +func (*simpleService) Echo(ctx context.Context, req *simple_grpc_pb.EchoRequest) (*simple_grpc_pb.EchoResponse, error) { + _, span := otel.Tracer("main").Start(ctx, "simpleService.Echo") + defer span.End() + resp := &simple_grpc_pb.EchoResponse{ + Message: req.Message, + } + return resp, nil +} + +func registerSimpleService(s *grpc.Server) { + simple_grpc_pb.RegisterSimpleServer(s, &simpleService{}) +} + +func initRuntime(bc bedrock.BuildContext) (bedrock.Runtime, error) { + logHandler := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{AddSource: true}) + + rt := brgrpc.NewRuntime( + brgrpc.ListenOnPort(9080), + brgrpc.LogHandler(logHandler), + brgrpc.Service( + registerSimpleService, + brgrpc.ServiceName("simple"), + brgrpc.Readiness(&health.Readiness{}), + ), + // register reflection service so you can test this example + // via Insomnia, Postman and any other API testing tool that + // understands gRPC reflection. + brgrpc.Service(func(s *grpc.Server) { + reflection.Register(s) + }), + ) + return rt, nil +} + +func main() { + bedrock.New( + bedrock.InitTracerProvider(func(bc bedrock.BuildContext) (otelconfig.Initializer, error) { + return otelconfig.Local( + otelconfig.ServiceName("simple_grpc"), + ), nil + }), + bedrock.WithRuntimeBuilderFunc(initRuntime), + ).Run() +} diff --git a/example/simple_grpc/simple_grpc.proto b/example/simple_grpc/simple_grpc.proto new file mode 100644 index 0000000..1713774 --- /dev/null +++ b/example/simple_grpc/simple_grpc.proto @@ -0,0 +1,22 @@ +// Copyright (c) 2023 Z5Labs and Contributors +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +syntax = "proto3"; + +package simple_grpc_pb; + +option go_package = "github.com/z5labs/bedrock/example/simple_grpc/simple_grpc_pb"; + +service Simple { + rpc Echo (EchoRequest) returns (EchoResponse); +} + +message EchoRequest { + string message = 1; +} + +message EchoResponse { + string message = 1; +} \ No newline at end of file diff --git a/example/simple_grpc/simple_grpc_pb/simple_grpc.pb.go b/example/simple_grpc/simple_grpc_pb/simple_grpc.pb.go new file mode 100644 index 0000000..ac16925 --- /dev/null +++ b/example/simple_grpc/simple_grpc_pb/simple_grpc.pb.go @@ -0,0 +1,215 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.19.6 +// source: simple_grpc.proto + +package simple_grpc_pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EchoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *EchoRequest) Reset() { + *x = EchoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_simple_grpc_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EchoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EchoRequest) ProtoMessage() {} + +func (x *EchoRequest) ProtoReflect() protoreflect.Message { + mi := &file_simple_grpc_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EchoRequest.ProtoReflect.Descriptor instead. +func (*EchoRequest) Descriptor() ([]byte, []int) { + return file_simple_grpc_proto_rawDescGZIP(), []int{0} +} + +func (x *EchoRequest) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type EchoResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *EchoResponse) Reset() { + *x = EchoResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_simple_grpc_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EchoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EchoResponse) ProtoMessage() {} + +func (x *EchoResponse) ProtoReflect() protoreflect.Message { + mi := &file_simple_grpc_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EchoResponse.ProtoReflect.Descriptor instead. +func (*EchoResponse) Descriptor() ([]byte, []int) { + return file_simple_grpc_proto_rawDescGZIP(), []int{1} +} + +func (x *EchoResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_simple_grpc_proto protoreflect.FileDescriptor + +var file_simple_grpc_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x70, 0x63, + 0x5f, 0x70, 0x62, 0x22, 0x27, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c, + 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x4b, 0x0a, 0x06, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x12, 0x41, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x1b, 0x2e, 0x73, 0x69, 0x6d, 0x70, 0x6c, + 0x65, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x62, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x67, + 0x72, 0x70, 0x63, 0x5f, 0x70, 0x62, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x3e, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x7a, 0x35, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x62, 0x65, 0x64, 0x72, 0x6f, 0x63, 0x6b, + 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x5f, + 0x67, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x70, 0x63, + 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_simple_grpc_proto_rawDescOnce sync.Once + file_simple_grpc_proto_rawDescData = file_simple_grpc_proto_rawDesc +) + +func file_simple_grpc_proto_rawDescGZIP() []byte { + file_simple_grpc_proto_rawDescOnce.Do(func() { + file_simple_grpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_simple_grpc_proto_rawDescData) + }) + return file_simple_grpc_proto_rawDescData +} + +var file_simple_grpc_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_simple_grpc_proto_goTypes = []interface{}{ + (*EchoRequest)(nil), // 0: simple_grpc_pb.EchoRequest + (*EchoResponse)(nil), // 1: simple_grpc_pb.EchoResponse +} +var file_simple_grpc_proto_depIdxs = []int32{ + 0, // 0: simple_grpc_pb.Simple.Echo:input_type -> simple_grpc_pb.EchoRequest + 1, // 1: simple_grpc_pb.Simple.Echo:output_type -> simple_grpc_pb.EchoResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_simple_grpc_proto_init() } +func file_simple_grpc_proto_init() { + if File_simple_grpc_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_simple_grpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EchoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_simple_grpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EchoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_simple_grpc_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_simple_grpc_proto_goTypes, + DependencyIndexes: file_simple_grpc_proto_depIdxs, + MessageInfos: file_simple_grpc_proto_msgTypes, + }.Build() + File_simple_grpc_proto = out.File + file_simple_grpc_proto_rawDesc = nil + file_simple_grpc_proto_goTypes = nil + file_simple_grpc_proto_depIdxs = nil +} diff --git a/example/simple_grpc/simple_grpc_pb/simple_grpc_grpc.pb.go b/example/simple_grpc/simple_grpc_pb/simple_grpc_grpc.pb.go new file mode 100644 index 0000000..d23cf37 --- /dev/null +++ b/example/simple_grpc/simple_grpc_pb/simple_grpc_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.19.6 +// source: simple_grpc.proto + +package simple_grpc_pb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// SimpleClient is the client API for Simple service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type SimpleClient interface { + Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) +} + +type simpleClient struct { + cc grpc.ClientConnInterface +} + +func NewSimpleClient(cc grpc.ClientConnInterface) SimpleClient { + return &simpleClient{cc} +} + +func (c *simpleClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) { + out := new(EchoResponse) + err := c.cc.Invoke(ctx, "/simple_grpc_pb.Simple/Echo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SimpleServer is the server API for Simple service. +// All implementations must embed UnimplementedSimpleServer +// for forward compatibility +type SimpleServer interface { + Echo(context.Context, *EchoRequest) (*EchoResponse, error) + mustEmbedUnimplementedSimpleServer() +} + +// UnimplementedSimpleServer must be embedded to have forward compatible implementations. +type UnimplementedSimpleServer struct { +} + +func (UnimplementedSimpleServer) Echo(context.Context, *EchoRequest) (*EchoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented") +} +func (UnimplementedSimpleServer) mustEmbedUnimplementedSimpleServer() {} + +// UnsafeSimpleServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SimpleServer will +// result in compilation errors. +type UnsafeSimpleServer interface { + mustEmbedUnimplementedSimpleServer() +} + +func RegisterSimpleServer(s grpc.ServiceRegistrar, srv SimpleServer) { + s.RegisterService(&Simple_ServiceDesc, srv) +} + +func _Simple_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EchoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SimpleServer).Echo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/simple_grpc_pb.Simple/Echo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SimpleServer).Echo(ctx, req.(*EchoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Simple_ServiceDesc is the grpc.ServiceDesc for Simple service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Simple_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "simple_grpc_pb.Simple", + HandlerType: (*SimpleServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Echo", + Handler: _Simple_Echo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "simple_grpc.proto", +} diff --git a/example/simple_http/Containerfile b/example/simple_http/Containerfile index 5c472d4..6054c4b 100644 --- a/example/simple_http/Containerfile +++ b/example/simple_http/Containerfile @@ -4,5 +4,6 @@ # https://opensource.org/licenses/MIT FROM scratch +EXPOSE 8080 COPY simple_http / ENTRYPOINT ["/simple_http"] \ No newline at end of file diff --git a/go.mod b/go.mod index 3169157..0210908 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/spf13/viper v1.18.1 github.com/stretchr/testify v1.8.4 go.opentelemetry.io/contrib/detectors/gcp v1.21.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 @@ -23,6 +24,7 @@ require ( go.uber.org/zap v1.26.0 golang.org/x/sync v0.5.0 google.golang.org/grpc v1.60.0 + google.golang.org/protobuf v1.31.0 ) require ( @@ -77,7 +79,6 @@ require ( google.golang.org/genproto v0.0.0-20231127180814-3a041ad873d4 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231127180814-3a041ad873d4 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect - google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 49fe81a..837f395 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,8 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -50,6 +52,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -168,6 +172,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/detectors/gcp v1.21.1 h1:VLAa8mb2eMu2Iq9pnVhece0Yla2mIq1yKYLUoZ/ifJs= go.opentelemetry.io/contrib/detectors/gcp v1.21.1/go.mod h1:yL1WBcIgLFgqHhuMqmxp6ddaXoNFPeUgk6ATnF4wBhI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= diff --git a/grpc/grpc.go b/grpc/grpc.go index 2c85e3b..6f3c535 100644 --- a/grpc/grpc.go +++ b/grpc/grpc.go @@ -17,6 +17,7 @@ import ( "github.com/z5labs/bedrock/pkg/noop" "github.com/z5labs/bedrock/pkg/slogfield" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -126,7 +127,12 @@ func NewRuntime(opts ...RuntimeOption) *Runtime { } var healthMonitors []serviceHealthMonitor - s := grpc.NewServer(grpc.Creds(insecure.NewCredentials())) + s := grpc.NewServer( + grpc.StatsHandler(otelgrpc.NewServerHandler( + otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents), + )), + grpc.Creds(insecure.NewCredentials()), + ) for _, svc := range ro.services { svc.registerFunc(s) healthMonitors = append(healthMonitors, serviceHealthMonitor{ @@ -194,7 +200,7 @@ func (rt *Runtime) Run(ctx context.Context) error { }) err = g.Wait() - if err == grpc.ErrServerStopped { + if err == nil || err == grpc.ErrServerStopped { return nil } rt.log.ErrorContext(gctx, "service encountered unexpected error", slogfield.Error(err)) diff --git a/http/http.go b/http/http.go index cf09a97..c4e2473 100644 --- a/http/http.go +++ b/http/http.go @@ -178,7 +178,7 @@ func (rt *Runtime) Run(ctx context.Context) error { }) err = g.Wait() - if err == http.ErrServerClosed { + if err == nil || err == http.ErrServerClosed { return nil } rt.log.Error("service encountered unexpected error", slogfield.Error(err))