From bede50522aa364766f44cd3e69019024fdb1cf69 Mon Sep 17 00:00:00 2001 From: Romain Marcadier Date: Mon, 25 Nov 2024 15:00:15 +0100 Subject: [PATCH] fix(grpc): the unary server interceptor was already set and may not be reset (#419) There can be only one interceptor set by `grpc.[With](Unary|Stream)Interceptor`, and a runtime error occurs if an interceptor is already defined and a second is attempted using the same API. Switch to using the chaining API, which appends to the existing list, and co-operates cleanly with the non-chained interceptor API. Also, adjust the gRPC test to verify adequate behavior occurs. Fixes #416 --- _integration-tests/tests/grpc/grpc.go | 41 +- internal/injector/builtin/generated.go | 1825 ----------------- .../builtin/testdata/client/grpc.go.snap | 2 +- .../builtin/testdata/server/grpc.go.snap | 2 +- internal/injector/builtin/yaml/rpc/grpc.yml | 8 +- 5 files changed, 45 insertions(+), 1833 deletions(-) delete mode 100644 internal/injector/builtin/generated.go diff --git a/_integration-tests/tests/grpc/grpc.go b/_integration-tests/tests/grpc/grpc.go index 7d49aff1..a382f6ea 100644 --- a/_integration-tests/tests/grpc/grpc.go +++ b/_integration-tests/tests/grpc/grpc.go @@ -10,6 +10,7 @@ package grpc import ( "context" "net" + "sync/atomic" "testing" "time" @@ -31,17 +32,50 @@ func (tc *TestCase) Setup(t *testing.T) { require.NoError(t, err) tc.addr = lis.Addr().String() - tc.Server = grpc.NewServer() + var ( + interceptedDirect atomic.Bool + interceptedChain atomic.Bool + ) + tc.Server = grpc.NewServer( + // Register a bunch of interceptors to ensure ours does not cause a runtime crash. + grpc.UnaryInterceptor(func(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { + interceptedDirect.Store(true) + return handler(ctx, req) + }), + grpc.ChainUnaryInterceptor(func(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { + interceptedChain.Store(true) + return handler(ctx, req) + }), + ) helloworld.RegisterGreeterServer(tc.Server, &server{}) go func() { assert.NoError(t, tc.Server.Serve(lis)) }() t.Cleanup(func() { tc.Server.GracefulStop() + assert.True(t, interceptedDirect.Load(), "original interceptor was not called") + assert.True(t, interceptedChain.Load(), "original chained interceptor was not called") }) } func (tc *TestCase) Run(t *testing.T) { - conn, err := grpc.NewClient(tc.addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + var ( + interceptedDirect atomic.Bool + interceptedChain atomic.Bool + ) + + conn, err := grpc.NewClient( + tc.addr, + // Register a bunch of interceptors to ensure ours does not cause a runtime crash. + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUnaryInterceptor(func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + interceptedDirect.Store(true) + return invoker(ctx, method, req, reply, cc, opts...) + }), + grpc.WithChainUnaryInterceptor(func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + interceptedChain.Store(true) + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) require.NoError(t, err) defer func() { require.NoError(t, conn.Close()) }() @@ -52,6 +86,9 @@ func (tc *TestCase) Run(t *testing.T) { resp, err := client.SayHello(ctx, &helloworld.HelloRequest{Name: "rob"}) require.NoError(t, err) require.Equal(t, "Hello rob", resp.GetMessage()) + + assert.True(t, interceptedDirect.Load(), "original interceptor was not called") + assert.True(t, interceptedChain.Load(), "original chained interceptor was not called") } func (*TestCase) ExpectedTraces() trace.Traces { diff --git a/internal/injector/builtin/generated.go b/internal/injector/builtin/generated.go deleted file mode 100644 index fa4f8e3f..00000000 --- a/internal/injector/builtin/generated.go +++ /dev/null @@ -1,1825 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2023-present Datadog, Inc. -// -// Code generated by "github.com/DataDog/orchestion/internal/injector/builtin/generator -i=yaml/*.yml -i=yaml/*/*.yml -p=builtin -y=./all.yml -o=./generated.go -d=./generated_deps.go -C=1 -docs=../../../_docs/content/docs/built-in/ -schemadocs=../../../_docs/content/contributing/aspects/"; DO NOT EDIT. - -package builtin - -import ( - aspect "github.com/DataDog/orchestrion/internal/injector/aspect" - advice "github.com/DataDog/orchestrion/internal/injector/aspect/advice" - code "github.com/DataDog/orchestrion/internal/injector/aspect/advice/code" - context "github.com/DataDog/orchestrion/internal/injector/aspect/context" - join "github.com/DataDog/orchestrion/internal/injector/aspect/join" -) - -// Aspects is the list of built-in aspects. -var Aspects = [...]aspect.Aspect{ - // From api/vault.yml - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/hashicorp/vault/api.Config"), join.StructLiteralMatchAny), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- .AST.Type -}}{\n {{- $hasField := false -}}\n {{ range .AST.Elts }}\n {{- if eq .Key.Name \"HttpClient\" }}\n {{- $hasField = true -}}\n HttpClient: vaulttrace.WrapHTTPClient({{ .Value }}),\n {{- else -}}\n {{ . }},\n {{ end -}}\n {{ end }}\n {{- if not $hasField -}}\n HttpClient: vaulttrace.NewHTTPClient(),\n {{- end }}\n}", - map[string]string{ - "vaulttrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/hashicorp/vault", - }, - context.GoLangVersion{}, - )), - }, - }, - // From civisibility/testing.yml - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Run"), - join.Receiver(join.MustTypeName("*testing.M")), - )), - ), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "//go:linkname __dd_civisibility_instrumentTestingM gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentTestingM\nfunc __dd_civisibility_instrumentTestingM(*M) func(int)", - map[string]string{}, - context.GoLangVersion{}, - ), []string{ - "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting", - }), - advice.PrependStmts(code.MustTemplate( - "exitFunc := __dd_civisibility_instrumentTestingM({{ .Function.Receiver }})\ndefer exitFunc({{ .Function.Receiver }}.exitCode)", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Run"), - join.Receiver(join.MustTypeName("*testing.T")), - )), - ), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "//go:linkname __dd_civisibility_instrumentTestingTFunc gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentTestingTFunc\nfunc __dd_civisibility_instrumentTestingTFunc(func(*T)) func(*T)\n\n//go:linkname __dd_civisibility_instrumentSetErrorInfo gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentSetErrorInfo\nfunc __dd_civisibility_instrumentSetErrorInfo(tb TB, errType string, errMessage string, skip int)\n\n//go:linkname __dd_civisibility_instrumentCloseAndSkip gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentCloseAndSkip\nfunc __dd_civisibility_instrumentCloseAndSkip(tb TB, skipReason string)\n\n//go:linkname __dd_civisibility_instrumentSkipNow gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentSkipNow\nfunc __dd_civisibility_instrumentSkipNow(tb TB)\n\n//go:linkname __dd_civisibility_ExitCiVisibility gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations.ExitCiVisibility\nfunc __dd_civisibility_ExitCiVisibility()", - map[string]string{}, - context.GoLangVersion{}, - ), []string{ - "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations", - "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting", - }), - advice.PrependStmts(code.MustTemplate( - "{{ .Function.Argument 1 }} = __dd_civisibility_instrumentTestingTFunc({{ .Function.Argument 1 }})", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Run"), - join.Receiver(join.MustTypeName("*testing.B")), - )), - ), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "//go:linkname __dd_civisibility_instrumentTestingBFunc gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentTestingBFunc\nfunc __dd_civisibility_instrumentTestingBFunc(*B, string, func(*B)) (string, func(*B))", - map[string]string{}, - context.GoLangVersion{}, - ), []string{ - "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting", - }), - advice.PrependStmts(code.MustTemplate( - "{{ .Function.Argument 0 }}, {{ .Function.Argument 1 }} = __dd_civisibility_instrumentTestingBFunc({{ .Function.Receiver }}, {{ .Function.Argument 0 }}, {{ .Function.Argument 1 }})", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Fail"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, \"Fail\", \"failed test\", 0)", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("FailNow"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, \"FailNow\", \"failed test\", 0)\n__dd_civisibility_ExitCiVisibility()", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Error"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, \"Error\", fmt.Sprint({{ .Function.Argument 0 }}...), 0)", - map[string]string{ - "fmt": "fmt", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Errorf"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, \"Errorf\", fmt.Sprintf({{ .Function.Argument 0 }}, {{ .Function.Argument 1 }}...), 0)", - map[string]string{ - "fmt": "fmt", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Fatal"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, \"Fatal\", fmt.Sprint({{ .Function.Argument 0 }}...), 0)", - map[string]string{ - "fmt": "fmt", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Fatalf"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, \"Fatalf\", fmt.Sprintf({{ .Function.Argument 0 }}, {{ .Function.Argument 1 }}...), 0)", - map[string]string{ - "fmt": "fmt", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Skip"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentCloseAndSkip({{ .Function.Receiver }}, fmt.Sprint({{ .Function.Argument 0 }}...))", - map[string]string{ - "fmt": "fmt", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("Skipf"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentCloseAndSkip({{ .Function.Receiver }}, fmt.Sprintf({{ .Function.Argument 0 }}, {{ .Function.Argument 1 }}...))", - map[string]string{ - "fmt": "fmt", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("testing"), - join.FunctionBody(join.Function( - join.Name("SkipNow"), - join.Receiver(join.MustTypeName("*testing.common")), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_civisibility_instrumentSkipNow({{ .Function.Receiver }})", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - // From cloud/aws-sdk-v2.yml - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/aws/aws-sdk-go-v2/aws.Config"), join.StructLiteralMatchValueOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg aws.Config) (aws.Config) {\n awstrace.AppendMiddleware(&cfg)\n return cfg\n}({{ . }})", - map[string]string{ - "aws": "github.com/aws/aws-sdk-go-v2/aws", - "awstrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.StructLiteral(join.MustTypeName("github.com/aws/aws-sdk-go-v2/aws.Config"), join.StructLiteralMatchPointerOnly), - join.FunctionCall("github.com/aws/aws-sdk-go-v2/aws", "NewConfig"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg *aws.Config) (*aws.Config) {\n awstrace.AppendMiddleware(cfg)\n return cfg\n}({{ . }})", - map[string]string{ - "aws": "github.com/aws/aws-sdk-go-v2/aws", - "awstrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws", - }, - context.GoLangVersion{}, - )), - }, - }, - // From cloud/aws-sdk.yml - { - JoinPoint: join.FunctionCall("github.com/aws/aws-sdk-go/aws/session", "NewSession"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(sess *session.Session, err error) (*session.Session, error) {\n if sess != nil {\n sess = awstrace.WrapSession(sess)\n }\n return sess, err\n}({{ . }})", - map[string]string{ - "awstrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws", - "session": "github.com/aws/aws-sdk-go/aws/session", - }, - context.GoLangVersion{}, - )), - }, - }, - // From databases/go-elasticsearch.yml - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/elastic/go-elasticsearch/v6.Config"), join.StructLiteralMatchValueOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg elasticsearch.Config) elasticsearch.Config {\n if cfg.Transport == nil {\n cfg.Transport = elastictrace.NewRoundTripper()\n } else {\n base := cfg.Transport\n cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base))\n }\n return cfg\n}({{ . }})", - map[string]string{ - "elasticsearch": "github.com/elastic/go-elasticsearch/v6", - "elastictrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/elastic/go-elasticsearch/v6.Config"), join.StructLiteralMatchPointerOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg *elasticsearch.Config) *elasticsearch.Config {\n if cfg.Transport == nil {\n cfg.Transport = elastictrace.NewRoundTripper()\n } else {\n base := cfg.Transport\n cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base))\n }\n return cfg\n}({{ . }})", - map[string]string{ - "elasticsearch": "github.com/elastic/go-elasticsearch/v6", - "elastictrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/elastic/go-elasticsearch/v7.Config"), join.StructLiteralMatchValueOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg elasticsearch.Config) elasticsearch.Config {\n if cfg.CACert != nil {\n // refuse to set transport as it will make the NewClient call fail.\n return cfg\n }\n if cfg.Transport == nil {\n cfg.Transport = elastictrace.NewRoundTripper()\n } else {\n base := cfg.Transport\n cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base))\n }\n return cfg\n}({{ . }})", - map[string]string{ - "elasticsearch": "github.com/elastic/go-elasticsearch/v7", - "elastictrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/elastic/go-elasticsearch/v7.Config"), join.StructLiteralMatchPointerOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg *elasticsearch.Config) *elasticsearch.Config {\n if cfg.CACert != nil {\n // refuse to set transport as it will make the NewClient call fail.\n return cfg\n }\n if cfg.Transport == nil {\n cfg.Transport = elastictrace.NewRoundTripper()\n } else {\n base := cfg.Transport\n cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base))\n }\n return cfg\n}({{ . }})", - map[string]string{ - "elasticsearch": "github.com/elastic/go-elasticsearch/v7", - "elastictrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/elastic/go-elasticsearch/v8.Config"), join.StructLiteralMatchValueOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg elasticsearch.Config) elasticsearch.Config {\n if cfg.CACert != nil {\n // refuse to set transport as it will make the NewClient call fail.\n return cfg\n }\n if cfg.Transport == nil {\n cfg.Transport = elastictrace.NewRoundTripper()\n } else {\n base := cfg.Transport\n cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base))\n }\n return cfg\n}({{ . }})", - map[string]string{ - "elasticsearch": "github.com/elastic/go-elasticsearch/v8", - "elastictrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/elastic/go-elasticsearch/v8.Config"), join.StructLiteralMatchPointerOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cfg *elasticsearch.Config) *elasticsearch.Config {\n if cfg.CACert != nil {\n // refuse to set transport as it will make the NewClient call fail.\n return cfg\n }\n if cfg.Transport == nil {\n cfg.Transport = elastictrace.NewRoundTripper()\n } else {\n base := cfg.Transport\n cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base))\n }\n return cfg\n}({{ . }})", - map[string]string{ - "elasticsearch": "github.com/elastic/go-elasticsearch/v8", - "elastictrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6", - }, - context.GoLangVersion{}, - )), - }, - }, - // From databases/go-redis.yml - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/go-redis/redis", "NewClient"), - join.FunctionCall("github.com/go-redis/redis", "NewFailoverClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (client *redis.Client) {\n client = {{ . }}\n trace.WrapClient(client)\n return\n}()", - map[string]string{ - "redis": "github.com/go-redis/redis", - "trace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/go-redis/redis/v7", "NewClient"), - join.FunctionCall("github.com/go-redis/redis/v7", "NewFailoverClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (client *redis.Client) {\n client = {{ . }}\n trace.WrapClient(client)\n return\n}()", - map[string]string{ - "redis": "github.com/go-redis/redis/v7", - "trace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v7", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/go-redis/redis/v8", "NewClient"), - join.FunctionCall("github.com/go-redis/redis/v8", "NewFailoverClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (client *redis.Client) {\n client = {{ . }}\n trace.WrapClient(client)\n return\n}()", - map[string]string{ - "redis": "github.com/go-redis/redis/v8", - "trace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v8", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/redis/go-redis/v9", "NewClient"), - join.FunctionCall("github.com/redis/go-redis/v9", "NewFailoverClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (client *redis.Client) {\n client = {{ . }}\n trace.WrapClient(client)\n return\n}()", - map[string]string{ - "redis": "github.com/redis/go-redis/v9", - "trace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/redis/go-redis.v9", - }, - context.GoLangVersion{}, - )), - }, - }, - // From databases/gocql.yml - { - JoinPoint: join.AllOf( - join.OneOf( - join.StructLiteral(join.MustTypeName("github.com/gocql/gocql.ClusterConfig"), join.StructLiteralMatchPointerOnly), - join.FunctionCall("github.com/gocql/gocql", "NewCluster"), - ), - join.Not(join.ImportPath("github.com/gocql/gocql")), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cluster *gocql.ClusterConfig) *gocql.ClusterConfig {\n obs := gocqltrace.NewObserver(cluster) \n cluster.QueryObserver = obs\n cluster.BatchObserver = obs\n cluster.ConnectObserver = obs\n return cluster\n}({{ . }})", - map[string]string{ - "gocql": "github.com/gocql/gocql", - "gocqltrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/gocql/gocql", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.StructLiteral(join.MustTypeName("github.com/gocql/gocql.ClusterConfig"), join.StructLiteralMatchValueOnly), - join.Not(join.ImportPath("github.com/gocql/gocql")), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(cluster gocql.ClusterConfig) gocql.ClusterConfig {\n obs := gocqltrace.NewObserver(&cluster) \n cluster.QueryObserver = obs\n cluster.BatchObserver = obs\n cluster.ConnectObserver = obs\n return cluster\n}({{ . }})", - map[string]string{ - "gocql": "github.com/gocql/gocql", - "gocqltrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/gocql/gocql", - }, - context.GoLangVersion{}, - )), - }, - }, - // From databases/gorm.yml - { - JoinPoint: join.FunctionCall("gorm.io/gorm", "Open"), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1", "Open"), - }, - }, - { - JoinPoint: join.FunctionCall("github.com/jinzhu/gorm", "Open"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (*gorm.DB, error) {\n db, err := {{ . }}\n if err != nil {\n return nil, err\n }\n return gormtrace.WithCallbacks(db), err\n}()", - map[string]string{ - "gorm": "github.com/jinzhu/gorm", - "gormtrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/jinzhu/gorm", - }, - context.GoLangVersion{}, - )), - }, - }, - // From databases/mongo.yml - { - JoinPoint: join.FunctionCall("go.mongodb.org/mongo-driver/mongo/options", "Client"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{ . }}.SetMonitor(mongotrace.NewMonitor())", - map[string]string{ - "mongotrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/go.mongodb.org/mongo-driver/mongo", - "options": "go.mongodb.org/mongo-driver/mongo/options", - }, - context.GoLangVersion{}, - )), - }, - }, - // From databases/pgx.yml - { - JoinPoint: join.AllOf( - join.FunctionCall("github.com/jackc/pgx/v5", "Connect"), - join.Not(join.OneOf( - join.ImportPath("github.com/jackc/pgx/v5"), - join.ImportPath("github.com/jackc/pgx/v5/pgxpool"), - )), - ), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5", "Connect"), - }, - }, - { - JoinPoint: join.AllOf( - join.FunctionCall("github.com/jackc/pgx/v5", "ConnectConfig"), - join.Not(join.OneOf( - join.ImportPath("github.com/jackc/pgx/v5"), - join.ImportPath("github.com/jackc/pgx/v5/pgxpool"), - )), - ), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5", "ConnectConfig"), - }, - }, - { - JoinPoint: join.AllOf( - join.FunctionCall("github.com/jackc/pgx/v5/pgxpool", "New"), - join.Not(join.OneOf( - join.ImportPath("github.com/jackc/pgx/v5"), - join.ImportPath("github.com/jackc/pgx/v5/pgxpool"), - )), - ), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5", "NewPool"), - }, - }, - { - JoinPoint: join.AllOf( - join.FunctionCall("github.com/jackc/pgx/v5/pgxpool", "NewWithConfig"), - join.Not(join.OneOf( - join.ImportPath("github.com/jackc/pgx/v5"), - join.ImportPath("github.com/jackc/pgx/v5/pgxpool"), - )), - ), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5", "NewPoolWithConfig"), - }, - }, - // From databases/redigo.yml - { - JoinPoint: join.FunctionCall("github.com/gomodule/redigo/redis", "Dial"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (redigo.Conn, error) {\n {{ if .AST.Ellipsis }}\n opts := {{ index .AST.Args 2 }}\n anyOpts := make([]interface{}, len(opts))\n for i, v := range opts {\n anyOpts[i] = v\n }\n return redigotrace.Dial({{ index .AST.Args 0 }}, {{ index .AST.Args 1 }}, anyOpts...)\n {{ else }}\n return redigotrace.Dial(\n {{- range .AST.Args -}}\n {{ . }},\n {{- end -}}\n )\n {{ end }}\n}()", - map[string]string{ - "redigo": "github.com/gomodule/redigo/redis", - "redigotrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.FunctionCall("github.com/gomodule/redigo/redis", "DialContext"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (redigo.Conn, error) {\n {{ if .AST.Ellipsis }}\n opts := {{ index .AST.Args 3 }}\n anyOpts := make([]interface{}, len(opts))\n for i, v := range opts {\n anyOpts[i] = v\n }\n return redigotrace.DialContext({{ index .AST.Args 0 }}, {{ index .AST.Args 1 }}, {{ index .AST.Args 2 }}, anyOpts...)\n {{ else }}\n return redigotrace.DialContext(\n {{- range .AST.Args -}}\n {{ . }},\n {{- end -}}\n )\n {{ end }}\n}()", - map[string]string{ - "redigo": "github.com/gomodule/redigo/redis", - "redigotrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.FunctionCall("github.com/gomodule/redigo/redis", "DialURL"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() (redigo.Conn, error) {\n {{ if .AST.Ellipsis }}\n opts := {{ index .AST.Args 1 }}\n anyOpts := make([]interface{}, len(opts))\n for i, v := range opts {\n anyOpts[i] = v\n }\n return redigotrace.DialURL({{ index .AST.Args 0 }}, anyOpts...)\n {{ else }}\n return redigotrace.DialURL(\n {{- range .AST.Args -}}\n {{ . }},\n {{- end -}}\n )\n {{ end }}\n}()", - map[string]string{ - "redigo": "github.com/gomodule/redigo/redis", - "redigotrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo", - }, - context.GoLangVersion{}, - )), - }, - }, - // From datastreams/confluentinc_kafka.yml - { - JoinPoint: join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "const __dd_ckgoVersion = tracing.CKGoVersion1", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - ), []string{}), - }, - }, - { - JoinPoint: join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "const __dd_ckgoVersion = tracing.CKGoVersion2", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - ), []string{}), - }, - }, - { - JoinPoint: join.OneOf( - join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - ), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "type __dd_wMessage struct {\n *Message\n}\n \nfunc __dd_wrapMessage(msg *Message) tracing.Message {\n if msg == nil {\n return nil\n }\n return &__dd_wMessage{msg}\n}\n \nfunc (w *__dd_wMessage) Unwrap() any {\n return w.Message\n}\n \nfunc (w *__dd_wMessage) GetValue() []byte {\n return w.Message.Value\n}\n \nfunc (w *__dd_wMessage) GetKey() []byte {\n return w.Message.Key\n}\n \nfunc (w *__dd_wMessage) GetHeaders() []tracing.Header {\n hs := make([]tracing.Header, 0, len(w.Headers))\n for _, h := range w.Headers {\n hs = append(hs, __dd_wrapHeader(h))\n }\n return hs\n}\n \nfunc (w *__dd_wMessage) SetHeaders(headers []tracing.Header) {\n hs := make([]Header, 0, len(headers))\n for _, h := range headers {\n hs = append(hs, Header{\n Key: h.GetKey(),\n Value: h.GetValue(),\n })\n }\n w.Message.Headers = hs\n}\n \nfunc (w *__dd_wMessage) GetTopicPartition() tracing.TopicPartition {\n return __dd_wrapTopicPartition(w.Message.TopicPartition)\n}\n \ntype __dd_wHeader struct {\n Header\n}\n \nfunc __dd_wrapHeader(h Header) tracing.Header {\n return &__dd_wHeader{h}\n}\n \nfunc (w __dd_wHeader) GetKey() string {\n return w.Header.Key\n}\n \nfunc (w __dd_wHeader) GetValue() []byte {\n return w.Header.Value\n}\n \ntype __dd_wTopicPartition struct {\n TopicPartition\n}\n \nfunc __dd_wrapTopicPartition(tp TopicPartition) tracing.TopicPartition {\n return __dd_wTopicPartition{tp}\n}\n \nfunc __dd_wrapTopicPartitions(tps []TopicPartition) []tracing.TopicPartition {\n wtps := make([]tracing.TopicPartition, 0, len(tps))\n for _, tp := range tps {\n wtps = append(wtps, __dd_wTopicPartition{tp})\n }\n return wtps\n}\n \nfunc (w __dd_wTopicPartition) GetTopic() string {\n if w.Topic == nil {\n return \"\"\n }\n return *w.Topic\n}\n \nfunc (w __dd_wTopicPartition) GetPartition() int32 {\n return w.Partition\n}\n \nfunc (w __dd_wTopicPartition) GetOffset() int64 {\n return int64(w.Offset)\n}\n \nfunc (w __dd_wTopicPartition) GetError() error {\n return w.Error\n}\n \ntype __dd_wEvent struct {\n Event\n}\n \nfunc __dd_wrapEvent(event Event) tracing.Event {\n return __dd_wEvent{event}\n}\n \nfunc (w __dd_wEvent) KafkaMessage() (tracing.Message, bool) {\n if m, ok := w.Event.(*Message); ok {\n return __dd_wrapMessage(m), true\n }\n return nil, false\n}\n \nfunc (w __dd_wEvent) KafkaOffsetsCommitted() (tracing.OffsetsCommitted, bool) {\n if oc, ok := w.Event.(OffsetsCommitted); ok {\n return __dd_wrapOffsetsCommitted(oc), true\n }\n return nil, false\n}\n \ntype __dd_wOffsetsCommitted struct {\n OffsetsCommitted\n}\n \nfunc __dd_wrapOffsetsCommitted(oc OffsetsCommitted) tracing.OffsetsCommitted {\n return __dd_wOffsetsCommitted{oc}\n}\n \nfunc (w __dd_wOffsetsCommitted) GetError() error {\n return w.Error\n}\n \nfunc (w __dd_wOffsetsCommitted) GetOffsets() []tracing.TopicPartition {\n ttps := make([]tracing.TopicPartition, 0, len(w.Offsets))\n for _, tp := range w.Offsets {\n ttps = append(ttps, __dd_wrapTopicPartition(tp))\n }\n return ttps\n}\n\ntype __dd_wConfigMap struct {\n cfg *ConfigMap\n}\n \nfunc __dd_wrapConfigMap(cm *ConfigMap) tracing.ConfigMap {\n return &__dd_wConfigMap{cm}\n}\n \nfunc (w *__dd_wConfigMap) Get(key string, defVal any) (any, error) {\n return w.cfg.Get(key, defVal)\n}\n\nfunc init() {\n telemetry.LoadIntegration(tracing.ComponentName(__dd_ckgoVersion))\n tracer.MarkIntegrationImported(tracing.IntegrationName(__dd_ckgoVersion))\n}\n\nfunc __dd_newKafkaTracer(opts ...tracing.Option) *tracing.KafkaTracer {\n v, _ := LibraryVersion()\n return tracing.NewKafkaTracer(__dd_ckgoVersion, v, opts...)\n}\n\nfunc __dd_initConsumer(c *Consumer) {\n if c.__dd_tracer != nil {\n return\n }\n var opts []tracing.Option\n if c.__dd_confmap != nil {\n opts = append(opts, tracing.WithConfig(__dd_wrapConfigMap(c.__dd_confmap)))\n }\n c.__dd_tracer = __dd_newKafkaTracer(opts...)\n // TODO: accessing c.events here might break if the library renames this variable...\n c.__dd_events = tracing.WrapConsumeEventsChannel(c.__dd_tracer, c.events, c, __dd_wrapEvent)\n}\n\nfunc __dd_initProducer(p *Producer) {\n if p.__dd_tracer != nil {\n return\n }\n p.__dd_tracer = __dd_newKafkaTracer()\n // TODO: accessing p.events and p.produceChannel here might break if the library renames this variable...\n p.__dd_events = p.events\n p.__dd_produceChannel = tracing.WrapProduceChannel(p.__dd_tracer, p.produceChannel, __dd_wrapMessage)\n if p.__dd_tracer.DSMEnabled() {\n p.__dd_events = tracing.WrapProduceEventsChannel(p.__dd_tracer, p.events, __dd_wrapEvent)\n }\n}\n\ntype __dd_eventChan = chan Event\ntype __dd_messageChan = chan *Message\ntype __dd_kafkaTracer = tracing.KafkaTracer", - map[string]string{ - "telemetry": "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.MustParseGoLangVersion("go1.18"), - ), []string{}), - }, - }, - { - JoinPoint: join.OneOf( - join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - ), - Advice: []advice.Advice{ - advice.AddStructField("__dd_tracer", join.MustTypeName("*__dd_kafkaTracer")), - advice.AddStructField("__dd_events", join.MustTypeName("__dd_eventChan")), - advice.AddStructField("__dd_confmap", join.MustTypeName("*ConfigMap")), - }, - }, - { - JoinPoint: join.OneOf( - join.AllOf( - join.ImportPath("github.com/confluentinc/confluent-kafka-go/kafka"), - join.FunctionBody(join.Function( - join.Name("NewConsumer"), - )), - ), - join.AllOf( - join.ImportPath("github.com/confluentinc/confluent-kafka-go/v2/kafka"), - join.FunctionBody(join.Function( - join.Name("NewConsumer"), - )), - ), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $conf := .Function.Argument 0 -}}\n{{- $c := .Function.Result 0 -}}\ndefer func() {\n if {{ $c }} == nil {\n return\n }\n {{ $c }}.__dd_confmap = {{ $conf }}\n __dd_initConsumer({{ $c }})\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.Name("Close"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - join.Name("Close"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $c := .Function.Receiver -}}\n__dd_initConsumer({{ $c }})\ndefer func() {\n if {{ $c }}.__dd_events == nil && {{ $c }}.__dd_tracer.PrevSpan != nil {\n {{ $c }}.__dd_tracer.PrevSpan.Finish()\n {{ $c }}.__dd_tracer.PrevSpan = nil\n }\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.Name("Events"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - join.Name("Events"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $c := .Function.Receiver -}}\n{{- $events := .Function.Result 0 -}}\n__dd_initConsumer({{ $c }})\ndefer func() {\n {{ $events }} = {{ $c }}.__dd_events\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.Name("Poll"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - join.Name("Poll"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $c := .Function.Receiver -}}\n{{- $event := .Function.Result 0 -}}\n__dd_initConsumer({{ $c }})\nif {{ $c }}.__dd_tracer.PrevSpan != nil {\n {{ $c }}.__dd_tracer.PrevSpan.Finish()\n {{ $c }}.__dd_tracer.PrevSpan = nil\n}\ndefer func() {\n if msg, ok := {{ $event }}.(*Message); ok {\n tMsg := __dd_wrapMessage(msg)\n {{ $c }}.__dd_tracer.SetConsumeCheckpoint(tMsg)\n {{ $c }}.__dd_tracer.PrevSpan = {{ $c }}.__dd_tracer.StartConsumeSpan(tMsg)\n } else if offset, ok := {{ $event }}.(OffsetsCommitted); ok {\n tOffsets := __dd_wrapTopicPartitions(offset.Offsets)\n {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, offset.Error)\n {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }})\n }\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.Name("Commit"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - join.Name("Commit"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $c := .Function.Receiver -}}\n{{- $tps := .Function.Result 0 -}}\n{{- $err := .Function.Result 1 -}}\n__dd_initConsumer({{ $c }})\ndefer func() {\n tOffsets := __dd_wrapTopicPartitions({{ $tps }})\n {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }})\n {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }})\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.Name("CommitMessage"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - join.Name("CommitMessage"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $c := .Function.Receiver -}}\n{{- $tps := .Function.Result 0 -}}\n{{- $err := .Function.Result 1 -}}\n__dd_initConsumer({{ $c }})\ndefer func() {\n tOffsets := __dd_wrapTopicPartitions({{ $tps }})\n {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }})\n {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }})\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Consumer")), - join.Name("CommitOffsets"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer")), - join.Name("CommitOffsets"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $c := .Function.Receiver -}}\n{{- $tps := .Function.Result 0 -}}\n{{- $err := .Function.Result 1 -}}\n__dd_initConsumer({{ $c }})\ndefer func() {\n tOffsets := __dd_wrapTopicPartitions({{ $tps }})\n {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }})\n {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }})\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/kafka.Producer")), - join.StructDefinition(join.MustTypeName("github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer")), - ), - Advice: []advice.Advice{ - advice.AddStructField("__dd_tracer", join.MustTypeName("*__dd_kafkaTracer")), - advice.AddStructField("__dd_events", join.MustTypeName("__dd_eventChan")), - advice.AddStructField("__dd_produceChannel", join.MustTypeName("__dd_messageChan")), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Producer")), - join.Name("Events"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer")), - join.Name("Events"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $p := .Function.Receiver -}}\n{{- $events := .Function.Result 0 -}}\n__dd_initProducer({{ $p }})\ndefer func() {\n {{ $events }} = {{ $p }}.__dd_events\n}()", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Producer")), - join.Name("ProduceChannel"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer")), - join.Name("ProduceChannel"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $p := .Function.Receiver -}}\n{{- $produceChannel := .Function.Result 0 -}}\n__dd_initProducer({{ $p }})\ndefer func() {\n {{ $produceChannel }} = {{ $p }}.__dd_produceChannel\n}()", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Producer")), - join.Name("Close"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer")), - join.Name("Close"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $p := .Function.Receiver -}}\n__dd_initProducer({{ $p }})\nclose({{ $p }}.__dd_produceChannel)", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/kafka.Producer")), - join.Name("Produce"), - )), - join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer")), - join.Name("Produce"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $p := .Function.Receiver -}}\n{{- $msg := .Function.Argument 0 -}}\n{{- $deliveryChan := .Function.Argument 1 -}}\n{{- $err := .Function.Result 0 -}}\n__dd_initProducer({{ $p }})\ntMsg := __dd_wrapMessage({{ $msg }})\nspan := p.__dd_tracer.StartProduceSpan(tMsg)\n\nvar errChan chan error\n{{ $deliveryChan }}, errChan = tracing.WrapDeliveryChannel({{ $p }}.__dd_tracer, {{ $deliveryChan }}, span, __dd_wrapEvent)\n\n{{ $p }}.__dd_tracer.SetProduceCheckpoint(tMsg)\ndefer func() {\n if {{ $err }} != nil {\n if errChan != nil {\n errChan <- {{ $err }}\n } else {\n span.Finish(tracer.WithError({{ $err }}))\n }\n }\n}()", - map[string]string{ - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - // From datastreams/gcp_pubsub.yml - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*cloud.google.com/go/pubsub.Subscription")), - join.Name("Receive"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $subscription := .Function.Receiver -}}\n{{- $handler := .Function.Argument 1 -}}\n__dd_traceFn := tracing.TraceReceiveFunc({{ $subscription }})\n__dd_wrapHandler := func(h func(ctx context.Context, msg *Message)) func(ctx context.Context, msg *Message) {\n return func(ctx context.Context, msg *Message) {\n __dd_traceMsg := &tracing.Message{\n ID: msg.ID,\n Data: msg.Data,\n OrderingKey: msg.OrderingKey,\n Attributes: msg.Attributes,\n DeliveryAttempt: msg.DeliveryAttempt,\n PublishTime: msg.PublishTime,\n }\n ctx, closeSpan := __dd_traceFn(ctx, __dd_traceMsg)\n defer closeSpan()\n h(ctx, msg)\n }\n}\n{{ $handler }} = __dd_wrapHandler({{ $handler }})", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/cloud.google.com/go/pubsub.v1/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructDefinition(join.MustTypeName("cloud.google.com/go/internal/pubsub.PublishResult")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "type DDCloseSpanFunc = func(serverID string, err error)", - map[string]string{}, - context.GoLangVersion{}, - ), []string{}), - advice.AddStructField("DDCloseSpan", join.MustTypeName("DDCloseSpanFunc")), - }, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*cloud.google.com/go/pubsub.Topic")), - join.Name("Publish"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $topic := .Function.Receiver -}}\n{{- $ctx := .Function.Argument 0 -}}\n{{- $msg := .Function.Argument 1 -}}\n{{- $publishResult := .Function.Result 0 -}}\n__dd_traceMsg := &tracing.Message{\n ID: {{ $msg }}.ID,\n Data: {{ $msg }}.Data,\n OrderingKey: {{ $msg }}.OrderingKey,\n Attributes: {{ $msg }}.Attributes,\n DeliveryAttempt: {{ $msg }}.DeliveryAttempt,\n PublishTime: {{ $msg }}.PublishTime,\n}\n__dd_ctx, __dd_closeSpan := tracing.TracePublish({{ $ctx }}, {{ $topic }}, __dd_traceMsg)\n{{ $ctx }} = __dd_ctx\n{{ $msg }}.Attributes = __dd_traceMsg.Attributes\n\ndefer func() {\n {{ $publishResult }}.DDCloseSpan = __dd_closeSpan\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/cloud.google.com/go/pubsub.v1/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*cloud.google.com/go/internal/pubsub.PublishResult")), - join.Name("Get"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $publishResult := .Function.Receiver -}}\n{{- $serverID := .Function.Result 0 -}}\n{{- $err := .Function.Result 1 -}}\ndefer func() {\n if {{ $publishResult }}.DDCloseSpan != nil {\n {{ $publishResult }}.DDCloseSpan({{ $serverID }}, {{ $err }})\n }\n}()", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - // From datastreams/ibm_sarama.yml - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/IBM/sarama", "NewConsumer"), - join.FunctionCall("github.com/IBM/sarama", "NewConsumerClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(c sarama.Consumer, err error) (sarama.Consumer, error) {\n if c != nil {\n c = saramatrace.WrapConsumer(c)\n }\n return c, err\n}({{ . }})", - map[string]string{ - "sarama": "github.com/IBM/sarama", - "saramatrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/IBM/sarama", "NewSyncProducer"), - join.FunctionCall("github.com/IBM/sarama", "NewSyncProducerFromClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- $cfg := .Function.ArgumentOfType \"sarama.Config\" -}}\nfunc(p sarama.SyncProducer, err error) (sarama.SyncProducer, error) {\n if p != nil {\n p = saramatrace.WrapSyncProducer(\n {{- if $cfg -}}\n {{ $cfg }},\n {{- else -}}\n nil,\n {{- end -}}\n p,\n )\n }\n return p, err\n}({{ . }})", - map[string]string{ - "sarama": "github.com/IBM/sarama", - "saramatrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/IBM/sarama", "NewAsyncProducer"), - join.FunctionCall("github.com/IBM/sarama", "NewAsyncProducerFromClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- $cfg := .Function.ArgumentOfType \"sarama.Config\" -}}\nfunc(p sarama.AsyncProducer, err error) (sarama.AsyncProducer, error) {\n if p != nil {\n p = saramatrace.WrapAsyncProducer(\n {{- if $cfg -}}\n {{ $cfg }},\n {{- else -}}\n nil,\n {{- end -}}\n p,\n )\n }\n return p, err\n}({{ . }})", - map[string]string{ - "sarama": "github.com/IBM/sarama", - "saramatrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1", - }, - context.GoLangVersion{}, - )), - }, - }, - // From datastreams/segmentio_kafka_v0.yml - { - JoinPoint: join.StructDefinition(join.MustTypeName("github.com/segmentio/kafka-go.Reader")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "type __dd_wMessage struct {\n *Message\n}\n \nfunc __dd_wrapMessage(msg *Message) tracing.Message {\n if msg == nil {\n return nil\n }\n return &__dd_wMessage{msg}\n}\n \nfunc (w *__dd_wMessage) GetValue() []byte {\n return w.Value\n}\n \nfunc (w *__dd_wMessage) GetKey() []byte {\n return w.Key\n}\n \nfunc (w *__dd_wMessage) GetHeaders() []tracing.Header {\n hs := make([]tracing.Header, 0, len(w.Headers))\n for _, h := range w.Headers {\n hs = append(hs, __dd_wrapHeader(h))\n }\n return hs\n}\n \nfunc (w *__dd_wMessage) SetHeaders(headers []tracing.Header) {\n hs := make([]Header, 0, len(headers))\n for _, h := range headers {\n hs = append(hs, Header{\n Key: h.GetKey(),\n Value: h.GetValue(),\n })\n }\n w.Message.Headers = hs\n}\n \nfunc (w *__dd_wMessage) GetTopic() string {\n return w.Topic\n}\n \nfunc (w *__dd_wMessage) GetPartition() int {\n return w.Partition\n}\n \nfunc (w *__dd_wMessage) GetOffset() int64 {\n return w.Offset\n}\n \ntype __dd_wHeader struct {\n Header\n}\n \nfunc __dd_wrapHeader(h Header) tracing.Header {\n return &__dd_wHeader{h}\n}\n \nfunc (w __dd_wHeader) GetKey() string {\n return w.Key\n}\n \nfunc (w __dd_wHeader) GetValue() []byte {\n return w.Value\n}\n \ntype __dd_wWriter struct {\n *Writer\n}\n \nfunc (w *__dd_wWriter) GetTopic() string {\n return w.Topic\n}\n \nfunc __dd_wrapTracingWriter(w *Writer) tracing.Writer {\n return &__dd_wWriter{w}\n}\n\nfunc __dd_initReader(r *Reader) {\n if r.__dd_tracer != nil {\n return\n }\n kafkaCfg := tracing.KafkaConfig{}\n if r.Config().Brokers != nil {\n kafkaCfg.BootstrapServers = strings.Join(r.Config().Brokers, \",\")\n }\n if r.Config().GroupID != \"\" {\n kafkaCfg.ConsumerGroupID = r.Config().GroupID\n }\n r.__dd_tracer = tracing.NewTracer(kafkaCfg)\n}\n\ntype __dd_span = ddtrace.Span", - map[string]string{ - "ddtrace": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace", - "strings": "strings", - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing", - }, - context.GoLangVersion{}, - ), []string{}), - advice.AddStructField("__dd_tracer", join.MustTypeName("*gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing.Tracer")), - advice.AddStructField("__dd_prevSpan", join.MustTypeName("__dd_span")), - }, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/segmentio/kafka-go.Reader")), - join.Name("FetchMessage"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $r := .Function.Receiver -}}\n{{- $ctx := .Function.Argument 0 -}}\n{{- $msg := .Function.Result 0 -}}\n{{- $err := .Function.Result 1 -}}\n__dd_initReader(r)\nif {{ $r }}.__dd_prevSpan != nil {\n {{ $r }}.__dd_prevSpan.Finish()\n {{ $r }}.__dd_prevSpan = nil\n}\ndefer func() {\n if {{ $err }} != nil {\n return\n }\n tMsg := __dd_wrapMessage(&{{ $msg }})\n {{ $r }}.__dd_prevSpan = {{ $r }}.__dd_tracer.StartConsumeSpan({{ $ctx }}, tMsg)\n {{ $r }}.__dd_tracer.SetConsumeDSMCheckpoint(tMsg)\n}()", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/segmentio/kafka-go.Reader")), - join.Name("Close"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $r := .Function.Receiver -}}\nif {{ $r }}.__dd_prevSpan != nil {\n {{ $r }}.__dd_prevSpan.Finish()\n {{ $r }}.__dd_prevSpan = nil\n}", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructDefinition(join.MustTypeName("github.com/segmentio/kafka-go.Writer")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "func __dd_initWriter(w *Writer) {\n if w.__dd_tracer != nil {\n return\n }\n kafkaCfg := tracing.KafkaConfig{\n BootstrapServers: w.Addr.String(),\n }\n w.__dd_tracer = tracing.NewTracer(kafkaCfg)\n}", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing", - }, - context.GoLangVersion{}, - ), []string{}), - advice.AddStructField("__dd_tracer", join.MustTypeName("*gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing.Tracer")), - }, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/segmentio/kafka-go.Writer")), - join.Name("WriteMessages"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $w := .Function.Receiver -}}\n{{- $ctx := .Function.Argument 0 -}}\n{{- $msgs := .Function.Argument 1 -}}\n{{- $err := .Function.Result 0 -}}\nspans := make([]ddtrace.Span, len({{ $msgs }}))\n__dd_initWriter(w)\n\nvar spanOpts []tracer.StartSpanOption\nprevSpan, ok := tracer.SpanFromContext({{ $ctx }})\nif ok {\n spanOpts = append(spanOpts, tracer.ChildOf(prevSpan.Context()))\n}\n\nfor i := range {{ $msgs }} {\n tMsg := __dd_wrapMessage(&{{ $msgs }}[i])\n tWriter := __dd_wrapTracingWriter({{ $w }})\n spans[i] = {{ $w }}.__dd_tracer.StartProduceSpan(nil, tWriter, tMsg, spanOpts...)\n {{ $w }}.__dd_tracer.SetProduceDSMCheckpoint(tMsg, tWriter)\n}\n\ndefer func() {\n for i, span := range spans {\n {{ $w }}.__dd_tracer.FinishProduceSpan(span, {{ $msgs }}[i].Partition, {{ $msgs }}[i].Offset, {{ $err }})\n }\n}()", - map[string]string{ - "ddtrace": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - // From datastreams/shopify_sarama.yml - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/Shopify/sarama", "NewConsumer"), - join.FunctionCall("github.com/Shopify/sarama", "NewConsumerClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(c sarama.Consumer, err error) (sarama.Consumer, error) {\n if c != nil {\n c = saramatrace.WrapConsumer(c)\n }\n return c, err\n}({{ . }})", - map[string]string{ - "sarama": "github.com/Shopify/sarama", - "saramatrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/Shopify/sarama", "NewSyncProducer"), - join.FunctionCall("github.com/Shopify/sarama", "NewSyncProducerFromClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- $cfg := .Function.ArgumentOfType \"sarama.Config\" -}}\nfunc(p sarama.SyncProducer, err error) (sarama.SyncProducer, error) {\n if p != nil {\n p = saramatrace.WrapSyncProducer(\n {{- if $cfg -}}\n {{ $cfg }},\n {{- else -}}\n nil,\n {{- end -}}\n p,\n )\n }\n return p, err\n}({{ . }})", - map[string]string{ - "sarama": "github.com/Shopify/sarama", - "saramatrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/Shopify/sarama", "NewAsyncProducer"), - join.FunctionCall("github.com/Shopify/sarama", "NewAsyncProducerFromClient"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- $cfg := .Function.ArgumentOfType \"sarama.Config\" -}}\nfunc(p sarama.AsyncProducer, err error) (sarama.AsyncProducer, error) {\n if p != nil {\n p = saramatrace.WrapAsyncProducer(\n {{- if $cfg -}}\n {{ $cfg }},\n {{- else -}}\n nil,\n {{- end -}}\n p,\n )\n }\n return p, err\n}({{ . }})", - map[string]string{ - "sarama": "github.com/Shopify/sarama", - "saramatrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama", - }, - context.GoLangVersion{}, - )), - }, - }, - // From dd-span.yml - { - JoinPoint: join.FunctionBody(join.Directive("dd:span")), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $ctx := .Function.ArgumentOfType \"context.Context\" -}}\n{{- $req := .Function.ArgumentOfType \"*net/http.Request\" -}}\n{{- if (eq $ctx \"\") -}}\n {{- $ctx = \"ctx\" -}}\n ctx := {{- with $req -}}\n {{ $req }}.Context()\n {{- else -}}\n context.TODO()\n {{- end }}\n{{ end -}}\n\n{{ $functionName := .Function.Name -}}\n{{- $opName := $functionName -}}\n{{- range .DirectiveArgs \"dd:span\" -}}\n {{- if eq $opName \"\" -}}\n {{ $opName = .Value }}\n {{- end -}}\n {{- if eq .Key \"span.name\" -}}\n {{- $opName = .Value -}}\n {{- break -}}\n {{- end -}}\n{{- end -}}\n\nvar span tracer.Span\nspan, {{ $ctx }} = tracer.StartSpanFromContext({{ $ctx }}, {{ printf \"%q\" $opName }},\n {{- with $functionName }}\n tracer.Tag(\"function-name\", {{ printf \"%q\" $functionName }}),\n {{ end -}}\n {{- range .DirectiveArgs \"dd:span\" }}\n {{ if eq .Key \"span.name\" -}}{{- continue -}}{{- end -}}\n tracer.Tag({{ printf \"%q\" .Key }}, {{ printf \"%q\" .Value }}),\n {{- end }}\n)\n{{- with $req }}\n {{ $req }} = {{ $req }}.WithContext({{ $ctx }})\n{{- end }}\n\n{{ with .Function.ResultOfType \"error\" -}}\n defer func(){\n span.Finish(tracer.WithError({{ . }}))\n }()\n{{ else -}}\n defer span.Finish()\n{{- end -}}", - map[string]string{ - "context": "context", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - }, - context.GoLangVersion{}, - )), - }, - }, - // From directive/orchestrion-enabled.yml - { - JoinPoint: join.AllOf( - join.ValueDeclaration(join.MustTypeName("bool")), - join.OneOf( - join.DeclarationOf("github.com/DataDog/orchestrion/runtime/built", "WithOrchestrion"), - join.Directive("dd:orchestrion-enabled"), - ), - ), - Advice: []advice.Advice{ - advice.AssignValue(code.MustTemplate( - "true", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - TracerInternal: true, - }, - // From go-main.yml - { - JoinPoint: join.AllOf( - join.PackageName("main"), - join.FunctionBody(join.Function( - join.Name("main"), - join.Signature( - nil, - nil, - ), - )), - ), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "//go:linkname __dd_civisibility_isCiVisibilityEnabled gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.isCiVisibilityEnabled\nfunc __dd_civisibility_isCiVisibilityEnabled() bool\n\nfunc init() {\n // Only initialize if is not a test process and if CI Visibility has not been disabled (by the kill switch).\n // For a test process the ci visibility instrumentation will initialize the tracer\n if !testing.Testing() || !__dd_civisibility_isCiVisibilityEnabled() {\n tracer.Start(tracer.WithOrchestrion(map[string]string{\"version\": {{printf \"%q\" Version}}}))\n }\n}", - map[string]string{ - "testing": "testing", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - }, - context.GoLangVersion{}, - ), []string{ - "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting", - }), - advice.InjectDeclarations(code.MustTemplate( - "func init() {\n switch os.Getenv(\"DD_PROFILING_ENABLED\") {\n case \"1\", \"true\", \"auto\":\n // The \"auto\" value can be set if profiling is enabled via the\n // Datadog Admission Controller. We always turn on the profiler in\n // the \"auto\" case since we only send profiles after at least a\n // minute, and we assume anything running that long is worth\n // profiling.\n err := profiler.Start(\n profiler.WithProfileTypes(\n profiler.CPUProfile,\n profiler.HeapProfile,\n // Non-default profiles which are highly likely to be useful:\n profiler.GoroutineProfile,\n profiler.MutexProfile,\n ),\n profiler.WithTags(\"orchestrion:true\"),\n )\n if err != nil {\n // TODO: is there a better reporting mechanism?\n // The tracer and profiler already use the stdlib logger, so\n // we're not adding anything new. But users might be using a\n // different logger.\n log.Printf(\"failed to start profiling: %s\", err)\n }\n }\n}", - map[string]string{ - "log": "log", - "os": "os", - "profiler": "gopkg.in/DataDog/dd-trace-go.v1/profiler", - }, - context.GoLangVersion{}, - ), []string{}), - advice.PrependStmts(code.MustTemplate( - "// Only finalize if is not a test process and if CI Visibility has not been disabled (by the kill switch).\n// For a test process the ci visibility instrumentation will finalize the tracer\nif !testing.Testing() || !__dd_civisibility_isCiVisibilityEnabled() {\n defer tracer.Stop()\n}\n\ndefer profiler.Stop()", - map[string]string{ - "profiler": "gopkg.in/DataDog/dd-trace-go.v1/profiler", - "testing": "testing", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - }, - context.GoLangVersion{}, - )), - }, - }, - // From graphql/gqlgen.yml - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/99designs/gqlgen/graphql/handler", "New"), - join.FunctionCall("github.com/99designs/gqlgen/graphql/handler", "NewDefaultServer"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(s *handler.Server) *handler.Server {\n s.Use(gqlgentrace.NewTracer())\n return s\n}({{ . }})", - map[string]string{ - "gqlgentrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/99designs/gqlgen", - "handler": "github.com/99designs/gqlgen/graphql/handler", - }, - context.GoLangVersion{}, - )), - }, - }, - // From graphql/graph-gophers.yml - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/graph-gophers/graphql-go", "MustParseSchema"), - join.FunctionCall("github.com/graph-gophers/graphql-go", "ParseSchema"), - ), - Advice: []advice.Advice{ - advice.AppendArgs( - join.MustTypeName("any"), - code.MustTemplate( - "graphql.Tracer(graphqltrace.NewTracer())", - map[string]string{ - "graphql": "github.com/graph-gophers/graphql-go", - "graphqltrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/graph-gophers/graphql-go", - }, - context.GoLangVersion{}, - ), - ), - }, - }, - // From graphql/graphql-go.yml - { - JoinPoint: join.FunctionCall("github.com/graphql-go/graphql", "NewSchema"), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/graphql-go/graphql", "NewSchema"), - }, - }, - // From http/chi.yml - { - JoinPoint: join.AllOf( - join.OneOf( - join.FunctionCall("github.com/go-chi/chi", "NewMux"), - join.FunctionCall("github.com/go-chi/chi", "NewRouter"), - ), - join.Not(join.OneOf( - join.ImportPath("github.com/go-chi/chi"), - join.ImportPath("github.com/go-chi/chi/middleware"), - )), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() *chi.Mux {\n mux := {{ . }}\n mux.Use(chitrace.Middleware())\n return mux\n}()", - map[string]string{ - "chi": "github.com/go-chi/chi", - "chitrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.OneOf( - join.FunctionCall("github.com/go-chi/chi/v5", "NewMux"), - join.FunctionCall("github.com/go-chi/chi/v5", "NewRouter"), - ), - join.Not(join.OneOf( - join.ImportPath("github.com/go-chi/chi/v5"), - join.ImportPath("github.com/go-chi/chi/v5/middleware"), - )), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() *chi.Mux {\n mux := {{ . }}\n mux.Use(chitrace.Middleware())\n return mux\n}()", - map[string]string{ - "chi": "github.com/go-chi/chi/v5", - "chitrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v5", - }, - context.GoLangVersion{}, - )), - }, - }, - // From http/echo.yml - { - JoinPoint: join.FunctionCall("github.com/labstack/echo/v4", "New"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() *echo.Echo {\n e := {{ . }}\n e.Use(echotrace.Middleware())\n return e\n}()", - map[string]string{ - "echo": "github.com/labstack/echo/v4", - "echotrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/labstack/echo.v4", - }, - context.GoLangVersion{}, - )), - }, - }, - // From http/fiber.yml - { - JoinPoint: join.FunctionCall("github.com/gofiber/fiber/v2", "New"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() *fiber.App {\n app := {{ . }}\n app.Use(fibertrace.Middleware())\n return app\n}()", - map[string]string{ - "fiber": "github.com/gofiber/fiber/v2", - "fibertrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/gofiber/fiber.v2", - }, - context.GoLangVersion{}, - )), - }, - }, - // From http/gin.yml - { - JoinPoint: join.OneOf( - join.FunctionCall("github.com/gin-gonic/gin", "Default"), - join.FunctionCall("github.com/gin-gonic/gin", "New"), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func() *gin.Engine {\n e := {{ . }}\n e.Use(gintrace.Middleware(\"\"))\n return e\n}()", - map[string]string{ - "gin": "github.com/gin-gonic/gin", - "gintrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/gin-gonic/gin", - }, - context.GoLangVersion{}, - )), - }, - }, - // From http/gorilla.yml - { - JoinPoint: join.StructDefinition(join.MustTypeName("github.com/gorilla/mux.Router")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "type ddRouterConfig struct {\n ignoreRequest func(*http.Request) bool\n headerTags *internal.LockMap\n resourceNamer func(*Router, *http.Request) string\n serviceName string\n spanOpts []ddtrace.StartSpanOption\n}\n\nfunc ddDefaultResourceNamer(router *Router, req *http.Request) string {\n var (\n match RouteMatch\n route = \"unknown\"\n )\n if router.Match(req, &match) && match.Route != nil {\n if r, err := match.Route.GetPathTemplate(); err == nil {\n route = r\n }\n }\n return fmt.Sprintf(\"%s %s\", req.Method, route)\n}\n\nfunc init() {\n telemetry.LoadIntegration(\"gorilla/mux\")\n tracer.MarkIntegrationImported(\"github.com/gorilla/mux\")\n}", - map[string]string{ - "ddtrace": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace", - "http": "net/http", - "internal": "gopkg.in/DataDog/dd-trace-go.v1/internal", - "telemetry": "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - }, - context.GoLangVersion{}, - ), []string{}), - advice.AddStructField("__dd_config", join.MustTypeName("ddRouterConfig")), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("github.com/gorilla/mux"), - join.FunctionBody(join.Function( - join.Name("NewRouter"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $res := .Function.Result 0 -}}\ndefer func() {\n var analyticsRate float64\n if internal.BoolEnv(\"DD_TRACE_MUX_ANALYTICS_ENABLED\", false) {\n analyticsRate = 1.0\n } else {\n analyticsRate = globalconfig.AnalyticsRate()\n }\n\n {{ $res }}.__dd_config.headerTags = globalconfig.HeaderTagMap()\n {{ $res }}.__dd_config.ignoreRequest = func(*http.Request) bool { return false }\n {{ $res }}.__dd_config.resourceNamer = ddDefaultResourceNamer\n {{ $res }}.__dd_config.serviceName = namingschema.ServiceName(\"mux.router\")\n {{ $res }}.__dd_config.spanOpts = []ddtrace.StartSpanOption{\n tracer.Tag(ext.Component, \"gorilla/mux\"),\n tracer.Tag(ext.SpanKind, ext.SpanKindServer),\n }\n if !math.IsNaN(analyticsRate) {\n {{ $res }}.__dd_config.spanOpts = append(\n {{ $res }}.__dd_config.spanOpts,\n tracer.Tag(ext.EventSampleRate, analyticsRate),\n )\n }\n}()", - map[string]string{ - "ddtrace": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace", - "ext": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext", - "globalconfig": "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig", - "http": "net/http", - "internal": "gopkg.in/DataDog/dd-trace-go.v1/internal", - "math": "math", - "namingschema": "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/gorilla/mux.Router")), - join.Name("ServeHTTP"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $r := .Function.Receiver -}}\n{{- $w := .Function.Argument 0 -}}\n{{- $req := .Function.Argument 1 -}}\nif !{{ $r }}.__dd_config.ignoreRequest({{ $req }}) {\n var (\n match RouteMatch\n route string\n spanOpts = options.Copy({{ $r }}.__dd_config.spanOpts...)\n )\n if {{ $r }}.Match({{ $req }}, &match) && match.Route != nil {\n if h, err := match.Route.GetHostTemplate(); err == nil {\n spanOpts = append(spanOpts, tracer.Tag(\"mux.host\", h))\n }\n route, _ = match.Route.GetPathTemplate()\n }\n spanOpts = append(spanOpts, httptraceinternal.HeaderTagsFromRequest({{ $req }}, {{ $r }}.__dd_config.headerTags))\n resource := {{ $r }}.__dd_config.resourceNamer({{ $r }}, {{ $req }})\n\n // This is a temporary workaround/hack to prevent endless recursion via httptrace.TraceAndServe, which\n // basically implies passing a shallow copy of this router that ignores all requests down to\n // httptrace.TraceAndServe.\n var rCopy Router\n rCopy = *{{ $r }}\n rCopy.__dd_config.ignoreRequest = func(*http.Request) bool { return true }\n\n httptrace.TraceAndServe(&rCopy, {{ $w }}, {{ $req }}, &httptrace.ServeConfig{\n Service: {{ $r }}.__dd_config.serviceName,\n Resource: resource,\n SpanOpts: spanOpts,\n RouteParams: match.Vars,\n Route: route,\n })\n return\n}", - map[string]string{ - "http": "net/http", - "httptrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http", - "httptraceinternal": "gopkg.in/DataDog/dd-trace-go.v1/contrib/internal/httptrace", - "options": "gopkg.in/DataDog/dd-trace-go.v1/contrib/internal/options", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - }, - context.GoLangVersion{}, - )), - }, - }, - // From http/julienschmidt_httprouter.yml - { - JoinPoint: join.StructDefinition(join.MustTypeName("github.com/julienschmidt/httprouter.Router")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "type __dd_wRouter struct {\n *Router\n}\n \nfunc __dd_wrapRouter(r *Router) tracing.Router {\n return &__dd_wRouter{r}\n}\n \nfunc (w __dd_wRouter) Lookup(method string, path string) (any, []tracing.Param, bool) {\n h, params, ok := w.Router.Lookup(method, path)\n return h, __dd_wrapParams(params), ok\n}\n \ntype __dd_wParam struct {\n Param\n}\n \nfunc __dd_wrapParams(params Params) []tracing.Param {\n wParams := make([]tracing.Param, len(params))\n for i, p := range params {\n wParams[i] = __dd_wParam{p}\n }\n return wParams\n}\n \nfunc (w __dd_wParam) GetKey() string {\n return w.Key\n}\n \nfunc (w __dd_wParam) GetValue() string {\n return w.Value\n}\n \nfunc __dd_init(r *Router) {\n if r.__dd_config != nil {\n return\n }\n r.__dd_config = tracing.NewConfig()\n return\n}", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter/internal/tracing", - }, - context.MustParseGoLangVersion("go1.18"), - ), []string{}), - advice.AddStructField("__dd_config", join.MustTypeName("*gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter/internal/tracing.Config")), - }, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*github.com/julienschmidt/httprouter.Router")), - join.Name("ServeHTTP"), - )), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $r := .Function.Receiver -}}\n{{- $w := .Function.Argument 0 -}}\n{{- $req := .Function.Argument 1 -}}\n__dd_init({{ $r }})\n\ntw, treq, afterHandle, handled := tracing.BeforeHandle({{ $r }}.__dd_config, {{ $r }}, __dd_wrapRouter, {{ $w }}, {{ $req }})\n{{ $w }} = tw\n{{ $req }} = treq\ndefer afterHandle()\nif handled {\n return\n}", - map[string]string{ - "tracing": "gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter/internal/tracing", - }, - context.GoLangVersion{}, - )), - }, - }, - // From k8s-client.yml - { - JoinPoint: join.StructLiteral(join.MustTypeName("k8s.io/client-go/rest.Config"), join.StructLiteralMatchAny), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- .AST.Type -}}{\n {{- $hasField := false -}}\n {{ range .AST.Elts }}\n {{- if eq .Key.Name \"WrapTransport\" }}\n {{- $hasField = true -}}\n WrapTransport: kubernetestransport.Wrappers({{ .Value }}, kubernetestrace.WrapRoundTripper),\n {{- else -}}\n {{ . }},\n {{ end -}}\n {{ end }}\n {{- if not $hasField -}}\n WrapTransport: kubernetestransport.Wrappers(nil, kubernetestrace.WrapRoundTripper),\n {{- end }}\n}", - map[string]string{ - "kubernetestrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/k8s.io/client-go/kubernetes", - "kubernetestransport": "k8s.io/client-go/transport", - }, - context.GoLangVersion{}, - )), - }, - }, - // From logs/logrus.yml - { - JoinPoint: join.StructDefinition(join.MustTypeName("github.com/sirupsen/logrus.Logger")), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "func init() {\n telemetry.LoadIntegration(\"sirupsen/logrus\")\n tracer.MarkIntegrationImported(\"github.com/sirupsen/logrus\")\n}\n\n// DDContextLogHook ensures that any span in the log context is correlated to log output.\ntype DDContextLogHook struct{}\n\n// Levels implements logrus.Hook interface, this hook applies to all defined levels\nfunc (d *DDContextLogHook) Levels() []Level {\n return []Level{PanicLevel, FatalLevel, ErrorLevel, WarnLevel, InfoLevel, DebugLevel, TraceLevel}\n}\n\n// Fire implements logrus.Hook interface, attaches trace and span details found in entry context\nfunc (d *DDContextLogHook) Fire(e *Entry) error {\n span, found := tracer.SpanFromContext(e.Context)\n if !found {\n return nil\n }\n e.Data[ext.LogKeyTraceID] = span.Context().TraceID()\n e.Data[ext.LogKeySpanID] = span.Context().SpanID()\n return nil\n}", - map[string]string{ - "ext": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext", - "telemetry": "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry", - "tracer": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - }, - context.GoLangVersion{}, - ), []string{}), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("github.com/sirupsen/logrus"), - join.FunctionBody(join.Function( - join.Name("New"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "{{- $logger := .Function.Result 0 -}}\ndefer func() {\n {{ $logger }}.AddHook(&DDContextLogHook{})\n}()", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/sirupsen/logrus.Logger"), join.StructLiteralMatchPointerOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(logger *logrus.Logger) *logrus.Logger {\n logger.AddHook(&logrus.DDContextLogHook{})\n return logger\n}({{ . }})", - map[string]string{ - "logrus": "github.com/sirupsen/logrus", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/sirupsen/logrus.Logger"), join.StructLiteralMatchValueOnly), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(logger logrus.Logger) logrus.Logger {\n logger.AddHook(&logrus.DDContextLogHook{})\n return logger\n}({{ . }})", - map[string]string{ - "logrus": "github.com/sirupsen/logrus", - }, - context.GoLangVersion{}, - )), - }, - }, - // From rpc/grpc.yml - { - JoinPoint: join.OneOf( - join.FunctionCall("google.golang.org/grpc", "Dial"), - join.FunctionCall("google.golang.org/grpc", "DialContext"), - join.FunctionCall("google.golang.org/grpc", "NewClient"), - ), - Advice: []advice.Advice{ - advice.AppendArgs( - join.MustTypeName("google.golang.org/grpc.DialOption"), - code.MustTemplate( - "grpc.WithStreamInterceptor(grpctrace.StreamClientInterceptor())", - map[string]string{ - "grpc": "google.golang.org/grpc", - "grpctrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc", - }, - context.GoLangVersion{}, - ), - code.MustTemplate( - "grpc.WithUnaryInterceptor(grpctrace.UnaryClientInterceptor())", - map[string]string{ - "grpc": "google.golang.org/grpc", - "grpctrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc", - }, - context.GoLangVersion{}, - ), - ), - }, - }, - { - JoinPoint: join.FunctionCall("google.golang.org/grpc", "NewServer"), - Advice: []advice.Advice{ - advice.AppendArgs( - join.MustTypeName("google.golang.org/grpc.ServerOption"), - code.MustTemplate( - "grpc.StreamInterceptor(grpctrace.StreamServerInterceptor())", - map[string]string{ - "grpc": "google.golang.org/grpc", - "grpctrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc", - }, - context.GoLangVersion{}, - ), - code.MustTemplate( - "grpc.UnaryInterceptor(grpctrace.UnaryServerInterceptor())", - map[string]string{ - "grpc": "google.golang.org/grpc", - "grpctrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc", - }, - context.GoLangVersion{}, - ), - ), - }, - }, - // From rpc/twirp.yml - { - JoinPoint: join.StructLiteral(join.MustTypeName("github.com/twitchtv/twirp.ServerOptions"), join.StructLiteralMatchAny), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- .AST.Type -}}{\n {{- $hasField := false -}}\n {{ range .AST.Elts }}\n {{- if eq .Key.Name \"Hooks\" }}\n {{- $hasField = true -}}\n Hooks: twirp.ChainHooks(twirptrace.NewServerHooks(), {{ .Value }}),\n {{- else -}}\n {{ . }},\n {{ end -}}\n {{ end }}\n {{- if not $hasField -}}\n Hooks: twirptrace.NewServerHooks(),\n {{- end }}\n}", - map[string]string{ - "twirp": "github.com/twitchtv/twirp", - "twirptrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/twitchtv/twirp", - }, - context.GoLangVersion{}, - )), - }, - }, - // From stdlib/database-sql.yml - { - JoinPoint: join.FunctionCall("database/sql", "Register"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "func(driverName string, driver driver.Driver) {\n sql.Register(driverName, driver)\n sqltrace.Register(driverName, driver)\n}({{ index .AST.Args 0 }}, {{ index .AST.Args 1 }})", - map[string]string{ - "sql": "database/sql", - "sqltrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.FunctionCall("database/sql", "Open"), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql", "Open"), - }, - }, - { - JoinPoint: join.FunctionCall("database/sql", "OpenDB"), - Advice: []advice.Advice{ - advice.ReplaceFunction("gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql", "OpenDB"), - }, - }, - // From stdlib/net-http.client.yml - { - JoinPoint: join.StructDefinition(join.MustTypeName("net/http.Transport")), - Advice: []advice.Advice{ - advice.AddStructField("DD__tracer_internal", join.MustTypeName("bool")), - }, - }, - { - JoinPoint: join.AllOf( - join.StructLiteral(join.MustTypeName("net/http.Transport"), join.StructLiteralMatchAny), - join.OneOf( - join.ImportPath("gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"), - join.ImportPath("gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/httputils"), - join.ImportPath("gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig"), - join.ImportPath("gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry"), - join.ImportPath("gopkg.in/DataDog/dd-trace-go.v1/profiler"), - join.ImportPath("datadoghq.dev/orchestrion/_integration-tests/utils/agent"), - ), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- .AST.Type -}}{\n DD__tracer_internal: true,\n {{ range .AST.Elts }}{{ . }},\n {{ end }}\n}", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - TracerInternal: true, - }, - { - JoinPoint: join.FunctionBody(join.Function( - join.Name("RoundTrip"), - join.Receiver(join.MustTypeName("*net/http.Transport")), - )), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "//go:linkname __dd_appsec_RASPEnabled gopkg.in/DataDog/dd-trace-go.v1/internal/appsec.RASPEnabled\nfunc __dd_appsec_RASPEnabled() bool\n\n//go:linkname __dd_httpsec_ProtectRoundTrip gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/httpsec.ProtectRoundTrip\nfunc __dd_httpsec_ProtectRoundTrip(context.Context, string) error\n\n//go:linkname __dd_tracer_SpanType gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.SpanType\nfunc __dd_tracer_SpanType(string) ddtrace.StartSpanOption\n\n//go:linkname __dd_tracer_ResourceName gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.ResourceName\nfunc __dd_tracer_ResourceName(string) ddtrace.StartSpanOption\n\n//go:linkname __dd_tracer_Tag gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.Tag\nfunc __dd_tracer_Tag(string, any) ddtrace.StartSpanOption\n\n//go:linkname __dd_tracer_StartSpanFromContext gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.StartSpanFromContext\nfunc __dd_tracer_StartSpanFromContext(context.Context, string, ...ddtrace.StartSpanOption) (ddtrace.Span, context.Context)\n\n//go:linkname __dd_tracer_WithError gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.WithError\nfunc __dd_tracer_WithError(error) ddtrace.FinishOption\n\n//go:linkname __dd_tracer_Inject gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.Inject\nfunc __dd_tracer_Inject(ddtrace.SpanContext, any) error\n\ntype __dd_tracer_HTTPHeadersCarrier Header\nfunc (c __dd_tracer_HTTPHeadersCarrier) Set(key, val string) {\n Header(c).Set(key, val)\n}", - map[string]string{ - "context": "context", - "ddtrace": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace", - }, - context.GoLangVersion{}, - ), []string{ - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec", - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/httpsec", - }), - advice.PrependStmts(code.MustTemplate( - "{{- /* Largely copied from https://github.com/DataDog/dd-trace-go/blob/v1.65.0-rc.2/contrib/net/http/roundtripper.go#L28-L104 */ -}}\n{{- $t := .Function.Receiver -}}\n{{- $req := .Function.Argument 0 -}}\n{{- $res := .Function.Result 0 -}}\n{{- $err := .Function.Result 1 -}}\nif !{{ $t }}.DD__tracer_internal {\n resourceName := fmt.Sprintf(\"%s %s\", {{ $req }}.Method, {{ $req }}.URL.Path)\n spanName := namingschema.OpName(namingschema.HTTPClient)\n // Copy the URL so we don't modify the outgoing request\n url := *{{ $req }}.URL\n url.User = nil // Don't include userinfo in the http.url tag\n opts := []ddtrace.StartSpanOption{\n __dd_tracer_SpanType(ext.SpanTypeHTTP),\n __dd_tracer_ResourceName(resourceName),\n __dd_tracer_Tag(ext.HTTPMethod, {{ $req }}.Method),\n __dd_tracer_Tag(ext.HTTPURL, url.String()),\n __dd_tracer_Tag(ext.Component, \"net/http\"),\n __dd_tracer_Tag(ext.SpanKind, ext.SpanKindClient),\n __dd_tracer_Tag(ext.NetworkDestinationName, url.Hostname()),\n }\n if analyticsRate := globalconfig.AnalyticsRate(); !math.IsNaN(analyticsRate) {\n opts = append(opts, __dd_tracer_Tag(ext.EventSampleRate, analyticsRate))\n }\n if port, err := strconv.Atoi(url.Port()); err == nil {\n opts = append(opts, __dd_tracer_Tag(ext.NetworkDestinationPort, port))\n }\n span, ctx := __dd_tracer_StartSpanFromContext({{ $req }}.Context(), spanName, opts...)\n {{ $req }} = {{ $req }}.Clone(ctx)\n defer func() {\n if !events.IsSecurityError({{ $err }}) {\n span.Finish(__dd_tracer_WithError({{ $err }}))\n } else {\n span.Finish()\n }\n }()\n\n if {{ $err }} = __dd_tracer_Inject(span.Context(), __dd_tracer_HTTPHeadersCarrier({{ $req }}.Header)); {{ $err }} != nil {\n fmt.Fprintf(os.Stderr, \"contrib/net/http.Roundtrip: failed to inject http headers: %v\\n\", {{ $err }})\n }\n\n if __dd_appsec_RASPEnabled() {\n if err := __dd_httpsec_ProtectRoundTrip(ctx, {{ $req }}.URL.String()); err != nil {\n return nil, err\n }\n }\n\n defer func() {\n if {{ $err }} != nil {\n span.SetTag(\"http.errors\", {{ $err }}.Error())\n span.SetTag(ext.Error, {{ $err }})\n } else {\n span.SetTag(ext.HTTPCode, strconv.Itoa({{ $res }}.StatusCode))\n if {{ $res }}.StatusCode >= 500 && {{ $res}}.StatusCode < 600 {\n // Treat HTTP 5XX as errors\n span.SetTag(\"http.errors\", {{ $res }}.Status)\n span.SetTag(ext.Error, fmt.Errorf(\"%d: %s\", {{ $res }}.StatusCode, StatusText({{ $res }}.StatusCode)))\n }\n }\n }()\n}", - map[string]string{ - "ddtrace": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace", - "events": "gopkg.in/DataDog/dd-trace-go.v1/appsec/events", - "ext": "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext", - "fmt": "fmt", - "globalconfig": "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig", - "math": "math", - "namingschema": "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema", - "os": "os", - "strconv": "strconv", - }, - context.GoLangVersion{}, - )), - }, - }, - { - JoinPoint: join.AllOf( - join.Not(join.ImportPath("net/http")), - join.OneOf( - join.FunctionCall("net/http", "Get"), - join.FunctionCall("net/http", "Head"), - join.FunctionCall("net/http", "Post"), - join.FunctionCall("net/http", "PostForm"), - ), - ), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{- $ctx := .Function.ArgumentOfType \"context.Context\" -}}\n{{- $req := .Function.ArgumentOfType \"*net/http.Request\" }}\n{{- if $ctx -}}\n instrument.{{ .AST.Fun.Name }}(\n {{ $ctx }},\n {{ range .AST.Args }}{{ . }},\n {{ end }}\n )\n{{- else if $req -}}\n instrument.{{ .AST.Fun.Name }}(\n {{ $req }}.Context(),\n {{ range .AST.Args }}{{ . }},\n {{ end }}\n )\n{{- else -}}\n {{ . }}\n{{- end -}}", - map[string]string{ - "instrument": "github.com/DataDog/orchestrion/instrument/net/http", - }, - context.GoLangVersion{}, - )), - }, - }, - // From stdlib/net-http.server.yml - { - JoinPoint: join.FunctionBody(join.Function( - join.Receiver(join.MustTypeName("*net/http.Server")), - join.Name("Serve"), - )), - Advice: []advice.Advice{ - advice.InjectDeclarations(code.MustTemplate( - "//go:linkname __dd_orchestrion_instrument_WrapHandler github.com/DataDog/orchestrion/instrument.WrapHandler\nfunc __dd_orchestrion_instrument_WrapHandler(handler Handler) Handler", - map[string]string{}, - context.GoLangVersion{}, - ), []string{ - "github.com/DataDog/orchestrion/instrument", - }), - advice.PrependStmts(code.MustTemplate( - "{{- $srv := .Function.Receiver -}}\nif {{ $srv }}.Handler != nil {\n {{ $srv }}.Handler = __dd_orchestrion_instrument_WrapHandler({{ $srv }}.Handler)\n}", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - // From stdlib/ossec.yml - { - JoinPoint: join.AllOf( - join.ImportPath("os"), - join.FunctionBody(join.Function( - join.Name("OpenFile"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "__dd_parent_op, _ := dyngo.FromContext(nil)\nif __dd_parent_op != nil {\n __dd_op := &ossec.OpenOperation{\n Operation: dyngo.NewOperation(__dd_parent_op),\n }\n\n var __dd_block bool\n dyngo.OnData(__dd_op, func(_ *events.BlockingSecurityEvent) {\n __dd_block = true\n })\n\n dyngo.StartOperation(__dd_op, ossec.OpenOperationArgs{\n Path: {{ .Function.Argument 0 }},\n Flags: {{ .Function.Argument 1 }},\n Perms: {{ .Function.Argument 2 }},\n })\n\n defer dyngo.FinishOperation(__dd_op, ossec.OpenOperationRes[*File]{\n File: &{{ .Function.Result 0 }},\n Err: &{{ .Function.Result 1 }},\n })\n\n if __dd_block {\n return\n }\n}", - map[string]string{ - "dyngo": "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo", - "events": "gopkg.in/DataDog/dd-trace-go.v1/appsec/events", - "ossec": "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/ossec", - }, - context.GoLangVersion{}, - )), - }, - }, - // From stdlib/runtime.yml - { - JoinPoint: join.StructDefinition(join.MustTypeName("runtime.g")), - Advice: []advice.Advice{ - advice.AddStructField("__dd_gls", join.MustTypeName("any")), - advice.AddBlankImport("unsafe"), - advice.InjectDeclarations(code.MustTemplate( - "//go:linkname __dd_orchestrion_gls_get __dd_orchestrion_gls_get\nvar __dd_orchestrion_gls_get = func() any {\n return getg().m.curg.__dd_gls\n}\n\n//go:linkname __dd_orchestrion_gls_set __dd_orchestrion_gls_set\nvar __dd_orchestrion_gls_set = func(val any) {\n getg().m.curg.__dd_gls = val\n}", - map[string]string{}, - context.GoLangVersion{}, - ), []string{}), - }, - }, - { - JoinPoint: join.AllOf( - join.ImportPath("runtime"), - join.FunctionBody(join.Function( - join.Name("goexit1"), - )), - ), - Advice: []advice.Advice{ - advice.PrependStmts(code.MustTemplate( - "getg().__dd_gls = nil", - map[string]string{}, - context.GoLangVersion{}, - )), - }, - }, - // From stdlib/slog.yml - { - JoinPoint: join.FunctionCall("log/slog", "New"), - Advice: []advice.Advice{ - advice.WrapExpression(code.MustTemplate( - "{{ .AST.Fun }}(slogtrace.WrapHandler({{ index .AST.Args 0 }}))", - map[string]string{ - "slogtrace": "gopkg.in/DataDog/dd-trace-go.v1/contrib/log/slog", - }, - context.GoLangVersion{}, - )), - }, - }, -} - -// InjectedPaths is a set of import paths that may be injected by built-in aspects. This list is used to ensure proper -// invalidation of cached artifacts when injected dependencies change. -var InjectedPaths = [...]string{ - "context", - "fmt", - "github.com/DataDog/orchestrion/instrument", - "github.com/DataDog/orchestrion/instrument/net/http", - "gopkg.in/DataDog/dd-trace-go.v1/appsec/events", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/99designs/gqlgen", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/cloud.google.com/go/pubsub.v1/internal/tracing", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/gin-gonic/gin", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v5", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v7", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v8", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/go.mongodb.org/mongo-driver/mongo", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/gocql/gocql", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/gofiber/fiber.v2", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/graph-gophers/graphql-go", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/graphql-go/graphql", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/hashicorp/vault", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/internal/httptrace", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/internal/options", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/jinzhu/gorm", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter/internal/tracing", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/k8s.io/client-go/kubernetes", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/labstack/echo.v4", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/log/slog", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/redis/go-redis.v9", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing", - "gopkg.in/DataDog/dd-trace-go.v1/contrib/twitchtv/twirp", - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace", - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext", - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", - "gopkg.in/DataDog/dd-trace-go.v1/internal", - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec", - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo", - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/httpsec", - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/ossec", - "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations", - "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting", - "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig", - "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema", - "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry", - "gopkg.in/DataDog/dd-trace-go.v1/profiler", - "k8s.io/client-go/transport", - "log", - "math", - "net/http", - "os", - "strconv", - "strings", - "testing", -} - -// Checksum is a checksum of the built-in configuration which can be used to invalidate caches. -const Checksum = "sha512:6M7isTPZdLAR2kLE936vCfN6doV2jpSSzCYkLZTAtwd3kXfbPk8IWcCqjDeKMRM4em9YzcIApY3rEMmsxMDODA==" diff --git a/internal/injector/builtin/testdata/client/grpc.go.snap b/internal/injector/builtin/testdata/client/grpc.go.snap index 6e8bd225..8a097fa8 100644 --- a/internal/injector/builtin/testdata/client/grpc.go.snap +++ b/internal/injector/builtin/testdata/client/grpc.go.snap @@ -18,7 +18,7 @@ import ( func grpcClient() { conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), //line - grpc.WithStreamInterceptor(__orchestrion_grpctrace.StreamClientInterceptor()), grpc.WithUnaryInterceptor(__orchestrion_grpctrace.UnaryClientInterceptor())) + grpc.WithChainStreamInterceptor(__orchestrion_grpctrace.StreamClientInterceptor()), grpc.WithChainUnaryInterceptor(__orchestrion_grpctrace.UnaryClientInterceptor())) //line samples/client/grpc.go:16 if err != nil { log.Fatal(err) diff --git a/internal/injector/builtin/testdata/server/grpc.go.snap b/internal/injector/builtin/testdata/server/grpc.go.snap index b647ef0c..ea272698 100644 --- a/internal/injector/builtin/testdata/server/grpc.go.snap +++ b/internal/injector/builtin/testdata/server/grpc.go.snap @@ -24,7 +24,7 @@ func grpcServer() { s := grpc.NewServer(grpc.EmptyServerOption{}, //line - grpc.StreamInterceptor(__orchestrion_grpctrace.StreamServerInterceptor()), grpc.UnaryInterceptor(__orchestrion_grpctrace.UnaryServerInterceptor())) + grpc.ChainStreamInterceptor(__orchestrion_grpctrace.StreamServerInterceptor()), grpc.ChainUnaryInterceptor(__orchestrion_grpctrace.UnaryServerInterceptor())) //line samples/server/grpc.go:22 if err := s.Serve(ln); err != nil { log.Fatalf("failed to serve: %v", err) diff --git a/internal/injector/builtin/yaml/rpc/grpc.yml b/internal/injector/builtin/yaml/rpc/grpc.yml index 09b7d803..e2e8344e 100644 --- a/internal/injector/builtin/yaml/rpc/grpc.yml +++ b/internal/injector/builtin/yaml/rpc/grpc.yml @@ -23,9 +23,9 @@ aspects: - imports: &imports grpc: google.golang.org/grpc grpctrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc - template: grpc.WithStreamInterceptor(grpctrace.StreamClientInterceptor()) + template: grpc.WithChainStreamInterceptor(grpctrace.StreamClientInterceptor()) - imports: *imports - template: grpc.WithUnaryInterceptor(grpctrace.UnaryClientInterceptor()) + template: grpc.WithChainUnaryInterceptor(grpctrace.UnaryClientInterceptor()) # Server Instrumentation - id: Add gRPC server interceptors @@ -36,6 +36,6 @@ aspects: type: google.golang.org/grpc.ServerOption values: - imports: *imports - template: grpc.StreamInterceptor(grpctrace.StreamServerInterceptor()) + template: grpc.ChainStreamInterceptor(grpctrace.StreamServerInterceptor()) - imports: *imports - template: grpc.UnaryInterceptor(grpctrace.UnaryServerInterceptor()) + template: grpc.ChainUnaryInterceptor(grpctrace.UnaryServerInterceptor())