Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dyngo: dynamically register listeners only if they're needed #2394

Merged
merged 14 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions contrib/99designs/gqlgen/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"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/appsec/emitter/graphqlsec"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/graphqlsec/types"
"gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema"
"gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry"

Expand Down Expand Up @@ -103,12 +104,12 @@ func (t *gqlTracer) Validate(_ graphql.ExecutableSchema) error {
func (t *gqlTracer) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
opCtx := graphql.GetOperationContext(ctx)
span, ctx := t.createRootSpan(ctx, opCtx)
ctx, req := graphqlsec.StartRequestOperation(ctx, nil /* root */, span, graphqlsec.RequestOperationArgs{
ctx, req := graphqlsec.StartRequestOperation(ctx, nil /* root */, span, types.RequestOperationArgs{
RawQuery: opCtx.RawQuery,
OperationName: opCtx.OperationName,
Variables: opCtx.Variables,
})
ctx, query := graphqlsec.StartExecutionOperation(ctx, req, span, graphqlsec.ExecutionOperationArgs{
ctx, query := graphqlsec.StartExecutionOperation(ctx, req, span, types.ExecutionOperationArgs{
Query: opCtx.RawQuery,
OperationName: opCtx.OperationName,
Variables: opCtx.Variables,
Expand All @@ -123,11 +124,11 @@ func (t *gqlTracer) InterceptOperation(ctx context.Context, next graphql.Operati
}
defer span.Finish(tracer.WithError(err))
}
query.Finish(graphqlsec.ExecutionOperationRes{
query.Finish(types.ExecutionOperationRes{
Data: response.Data, // NB - This is raw data, but rather not parse it (possibly expensive).
Error: response.Errors,
})
req.Finish(graphqlsec.RequestOperationRes{
req.Finish(types.RequestOperationRes{
Data: response.Data, // NB - This is raw data, but rather not parse it (possibly expensive).
Error: response.Errors,
})
Expand All @@ -150,13 +151,13 @@ func (t *gqlTracer) InterceptField(ctx context.Context, next graphql.Resolver) (
}
span, ctx := tracer.StartSpanFromContext(ctx, fieldOp, opts...)
defer func() { span.Finish(tracer.WithError(err)) }()
ctx, op := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*graphqlsec.ExecutionOperation](ctx), span, graphqlsec.ResolveOperationArgs{
ctx, op := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*types.ExecutionOperation](ctx), span, types.ResolveOperationArgs{
Arguments: fieldCtx.Args,
TypeName: fieldCtx.Object,
FieldName: fieldCtx.Field.Name,
Trivial: !(fieldCtx.IsMethod || fieldCtx.IsResolver), // TODO: Is this accurate?
})
defer func() { op.Finish(graphqlsec.ResolveOperationRes{Data: res, Error: err}) }()
defer func() { op.Finish(types.ResolveOperationRes{Data: res, Error: err}) }()
res, err = next(ctx)
return
}
Expand Down
43 changes: 24 additions & 19 deletions contrib/google.golang.org/grpc/appsec.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/grpcsec"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/grpcsec/types"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/trace"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/trace/grpctrace"
Expand All @@ -33,14 +34,16 @@ func appsecUnaryHandlerMiddleware(span ddtrace.Span, handler grpc.UnaryHandler)
var blocked bool
md, _ := metadata.FromIncomingContext(ctx)
clientIP := setClientIP(ctx, span, md)
args := grpcsec.HandlerOperationArgs{Metadata: md, ClientIP: clientIP}
ctx, op := grpcsec.StartHandlerOperation(ctx, args, nil, dyngo.NewDataListener(func(a *sharedsec.Action) {
code, e := a.GRPC()(md)
blocked = a.Blocking()
err = status.Error(codes.Code(code), e.Error())
}))
args := types.HandlerOperationArgs{Metadata: md, ClientIP: clientIP}
ctx, op := grpcsec.StartHandlerOperation(ctx, args, nil, func(op *types.HandlerOperation) {
dyngo.OnData(op, func(a *sharedsec.Action) {
code, e := a.GRPC()(md)
blocked = a.Blocking()
err = status.Error(codes.Code(code), e.Error())
})
})
defer func() {
events := op.Finish(grpcsec.HandlerOperationRes{})
events := op.Finish(types.HandlerOperationRes{})
if blocked {
op.SetTag(trace.BlockedRequestTag, true)
}
Expand All @@ -54,9 +57,9 @@ func appsecUnaryHandlerMiddleware(span ddtrace.Span, handler grpc.UnaryHandler)
if err != nil {
return nil, err
}
defer grpcsec.StartReceiveOperation(grpcsec.ReceiveOperationArgs{}, op).Finish(grpcsec.ReceiveOperationRes{Message: req})
defer grpcsec.StartReceiveOperation(types.ReceiveOperationArgs{}, op).Finish(types.ReceiveOperationRes{Message: req})
rv, err := handler(ctx, req)
if e, ok := err.(*grpcsec.MonitoringError); ok {
if e, ok := err.(*types.MonitoringError); ok {
err = status.Error(codes.Code(e.GRPCStatus()), e.Error())
}
return rv, err
Expand All @@ -74,18 +77,20 @@ func appsecStreamHandlerMiddleware(span ddtrace.Span, handler grpc.StreamHandler
clientIP := setClientIP(ctx, span, md)
grpctrace.SetRequestMetadataTags(span, md)

ctx, op := grpcsec.StartHandlerOperation(ctx, grpcsec.HandlerOperationArgs{Metadata: md, ClientIP: clientIP}, nil, dyngo.NewDataListener(func(a *sharedsec.Action) {
code, e := a.GRPC()(md)
blocked = a.Blocking()
err = status.Error(codes.Code(code), e.Error())
}))
ctx, op := grpcsec.StartHandlerOperation(ctx, types.HandlerOperationArgs{Metadata: md, ClientIP: clientIP}, nil, func(op *types.HandlerOperation) {
dyngo.OnData(op, func(a *sharedsec.Action) {
code, e := a.GRPC()(md)
blocked = a.Blocking()
err = status.Error(codes.Code(code), e.Error())
})
})
stream = appsecServerStream{
ServerStream: stream,
handlerOperation: op,
ctx: ctx,
}
defer func() {
events := op.Finish(grpcsec.HandlerOperationRes{})
events := op.Finish(types.HandlerOperationRes{})
if blocked {
op.SetTag(trace.BlockedRequestTag, true)
}
Expand All @@ -100,7 +105,7 @@ func appsecStreamHandlerMiddleware(span ddtrace.Span, handler grpc.StreamHandler
}

err = handler(srv, stream)
if e, ok := err.(*grpcsec.MonitoringError); ok {
if e, ok := err.(*types.MonitoringError); ok {
err = status.Error(codes.Code(e.GRPCStatus()), e.Error())
}
return err
Expand All @@ -109,16 +114,16 @@ func appsecStreamHandlerMiddleware(span ddtrace.Span, handler grpc.StreamHandler

type appsecServerStream struct {
grpc.ServerStream
handlerOperation *grpcsec.HandlerOperation
handlerOperation *types.HandlerOperation
ctx context.Context
}

// RecvMsg implements grpc.ServerStream interface method to monitor its
// execution with AppSec.
func (ss appsecServerStream) RecvMsg(m interface{}) error {
op := grpcsec.StartReceiveOperation(grpcsec.ReceiveOperationArgs{}, ss.handlerOperation)
op := grpcsec.StartReceiveOperation(types.ReceiveOperationArgs{}, ss.handlerOperation)
defer func() {
op.Finish(grpcsec.ReceiveOperationRes{Message: m})
op.Finish(types.ReceiveOperationRes{Message: m})
}()
return ss.ServerStream.RecvMsg(m)
}
Expand Down
13 changes: 7 additions & 6 deletions contrib/graph-gophers/graphql-go/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/graphqlsec"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/graphqlsec/types"
"gopkg.in/DataDog/dd-trace-go.v1/internal/log"
"gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry"

Expand Down Expand Up @@ -70,12 +71,12 @@ func (t *Tracer) TraceQuery(ctx context.Context, queryString, operationName stri
}
span, ctx := ddtracer.StartSpanFromContext(ctx, t.cfg.querySpanName, opts...)

ctx, request := graphqlsec.StartRequestOperation(ctx, nil, span, graphqlsec.RequestOperationArgs{
ctx, request := graphqlsec.StartRequestOperation(ctx, nil, span, types.RequestOperationArgs{
RawQuery: queryString,
OperationName: operationName,
Variables: variables,
})
ctx, query := graphqlsec.StartExecutionOperation(ctx, request, span, graphqlsec.ExecutionOperationArgs{
ctx, query := graphqlsec.StartExecutionOperation(ctx, request, span, types.ExecutionOperationArgs{
Query: queryString,
OperationName: operationName,
Variables: variables,
Expand All @@ -92,8 +93,8 @@ func (t *Tracer) TraceQuery(ctx context.Context, queryString, operationName stri
err = fmt.Errorf("%s (and %d more errors)", errs[0], n-1)
}
defer span.Finish(ddtracer.WithError(err))
defer request.Finish(graphqlsec.RequestOperationRes{Error: err})
query.Finish(graphqlsec.ExecutionOperationRes{Error: err})
defer request.Finish(types.RequestOperationRes{Error: err})
query.Finish(types.ExecutionOperationRes{Error: err})
}
}

Expand All @@ -119,15 +120,15 @@ func (t *Tracer) TraceField(ctx context.Context, _, typeName, fieldName string,
}
span, ctx := ddtracer.StartSpanFromContext(ctx, "graphql.field", opts...)

ctx, field := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*graphqlsec.ExecutionOperation](ctx), span, graphqlsec.ResolveOperationArgs{
ctx, field := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*types.ExecutionOperation](ctx), span, types.ResolveOperationArgs{
TypeName: typeName,
FieldName: fieldName,
Arguments: arguments,
Trivial: trivial,
})

return ctx, func(err *errors.QueryError) {
field.Finish(graphqlsec.ResolveOperationRes{Error: err})
field.Finish(types.ResolveOperationRes{Error: err})

// must explicitly check for nil, see issue golang/go#22729
if err != nil {
Expand Down
15 changes: 8 additions & 7 deletions contrib/graphql-go/graphql/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"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/appsec/emitter/graphqlsec"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/graphqlsec/types"
"gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry"

"github.com/graphql-go/graphql"
Expand Down Expand Up @@ -63,7 +64,7 @@ type datadogExtension struct{ config }
type contextKey struct{}
type contextData struct {
serverSpan tracer.Span
requestOp *graphqlsec.RequestOperation
requestOp *types.RequestOperation
variables map[string]any
query string
operationName string
Expand All @@ -72,7 +73,7 @@ type contextData struct {
// finish closes the top-level request operation, as well as the server span.
func (c *contextData) finish(data any, err error) {
defer c.serverSpan.Finish(tracer.WithError(err))
c.requestOp.Finish(graphqlsec.RequestOperationRes{Data: data, Error: err})
c.requestOp.Finish(types.RequestOperationRes{Data: data, Error: err})
}

var extensionName = reflect.TypeOf((*datadogExtension)(nil)).Elem().Name()
Expand All @@ -97,7 +98,7 @@ func (i datadogExtension) Init(ctx context.Context, params *graphql.Params) cont
tracer.Tag(ext.Component, componentName),
tracer.Measured(),
)
ctx, request := graphqlsec.StartRequestOperation(ctx, nil, span, graphqlsec.RequestOperationArgs{
ctx, request := graphqlsec.StartRequestOperation(ctx, nil, span, types.RequestOperationArgs{
RawQuery: params.RequestString,
Variables: params.VariableValues,
OperationName: params.OperationName,
Expand Down Expand Up @@ -192,7 +193,7 @@ func (i datadogExtension) ExecutionDidStart(ctx context.Context) (context.Contex
opts = append(opts, tracer.Tag(ext.EventSampleRate, i.config.analyticsRate))
}
span, ctx := tracer.StartSpanFromContext(ctx, spanExecute, opts...)
ctx, op := graphqlsec.StartExecutionOperation(ctx, graphqlsec.FromContext[*graphqlsec.RequestOperation](ctx), span, graphqlsec.ExecutionOperationArgs{
ctx, op := graphqlsec.StartExecutionOperation(ctx, graphqlsec.FromContext[*types.RequestOperation](ctx), span, types.ExecutionOperationArgs{
Query: data.query,
OperationName: data.operationName,
Variables: data.variables,
Expand All @@ -203,7 +204,7 @@ func (i datadogExtension) ExecutionDidStart(ctx context.Context) (context.Contex
defer data.finish(result.Data, err)
span.Finish(tracer.WithError(err))
}()
op.Finish(graphqlsec.ExecutionOperationRes{Data: result.Data, Error: err})
op.Finish(types.ExecutionOperationRes{Data: result.Data, Error: err})
}
}

Expand Down Expand Up @@ -239,14 +240,14 @@ func (i datadogExtension) ResolveFieldDidStart(ctx context.Context, info *graphq
opts = append(opts, tracer.Tag(ext.EventSampleRate, i.config.analyticsRate))
}
span, ctx := tracer.StartSpanFromContext(ctx, spanResolve, opts...)
ctx, op := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*graphqlsec.ExecutionOperation](ctx), span, graphqlsec.ResolveOperationArgs{
ctx, op := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*types.ExecutionOperation](ctx), span, types.ResolveOperationArgs{
TypeName: info.ParentType.Name(),
FieldName: info.FieldName,
Arguments: collectArguments(info),
})
return ctx, func(result any, err error) {
defer span.Finish(tracer.WithError(err))
op.Finish(graphqlsec.ResolveOperationRes{Error: err, Data: result})
op.Finish(types.ResolveOperationRes{Error: err, Data: result})
}
}

Expand Down
3 changes: 2 additions & 1 deletion contrib/labstack/echo.v4/appsec.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/httpsec"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/httpsec/types"

"github.com/labstack/echo/v4"
)
Expand All @@ -26,7 +27,7 @@ func withAppSec(next echo.HandlerFunc, span tracer.Span) echo.HandlerFunc {
err = next(c)
// If the error is a monitoring one, it means appsec actions will take care of writing the response
// and handling the error. Don't call the echo error handler in this case
if _, ok := err.(*httpsec.MonitoringError); !ok && err != nil {
if _, ok := err.(*types.MonitoringError); !ok && err != nil {
c.Error(err)
}
})
Expand Down
Loading
Loading