diff --git a/demo/pkg/subgraphs/projects/go.mod b/demo/pkg/subgraphs/projects/go.mod index bc041968de..1fde2a01fa 100644 --- a/demo/pkg/subgraphs/projects/go.mod +++ b/demo/pkg/subgraphs/projects/go.mod @@ -3,26 +3,45 @@ module github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects go 1.23.0 require ( + github.com/hashicorp/go-hclog v1.6.3 github.com/stretchr/testify v1.10.0 - github.com/wundergraph/cosmo/router-plugin v0.0.0-20250616075713-f2b99c96cec4 // v0.1.0 + github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e google.golang.org/grpc v1.68.1 google.golang.org/protobuf v1.36.5 ) require ( + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect github.com/hashicorp/go-plugin v1.6.3 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/oklog/run v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/tonglil/opentelemetry-go-datadog-propagator v0.1.3 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.23.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.23.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.1.0 // indirect golang.org/x/net v0.38.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/wundergraph/cosmo/router-plugin => ../../../../router-plugin diff --git a/demo/pkg/subgraphs/projects/go.sum b/demo/pkg/subgraphs/projects/go.sum index 35c700d178..d9f7217abd 100644 --- a/demo/pkg/subgraphs/projects/go.sum +++ b/demo/pkg/subgraphs/projects/go.sum @@ -1,15 +1,26 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= @@ -18,6 +29,10 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -31,12 +46,36 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/wundergraph/cosmo/router-plugin v0.0.0-20250616075713-f2b99c96cec4 h1:vJpU4oYzu8tjoHfVPNE3kmwemjhFT5vkNU17+zJGyE8= -github.com/wundergraph/cosmo/router-plugin v0.0.0-20250616075713-f2b99c96cec4/go.mod h1:McnhVVFedqB0JqkwQcbBUEO6tExEi9HVPpKc/33oFqk= +github.com/tonglil/opentelemetry-go-datadog-propagator v0.1.3 h1:Ozy1UnlID19jL6+vixEcA1t4NMf8hp01uDAY1nwGl8U= +github.com/tonglil/opentelemetry-go-datadog-propagator v0.1.3/go.mod h1:Ijp5eaviP2mk8CJM+0EDYFKNULr+kicPSB9FOvxOhW0= +go.opentelemetry.io/contrib/propagators/b3 v1.23.0 h1:aaIGWc5JdfRGpCafLRxMJbD65MfTa206AwSKkvGS0Hg= +go.opentelemetry.io/contrib/propagators/b3 v1.23.0/go.mod h1:Gyz7V7XghvwTq+mIhLFlTgcc03UDroOg8vezs4NLhwU= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0 h1:KFxfTCTkH1usVFzDaWzbmNdFX7ybUTCtkLsUTww0nG4= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0/go.mod h1:xU+81opGquQICJGzwscLXAQLnIPWI+q7Zu4AQSrgXf8= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1 h1:o8iWeVFa1BcLtVEV0LzrCxV2/55tB3xLxADr6Kyoey4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1/go.mod h1:SEVfdK4IoBnbT2FXNM/k8yC08MrfbhWk3U4ljM8B3HE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1 h1:p3A5+f5l9e/kuEBwLOrnpkIDHQFlHmbiVxMURWRK6gQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1/go.mod h1:OClrnXUjBqQbInvjJFjYSnMxBSCXBF8r3b34WqjiIrQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1 h1:cfuy3bXmLJS7M1RZmAL6SuhGtKUp2KEsrm00OlAXkq4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1/go.mod h1:22jr92C6KwlwItJmQzfixzQM3oyyuYLCfHiMY+rpsPU= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -50,13 +89,16 @@ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/demo/pkg/subgraphs/projects/src/main.go b/demo/pkg/subgraphs/projects/src/main.go index a8a8932620..7c70c3d876 100644 --- a/demo/pkg/subgraphs/projects/src/main.go +++ b/demo/pkg/subgraphs/projects/src/main.go @@ -3,6 +3,7 @@ package main import ( "log" + "github.com/hashicorp/go-hclog" projects "github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects/generated" "github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects/src/service" routerplugin "github.com/wundergraph/cosmo/router-plugin" @@ -10,11 +11,16 @@ import ( ) func main() { - pl, err := routerplugin.NewRouterPlugin(func(s *grpc.Server) { + + registerFunc := func(s *grpc.Server) { s.RegisterService(&projects.ProjectsService_ServiceDesc, &service.ProjectsService{ NextID: 1, }) - }) + } + + pl, err := routerplugin.NewRouterPlugin(registerFunc, + routerplugin.WithLogger(hclog.Info), + ) if err != nil { log.Fatalf("failed to create router plugin: %v", err) diff --git a/demo/pkg/subgraphs/projects/src/service/service.go b/demo/pkg/subgraphs/projects/src/service/service.go index ca137a103b..f9e696fcd9 100644 --- a/demo/pkg/subgraphs/projects/src/service/service.go +++ b/demo/pkg/subgraphs/projects/src/service/service.go @@ -8,6 +8,7 @@ import ( "syscall" "time" + "github.com/hashicorp/go-hclog" service "github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects/generated" "github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects/src/data" "google.golang.org/grpc/codes" @@ -107,6 +108,14 @@ func (p *ProjectsService) getRelatedProductsByProjectId(projectId string) []*ser // LookupMilestoneById implements projects.ProjectsServiceServer. func (p *ProjectsService) LookupMilestoneById(ctx context.Context, req *service.LookupMilestoneByIdRequest) (*service.LookupMilestoneByIdResponse, error) { + logger := hclog.FromContext(ctx) + if len(req.Keys) == 0 { + logger.Info("LookupMilestoneById", "no keys provided") + return &service.LookupMilestoneByIdResponse{Result: []*service.Milestone{}}, nil + } + + logger.Info("LookupMilestoneById", "milestone_id", req.Keys[0].Id) + p.lock.RLock() defer p.lock.RUnlock() @@ -134,6 +143,14 @@ func (p *ProjectsService) LookupMilestoneById(ctx context.Context, req *service. // LookupTaskById implements projects.ProjectsServiceServer. func (p *ProjectsService) LookupTaskById(ctx context.Context, req *service.LookupTaskByIdRequest) (*service.LookupTaskByIdResponse, error) { + logger := hclog.FromContext(ctx) + if len(req.Keys) == 0 { + logger.Info("LookupTaskById", "no keys provided") + return &service.LookupTaskByIdResponse{Result: []*service.Task{}}, nil + } + + logger.Info("LookupTaskById", "task_id", req.Keys[0].Id) + p.lock.RLock() defer p.lock.RUnlock() @@ -161,6 +178,15 @@ func (p *ProjectsService) LookupTaskById(ctx context.Context, req *service.Looku // LookupProductByUpc implements projects.ProjectsServiceServer. func (p *ProjectsService) LookupProductByUpc(ctx context.Context, req *service.LookupProductByUpcRequest) (*service.LookupProductByUpcResponse, error) { + logger := hclog.FromContext(ctx) + + if len(req.Keys) == 0 { + logger.Info("LookupProductByUpc", "no keys provided") + return &service.LookupProductByUpcResponse{Result: []*service.Product{}}, nil + } + + logger.Info("LookupProductByUpc", "upc", req.Keys[0].Upc) + p.lock.RLock() defer p.lock.RUnlock() @@ -186,6 +212,9 @@ func (p *ProjectsService) LookupProductByUpc(ctx context.Context, req *service.L // MutationAddMilestone implements projects.ProjectsServiceServer. func (p *ProjectsService) MutationAddMilestone(ctx context.Context, req *service.MutationAddMilestoneRequest) (*service.MutationAddMilestoneResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("MutationAddMilestone", "project_id", req.Milestone.ProjectId) + p.lock.Lock() defer p.lock.Unlock() @@ -220,6 +249,9 @@ func (p *ProjectsService) MutationAddMilestone(ctx context.Context, req *service // MutationAddTask implements projects.ProjectsServiceServer. func (p *ProjectsService) MutationAddTask(ctx context.Context, req *service.MutationAddTaskRequest) (*service.MutationAddTaskResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("MutationAddTask", "project_id", req.Task.ProjectId) + p.lock.Lock() defer p.lock.Unlock() @@ -252,6 +284,9 @@ func (p *ProjectsService) MutationAddTask(ctx context.Context, req *service.Muta // MutationUpdateProjectStatus implements projects.ProjectsServiceServer. func (p *ProjectsService) MutationUpdateProjectStatus(ctx context.Context, req *service.MutationUpdateProjectStatusRequest) (*service.MutationUpdateProjectStatusResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("MutationUpdateProjectStatus", "project_id", req.ProjectId, "status", req.Status) + p.lock.Lock() defer p.lock.Unlock() @@ -299,6 +334,9 @@ func (p *ProjectsService) MutationUpdateProjectStatus(ctx context.Context, req * // QueryMilestones implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryMilestones(ctx context.Context, req *service.QueryMilestonesRequest) (*service.QueryMilestonesResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryMilestones", "project_id", req.ProjectId) + p.lock.RLock() defer p.lock.RUnlock() @@ -311,6 +349,9 @@ func (p *ProjectsService) QueryMilestones(ctx context.Context, req *service.Quer // QueryTasks implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryTasks(ctx context.Context, req *service.QueryTasksRequest) (*service.QueryTasksResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryTasks", "project_id", req.ProjectId) + p.lock.RLock() defer p.lock.RUnlock() @@ -323,6 +364,9 @@ func (p *ProjectsService) QueryTasks(ctx context.Context, req *service.QueryTask // QueryProjectActivities implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryProjectActivities(ctx context.Context, req *service.QueryProjectActivitiesRequest) (*service.QueryProjectActivitiesResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryProjectActivities", "project_id", req.ProjectId) + p.lock.RLock() defer p.lock.RUnlock() @@ -357,6 +401,9 @@ func (p *ProjectsService) QueryProjectActivities(ctx context.Context, req *servi // QueryProjectResources implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryProjectResources(ctx context.Context, req *service.QueryProjectResourcesRequest) (*service.QueryProjectResourcesResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryProjectResources", "project_id", req.ProjectId) + p.lock.RLock() defer p.lock.RUnlock() @@ -411,6 +458,9 @@ func (p *ProjectsService) QueryProjectResources(ctx context.Context, req *servic // QuerySearchProjects implements projects.ProjectsServiceServer. func (p *ProjectsService) QuerySearchProjects(ctx context.Context, req *service.QuerySearchProjectsRequest) (*service.QuerySearchProjectsResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QuerySearchProjects", "query", req.Query) + p.lock.RLock() defer p.lock.RUnlock() @@ -462,11 +512,20 @@ func (p *ProjectsService) QueryKillService(context.Context, *service.QueryKillSe // QueryPanic implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryPanic(context.Context, *service.QueryPanicRequest) (*service.QueryPanicResponse, error) { - panic("Panic") + panic("The panic was triggered from QueryPanic") } // LookupEmployeeById implements projects.ProjectsServiceServer. func (p *ProjectsService) LookupEmployeeById(ctx context.Context, req *service.LookupEmployeeByIdRequest) (*service.LookupEmployeeByIdResponse, error) { + logger := hclog.FromContext(ctx) + + if len(req.Keys) == 0 { + logger.Info("LookupEmployeeById", "no keys provided") + return &service.LookupEmployeeByIdResponse{Result: []*service.Employee{}}, nil + } + + logger.Info("LookupEmployeeById", "employee_id", req.Keys[0].Id) + p.lock.RLock() defer p.lock.RUnlock() @@ -497,6 +556,15 @@ func (p *ProjectsService) LookupEmployeeById(ctx context.Context, req *service.L // LookupProjectById implements projects.ProjectsServiceServer. func (p *ProjectsService) LookupProjectById(ctx context.Context, req *service.LookupProjectByIdRequest) (*service.LookupProjectByIdResponse, error) { + logger := hclog.FromContext(ctx) + + if len(req.Keys) == 0 { + logger.Info("LookupProjectById", "no keys provided") + return &service.LookupProjectByIdResponse{Result: []*service.Project{}}, nil + } + + logger.Info("LookupProjectById", "project_id", req.Keys[0].Id) + p.lock.RLock() defer p.lock.RUnlock() @@ -522,6 +590,9 @@ func (p *ProjectsService) LookupProjectById(ctx context.Context, req *service.Lo // MutationAddProject implements projects.ProjectsServiceServer. func (p *ProjectsService) MutationAddProject(ctx context.Context, req *service.MutationAddProjectRequest) (*service.MutationAddProjectResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("MutationAddProject") + p.lock.Lock() defer p.lock.Unlock() @@ -556,6 +627,9 @@ func (p *ProjectsService) MutationAddProject(ctx context.Context, req *service.M // QueryProject implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryProject(ctx context.Context, req *service.QueryProjectRequest) (*service.QueryProjectResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryProject", "project_id", req.Id) + p.lock.RLock() defer p.lock.RUnlock() @@ -569,7 +643,10 @@ func (p *ProjectsService) QueryProject(ctx context.Context, req *service.QueryPr } // QueryProjectStatuses implements projects.ProjectsServiceServer. -func (p *ProjectsService) QueryProjectStatuses(context.Context, *service.QueryProjectStatusesRequest) (*service.QueryProjectStatusesResponse, error) { +func (p *ProjectsService) QueryProjectStatuses(ctx context.Context, _ *service.QueryProjectStatusesRequest) (*service.QueryProjectStatusesResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryProjectStatuses") + p.lock.RLock() defer p.lock.RUnlock() @@ -591,6 +668,9 @@ func (p *ProjectsService) QueryProjectStatuses(context.Context, *service.QueryPr // QueryProjects implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryProjects(ctx context.Context, req *service.QueryProjectsRequest) (*service.QueryProjectsResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryProjects") + p.lock.RLock() defer p.lock.RUnlock() @@ -605,6 +685,9 @@ func (p *ProjectsService) QueryProjects(ctx context.Context, req *service.QueryP // QueryProjectsByStatus implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryProjectsByStatus(ctx context.Context, req *service.QueryProjectsByStatusRequest) (*service.QueryProjectsByStatusResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryProjectsByStatus", "status", req.Status) + p.lock.RLock() defer p.lock.RUnlock() @@ -621,6 +704,9 @@ func (p *ProjectsService) QueryProjectsByStatus(ctx context.Context, req *servic // QueryProjectTags implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryProjectTags(ctx context.Context, req *service.QueryProjectTagsRequest) (*service.QueryProjectTagsResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryProjectTags") + p.lock.RLock() defer p.lock.RUnlock() @@ -630,6 +716,9 @@ func (p *ProjectsService) QueryProjectTags(ctx context.Context, req *service.Que // QueryArchivedProjects implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryArchivedProjects(ctx context.Context, req *service.QueryArchivedProjectsRequest) (*service.QueryArchivedProjectsResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryArchivedProjects") + p.lock.RLock() defer p.lock.RUnlock() @@ -639,6 +728,9 @@ func (p *ProjectsService) QueryArchivedProjects(ctx context.Context, req *servic // QueryTasksByPriority implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryTasksByPriority(ctx context.Context, req *service.QueryTasksByPriorityRequest) (*service.QueryTasksByPriorityResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryTasksByPriority", "project_id", req.ProjectId) + p.lock.RLock() defer p.lock.RUnlock() @@ -682,6 +774,9 @@ func (p *ProjectsService) QueryTasksByPriority(ctx context.Context, req *service // QueryResourceMatrix implements projects.ProjectsServiceServer. func (p *ProjectsService) QueryResourceMatrix(ctx context.Context, req *service.QueryResourceMatrixRequest) (*service.QueryResourceMatrixResponse, error) { + logger := hclog.FromContext(ctx) + logger.Info("QueryResourceMatrix", "project_id", req.ProjectId) + p.lock.RLock() defer p.lock.RUnlock() diff --git a/router-plugin/middleware/logging.go b/router-plugin/middleware/logging.go new file mode 100644 index 0000000000..1183c56693 --- /dev/null +++ b/router-plugin/middleware/logging.go @@ -0,0 +1,18 @@ +package middleware + +import ( + "context" + + "github.com/hashicorp/go-hclog" + "google.golang.org/grpc" +) + +// Logging ensures that the default logger is available in the context. +// This is useful for logging in the service implementation. +func Logging(logger hclog.Logger) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + logger.Trace("LoggingInterceptor", "method", info.FullMethod) + ctx = hclog.WithContext(ctx, logger) + return handler(ctx, req) + } +} diff --git a/router-plugin/middleware/recovery.go b/router-plugin/middleware/recovery.go new file mode 100644 index 0000000000..dfdb2c6677 --- /dev/null +++ b/router-plugin/middleware/recovery.go @@ -0,0 +1,24 @@ +package middleware + +import ( + "context" + + "github.com/hashicorp/go-hclog" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Recovery is a middleware that recovers from panics and logs the error. +// It is used to ensure that the panic is logged and the request is not aborted. +func Recovery(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { + defer func() { + if r := recover(); r != nil { + hclog.FromContext(ctx).Error("panic", "error", r, "plugin_stack", hclog.Stacktrace()) + resp = nil + err = status.Errorf(codes.Internal, "internal server error") + } + }() + + return handler(ctx, req) +} diff --git a/router-plugin/plugin.go b/router-plugin/plugin.go index 47702eb459..bdd987fb33 100644 --- a/router-plugin/plugin.go +++ b/router-plugin/plugin.go @@ -4,10 +4,12 @@ import ( "context" "encoding/json" "errors" + "os" + "github.com/wundergraph/cosmo/router-plugin/config" "github.com/wundergraph/cosmo/router-plugin/setup" - "os" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" "google.golang.org/grpc" ) @@ -61,24 +63,52 @@ func WithTracing() PluginOption { } } +// WithServiceName sets the service name for the plugin. func WithServiceName(serviceName string) PluginOption { return func(c *RouterPlugin) { c.config.ServiceName = serviceName } } +// WithTracingErrorHandler sets the tracing error handler for the plugin. func WithTracingErrorHandler(errHandler func(err error)) PluginOption { return func(c *RouterPlugin) { c.config.TracingErrorHandler = errHandler } } +// WithServiceVersion sets the service version for the plugin. func WithServiceVersion(serviceVersion string) PluginOption { return func(c *RouterPlugin) { c.config.ServiceVersion = serviceVersion } } +// WithLogger configures a plugin logger at the provided level. +// The `level` parameter is the level of the logger. +func WithLogger(level hclog.Level) PluginOption { + return func(c *RouterPlugin) { + logger := hclog.New(&hclog.LoggerOptions{ + Level: level, + // We use JSON format as we can retrieve those as args in the router. + JSONFormat: true, + // Disable timestamps to prevent duplicates when router ingests logs. + DisableTime: true, + }) + + c.serveConfig.Logger = logger + } +} + +// WithCustomLogger sets the logger to the provided logger. +// This is useful for when you want to use a custom logger. +// For example, when you want to use a custom logger for the plugin. +func WithCustomLogger(logger hclog.Logger) PluginOption { + return func(c *RouterPlugin) { + c.serveConfig.Logger = logger + } +} + func NewRouterPlugin(registrationfunc func(*grpc.Server), opts ...PluginOption) (*RouterPlugin, error) { if registrationfunc == nil { return nil, errors.New("unable to register service, registration function not provided") @@ -108,16 +138,25 @@ func NewRouterPlugin(registrationfunc func(*grpc.Server), opts ...PluginOption) } } + logger := routerPlugin.serveConfig.Logger + if logger == nil { + logger = hclog.New(&hclog.LoggerOptions{ + Level: hclog.Debug, + JSONFormat: true, + DisableTime: true, + }) + } + grpcServerFunc, err := setup.GrpcServer(setup.GrpcServerInitOpts{ StartupConfig: startupConfig, PluginConfig: routerPlugin.config, + Logger: logger, }) if err != nil { return nil, err } routerPlugin.serveConfig.GRPCServer = grpcServerFunc - return routerPlugin, nil } diff --git a/router-plugin/setup/init.go b/router-plugin/setup/init.go index e64db1030f..6975a15f5e 100644 --- a/router-plugin/setup/init.go +++ b/router-plugin/setup/init.go @@ -2,7 +2,10 @@ package setup import ( "fmt" + + "github.com/hashicorp/go-hclog" "github.com/wundergraph/cosmo/router-plugin/config" + "github.com/wundergraph/cosmo/router-plugin/middleware" "github.com/wundergraph/cosmo/router-plugin/tracing" "google.golang.org/grpc" ) @@ -15,6 +18,7 @@ const ( ) type GrpcServerInitOpts struct { + Logger hclog.Logger StartupConfig config.StartupConfig PluginConfig config.RouterPluginConfig } @@ -22,6 +26,16 @@ type GrpcServerInitOpts struct { func GrpcServer(opts GrpcServerInitOpts) (GrpcServerInitFunc, error) { grpcOpts := make([]grpc.ServerOption, 0) + interceptors := make([]grpc.UnaryServerInterceptor, 0, 1) + + // We need to make sure the logger is available in the context for the recovery interceptor. + // Otherwise the default logger would have to be used, which might cause that no output is logged. + if opts.Logger != nil { + interceptors = append(interceptors, middleware.Logging(opts.Logger)) + } + + interceptors = append(interceptors, middleware.Recovery) + isTracingEnabled := opts.PluginConfig.TracingEnabled && opts.StartupConfig.Telemetry != nil && opts.StartupConfig.Telemetry.Tracing != nil @@ -47,10 +61,11 @@ func GrpcServer(opts GrpcServerInitOpts) (GrpcServerInitFunc, error) { if err != nil { return nil, fmt.Errorf("failed to create tracing interceptor: %w", err) } - interceptor := grpc.UnaryInterceptor(tracingInterceptor) - grpcOpts = append(grpcOpts, interceptor) + interceptors = append(interceptors, tracingInterceptor) } + grpcOpts = append(grpcOpts, grpc.ChainUnaryInterceptor(interceptors...)) + grpcServerFunc := func(serverOpts []grpc.ServerOption) *grpc.Server { allOpts := append([]grpc.ServerOption{}, serverOpts...) allOpts = append(allOpts, grpcOpts...) diff --git a/router-tests/go.mod b/router-tests/go.mod index 637ee0a8d5..a0f49d2381 100644 --- a/router-tests/go.mod +++ b/router-tests/go.mod @@ -26,7 +26,7 @@ require ( github.com/wundergraph/cosmo/demo v0.0.0-20250807150641-0bf88c35298d github.com/wundergraph/cosmo/demo/pkg/subgraphs/projects v0.0.0-20250715110703-10f2e5f9c79e github.com/wundergraph/cosmo/router v0.0.0-20250807150641-0bf88c35298d - github.com/wundergraph/cosmo/router-plugin v0.0.0-20250616075713-f2b99c96cec4 + github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.219 go.opentelemetry.io/otel v1.36.0 go.opentelemetry.io/otel/sdk v1.36.0