diff --git a/.github/workflows/orchestrion.yml b/.github/workflows/orchestrion.yml index ddd0e7435b..efd9d654af 100644 --- a/.github/workflows/orchestrion.yml +++ b/.github/workflows/orchestrion.yml @@ -18,6 +18,22 @@ concurrency: cancel-in-progress: true jobs: + generate: + name: Verify root orchestrion.yml file is up-to-date + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: stable + cache: true + - name: Run generator + run: go generate ./internal/orchestrion + - name: Check for changes + run: git diff --exit-code + test: name: 'Run Tests' uses: DataDog/orchestrion/.github/workflows/workflow_call.yml@main # we don't want to pin our own action diff --git a/contrib/99designs/gqlgen/orchestrion.yml b/contrib/99designs/gqlgen/orchestrion.yml new file mode 100644 index 0000000000..201187cfcb --- /dev/null +++ b/contrib/99designs/gqlgen/orchestrion.yml @@ -0,0 +1,26 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/99designs/gqlgen + description: gqlgen is a Go library for building GraphQL servers without any fuss. + +aspects: + - id: New + join-point: + one-of: + - function-call: github.com/99designs/gqlgen/graphql/handler.New + - function-call: github.com/99designs/gqlgen/graphql/handler.NewDefaultServer + advice: + - wrap-expression: + imports: + handler: github.com/99designs/gqlgen/graphql/handler + gqlgentrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/99designs/gqlgen + template: |- + func(s *handler.Server) *handler.Server { + s.Use(gqlgentrace.NewTracer()) + return s + }({{ . }}) diff --git a/contrib/IBM/sarama.v1/orchestrion.yml b/contrib/IBM/sarama.v1/orchestrion.yml new file mode 100644 index 0000000000..2556d4f9c6 --- /dev/null +++ b/contrib/IBM/sarama.v1/orchestrion.yml @@ -0,0 +1,80 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1 + description: Sarama is a Go library for Apache Kafka + +aspects: + - id: NewConsumer + join-point: + one-of: + - function-call: github.com/IBM/sarama.NewConsumer + - function-call: github.com/IBM/sarama.NewConsumerClient + advice: + - wrap-expression: + imports: + saramatrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1 + sarama: github.com/IBM/sarama + template: |- + func(c sarama.Consumer, err error) (sarama.Consumer, error) { + if c != nil { + c = saramatrace.WrapConsumer(c) + } + return c, err + }({{ . }}) + + - id: NewSyncProducer + join-point: + one-of: + - function-call: github.com/IBM/sarama.NewSyncProducer + - function-call: github.com/IBM/sarama.NewSyncProducerFromClient + advice: + - wrap-expression: + imports: + saramatrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1 + sarama: github.com/IBM/sarama + template: |- + {{- $cfg := .Function.ArgumentOfType "sarama.Config" -}} + func(p sarama.SyncProducer, err error) (sarama.SyncProducer, error) { + if p != nil { + p = saramatrace.WrapSyncProducer( + {{- if $cfg -}} + {{ $cfg }}, + {{- else -}} + nil, + {{- end -}} + p, + ) + } + return p, err + }({{ . }}) + + - id: NewAsyncProducer + join-point: + one-of: + - function-call: github.com/IBM/sarama.NewAsyncProducer + - function-call: github.com/IBM/sarama.NewAsyncProducerFromClient + advice: + - wrap-expression: + imports: + saramatrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1 + sarama: github.com/IBM/sarama + template: |- + {{- $cfg := .Function.ArgumentOfType "sarama.Config" -}} + func(p sarama.AsyncProducer, err error) (sarama.AsyncProducer, error) { + if p != nil { + p = saramatrace.WrapAsyncProducer( + {{- if $cfg -}} + {{ $cfg }}, + {{- else -}} + nil, + {{- end -}} + p, + ) + } + return p, err + }({{ . }}) diff --git a/contrib/Shopify/sarama/orchestrion.yml b/contrib/Shopify/sarama/orchestrion.yml new file mode 100644 index 0000000000..a80d492498 --- /dev/null +++ b/contrib/Shopify/sarama/orchestrion.yml @@ -0,0 +1,80 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama + description: Sarama is a Go library for Apache Kafka + +aspects: + - id: NewConsumer + join-point: + one-of: + - function-call: github.com/Shopify/sarama.NewConsumer + - function-call: github.com/Shopify/sarama.NewConsumerClient + advice: + - wrap-expression: + imports: + saramatrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama + sarama: github.com/Shopify/sarama + template: |- + func(c sarama.Consumer, err error) (sarama.Consumer, error) { + if c != nil { + c = saramatrace.WrapConsumer(c) + } + return c, err + }({{ . }}) + + - id: NewSyncProducer + join-point: + one-of: + - function-call: github.com/Shopify/sarama.NewSyncProducer + - function-call: github.com/Shopify/sarama.NewSyncProducerFromClient + advice: + - wrap-expression: + imports: + saramatrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama + sarama: github.com/Shopify/sarama + template: |- + {{- $cfg := .Function.ArgumentOfType "sarama.Config" -}} + func(p sarama.SyncProducer, err error) (sarama.SyncProducer, error) { + if p != nil { + p = saramatrace.WrapSyncProducer( + {{- if $cfg -}} + {{ $cfg }}, + {{- else -}} + nil, + {{- end -}} + p, + ) + } + return p, err + }({{ . }}) + + - id: NewAsyncProducer + join-point: + one-of: + - function-call: github.com/Shopify/sarama.NewAsyncProducer + - function-call: github.com/Shopify/sarama.NewAsyncProducerFromClient + advice: + - wrap-expression: + imports: + saramatrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama + sarama: github.com/Shopify/sarama + template: |- + {{- $cfg := .Function.ArgumentOfType "sarama.Config" -}} + func(p sarama.AsyncProducer, err error) (sarama.AsyncProducer, error) { + if p != nil { + p = saramatrace.WrapAsyncProducer( + {{- if $cfg -}} + {{ $cfg }}, + {{- else -}} + nil, + {{- end -}} + p, + ) + } + return p, err + }({{ . }}) diff --git a/contrib/aws/aws-sdk-go-v2/aws/orchestrion.yml b/contrib/aws/aws-sdk-go-v2/aws/orchestrion.yml new file mode 100644 index 0000000000..1a9dfbda00 --- /dev/null +++ b/contrib/aws/aws-sdk-go-v2/aws/orchestrion.yml @@ -0,0 +1,44 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws + description: The official AWS SDK for Go + +aspects: + - id: Config + join-point: + struct-literal: + type: github.com/aws/aws-sdk-go-v2/aws.Config + match: value-only + advice: + - wrap-expression: + imports: + awstrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws + aws: github.com/aws/aws-sdk-go-v2/aws + template: |- + func(cfg aws.Config) (aws.Config) { + awstrace.AppendMiddleware(&cfg) + return cfg + }({{ . }}) + + - id: '*Config' + join-point: + one-of: + - struct-literal: + type: github.com/aws/aws-sdk-go-v2/aws.Config + match: pointer-only + - function-call: github.com/aws/aws-sdk-go-v2/aws.NewConfig + advice: + - wrap-expression: + imports: + awstrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws + aws: github.com/aws/aws-sdk-go-v2/aws + template: |- + func(cfg *aws.Config) (*aws.Config) { + awstrace.AppendMiddleware(cfg) + return cfg + }({{ . }}) diff --git a/contrib/aws/aws-sdk-go/aws/orchestrion.yml b/contrib/aws/aws-sdk-go/aws/orchestrion.yml new file mode 100644 index 0000000000..73b497cf5b --- /dev/null +++ b/contrib/aws/aws-sdk-go/aws/orchestrion.yml @@ -0,0 +1,27 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws + description: The official AWS SDK for Go + +aspects: + - id: session.NewSession + join-point: + function-call: github.com/aws/aws-sdk-go/aws/session.NewSession + advice: + - wrap-expression: + imports: + awstrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws + session: github.com/aws/aws-sdk-go/aws/session + template: |- + func(sess *session.Session, err error) (*session.Session, error) { + if sess != nil { + sess = awstrace.WrapSession(sess) + } + return sess, err + }({{ . }}) + diff --git a/contrib/cloud.google.com/go/pubsub.v1/orchestrion.yml b/contrib/cloud.google.com/go/pubsub.v1/orchestrion.yml new file mode 100644 index 0000000000..510cccaede --- /dev/null +++ b/contrib/cloud.google.com/go/pubsub.v1/orchestrion.yml @@ -0,0 +1,103 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/cloud.google.com/go/pubsub.v1 + description: Package pubsub provides an easy way to publish and receive Google Cloud Pub/Sub messages, hiding the details of the underlying server RPCs + +aspects: + ## Trace Receive ## + - id: Subscription.Receive + join-point: + function-body: + function: + - receiver: '*cloud.google.com/go/pubsub.Subscription' + - name: Receive + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/cloud.google.com/go/pubsub.v1/internal/tracing + template: |- + {{- $subscription := .Function.Receiver -}} + {{- $handler := .Function.Argument 1 -}} + __dd_traceFn := tracing.TraceReceiveFunc({{ $subscription }}) + __dd_wrapHandler := func(h func(ctx context.Context, msg *Message)) func(ctx context.Context, msg *Message) { + return func(ctx context.Context, msg *Message) { + __dd_traceMsg := &tracing.Message{ + ID: msg.ID, + Data: msg.Data, + OrderingKey: msg.OrderingKey, + Attributes: msg.Attributes, + DeliveryAttempt: msg.DeliveryAttempt, + PublishTime: msg.PublishTime, + } + ctx, closeSpan := __dd_traceFn(ctx, __dd_traceMsg) + defer closeSpan() + h(ctx, msg) + } + } + {{ $handler }} = __dd_wrapHandler({{ $handler }}) + + ## Trace Publish ## + - id: PublishResult + join-point: + struct-definition: cloud.google.com/go/internal/pubsub.PublishResult + advice: + - inject-declarations: + template: |- + type DDCloseSpanFunc = func(serverID string, err error) + - add-struct-field: + name: DDCloseSpan + type: DDCloseSpanFunc + + - id: Topic.Publish + join-point: + function-body: + function: + - receiver: '*cloud.google.com/go/pubsub.Topic' + - name: Publish + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/cloud.google.com/go/pubsub.v1/internal/tracing + template: |- + {{- $topic := .Function.Receiver -}} + {{- $ctx := .Function.Argument 0 -}} + {{- $msg := .Function.Argument 1 -}} + {{- $publishResult := .Function.Result 0 -}} + __dd_traceMsg := &tracing.Message{ + ID: {{ $msg }}.ID, + Data: {{ $msg }}.Data, + OrderingKey: {{ $msg }}.OrderingKey, + Attributes: {{ $msg }}.Attributes, + DeliveryAttempt: {{ $msg }}.DeliveryAttempt, + PublishTime: {{ $msg }}.PublishTime, + } + __dd_ctx, __dd_closeSpan := tracing.TracePublish({{ $ctx }}, {{ $topic }}, __dd_traceMsg) + {{ $ctx }} = __dd_ctx + {{ $msg }}.Attributes = __dd_traceMsg.Attributes + + defer func() { + {{ $publishResult }}.DDCloseSpan = __dd_closeSpan + }() + + - id: PublishResult.Get + join-point: + function-body: + function: + - receiver: '*cloud.google.com/go/internal/pubsub.PublishResult' + - name: Get + advice: + - prepend-statements: + template: |- + {{- $publishResult := .Function.Receiver -}} + {{- $serverID := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + defer func() { + if {{ $publishResult }}.DDCloseSpan != nil { + {{ $publishResult }}.DDCloseSpan({{ $serverID }}, {{ $err }}) + } + }() diff --git a/contrib/confluentinc/confluent-kafka-go/kafka.v2/orchestrion.yml b/contrib/confluentinc/confluent-kafka-go/kafka.v2/orchestrion.yml new file mode 100644 index 0000000000..55dabaf24e --- /dev/null +++ b/contrib/confluentinc/confluent-kafka-go/kafka.v2/orchestrion.yml @@ -0,0 +1,473 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/kafka.v2 + description: confluent-kafka-go is a Go library for Apache Kafka + +aspects: + - id: Consumer + join-point: + struct-definition: github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer + advice: + - add-struct-field: + name: __dd_tracer + type: "*__dd_kafkaTracer" + - add-struct-field: + name: __dd_events + type: "__dd_eventChan" + - add-struct-field: + name: __dd_confmap + type: "*ConfigMap" + - inject-declarations: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + telemetry: gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + lang: go1.18 + template: |- + const __dd_ckgoVersion = tracing.CKGoVersion2 + + type __dd_wMessage struct { + *Message + } + + func __dd_wrapMessage(msg *Message) tracing.Message { + if msg == nil { + return nil + } + return &__dd_wMessage{msg} + } + + func (w *__dd_wMessage) Unwrap() any { + return w.Message + } + + func (w *__dd_wMessage) GetValue() []byte { + return w.Message.Value + } + + func (w *__dd_wMessage) GetKey() []byte { + return w.Message.Key + } + + func (w *__dd_wMessage) GetHeaders() []tracing.Header { + hs := make([]tracing.Header, 0, len(w.Headers)) + for _, h := range w.Headers { + hs = append(hs, __dd_wrapHeader(h)) + } + return hs + } + + func (w *__dd_wMessage) SetHeaders(headers []tracing.Header) { + hs := make([]Header, 0, len(headers)) + for _, h := range headers { + hs = append(hs, Header{ + Key: h.GetKey(), + Value: h.GetValue(), + }) + } + w.Message.Headers = hs + } + + func (w *__dd_wMessage) GetTopicPartition() tracing.TopicPartition { + return __dd_wrapTopicPartition(w.Message.TopicPartition) + } + + type __dd_wHeader struct { + Header + } + + func __dd_wrapHeader(h Header) tracing.Header { + return &__dd_wHeader{h} + } + + func (w __dd_wHeader) GetKey() string { + return w.Header.Key + } + + func (w __dd_wHeader) GetValue() []byte { + return w.Header.Value + } + + type __dd_wTopicPartition struct { + TopicPartition + } + + func __dd_wrapTopicPartition(tp TopicPartition) tracing.TopicPartition { + return __dd_wTopicPartition{tp} + } + + func __dd_wrapTopicPartitions(tps []TopicPartition) []tracing.TopicPartition { + wtps := make([]tracing.TopicPartition, 0, len(tps)) + for _, tp := range tps { + wtps = append(wtps, __dd_wTopicPartition{tp}) + } + return wtps + } + + func (w __dd_wTopicPartition) GetTopic() string { + if w.Topic == nil { + return "" + } + return *w.Topic + } + + func (w __dd_wTopicPartition) GetPartition() int32 { + return w.Partition + } + + func (w __dd_wTopicPartition) GetOffset() int64 { + return int64(w.Offset) + } + + func (w __dd_wTopicPartition) GetError() error { + return w.Error + } + + type __dd_wEvent struct { + Event + } + + func __dd_wrapEvent(event Event) tracing.Event { + return __dd_wEvent{event} + } + + func (w __dd_wEvent) KafkaMessage() (tracing.Message, bool) { + if m, ok := w.Event.(*Message); ok { + return __dd_wrapMessage(m), true + } + return nil, false + } + + func (w __dd_wEvent) KafkaOffsetsCommitted() (tracing.OffsetsCommitted, bool) { + if oc, ok := w.Event.(OffsetsCommitted); ok { + return __dd_wrapOffsetsCommitted(oc), true + } + return nil, false + } + + type __dd_wOffsetsCommitted struct { + OffsetsCommitted + } + + func __dd_wrapOffsetsCommitted(oc OffsetsCommitted) tracing.OffsetsCommitted { + return __dd_wOffsetsCommitted{oc} + } + + func (w __dd_wOffsetsCommitted) GetError() error { + return w.Error + } + + func (w __dd_wOffsetsCommitted) GetOffsets() []tracing.TopicPartition { + ttps := make([]tracing.TopicPartition, 0, len(w.Offsets)) + for _, tp := range w.Offsets { + ttps = append(ttps, __dd_wrapTopicPartition(tp)) + } + return ttps + } + + type __dd_wConfigMap struct { + cfg *ConfigMap + } + + func __dd_wrapConfigMap(cm *ConfigMap) tracing.ConfigMap { + return &__dd_wConfigMap{cm} + } + + func (w *__dd_wConfigMap) Get(key string, defVal any) (any, error) { + return w.cfg.Get(key, defVal) + } + + func init() { + telemetry.LoadIntegration(tracing.ComponentName(__dd_ckgoVersion)) + tracer.MarkIntegrationImported(tracing.IntegrationName(__dd_ckgoVersion)) + } + + func __dd_newKafkaTracer(opts ...tracing.Option) *tracing.KafkaTracer { + v, _ := LibraryVersion() + return tracing.NewKafkaTracer(__dd_ckgoVersion, v, opts...) + } + + func __dd_initConsumer(c *Consumer) { + if c.__dd_tracer != nil { + return + } + var opts []tracing.Option + if c.__dd_confmap != nil { + opts = append(opts, tracing.WithConfig(__dd_wrapConfigMap(c.__dd_confmap))) + } + c.__dd_tracer = __dd_newKafkaTracer(opts...) + // TODO: accessing c.events here might break if the library renames this variable... + c.__dd_events = tracing.WrapConsumeEventsChannel(c.__dd_tracer, c.events, c, __dd_wrapEvent) + } + + func __dd_initProducer(p *Producer) { + if p.__dd_tracer != nil { + return + } + p.__dd_tracer = __dd_newKafkaTracer() + // TODO: accessing p.events and p.produceChannel here might break if the library renames this variable... + p.__dd_events = p.events + p.__dd_produceChannel = tracing.WrapProduceChannel(p.__dd_tracer, p.produceChannel, __dd_wrapMessage) + if p.__dd_tracer.DSMEnabled() { + p.__dd_events = tracing.WrapProduceEventsChannel(p.__dd_tracer, p.events, __dd_wrapEvent) + } + } + + type __dd_eventChan = chan Event + type __dd_messageChan = chan *Message + type __dd_kafkaTracer = tracing.KafkaTracer + + ## Trace Consumer ## + - id: NewConsumer + join-point: + all-of: + - import-path: github.com/confluentinc/confluent-kafka-go/v2/kafka + - function-body: + function: + - name: NewConsumer + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $conf := .Function.Argument 0 -}} + {{- $c := .Function.Result 0 -}} + defer func() { + if {{ $c }} == nil { + return + } + {{ $c }}.__dd_confmap = {{ $conf }} + __dd_initConsumer({{ $c }}) + }() + + - id: Consumer.Close + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer' + - name: Close + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + __dd_initConsumer({{ $c }}) + defer func() { + if {{ $c }}.__dd_events == nil && {{ $c }}.__dd_tracer.PrevSpan != nil { + {{ $c }}.__dd_tracer.PrevSpan.Finish() + {{ $c }}.__dd_tracer.PrevSpan = nil + } + }() + + - id: Consumer.Events + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer' + - name: Events + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $events := .Function.Result 0 -}} + __dd_initConsumer({{ $c }}) + defer func() { + {{ $events }} = {{ $c }}.__dd_events + }() + + # kafka.Consumer#ReadMessage calls kafka.Consumer#Poll internally, so there's no need to trace it. + - id: Consumer.Poll + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer' + - name: Poll + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $event := .Function.Result 0 -}} + __dd_initConsumer({{ $c }}) + if {{ $c }}.__dd_tracer.PrevSpan != nil { + {{ $c }}.__dd_tracer.PrevSpan.Finish() + {{ $c }}.__dd_tracer.PrevSpan = nil + } + defer func() { + if msg, ok := {{ $event }}.(*Message); ok { + tMsg := __dd_wrapMessage(msg) + {{ $c }}.__dd_tracer.SetConsumeCheckpoint(tMsg) + {{ $c }}.__dd_tracer.PrevSpan = {{ $c }}.__dd_tracer.StartConsumeSpan(tMsg) + } else if offset, ok := {{ $event }}.(OffsetsCommitted); ok { + tOffsets := __dd_wrapTopicPartitions(offset.Offsets) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, offset.Error) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + } + }() + + - id: Consumer.Commit + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer' + - name: Commit + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $tps := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + __dd_initConsumer({{ $c }}) + defer func() { + tOffsets := __dd_wrapTopicPartitions({{ $tps }}) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }}) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + }() + + - id: Consumer.CommitMessage + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer' + - name: CommitMessage + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $tps := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + __dd_initConsumer({{ $c }}) + defer func() { + tOffsets := __dd_wrapTopicPartitions({{ $tps }}) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }}) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + }() + + - id: Consumer.CommitOffsets + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Consumer' + - name: CommitOffsets + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $tps := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + __dd_initConsumer({{ $c }}) + defer func() { + tOffsets := __dd_wrapTopicPartitions({{ $tps }}) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }}) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + }() + + ## Trace Producer ## + + - id: Producer + join-point: + struct-definition: github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer + advice: + - add-struct-field: + name: __dd_tracer + type: "*__dd_kafkaTracer" + - add-struct-field: + name: __dd_events + type: "__dd_eventChan" + - add-struct-field: + name: __dd_produceChannel + type: "__dd_messageChan" + + - id: Producer.Events + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer' + - name: Events + advice: + - prepend-statements: + template: |- + {{- $p := .Function.Receiver -}} + {{- $events := .Function.Result 0 -}} + __dd_initProducer({{ $p }}) + defer func() { + {{ $events }} = {{ $p }}.__dd_events + }() + + - id: Producer.ProduceChannel + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer' + - name: ProduceChannel + advice: + - prepend-statements: + template: |- + {{- $p := .Function.Receiver -}} + {{- $produceChannel := .Function.Result 0 -}} + __dd_initProducer({{ $p }}) + defer func() { + {{ $produceChannel }} = {{ $p }}.__dd_produceChannel + }() + + - id: Producer.Close + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer' + - name: Close + advice: + - prepend-statements: + template: |- + {{- $p := .Function.Receiver -}} + __dd_initProducer({{ $p }}) + close({{ $p }}.__dd_produceChannel) + + - id: Producer.Produce + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/v2/kafka.Producer' + - name: Produce + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + template: |- + {{- $p := .Function.Receiver -}} + {{- $msg := .Function.Argument 0 -}} + {{- $deliveryChan := .Function.Argument 1 -}} + {{- $err := .Function.Result 0 -}} + __dd_initProducer({{ $p }}) + tMsg := __dd_wrapMessage({{ $msg }}) + span := p.__dd_tracer.StartProduceSpan(tMsg) + + var errChan chan error + {{ $deliveryChan }}, errChan = tracing.WrapDeliveryChannel({{ $p }}.__dd_tracer, {{ $deliveryChan }}, span, __dd_wrapEvent) + + {{ $p }}.__dd_tracer.SetProduceCheckpoint(tMsg) + defer func() { + if {{ $err }} != nil { + if errChan != nil { + errChan <- {{ $err }} + } else { + span.Finish(tracer.WithError({{ $err }})) + } + } + }() diff --git a/contrib/confluentinc/confluent-kafka-go/kafka/orchestrion.yml b/contrib/confluentinc/confluent-kafka-go/kafka/orchestrion.yml new file mode 100644 index 0000000000..ede1ceb7b9 --- /dev/null +++ b/contrib/confluentinc/confluent-kafka-go/kafka/orchestrion.yml @@ -0,0 +1,474 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/kafka + description: confluent-kafka-go is a Go library for Apache Kafka + +aspects: + - id: Consumer + join-point: + struct-definition: github.com/confluentinc/confluent-kafka-go/kafka.Consumer + advice: + - add-struct-field: + name: __dd_tracer + type: "*__dd_kafkaTracer" + - add-struct-field: + name: __dd_events + type: "__dd_eventChan" + - add-struct-field: + name: __dd_confmap + type: "*ConfigMap" + - inject-declarations: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + telemetry: gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + lang: go1.18 + template: |- + const __dd_ckgoVersion = tracing.CKGoVersion1 + + type __dd_wMessage struct { + *Message + } + + func __dd_wrapMessage(msg *Message) tracing.Message { + if msg == nil { + return nil + } + return &__dd_wMessage{msg} + } + + func (w *__dd_wMessage) Unwrap() any { + return w.Message + } + + func (w *__dd_wMessage) GetValue() []byte { + return w.Message.Value + } + + func (w *__dd_wMessage) GetKey() []byte { + return w.Message.Key + } + + func (w *__dd_wMessage) GetHeaders() []tracing.Header { + hs := make([]tracing.Header, 0, len(w.Headers)) + for _, h := range w.Headers { + hs = append(hs, __dd_wrapHeader(h)) + } + return hs + } + + func (w *__dd_wMessage) SetHeaders(headers []tracing.Header) { + hs := make([]Header, 0, len(headers)) + for _, h := range headers { + hs = append(hs, Header{ + Key: h.GetKey(), + Value: h.GetValue(), + }) + } + w.Message.Headers = hs + } + + func (w *__dd_wMessage) GetTopicPartition() tracing.TopicPartition { + return __dd_wrapTopicPartition(w.Message.TopicPartition) + } + + type __dd_wHeader struct { + Header + } + + func __dd_wrapHeader(h Header) tracing.Header { + return &__dd_wHeader{h} + } + + func (w __dd_wHeader) GetKey() string { + return w.Header.Key + } + + func (w __dd_wHeader) GetValue() []byte { + return w.Header.Value + } + + type __dd_wTopicPartition struct { + TopicPartition + } + + func __dd_wrapTopicPartition(tp TopicPartition) tracing.TopicPartition { + return __dd_wTopicPartition{tp} + } + + func __dd_wrapTopicPartitions(tps []TopicPartition) []tracing.TopicPartition { + wtps := make([]tracing.TopicPartition, 0, len(tps)) + for _, tp := range tps { + wtps = append(wtps, __dd_wTopicPartition{tp}) + } + return wtps + } + + func (w __dd_wTopicPartition) GetTopic() string { + if w.Topic == nil { + return "" + } + return *w.Topic + } + + func (w __dd_wTopicPartition) GetPartition() int32 { + return w.Partition + } + + func (w __dd_wTopicPartition) GetOffset() int64 { + return int64(w.Offset) + } + + func (w __dd_wTopicPartition) GetError() error { + return w.Error + } + + type __dd_wEvent struct { + Event + } + + func __dd_wrapEvent(event Event) tracing.Event { + return __dd_wEvent{event} + } + + func (w __dd_wEvent) KafkaMessage() (tracing.Message, bool) { + if m, ok := w.Event.(*Message); ok { + return __dd_wrapMessage(m), true + } + return nil, false + } + + func (w __dd_wEvent) KafkaOffsetsCommitted() (tracing.OffsetsCommitted, bool) { + if oc, ok := w.Event.(OffsetsCommitted); ok { + return __dd_wrapOffsetsCommitted(oc), true + } + return nil, false + } + + type __dd_wOffsetsCommitted struct { + OffsetsCommitted + } + + func __dd_wrapOffsetsCommitted(oc OffsetsCommitted) tracing.OffsetsCommitted { + return __dd_wOffsetsCommitted{oc} + } + + func (w __dd_wOffsetsCommitted) GetError() error { + return w.Error + } + + func (w __dd_wOffsetsCommitted) GetOffsets() []tracing.TopicPartition { + ttps := make([]tracing.TopicPartition, 0, len(w.Offsets)) + for _, tp := range w.Offsets { + ttps = append(ttps, __dd_wrapTopicPartition(tp)) + } + return ttps + } + + type __dd_wConfigMap struct { + cfg *ConfigMap + } + + func __dd_wrapConfigMap(cm *ConfigMap) tracing.ConfigMap { + return &__dd_wConfigMap{cm} + } + + func (w *__dd_wConfigMap) Get(key string, defVal any) (any, error) { + return w.cfg.Get(key, defVal) + } + + func init() { + telemetry.LoadIntegration(tracing.ComponentName(__dd_ckgoVersion)) + tracer.MarkIntegrationImported(tracing.IntegrationName(__dd_ckgoVersion)) + } + + func __dd_newKafkaTracer(opts ...tracing.Option) *tracing.KafkaTracer { + v, _ := LibraryVersion() + return tracing.NewKafkaTracer(__dd_ckgoVersion, v, opts...) + } + + func __dd_initConsumer(c *Consumer) { + if c.__dd_tracer != nil { + return + } + var opts []tracing.Option + if c.__dd_confmap != nil { + opts = append(opts, tracing.WithConfig(__dd_wrapConfigMap(c.__dd_confmap))) + } + c.__dd_tracer = __dd_newKafkaTracer(opts...) + // TODO: accessing c.events here might break if the library renames this variable... + c.__dd_events = tracing.WrapConsumeEventsChannel(c.__dd_tracer, c.events, c, __dd_wrapEvent) + } + + func __dd_initProducer(p *Producer) { + if p.__dd_tracer != nil { + return + } + p.__dd_tracer = __dd_newKafkaTracer() + // TODO: accessing p.events and p.produceChannel here might break if the library renames this variable... + p.__dd_events = p.events + p.__dd_produceChannel = tracing.WrapProduceChannel(p.__dd_tracer, p.produceChannel, __dd_wrapMessage) + if p.__dd_tracer.DSMEnabled() { + p.__dd_events = tracing.WrapProduceEventsChannel(p.__dd_tracer, p.events, __dd_wrapEvent) + } + } + + type __dd_eventChan = chan Event + type __dd_messageChan = chan *Message + type __dd_kafkaTracer = tracing.KafkaTracer + + ## Trace Consumer ## + + - id: NewConsumer + join-point: + all-of: + - import-path: github.com/confluentinc/confluent-kafka-go/kafka + - function-body: + function: + - name: NewConsumer + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $conf := .Function.Argument 0 -}} + {{- $c := .Function.Result 0 -}} + defer func() { + if {{ $c }} == nil { + return + } + {{ $c }}.__dd_confmap = {{ $conf }} + __dd_initConsumer({{ $c }}) + }() + + - id: Consumer.Close + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Consumer' + - name: Close + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + __dd_initConsumer({{ $c }}) + defer func() { + if {{ $c }}.__dd_events == nil && {{ $c }}.__dd_tracer.PrevSpan != nil { + {{ $c }}.__dd_tracer.PrevSpan.Finish() + {{ $c }}.__dd_tracer.PrevSpan = nil + } + }() + + - id: Consumer.Events + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Consumer' + - name: Events + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $events := .Function.Result 0 -}} + __dd_initConsumer({{ $c }}) + defer func() { + {{ $events }} = {{ $c }}.__dd_events + }() + + # kafka.Consumer#ReadMessage calls kafka.Consumer#Poll internally, so there's no need to trace it. + - id: Consumer.Poll + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Consumer' + - name: Poll + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $event := .Function.Result 0 -}} + __dd_initConsumer({{ $c }}) + if {{ $c }}.__dd_tracer.PrevSpan != nil { + {{ $c }}.__dd_tracer.PrevSpan.Finish() + {{ $c }}.__dd_tracer.PrevSpan = nil + } + defer func() { + if msg, ok := {{ $event }}.(*Message); ok { + tMsg := __dd_wrapMessage(msg) + {{ $c }}.__dd_tracer.SetConsumeCheckpoint(tMsg) + {{ $c }}.__dd_tracer.PrevSpan = {{ $c }}.__dd_tracer.StartConsumeSpan(tMsg) + } else if offset, ok := {{ $event }}.(OffsetsCommitted); ok { + tOffsets := __dd_wrapTopicPartitions(offset.Offsets) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, offset.Error) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + } + }() + + - id: Consumer.Commit + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Consumer' + - name: Commit + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $tps := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + __dd_initConsumer({{ $c }}) + defer func() { + tOffsets := __dd_wrapTopicPartitions({{ $tps }}) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }}) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + }() + + - id: Consumer.CommitMessage + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Consumer' + - name: CommitMessage + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $tps := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + __dd_initConsumer({{ $c }}) + defer func() { + tOffsets := __dd_wrapTopicPartitions({{ $tps }}) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }}) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + }() + + - id: Consumer.CommitOffsets + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Consumer' + - name: CommitOffsets + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + template: |- + {{- $c := .Function.Receiver -}} + {{- $tps := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + __dd_initConsumer({{ $c }}) + defer func() { + tOffsets := __dd_wrapTopicPartitions({{ $tps }}) + {{ $c }}.__dd_tracer.TrackCommitOffsets(tOffsets, {{ $err }}) + {{ $c }}.__dd_tracer.TrackHighWatermarkOffset(tOffsets, {{ $c }}) + }() + + ## Trace Producer ## + + - id: Producer + join-point: + struct-definition: github.com/confluentinc/confluent-kafka-go/kafka.Producer + advice: + - add-struct-field: + name: __dd_tracer + type: "*__dd_kafkaTracer" + - add-struct-field: + name: __dd_events + type: "__dd_eventChan" + - add-struct-field: + name: __dd_produceChannel + type: "__dd_messageChan" + + - id: Producer.Events + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Producer' + - name: Events + advice: + - prepend-statements: + template: |- + {{- $p := .Function.Receiver -}} + {{- $events := .Function.Result 0 -}} + __dd_initProducer({{ $p }}) + defer func() { + {{ $events }} = {{ $p }}.__dd_events + }() + + - id: Producer.ProduceChannel + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Producer' + - name: ProduceChannel + advice: + - prepend-statements: + template: |- + {{- $p := .Function.Receiver -}} + {{- $produceChannel := .Function.Result 0 -}} + __dd_initProducer({{ $p }}) + defer func() { + {{ $produceChannel }} = {{ $p }}.__dd_produceChannel + }() + + - id: Producer.Close + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Producer' + - name: Close + advice: + - prepend-statements: + template: |- + {{- $p := .Function.Receiver -}} + __dd_initProducer({{ $p }}) + close({{ $p }}.__dd_produceChannel) + + - id: Producer.Produce + join-point: + function-body: + function: + - receiver: '*github.com/confluentinc/confluent-kafka-go/kafka.Producer' + - name: Produce + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/internal/tracing + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + template: |- + {{- $p := .Function.Receiver -}} + {{- $msg := .Function.Argument 0 -}} + {{- $deliveryChan := .Function.Argument 1 -}} + {{- $err := .Function.Result 0 -}} + __dd_initProducer({{ $p }}) + tMsg := __dd_wrapMessage({{ $msg }}) + span := p.__dd_tracer.StartProduceSpan(tMsg) + + var errChan chan error + {{ $deliveryChan }}, errChan = tracing.WrapDeliveryChannel({{ $p }}.__dd_tracer, {{ $deliveryChan }}, span, __dd_wrapEvent) + + {{ $p }}.__dd_tracer.SetProduceCheckpoint(tMsg) + defer func() { + if {{ $err }} != nil { + if errChan != nil { + errChan <- {{ $err }} + } else { + span.Finish(tracer.WithError({{ $err }})) + } + } + }() diff --git a/contrib/database/sql/orchestrion.yml b/contrib/database/sql/orchestrion.yml new file mode 100644 index 0000000000..37d84f2e6f --- /dev/null +++ b/contrib/database/sql/orchestrion.yml @@ -0,0 +1,37 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql + description: A generic interface around SQL (or SQL-like) databases. + +aspects: + - id: sql.Register + join-point: + function-call: database/sql.Register + advice: + - wrap-expression: + imports: + sqltrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql + sql: database/sql + driver: database/sql/driver + template: |- + func(driverName string, driver driver.Driver) { + sql.Register(driverName, driver) + sqltrace.Register(driverName, driver) + }({{ index .AST.Args 0 }}, {{ index .AST.Args 1 }}) + + - id: sql.Open + join-point: + function-call: database/sql.Open + advice: + - replace-function: gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql.Open + + - id: sql.OpenDB + join-point: + function-call: database/sql.OpenDB + advice: + - replace-function: gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql.OpenDB diff --git a/contrib/elastic/go-elasticsearch.v6/orchestrion.yml b/contrib/elastic/go-elasticsearch.v6/orchestrion.yml new file mode 100644 index 0000000000..501fe941b5 --- /dev/null +++ b/contrib/elastic/go-elasticsearch.v6/orchestrion.yml @@ -0,0 +1,132 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6 + description: The official Go client for Elasticsearch + +# FIXME: in v7 and v8, if elasticsearch.Config.CACert != nil, the call to elasticsearch.NewClient will always return +# an error when setting a non *http.Transport in the Transport (http.RoundTripper) field. +# The workaround from the user-side is to configure the certificate in the Transport instead, but for now we decide +# to not modify the configuration so we don't make the user's application crash. +# This problem will be fixed by migrating our instrumentation to use the elasticsearch.Config.Instrumentation +# field instead. +aspects: + - id: v6.Config + join-point: + struct-literal: + type: github.com/elastic/go-elasticsearch/v6.Config + match: value-only + advice: + - wrap-expression: + imports: + elasticsearch: github.com/elastic/go-elasticsearch/v6 + elastictrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6 + template: |- + func(cfg elasticsearch.Config) elasticsearch.Config { + if cfg.Transport == nil { + cfg.Transport = elastictrace.NewRoundTripper() + } else { + base := cfg.Transport + cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base)) + } + return cfg + }({{ . }}) + + - id: '*v6.Config' + join-point: + struct-literal: + type: github.com/elastic/go-elasticsearch/v6.Config + match: pointer-only + advice: + - wrap-expression: + imports: + elasticsearch: github.com/elastic/go-elasticsearch/v6 + elastictrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6 + template: |- + func(cfg *elasticsearch.Config) *elasticsearch.Config { + if cfg.Transport == nil { + cfg.Transport = elastictrace.NewRoundTripper() + } else { + base := cfg.Transport + cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base)) + } + return cfg + }({{ . }}) + + - id: v7.Config + join-point: + struct-literal: + type: github.com/elastic/go-elasticsearch/v7.Config + match: value-only + advice: + - wrap-expression: + imports: + elasticsearch: github.com/elastic/go-elasticsearch/v7 + elastictrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6 + template: &templateValV7 |- + func(cfg elasticsearch.Config) elasticsearch.Config { + if cfg.CACert != nil { + // refuse to set transport as it will make the NewClient call fail. + return cfg + } + if cfg.Transport == nil { + cfg.Transport = elastictrace.NewRoundTripper() + } else { + base := cfg.Transport + cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base)) + } + return cfg + }({{ . }}) + + - id: '*v7.Config' + join-point: + struct-literal: + type: github.com/elastic/go-elasticsearch/v7.Config + match: pointer-only + advice: + - wrap-expression: + imports: + elasticsearch: github.com/elastic/go-elasticsearch/v7 + elastictrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6 + template: &templatePtrV7 |- + func(cfg *elasticsearch.Config) *elasticsearch.Config { + if cfg.CACert != nil { + // refuse to set transport as it will make the NewClient call fail. + return cfg + } + if cfg.Transport == nil { + cfg.Transport = elastictrace.NewRoundTripper() + } else { + base := cfg.Transport + cfg.Transport = elastictrace.NewRoundTripper(elastictrace.WithTransport(base)) + } + return cfg + }({{ . }}) + + - id: v8.Config + join-point: + struct-literal: + type: github.com/elastic/go-elasticsearch/v8.Config + match: value-only + advice: + - wrap-expression: + imports: + elasticsearch: github.com/elastic/go-elasticsearch/v8 + elastictrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6 + template: *templateValV7 + + - id: '*v8.Config' + join-point: + struct-literal: + type: github.com/elastic/go-elasticsearch/v8.Config + match: pointer-only + advice: + - wrap-expression: + imports: + elasticsearch: github.com/elastic/go-elasticsearch/v8 + elastictrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6 + template: *templatePtrV7 diff --git a/contrib/gin-gonic/gin/orchestrion.yml b/contrib/gin-gonic/gin/orchestrion.yml new file mode 100644 index 0000000000..6a686d0580 --- /dev/null +++ b/contrib/gin-gonic/gin/orchestrion.yml @@ -0,0 +1,27 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/gin-gonic/gin + description: Gin is a web framework written in Go. + +aspects: + - id: New + join-point: + one-of: + - function-call: github.com/gin-gonic/gin.Default + - function-call: github.com/gin-gonic/gin.New + advice: + - wrap-expression: + imports: + gin: github.com/gin-gonic/gin + gintrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/gin-gonic/gin + template: |- + func() *gin.Engine { + e := {{ . }} + e.Use(gintrace.Middleware("")) + return e + }() diff --git a/contrib/go-chi/chi.v5/orchestrion.yml b/contrib/go-chi/chi.v5/orchestrion.yml new file mode 100644 index 0000000000..d7d20995fe --- /dev/null +++ b/contrib/go-chi/chi.v5/orchestrion.yml @@ -0,0 +1,33 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v5 + description: chi is a lightweight, idiomatic and composable router for building Go HTTP services. + +aspects: + - id: NewMux + join-point: + all-of: + - one-of: + - function-call: github.com/go-chi/chi/v5.NewMux + - function-call: github.com/go-chi/chi/v5.NewRouter + # No instrumenting github.com/go-chi/chi/v5 as this causes a circular dependency. + - not: + one-of: + - import-path: github.com/go-chi/chi/v5 + - import-path: github.com/go-chi/chi/v5/middleware + advice: + - wrap-expression: + imports: + chi: github.com/go-chi/chi/v5 + chitrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v5 + template: |- + func() *chi.Mux { + mux := {{ . }} + mux.Use(chitrace.Middleware()) + return mux + }() diff --git a/contrib/go-chi/chi/orchestrion.yml b/contrib/go-chi/chi/orchestrion.yml new file mode 100644 index 0000000000..ca5e59199f --- /dev/null +++ b/contrib/go-chi/chi/orchestrion.yml @@ -0,0 +1,33 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi + description: chi is a lightweight, idiomatic and composable router for building Go HTTP services. + +aspects: + - id: NewMux + join-point: + all-of: + - one-of: + - function-call: github.com/go-chi/chi.NewMux + - function-call: github.com/go-chi/chi.NewRouter + # No instrumenting github.com/go-chi/chi as this causes a circular dependency. + - not: + one-of: + - import-path: github.com/go-chi/chi + - import-path: github.com/go-chi/chi/middleware + advice: + - wrap-expression: + imports: + chi: github.com/go-chi/chi + chitrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi + template: |- + func() *chi.Mux { + mux := {{ . }} + mux.Use(chitrace.Middleware()) + return mux + }() diff --git a/contrib/go-redis/redis.v7/orchestrion.yml b/contrib/go-redis/redis.v7/orchestrion.yml new file mode 100644 index 0000000000..f14e2a167e --- /dev/null +++ b/contrib/go-redis/redis.v7/orchestrion.yml @@ -0,0 +1,27 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v7 + description: Redis client for Go. + +aspects: + - id: NewClient + join-point: + one-of: + - function-call: github.com/go-redis/redis/v7.NewClient + - function-call: github.com/go-redis/redis/v7.NewFailoverClient + advice: + - wrap-expression: + imports: + redis: github.com/go-redis/redis/v7 + trace: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v7 + template: |- + func() (client *redis.Client) { + client = {{ . }} + trace.WrapClient(client) + return + }() diff --git a/contrib/go-redis/redis.v8/orchestrion.yml b/contrib/go-redis/redis.v8/orchestrion.yml new file mode 100644 index 0000000000..cc00750da3 --- /dev/null +++ b/contrib/go-redis/redis.v8/orchestrion.yml @@ -0,0 +1,27 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v8 + description: Redis client for Go. + +aspects: + - id: NewClient + join-point: + one-of: + - function-call: github.com/go-redis/redis/v8.NewClient + - function-call: github.com/go-redis/redis/v8.NewFailoverClient + advice: + - wrap-expression: + imports: + redis: github.com/go-redis/redis/v8 + trace: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v8 + template: |- + func() (client *redis.Client) { + client = {{ . }} + trace.WrapClient(client) + return + }() diff --git a/contrib/go-redis/redis/orchestrion.yml b/contrib/go-redis/redis/orchestrion.yml new file mode 100644 index 0000000000..f053bb809c --- /dev/null +++ b/contrib/go-redis/redis/orchestrion.yml @@ -0,0 +1,27 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis + description: Redis client for Go. + +aspects: + - id: NewClient + join-point: + one-of: + - function-call: github.com/go-redis/redis.NewClient + - function-call: github.com/go-redis/redis.NewFailoverClient + advice: + - wrap-expression: + imports: + redis: github.com/go-redis/redis + trace: gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis + template: |- + func() (client *redis.Client) { + client = {{ . }} + trace.WrapClient(client) + return + }() diff --git a/contrib/go.mongodb.org/mongo-driver/mongo/orchestrion.yml b/contrib/go.mongodb.org/mongo-driver/mongo/orchestrion.yml new file mode 100644 index 0000000000..1eee6ad8cb --- /dev/null +++ b/contrib/go.mongodb.org/mongo-driver/mongo/orchestrion.yml @@ -0,0 +1,21 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/go.mongodb.org/mongo-driver/mongo + description: Official Golang driver for MongoDB. + +aspects: + - id: Client + join-point: + function-call: go.mongodb.org/mongo-driver/mongo/options.Client + advice: + - wrap-expression: + imports: + options: go.mongodb.org/mongo-driver/mongo/options + mongotrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/go.mongodb.org/mongo-driver/mongo + template: |- + {{ . }}.SetMonitor(mongotrace.NewMonitor()) diff --git a/contrib/gocql/gocql/orchestrion.yml b/contrib/gocql/gocql/orchestrion.yml new file mode 100644 index 0000000000..bb5db42da3 --- /dev/null +++ b/contrib/gocql/gocql/orchestrion.yml @@ -0,0 +1,58 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/gocql/gocql + description: Package gocql implements a fast and robust Cassandra client for the Go programming language. + +aspects: + - id: ClusterConfig + join-point: + all-of: + - one-of: + - struct-literal: + type: github.com/gocql/gocql.ClusterConfig + match: pointer-only + - function-call: github.com/gocql/gocql.NewCluster + # Avoid circular dependency. + - not: + import-path: github.com/gocql/gocql + advice: + - wrap-expression: + imports: + gocql: github.com/gocql/gocql + gocqltrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/gocql/gocql + template: |- + func(cluster *gocql.ClusterConfig) *gocql.ClusterConfig { + obs := gocqltrace.NewObserver(cluster) + cluster.QueryObserver = obs + cluster.BatchObserver = obs + cluster.ConnectObserver = obs + return cluster + }({{ . }}) + + - id: '*ClusterConfig' + join-point: + all-of: + - struct-literal: + type: github.com/gocql/gocql.ClusterConfig + match: value-only + # Avoid circular dependency. + - not: + import-path: github.com/gocql/gocql + advice: + - wrap-expression: + imports: + gocql: github.com/gocql/gocql + gocqltrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/gocql/gocql + template: |- + func(cluster gocql.ClusterConfig) gocql.ClusterConfig { + obs := gocqltrace.NewObserver(&cluster) + cluster.QueryObserver = obs + cluster.BatchObserver = obs + cluster.ConnectObserver = obs + return cluster + }({{ . }}) diff --git a/contrib/gofiber/fiber.v2/orchestrion.yml b/contrib/gofiber/fiber.v2/orchestrion.yml new file mode 100644 index 0000000000..23a6d66d75 --- /dev/null +++ b/contrib/gofiber/fiber.v2/orchestrion.yml @@ -0,0 +1,25 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/gofiber/fiber.v2 + description: An Express inspired web framework built on Fasthttp, the fastest HTTP engine for Go. + +aspects: + - id: New + join-point: + function-call: github.com/gofiber/fiber/v2.New + advice: + - wrap-expression: + imports: + fiber: github.com/gofiber/fiber/v2 + fibertrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/gofiber/fiber.v2 + template: |- + func() *fiber.App { + app := {{ . }} + app.Use(fibertrace.Middleware()) + return app + }() diff --git a/contrib/gomodule/redigo/orchestrion.yml b/contrib/gomodule/redigo/orchestrion.yml new file mode 100644 index 0000000000..cfe7c2cf4f --- /dev/null +++ b/contrib/gomodule/redigo/orchestrion.yml @@ -0,0 +1,94 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo + description: Redigo is a Go client for the Redis database. + +aspects: + ############################################################################## + # Dial + - id: Dial + join-point: + function-call: github.com/gomodule/redigo/redis.Dial + advice: + - wrap-expression: + imports: + redigo: github.com/gomodule/redigo/redis + redigotrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo + template: |- + func() (redigo.Conn, error) { + {{ if .AST.Ellipsis }} + opts := {{ index .AST.Args 2 }} + anyOpts := make([]interface{}, len(opts)) + for i, v := range opts { + anyOpts[i] = v + } + return redigotrace.Dial({{ index .AST.Args 0 }}, {{ index .AST.Args 1 }}, anyOpts...) + {{ else }} + return redigotrace.Dial( + {{- range .AST.Args -}} + {{ . }}, + {{- end -}} + ) + {{ end }} + }() + + ############################################################################## + # DialContext + - id: DialContext + join-point: + function-call: github.com/gomodule/redigo/redis.DialContext + advice: + - wrap-expression: + imports: + redigo: github.com/gomodule/redigo/redis + redigotrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo + template: |- + func() (redigo.Conn, error) { + {{ if .AST.Ellipsis }} + opts := {{ index .AST.Args 3 }} + anyOpts := make([]interface{}, len(opts)) + for i, v := range opts { + anyOpts[i] = v + } + return redigotrace.DialContext({{ index .AST.Args 0 }}, {{ index .AST.Args 1 }}, {{ index .AST.Args 2 }}, anyOpts...) + {{ else }} + return redigotrace.DialContext( + {{- range .AST.Args -}} + {{ . }}, + {{- end -}} + ) + {{ end }} + }() + + ############################################################################## + # DialURL + - id: DialURL + join-point: + function-call: github.com/gomodule/redigo/redis.DialURL + advice: + - wrap-expression: + imports: + redigo: github.com/gomodule/redigo/redis + redigotrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo + template: |- + func() (redigo.Conn, error) { + {{ if .AST.Ellipsis }} + opts := {{ index .AST.Args 1 }} + anyOpts := make([]interface{}, len(opts)) + for i, v := range opts { + anyOpts[i] = v + } + return redigotrace.DialURL({{ index .AST.Args 0 }}, anyOpts...) + {{ else }} + return redigotrace.DialURL( + {{- range .AST.Args -}} + {{ . }}, + {{- end -}} + ) + {{ end }} + }() diff --git a/contrib/google.golang.org/grpc/orchestrion.yml b/contrib/google.golang.org/grpc/orchestrion.yml new file mode 100644 index 0000000000..07f441d76e --- /dev/null +++ b/contrib/google.golang.org/grpc/orchestrion.yml @@ -0,0 +1,41 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc + description: The Go implementation of gRPC. + +aspects: + # Client Instrumentation + - id: Client + join-point: + one-of: + - function-call: google.golang.org/grpc.Dial + - function-call: google.golang.org/grpc.DialContext + - function-call: google.golang.org/grpc.NewClient + advice: + - append-args: + type: google.golang.org/grpc.DialOption + values: + - imports: &imports + grpc: google.golang.org/grpc + grpctrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc + template: grpc.WithChainStreamInterceptor(grpctrace.StreamClientInterceptor()) + - imports: *imports + template: grpc.WithChainUnaryInterceptor(grpctrace.UnaryClientInterceptor()) + + # Server Instrumentation + - id: Server + join-point: + function-call: google.golang.org/grpc.NewServer + advice: + - append-args: + type: google.golang.org/grpc.ServerOption + values: + - imports: *imports + template: grpc.ChainStreamInterceptor(grpctrace.StreamServerInterceptor()) + - imports: *imports + template: grpc.ChainUnaryInterceptor(grpctrace.UnaryServerInterceptor()) diff --git a/contrib/gorilla/mux/orchestrion.yml b/contrib/gorilla/mux/orchestrion.yml new file mode 100644 index 0000000000..41d85a0f02 --- /dev/null +++ b/contrib/gorilla/mux/orchestrion.yml @@ -0,0 +1,149 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/gorilla/mux + description: Package gorilla/mux implements a request router and dispatcher for matching incoming requests to their respective handler. + +aspects: + # TODO(romain.marcadier): This is a temporary solution to instrument + # mux.Router without doing any refactor work in dd-trace-go at the moment. It + # contains a lot of code copied from the contrib that should be refactored so + # it can be re-used instead. + - id: Router.__dd_config + join-point: + struct-definition: github.com/gorilla/mux.Router + advice: + - inject-declarations: + imports: + 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 + template: |- + type ddRouterConfig struct { + ignoreRequest func(*http.Request) bool + headerTags *internal.LockMap + resourceNamer func(*Router, *http.Request) string + serviceName string + spanOpts []ddtrace.StartSpanOption + } + + func ddDefaultResourceNamer(router *Router, req *http.Request) string { + var ( + match RouteMatch + route = "unknown" + ) + if router.Match(req, &match) && match.Route != nil { + if r, err := match.Route.GetPathTemplate(); err == nil { + route = r + } + } + return fmt.Sprintf("%s %s", req.Method, route) + } + + func init() { + telemetry.LoadIntegration("gorilla/mux") + tracer.MarkIntegrationImported("github.com/gorilla/mux") + } + - add-struct-field: + name: __dd_config + type: ddRouterConfig + + - id: NewRouter + join-point: + all-of: + - import-path: github.com/gorilla/mux + - function-body: + function: + - name: NewRouter + advice: + - prepend-statements: + imports: + 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 + template: |- + {{- $res := .Function.Result 0 -}} + defer func() { + var analyticsRate float64 + if internal.BoolEnv("DD_TRACE_MUX_ANALYTICS_ENABLED", false) { + analyticsRate = 1.0 + } else { + analyticsRate = globalconfig.AnalyticsRate() + } + + {{ $res }}.__dd_config.headerTags = globalconfig.HeaderTagMap() + {{ $res }}.__dd_config.ignoreRequest = func(*http.Request) bool { return false } + {{ $res }}.__dd_config.resourceNamer = ddDefaultResourceNamer + {{ $res }}.__dd_config.serviceName = namingschema.ServiceName("mux.router") + {{ $res }}.__dd_config.spanOpts = []ddtrace.StartSpanOption{ + tracer.Tag(ext.Component, "gorilla/mux"), + tracer.Tag(ext.SpanKind, ext.SpanKindServer), + } + if !math.IsNaN(analyticsRate) { + {{ $res }}.__dd_config.spanOpts = append( + {{ $res }}.__dd_config.spanOpts, + tracer.Tag(ext.EventSampleRate, analyticsRate), + ) + } + }() + + - id: Router.ServeHTTP + join-point: + function-body: + function: + - receiver: '*github.com/gorilla/mux.Router' + - name: ServeHTTP + advice: + - prepend-statements: + imports: + 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 + template: |- + {{- $r := .Function.Receiver -}} + {{- $w := .Function.Argument 0 -}} + {{- $req := .Function.Argument 1 -}} + if !{{ $r }}.__dd_config.ignoreRequest({{ $req }}) { + var ( + match RouteMatch + route string + spanOpts = options.Copy({{ $r }}.__dd_config.spanOpts...) + ) + if {{ $r }}.Match({{ $req }}, &match) && match.Route != nil { + if h, err := match.Route.GetHostTemplate(); err == nil { + spanOpts = append(spanOpts, tracer.Tag("mux.host", h)) + } + route, _ = match.Route.GetPathTemplate() + } + spanOpts = append(spanOpts, httptraceinternal.HeaderTagsFromRequest({{ $req }}, {{ $r }}.__dd_config.headerTags)) + resource := {{ $r }}.__dd_config.resourceNamer({{ $r }}, {{ $req }}) + + // This is a temporary workaround/hack to prevent endless recursion via httptrace.TraceAndServe, which + // basically implies passing a shallow copy of this router that ignores all requests down to + // httptrace.TraceAndServe. + var rCopy Router + rCopy = *{{ $r }} + rCopy.__dd_config.ignoreRequest = func(*http.Request) bool { return true } + + httptrace.TraceAndServe(&rCopy, {{ $w }}, {{ $req }}, &httptrace.ServeConfig{ + Service: {{ $r }}.__dd_config.serviceName, + Resource: resource, + SpanOpts: spanOpts, + RouteParams: match.Vars, + Route: route, + }) + return + } diff --git a/contrib/gorm.io/gorm.v1/orchestrion.yml b/contrib/gorm.io/gorm.v1/orchestrion.yml new file mode 100644 index 0000000000..bb0eac6e12 --- /dev/null +++ b/contrib/gorm.io/gorm.v1/orchestrion.yml @@ -0,0 +1,16 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1 + description: The fantastic ORM library for Golang. + +aspects: + - id: Open + join-point: + function-call: gorm.io/gorm.Open + advice: + - replace-function: gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1.Open diff --git a/contrib/graph-gophers/graphql-go/orchestrion.yml b/contrib/graph-gophers/graphql-go/orchestrion.yml new file mode 100644 index 0000000000..3f248dfc22 --- /dev/null +++ b/contrib/graph-gophers/graphql-go/orchestrion.yml @@ -0,0 +1,25 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/graph-gophers/graphql-go + description: |- + The goal of this project is to provide full support of the [October 2021 GraphQL specification](https://spec.graphql.org/October2021/) with a set of idiomatic, easy to use Go packages. + +aspects: + - id: ParseSchema + join-point: + one-of: + - function-call: github.com/graph-gophers/graphql-go.MustParseSchema + - function-call: github.com/graph-gophers/graphql-go.ParseSchema + advice: + - append-args: + type: any + values: + - imports: + graphql: github.com/graph-gophers/graphql-go + graphqltrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/graph-gophers/graphql-go + template: graphql.Tracer(graphqltrace.NewTracer()) diff --git a/contrib/graphql-go/graphql/orchestrion.yml b/contrib/graphql-go/graphql/orchestrion.yml new file mode 100644 index 0000000000..3e4d79a051 --- /dev/null +++ b/contrib/graphql-go/graphql/orchestrion.yml @@ -0,0 +1,17 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/graphql-go/graphql + description: |- + An implementation of GraphQL in Go. Follows the official reference implementation [graphql-js](https://github.com/graphql/graphql-js). + +aspects: + - id: NewSchema + join-point: + function-call: github.com/graphql-go/graphql.NewSchema + advice: + - replace-function: gopkg.in/DataDog/dd-trace-go.v1/contrib/graphql-go/graphql.NewSchema diff --git a/contrib/hashicorp/vault/orchestrion.yml b/contrib/hashicorp/vault/orchestrion.yml new file mode 100644 index 0000000000..5226586518 --- /dev/null +++ b/contrib/hashicorp/vault/orchestrion.yml @@ -0,0 +1,34 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/hashicorp/vault + description: Official package for interacting with a Vault server. + +aspects: + - id: Config + join-point: + struct-literal: + type: github.com/hashicorp/vault/api.Config + advice: + - wrap-expression: + imports: + vaulttrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/hashicorp/vault + template: |- + {{- .AST.Type -}}{ + {{- $hasField := false -}} + {{ range .AST.Elts }} + {{- if eq .Key.Name "HttpClient" }} + {{- $hasField = true -}} + HttpClient: vaulttrace.WrapHTTPClient({{ .Value }}), + {{- else -}} + {{ . }}, + {{ end -}} + {{ end }} + {{- if not $hasField -}} + HttpClient: vaulttrace.NewHTTPClient(), + {{- end }} + } diff --git a/contrib/jackc/pgx.v5/orchestrion.yml b/contrib/jackc/pgx.v5/orchestrion.yml new file mode 100644 index 0000000000..092769db0c --- /dev/null +++ b/contrib/jackc/pgx.v5/orchestrion.yml @@ -0,0 +1,58 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5 + description: PostgreSQL driver and toolkit for Go. + +aspects: + - id: Connect + join-point: + all-of: + - function-call: github.com/jackc/pgx/v5.Connect + - not: + one-of: + - import-path: github.com/jackc/pgx/v5 + - import-path: github.com/jackc/pgx/v5/pgxpool + advice: + - replace-function: + gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5.Connect + + - id: ConnectConfig + join-point: + all-of: + - function-call: github.com/jackc/pgx/v5.ConnectConfig + - not: + one-of: + - import-path: github.com/jackc/pgx/v5 + - import-path: github.com/jackc/pgx/v5/pgxpool + advice: + - replace-function: + gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5.ConnectConfig + + - id: pgxpool.New + join-point: + all-of: + - function-call: github.com/jackc/pgx/v5/pgxpool.New + - not: + one-of: + - import-path: github.com/jackc/pgx/v5 + - import-path: github.com/jackc/pgx/v5/pgxpool + advice: + - replace-function: + gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5.NewPool + + - id: pgxpool.NewWithConfig + join-point: + all-of: + - function-call: github.com/jackc/pgx/v5/pgxpool.NewWithConfig + - not: + one-of: + - import-path: github.com/jackc/pgx/v5 + - import-path: github.com/jackc/pgx/v5/pgxpool + advice: + - replace-function: + gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5.NewPoolWithConfig diff --git a/contrib/jinzhu/gorm/orchestrion.yml b/contrib/jinzhu/gorm/orchestrion.yml new file mode 100644 index 0000000000..2bb5085a01 --- /dev/null +++ b/contrib/jinzhu/gorm/orchestrion.yml @@ -0,0 +1,27 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/jinzhu/gorm + description: The fantastic ORM library for Golang. + +aspects: + - id: Open + join-point: + function-call: github.com/jinzhu/gorm.Open + advice: + - wrap-expression: + imports: + gorm: github.com/jinzhu/gorm + gormtrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/jinzhu/gorm + template: |- + func() (*gorm.DB, error) { + db, err := {{ . }} + if err != nil { + return nil, err + } + return gormtrace.WithCallbacks(db), err + }() diff --git a/contrib/julienschmidt/httprouter/orchestrion.yml b/contrib/julienschmidt/httprouter/orchestrion.yml new file mode 100644 index 0000000000..3a69a875af --- /dev/null +++ b/contrib/julienschmidt/httprouter/orchestrion.yml @@ -0,0 +1,88 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter + description: A high performance HTTP request router that scales well. + +aspects: + - id: Router.__dd_config + join-point: + struct-definition: github.com/julienschmidt/httprouter.Router + advice: + - inject-declarations: + imports: + tracing: "gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter/internal/tracing" + lang: go1.18 + template: |- + type __dd_wRouter struct { + *Router + } + + func __dd_wrapRouter(r *Router) tracing.Router { + return &__dd_wRouter{r} + } + + func (w __dd_wRouter) Lookup(method string, path string) (any, []tracing.Param, bool) { + h, params, ok := w.Router.Lookup(method, path) + return h, __dd_wrapParams(params), ok + } + + type __dd_wParam struct { + Param + } + + func __dd_wrapParams(params Params) []tracing.Param { + wParams := make([]tracing.Param, len(params)) + for i, p := range params { + wParams[i] = __dd_wParam{p} + } + return wParams + } + + func (w __dd_wParam) GetKey() string { + return w.Key + } + + func (w __dd_wParam) GetValue() string { + return w.Value + } + + func __dd_init(r *Router) { + if r.__dd_config != nil { + return + } + r.__dd_config = tracing.NewConfig() + return + } + + - add-struct-field: + name: __dd_config + type: "*gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter/internal/tracing.Config" + + - id: Router.ServeHTTP + join-point: + function-body: + function: + - receiver: "*github.com/julienschmidt/httprouter.Router" + - name: ServeHTTP + advice: + - prepend-statements: + imports: + tracing: "gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter/internal/tracing" + template: |- + {{- $r := .Function.Receiver -}} + {{- $w := .Function.Argument 0 -}} + {{- $req := .Function.Argument 1 -}} + __dd_init({{ $r }}) + + tw, treq, afterHandle, handled := tracing.BeforeHandle({{ $r }}.__dd_config, {{ $r }}, __dd_wrapRouter, {{ $w }}, {{ $req }}) + {{ $w }} = tw + {{ $req }} = treq + defer afterHandle() + if handled { + return + } diff --git a/contrib/k8s.io/client-go/kubernetes/orchestrion.yml b/contrib/k8s.io/client-go/kubernetes/orchestrion.yml new file mode 100644 index 0000000000..c449b35ecb --- /dev/null +++ b/contrib/k8s.io/client-go/kubernetes/orchestrion.yml @@ -0,0 +1,35 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/k8s.io/client-go/kubernetes + description: Go client for Kubernetes. + +aspects: + - id: rest.Config + join-point: + struct-literal: + type: k8s.io/client-go/rest.Config + advice: + - wrap-expression: + imports: + kubernetestrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/k8s.io/client-go/kubernetes + kubernetestransport: k8s.io/client-go/transport + template: |- + {{- .AST.Type -}}{ + {{- $hasField := false -}} + {{ range .AST.Elts }} + {{- if eq .Key.Name "WrapTransport" }} + {{- $hasField = true -}} + WrapTransport: kubernetestransport.Wrappers({{ .Value }}, kubernetestrace.WrapRoundTripper), + {{- else -}} + {{ . }}, + {{ end -}} + {{ end }} + {{- if not $hasField -}} + WrapTransport: kubernetestransport.Wrappers(nil, kubernetestrace.WrapRoundTripper), + {{- end }} + } diff --git a/contrib/labstack/echo.v4/orchestrion.yml b/contrib/labstack/echo.v4/orchestrion.yml new file mode 100644 index 0000000000..4e4b238e66 --- /dev/null +++ b/contrib/labstack/echo.v4/orchestrion.yml @@ -0,0 +1,25 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/labstack/echo.v4 + description: High performance, extensible, minimalist Go web framework. + +aspects: + - id: New + join-point: + function-call: github.com/labstack/echo/v4.New + advice: + - wrap-expression: + imports: + echo: github.com/labstack/echo/v4 + echotrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/labstack/echo.v4 + template: |- + func() *echo.Echo { + e := {{ . }} + e.Use(echotrace.Middleware()) + return e + }() diff --git a/contrib/log/slog/orchestrion.yml b/contrib/log/slog/orchestrion.yml new file mode 100644 index 0000000000..be7e22643a --- /dev/null +++ b/contrib/log/slog/orchestrion.yml @@ -0,0 +1,22 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/log/slog + description: |- + Package slog provides structured logging, in which log records include a message, a severity level, and various + other attributes expressed as key-value pairs. + +aspects: + - id: New + join-point: + function-call: log/slog.New + advice: + - wrap-expression: + imports: + slogtrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/log/slog + template: |- + {{ .AST.Fun }}(slogtrace.WrapHandler({{ index .AST.Args 0 }})) diff --git a/contrib/net/http/orchestrion.client.yml b/contrib/net/http/orchestrion.client.yml new file mode 100644 index 0000000000..2f1cfbe6b9 --- /dev/null +++ b/contrib/net/http/orchestrion.client.yml @@ -0,0 +1,197 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http.Client + description: HTTP client implementation. + +aspects: + # Add tracing to the default http.RoundTripper implementation. + - id: Transport.DD__tracer_internal + join-point: + struct-definition: net/http.Transport + advice: + - add-struct-field: + name: DD__tracer_internal + type: bool + # In tracer internals, set the DD__tracer_internal field to true so that we do not end up + # instrumenting the tracer's internal HTTP clients (this would be a span bomb!) + - id: Transport.DD__tracer_internal=true + tracer-internal: true + join-point: + all-of: + - struct-literal: + type: net/http.Transport + - one-of: + - import-path: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + - import-path: gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/httputils + - import-path: gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig + - import-path: gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry + - import-path: gopkg.in/DataDog/dd-trace-go.v1/profiler + - import-path: datadoghq.dev/orchestrion/_integration-tests/utils/agent # Integration test's custom transport. + advice: + - wrap-expression: + template: |- + {{- .AST.Type -}}{ + DD__tracer_internal: true, + {{ range .AST.Elts }}{{ . }}, + {{ end }} + } + - id: Transport.RoundTrip + join-point: + function-body: + function: + - name: RoundTrip + - receiver: '*net/http.Transport' + advice: + - inject-declarations: + # We need to use go:linkname to refer to a number of declarations in order to avoid creating + # circular dependencies, as these features have transitive dependencies on `net/http`... + imports: + context: context + ddtrace: gopkg.in/DataDog/dd-trace-go.v1/ddtrace + links: + - gopkg.in/DataDog/dd-trace-go.v1/internal/appsec + - gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/httpsec + - gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + template: |- + //go:linkname __dd_appsec_RASPEnabled gopkg.in/DataDog/dd-trace-go.v1/internal/appsec.RASPEnabled + func __dd_appsec_RASPEnabled() bool + + //go:linkname __dd_httpsec_ProtectRoundTrip gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/httpsec.ProtectRoundTrip + func __dd_httpsec_ProtectRoundTrip(context.Context, string) error + + //go:linkname __dd_tracer_SpanType gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.SpanType + func __dd_tracer_SpanType(string) ddtrace.StartSpanOption + + //go:linkname __dd_tracer_ResourceName gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.ResourceName + func __dd_tracer_ResourceName(string) ddtrace.StartSpanOption + + //go:linkname __dd_tracer_Tag gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.Tag + func __dd_tracer_Tag(string, any) ddtrace.StartSpanOption + + //go:linkname __dd_tracer_StartSpanFromContext gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.StartSpanFromContext + func __dd_tracer_StartSpanFromContext(context.Context, string, ...ddtrace.StartSpanOption) (ddtrace.Span, context.Context) + + //go:linkname __dd_tracer_WithError gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.WithError + func __dd_tracer_WithError(error) ddtrace.FinishOption + + //go:linkname __dd_tracer_Inject gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.Inject + func __dd_tracer_Inject(ddtrace.SpanContext, any) error + + type __dd_tracer_HTTPHeadersCarrier Header + func (c __dd_tracer_HTTPHeadersCarrier) Set(key, val string) { + Header(c).Set(key, val) + } + - prepend-statements: + imports: + 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 + template: |- + {{- /* Largely copied from https://github.com/DataDog/dd-trace-go/blob/v1.65.0-rc.2/contrib/net/http/roundtripper.go#L28-L104 */ -}} + {{- $t := .Function.Receiver -}} + {{- $req := .Function.Argument 0 -}} + {{- $res := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + if !{{ $t }}.DD__tracer_internal { + resourceName := fmt.Sprintf("%s %s", {{ $req }}.Method, {{ $req }}.URL.Path) + spanName := namingschema.OpName(namingschema.HTTPClient) + // Copy the URL so we don't modify the outgoing request + url := *{{ $req }}.URL + url.User = nil // Don't include userinfo in the http.url tag + opts := []ddtrace.StartSpanOption{ + __dd_tracer_SpanType(ext.SpanTypeHTTP), + __dd_tracer_ResourceName(resourceName), + __dd_tracer_Tag(ext.HTTPMethod, {{ $req }}.Method), + __dd_tracer_Tag(ext.HTTPURL, url.String()), + __dd_tracer_Tag(ext.Component, "net/http"), + __dd_tracer_Tag(ext.SpanKind, ext.SpanKindClient), + __dd_tracer_Tag(ext.NetworkDestinationName, url.Hostname()), + } + if analyticsRate := globalconfig.AnalyticsRate(); !math.IsNaN(analyticsRate) { + opts = append(opts, __dd_tracer_Tag(ext.EventSampleRate, analyticsRate)) + } + if port, err := strconv.Atoi(url.Port()); err == nil { + opts = append(opts, __dd_tracer_Tag(ext.NetworkDestinationPort, port)) + } + span, ctx := __dd_tracer_StartSpanFromContext({{ $req }}.Context(), spanName, opts...) + {{ $req }} = {{ $req }}.Clone(ctx) + defer func() { + if !events.IsSecurityError({{ $err }}) { + span.Finish(__dd_tracer_WithError({{ $err }})) + } else { + span.Finish() + } + }() + + if {{ $err }} = __dd_tracer_Inject(span.Context(), __dd_tracer_HTTPHeadersCarrier({{ $req }}.Header)); {{ $err }} != nil { + fmt.Fprintf(os.Stderr, "contrib/net/http.Roundtrip: failed to inject http headers: %v\n", {{ $err }}) + } + + if __dd_appsec_RASPEnabled() { + if err := __dd_httpsec_ProtectRoundTrip(ctx, {{ $req }}.URL.String()); err != nil { + return nil, err + } + } + + defer func() { + if {{ $err }} != nil { + span.SetTag("http.errors", {{ $err }}.Error()) + span.SetTag(ext.Error, {{ $err }}) + } else { + span.SetTag(ext.HTTPCode, strconv.Itoa({{ $res }}.StatusCode)) + if {{ $res }}.StatusCode >= 400 && {{ $res}}.StatusCode < 500 { + // Treat HTTP 4XX as errors + span.SetTag("http.errors", {{ $res }}.Status) + span.SetTag(ext.Error, fmt.Errorf("%d: %s", {{ $res }}.StatusCode, StatusText({{ $res }}.StatusCode))) + } + } + }() + } + + # Replace the http.Get, http.Head, http.Post, and http.PostForm short-hands with the longer forms if + # there is a context available from the surroundings. + - id: Get|Head|Post|PostForm + join-point: + all-of: + - not: + # We don't want to instrument in net/http, it'd create a circular dependency! + import-path: net/http + - one-of: + - function-call: net/http.Get + - function-call: net/http.Head + - function-call: net/http.Post + - function-call: net/http.PostForm + advice: + # Wire the context that is found to the handlers... + - wrap-expression: + imports: + instrument: github.com/DataDog/orchestrion/instrument/net/http + template: |- + {{- $ctx := .Function.ArgumentOfType "context.Context" -}} + {{- $req := .Function.ArgumentOfType "*net/http.Request" }} + {{- if $ctx -}} + instrument.{{ .AST.Fun.Name }}( + {{ $ctx }}, + {{ range .AST.Args }}{{ . }}, + {{ end }} + ) + {{- else if $req -}} + instrument.{{ .AST.Fun.Name }}( + {{ $req }}.Context(), + {{ range .AST.Args }}{{ . }}, + {{ end }} + ) + {{- else -}} + {{ . }} + {{- end -}} diff --git a/contrib/net/http/orchestrion.server.yml b/contrib/net/http/orchestrion.server.yml new file mode 100644 index 0000000000..e205633f80 --- /dev/null +++ b/contrib/net/http/orchestrion.server.yml @@ -0,0 +1,34 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http.Server + description: HTTP server implementation. + +aspects: + - id: Server.Serve + join-point: + function-body: + function: + - receiver: '*net/http.Server' + - name: Serve + advice: + - inject-declarations: + # We need to use go:linkname to refer to a number of declarations in order to avoid creating + # circular dependencies, as these features have transitive dependencies on `net/http`... + links: + - github.com/DataDog/orchestrion/instrument + template: |- + //go:linkname __dd_orchestrion_instrument_WrapHandler github.com/DataDog/orchestrion/instrument.WrapHandler + func __dd_orchestrion_instrument_WrapHandler(handler Handler) Handler + - prepend-statements: + template: |- + {{- $srv := .Function.Receiver -}} + if {{ $srv }}.Handler == nil { + {{ $srv }}.Handler = __dd_orchestrion_instrument_WrapHandler(DefaultServeMux) + } else { + {{ $srv }}.Handler = __dd_orchestrion_instrument_WrapHandler({{ $srv }}.Handler) + } diff --git a/contrib/net/http/orchestrion.yml b/contrib/net/http/orchestrion.yml new file mode 100644 index 0000000000..2f8cd751d7 --- /dev/null +++ b/contrib/net/http/orchestrion.yml @@ -0,0 +1,13 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http + description: HTTP stack implementation. + +extends: + - ./orchestrion.client.yml + - ./orchestrion.server.yml diff --git a/contrib/os/orchestrion.yml b/contrib/os/orchestrion.yml new file mode 100644 index 0000000000..c2ce8292e2 --- /dev/null +++ b/contrib/os/orchestrion.yml @@ -0,0 +1,59 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/os + description: |- + Protection from Local File Inclusion (LFI) Attacks + + All known functions that open files are susceptible to Local File Inclusion (LFI) attacks. This aspect protects + against LFI attacks by wrapping the `os.OpenFile` function with a security operation that will block the operation if + it is deemed unsafe. + + Instrumenting only the `os.OpenFile` function is sufficient to protect against LFI attacks, as all other functions in + the `os` package that open files ultimately call `os.OpenFile` (as of Go 1.23). + +aspects: + - id: OpenFile + join-point: + all-of: + - import-path: os + - function-body: + function: + - name: OpenFile + advice: + - prepend-statements: + imports: + ossec: gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/ossec + dyngo: gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo + events: gopkg.in/DataDog/dd-trace-go.v1/appsec/events + template: |- + __dd_parent_op, _ := dyngo.FromContext(nil) + if __dd_parent_op != nil { + __dd_op := &ossec.OpenOperation{ + Operation: dyngo.NewOperation(__dd_parent_op), + } + + var __dd_block bool + dyngo.OnData(__dd_op, func(_ *events.BlockingSecurityEvent) { + __dd_block = true + }) + + dyngo.StartOperation(__dd_op, ossec.OpenOperationArgs{ + Path: {{ .Function.Argument 0 }}, + Flags: {{ .Function.Argument 1 }}, + Perms: {{ .Function.Argument 2 }}, + }) + + defer dyngo.FinishOperation(__dd_op, ossec.OpenOperationRes[*File]{ + File: &{{ .Function.Result 0 }}, + Err: &{{ .Function.Result 1 }}, + }) + + if __dd_block { + return + } + } diff --git a/contrib/os/os.go b/contrib/os/os.go new file mode 100644 index 0000000000..ff079a2e63 --- /dev/null +++ b/contrib/os/os.go @@ -0,0 +1,13 @@ +// 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 2024 Datadog, Inc. + +package os + +// These imports satisfy injected dependencies for Orchestrion auto instrumentation. +import ( + _ "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/ossec" + _ "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo" + _ "gopkg.in/DataDog/dd-trace-go.v1/appsec/events" +) diff --git a/contrib/redis/go-redis.v9/orchestrion.yml b/contrib/redis/go-redis.v9/orchestrion.yml new file mode 100644 index 0000000000..b1b0f7112b --- /dev/null +++ b/contrib/redis/go-redis.v9/orchestrion.yml @@ -0,0 +1,27 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/redis/go-redis.v9 + description: Redis client for Go. + +aspects: + - id: NewClient + join-point: + one-of: + - function-call: github.com/redis/go-redis/v9.NewClient + - function-call: github.com/redis/go-redis/v9.NewFailoverClient + advice: + - wrap-expression: + imports: + redis: github.com/redis/go-redis/v9 + trace: gopkg.in/DataDog/dd-trace-go.v1/contrib/redis/go-redis.v9 + template: |- + func() (client *redis.Client) { + client = {{ . }} + trace.WrapClient(client) + return + }() diff --git a/contrib/segmentio/kafka.go.v0/orchestrion.yml b/contrib/segmentio/kafka.go.v0/orchestrion.yml new file mode 100644 index 0000000000..64632715f8 --- /dev/null +++ b/contrib/segmentio/kafka.go.v0/orchestrion.yml @@ -0,0 +1,230 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kefla.go.v0 + description: Kafka library in Go + +aspects: + ## Trace Consume ## + - id: Reader + join-point: + struct-definition: github.com/segmentio/kafka-go.Reader + advice: + - inject-declarations: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing + ddtrace: gopkg.in/DataDog/dd-trace-go.v1/ddtrace + strings: strings + template: |- + type __dd_wMessage struct { + *Message + } + + func __dd_wrapMessage(msg *Message) tracing.Message { + if msg == nil { + return nil + } + return &__dd_wMessage{msg} + } + + func (w *__dd_wMessage) GetValue() []byte { + return w.Value + } + + func (w *__dd_wMessage) GetKey() []byte { + return w.Key + } + + func (w *__dd_wMessage) GetHeaders() []tracing.Header { + hs := make([]tracing.Header, 0, len(w.Headers)) + for _, h := range w.Headers { + hs = append(hs, __dd_wrapHeader(h)) + } + return hs + } + + func (w *__dd_wMessage) SetHeaders(headers []tracing.Header) { + hs := make([]Header, 0, len(headers)) + for _, h := range headers { + hs = append(hs, Header{ + Key: h.GetKey(), + Value: h.GetValue(), + }) + } + w.Message.Headers = hs + } + + func (w *__dd_wMessage) GetTopic() string { + return w.Topic + } + + func (w *__dd_wMessage) GetPartition() int { + return w.Partition + } + + func (w *__dd_wMessage) GetOffset() int64 { + return w.Offset + } + + type __dd_wHeader struct { + Header + } + + func __dd_wrapHeader(h Header) tracing.Header { + return &__dd_wHeader{h} + } + + func (w __dd_wHeader) GetKey() string { + return w.Key + } + + func (w __dd_wHeader) GetValue() []byte { + return w.Value + } + + type __dd_wWriter struct { + *Writer + } + + func (w *__dd_wWriter) GetTopic() string { + return w.Topic + } + + func __dd_wrapTracingWriter(w *Writer) tracing.Writer { + return &__dd_wWriter{w} + } + + func __dd_initReader(r *Reader) { + if r.__dd_tracer != nil { + return + } + kafkaCfg := tracing.KafkaConfig{} + if r.Config().Brokers != nil { + kafkaCfg.BootstrapServers = strings.Join(r.Config().Brokers, ",") + } + if r.Config().GroupID != "" { + kafkaCfg.ConsumerGroupID = r.Config().GroupID + } + r.__dd_tracer = tracing.NewTracer(kafkaCfg) + } + + type __dd_span = ddtrace.Span + - add-struct-field: + name: __dd_tracer + type: "*gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing.Tracer" + - add-struct-field: + name: __dd_prevSpan + type: "__dd_span" + + - id: Reader.FetchMessage + join-point: + function-body: + function: + - receiver: '*github.com/segmentio/kafka-go.Reader' + - name: FetchMessage # ReadMessage calls FetchMessage internally, so tracing this should be enough. + advice: + - prepend-statements: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing + template: |- + {{- $r := .Function.Receiver -}} + {{- $ctx := .Function.Argument 0 -}} + {{- $msg := .Function.Result 0 -}} + {{- $err := .Function.Result 1 -}} + __dd_initReader(r) + if {{ $r }}.__dd_prevSpan != nil { + {{ $r }}.__dd_prevSpan.Finish() + {{ $r }}.__dd_prevSpan = nil + } + defer func() { + if {{ $err }} != nil { + return + } + tMsg := __dd_wrapMessage(&{{ $msg }}) + {{ $r }}.__dd_prevSpan = {{ $r }}.__dd_tracer.StartConsumeSpan({{ $ctx }}, tMsg) + {{ $r }}.__dd_tracer.SetConsumeDSMCheckpoint(tMsg) + }() + + - id: Reader.Close + join-point: + function-body: + function: + - receiver: '*github.com/segmentio/kafka-go.Reader' + - name: Close + advice: + - prepend-statements: + template: |- + {{- $r := .Function.Receiver -}} + if {{ $r }}.__dd_prevSpan != nil { + {{ $r }}.__dd_prevSpan.Finish() + {{ $r }}.__dd_prevSpan = nil + } + + ## Trace Produce ## + + - id: Writer + join-point: + struct-definition: github.com/segmentio/kafka-go.Writer + advice: + - inject-declarations: + imports: + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing + template: |- + func __dd_initWriter(w *Writer) { + if w.__dd_tracer != nil { + return + } + kafkaCfg := tracing.KafkaConfig{ + BootstrapServers: w.Addr.String(), + } + w.__dd_tracer = tracing.NewTracer(kafkaCfg) + } + - add-struct-field: + name: __dd_tracer + type: "*gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing.Tracer" + + - id: Writer.WriteMessages + join-point: + function-body: + function: + - receiver: '*github.com/segmentio/kafka-go.Writer' + - name: WriteMessages + advice: + - prepend-statements: + imports: + ddtrace: gopkg.in/DataDog/dd-trace-go.v1/ddtrace + tracing: gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0/internal/tracing + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + # Here we pass a nil context to tracing.StartProduceSpan as the GLS modifies the context and makes the + # spans started in the for loop child of the previous ones instead of being sibling spans (which is the + # desired behavior). Until GLS supports starting sibling spans, we set the parent span manually as a workaround. + template: |- + {{- $w := .Function.Receiver -}} + {{- $ctx := .Function.Argument 0 -}} + {{- $msgs := .Function.Argument 1 -}} + {{- $err := .Function.Result 0 -}} + spans := make([]ddtrace.Span, len({{ $msgs }})) + __dd_initWriter(w) + + var spanOpts []tracer.StartSpanOption + prevSpan, ok := tracer.SpanFromContext({{ $ctx }}) + if ok { + spanOpts = append(spanOpts, tracer.ChildOf(prevSpan.Context())) + } + + for i := range {{ $msgs }} { + tMsg := __dd_wrapMessage(&{{ $msgs }}[i]) + tWriter := __dd_wrapTracingWriter({{ $w }}) + spans[i] = {{ $w }}.__dd_tracer.StartProduceSpan(nil, tWriter, tMsg, spanOpts...) + {{ $w }}.__dd_tracer.SetProduceDSMCheckpoint(tMsg, tWriter) + } + + defer func() { + for i, span := range spans { + {{ $w }}.__dd_tracer.FinishProduceSpan(span, {{ $msgs }}[i].Partition, {{ $msgs }}[i].Offset, {{ $err }}) + } + }() diff --git a/contrib/sirupsen/logrus/orchestrion.yml b/contrib/sirupsen/logrus/orchestrion.yml new file mode 100644 index 0000000000..a0e02f95e3 --- /dev/null +++ b/contrib/sirupsen/logrus/orchestrion.yml @@ -0,0 +1,89 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/sirupsen/logrus + description: Structured, pluggable logging for Go. + +aspects: + - id: DDContextLogHook + join-point: + struct-definition: github.com/sirupsen/logrus.Logger + advice: + - inject-declarations: + imports: + telemetry: gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + ext: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext + template: |- + func init() { + telemetry.LoadIntegration("sirupsen/logrus") + tracer.MarkIntegrationImported("github.com/sirupsen/logrus") + } + + // DDContextLogHook ensures that any span in the log context is correlated to log output. + type DDContextLogHook struct{} + + // Levels implements logrus.Hook interface, this hook applies to all defined levels + func (d *DDContextLogHook) Levels() []Level { + return []Level{PanicLevel, FatalLevel, ErrorLevel, WarnLevel, InfoLevel, DebugLevel, TraceLevel} + } + + // Fire implements logrus.Hook interface, attaches trace and span details found in entry context + func (d *DDContextLogHook) Fire(e *Entry) error { + span, found := tracer.SpanFromContext(e.Context) + if !found { + return nil + } + e.Data[ext.LogKeyTraceID] = span.Context().TraceID() + e.Data[ext.LogKeySpanID] = span.Context().SpanID() + return nil + } + + - id: New + join-point: + all-of: + - import-path: github.com/sirupsen/logrus + - function-body: + function: + - name: New + advice: + - prepend-statements: + template: |- + {{- $logger := .Function.Result 0 -}} + defer func() { + {{ $logger }}.AddHook(&DDContextLogHook{}) + }() + + - id: '*logrus.Logger' + join-point: + struct-literal: + type: github.com/sirupsen/logrus.Logger + match: pointer-only + advice: + - wrap-expression: + imports: + logrus: github.com/sirupsen/logrus + template: |- + func(logger *logrus.Logger) *logrus.Logger { + logger.AddHook(&logrus.DDContextLogHook{}) + return logger + }({{ . }}) + + - id: logrus.Logger + join-point: + struct-literal: + type: github.com/sirupsen/logrus.Logger + match: value-only + advice: + - wrap-expression: + imports: + logrus: github.com/sirupsen/logrus + template: |- + func(logger logrus.Logger) logrus.Logger { + logger.AddHook(&logrus.DDContextLogHook{}) + return logger + }({{ . }}) diff --git a/contrib/twitchtv/twirp/orchestrion.yml b/contrib/twitchtv/twirp/orchestrion.yml new file mode 100644 index 0000000000..fff7213c87 --- /dev/null +++ b/contrib/twitchtv/twirp/orchestrion.yml @@ -0,0 +1,47 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/contrib/twitchtv/twirp + description: |- + A simple RPC framework with protobuf service definitions. + + ### Details & Caveats + - This instrumentation currently uses the wrappers from the dd-trace-go `contrib/net/http` package, not the + ones from `contrib/twitchtv/twirp`. + - The server-side instrumentation additionally adds rpc / twirp specific spans using `*twirp.ServerHooks`. + - Currently, the `*twirp.ClientHooks` implementation is not available in dd-trace-go, so the only generated spans are + the ones from `net/http`. + +aspects: + # TODO: Client Instrumentation - dd-trace-go does not have a *twirp.ClientHooks implementation for now. + + # Server Instrumentation + - id: Server + join-point: + struct-literal: + type: github.com/twitchtv/twirp.ServerOptions + match: any + advice: + - wrap-expression: + imports: + twirp: github.com/twitchtv/twirp + twirptrace: gopkg.in/DataDog/dd-trace-go.v1/contrib/twitchtv/twirp + template: |- + {{- .AST.Type -}}{ + {{- $hasField := false -}} + {{ range .AST.Elts }} + {{- if eq .Key.Name "Hooks" }} + {{- $hasField = true -}} + Hooks: twirp.ChainHooks(twirptrace.NewServerHooks(), {{ .Value }}), + {{- else -}} + {{ . }}, + {{ end -}} + {{ end }} + {{- if not $hasField -}} + Hooks: twirptrace.NewServerHooks(), + {{- end }} + } diff --git a/ddtrace/tracer/context.go b/ddtrace/tracer/context.go index ee29dca37d..3ae0c87757 100644 --- a/ddtrace/tracer/context.go +++ b/ddtrace/tracer/context.go @@ -27,10 +27,16 @@ func SpanFromContext(ctx context.Context) (Span, bool) { return &traceinternal.NoopSpan{}, false } v := orchestrion.WrapContext(ctx).Value(internal.ActiveSpanKey) - if s, ok := v.(ddtrace.Span); ok { + switch s := v.(type) { + case *traceinternal.NoopSpan: + return s, false + case traceinternal.NoopSpan: + return s, false + case ddtrace.Span: return s, true + default: + return traceinternal.NoopSpan{}, false } - return &traceinternal.NoopSpan{}, false } // StartSpanFromContext returns a new span with the given operation name and options. If a span diff --git a/ddtrace/tracer/orchestrion.yml b/ddtrace/tracer/orchestrion.yml new file mode 100644 index 0000000000..054818e9ae --- /dev/null +++ b/ddtrace/tracer/orchestrion.yml @@ -0,0 +1,123 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + description: |- + Automatically starts the gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + at the start of the application, and closes it at exit of the main function. + + Adding the `//dd:span` directive on functions creates custom spans + representing every call to that function. The default operation (span) name + is the name of the function, and this can be overridden using a "span.name" + argument to the directive: + + ```go + //dd:span span.name:custom-operation-name other:tag + func myFunction() { + // The default operation name would have been "myFunction" + } + ``` + + Function literal expressions don't have a function name, and their default operation name is the value of the very + first directive argument (if there is one). If there are no directive arguments, the operation name will remain + blank. + + ```go + //dd:span other:tag span.name:custom-operation-name + myOp := func() { + // The default operation name would have been "tag" + } + ``` + +extends: + - ../../internal/orchestrion/_.orchestrion.yml + +aspects: + # Automatically manage the tracer lifecycle + - id: func main() + join-point: + all-of: + - package-name: main + - test-main: false + - function-body: + function: + - name: main + - signature: {} + advice: + - inject-declarations: + imports: + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + # Note: it is valid to have multiple func init() in a single compile unit (e.g, `.go` file), in which case + # they get executed in declaration order. This means it's okay for us to add a new init function if there is + # already one in the file, but as it currently is appended AFTER all other declarations in the file, it means + # that it will be executed last (tracing contents of previous init functions will not be possible). + template: |- + func init() { + tracer.Start(tracer.WithOrchestrion(map[string]string{"version": {{printf "%q" Version}}})) + } + # We need to stop the tracer at the end of `main` to ensure all spans are properly flushed. + - prepend-statements: + imports: + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + template: |- + defer tracer.Stop() + + # Create spans for each function annotated with the //dd:span directive. + - id: '//dd:span' + join-point: + function-body: + directive: 'dd:span' + advice: + - prepend-statements: + imports: + context: context + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + template: |- + {{- $ctx := .Function.ArgumentOfType "context.Context" -}} + {{- $req := .Function.ArgumentOfType "*net/http.Request" -}} + {{- if (eq $ctx "") -}} + {{- $ctx = "ctx" -}} + ctx := {{- with $req -}} + {{ $req }}.Context() + {{- else -}} + context.TODO() + {{- end }} + {{ end -}} + + {{ $functionName := .Function.Name -}} + {{- $opName := $functionName -}} + {{- range .DirectiveArgs "dd:span" -}} + {{- if eq $opName "" -}} + {{ $opName = .Value }} + {{- end -}} + {{- if eq .Key "span.name" -}} + {{- $opName = .Value -}} + {{- break -}} + {{- end -}} + {{- end -}} + + var span tracer.Span + span, {{ $ctx }} = tracer.StartSpanFromContext({{ $ctx }}, {{ printf "%q" $opName }}, + {{- with $functionName }} + tracer.Tag("function-name", {{ printf "%q" $functionName }}), + {{ end -}} + {{- range .DirectiveArgs "dd:span" }} + {{ if eq .Key "span.name" -}}{{- continue -}}{{- end -}} + tracer.Tag({{ printf "%q" .Key }}, {{ printf "%q" .Value }}), + {{- end }} + ) + {{- with $req }} + {{ $req }} = {{ $req }}.WithContext({{ $ctx }}) + {{- end }} + + {{ with .Function.ResultOfType "error" -}} + defer func(){ + span.Finish(tracer.WithError({{ . }})) + }() + {{ else -}} + defer span.Finish() + {{- end -}} diff --git a/go.mod b/go.mod index f78b187447..6ccc07d034 100644 --- a/go.mod +++ b/go.mod @@ -104,6 +104,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/sys v0.28.0 golang.org/x/time v0.6.0 + golang.org/x/tools v0.24.0 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 google.golang.org/api v0.169.0 google.golang.org/grpc v1.64.1 @@ -298,7 +299,6 @@ require ( golang.org/x/net v0.33.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.24.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect diff --git a/internal/civisibility/integrations/gotesting/orchestrion.yml b/internal/civisibility/integrations/gotesting/orchestrion.yml new file mode 100644 index 0000000000..81f18b5761 --- /dev/null +++ b/internal/civisibility/integrations/gotesting/orchestrion.yml @@ -0,0 +1,212 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting + description: Testing instrumentation + +aspects: + - id: M.Run + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Run + - receiver: '*testing.M' + advice: + - inject-declarations: + links: + - gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting + template: |- + //go:linkname __dd_civisibility_instrumentTestingM gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentTestingM + func __dd_civisibility_instrumentTestingM(*M) func(int) + - prepend-statements: + template: |- + exitFunc := __dd_civisibility_instrumentTestingM({{ .Function.Receiver }}) + defer exitFunc({{ .Function.Receiver }}.exitCode) + + - id: T.Run + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Run + - receiver: '*testing.T' + advice: + - inject-declarations: + links: + - gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting + - gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations + template: |- + //go:linkname __dd_civisibility_instrumentTestingTFunc gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentTestingTFunc + func __dd_civisibility_instrumentTestingTFunc(func(*T)) func(*T) + + //go:linkname __dd_civisibility_instrumentSetErrorInfo gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentSetErrorInfo + func __dd_civisibility_instrumentSetErrorInfo(tb TB, errType string, errMessage string, skip int) + + //go:linkname __dd_civisibility_instrumentCloseAndSkip gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentCloseAndSkip + func __dd_civisibility_instrumentCloseAndSkip(tb TB, skipReason string) + + //go:linkname __dd_civisibility_instrumentSkipNow gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentSkipNow + func __dd_civisibility_instrumentSkipNow(tb TB) + + //go:linkname __dd_civisibility_ExitCiVisibility gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations.ExitCiVisibility + func __dd_civisibility_ExitCiVisibility() + + - prepend-statements: + template: |- + {{ .Function.Argument 1 }} = __dd_civisibility_instrumentTestingTFunc({{ .Function.Argument 1 }}) + + - id: B.Run + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Run + - receiver: '*testing.B' + advice: + - inject-declarations: + links: + - gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting + template: |- + //go:linkname __dd_civisibility_instrumentTestingBFunc gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting.instrumentTestingBFunc + func __dd_civisibility_instrumentTestingBFunc(*B, string, func(*B)) (string, func(*B)) + - prepend-statements: + template: |- + {{ .Function.Argument 0 }}, {{ .Function.Argument 1 }} = __dd_civisibility_instrumentTestingBFunc({{ .Function.Receiver }}, {{ .Function.Argument 0 }}, {{ .Function.Argument 1 }}) + + - id: common.Fail + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Fail + - receiver: '*testing.common' + advice: + - prepend-statements: + template: |- + __dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, "Fail", "failed test", 0) + + - id: common.FailNow + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: FailNow + - receiver: '*testing.common' + advice: + - prepend-statements: + template: |- + __dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, "FailNow", "failed test", 0) + __dd_civisibility_ExitCiVisibility() + + - id: common.Error + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Error + - receiver: '*testing.common' + advice: + - prepend-statements: + imports: + fmt: fmt + template: |- + __dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, "Error", fmt.Sprint({{ .Function.Argument 0 }}...), 0) + + - id: common.Errorf + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Errorf + - receiver: '*testing.common' + advice: + - prepend-statements: + imports: + fmt: fmt + template: |- + __dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, "Errorf", fmt.Sprintf({{ .Function.Argument 0 }}, {{ .Function.Argument 1 }}...), 0) + + - id: common.Fatal + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Fatal + - receiver: '*testing.common' + advice: + - prepend-statements: + imports: + fmt: fmt + template: |- + __dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, "Fatal", fmt.Sprint({{ .Function.Argument 0 }}...), 0) + + - id: common.Fatalf + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Fatalf + - receiver: '*testing.common' + advice: + - prepend-statements: + imports: + fmt: fmt + template: |- + __dd_civisibility_instrumentSetErrorInfo({{ .Function.Receiver }}, "Fatalf", fmt.Sprintf({{ .Function.Argument 0 }}, {{ .Function.Argument 1 }}...), 0) + + - id: common.Skip + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Skip + - receiver: '*testing.common' + advice: + - prepend-statements: + imports: + fmt: fmt + template: |- + __dd_civisibility_instrumentCloseAndSkip({{ .Function.Receiver }}, fmt.Sprint({{ .Function.Argument 0 }}...)) + + - id: common.Skipf + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: Skipf + - receiver: '*testing.common' + advice: + - prepend-statements: + imports: + fmt: fmt + template: |- + __dd_civisibility_instrumentCloseAndSkip({{ .Function.Receiver }}, fmt.Sprintf({{ .Function.Argument 0 }}, {{ .Function.Argument 1 }}...)) + + - id: common.SkipNow + join-point: + all-of: + - import-path: testing + - function-body: + function: + - name: SkipNow + - receiver: '*testing.common' + advice: + - prepend-statements: + template: |- + __dd_civisibility_instrumentSkipNow({{ .Function.Receiver }}) diff --git a/internal/orchestrion/_.orchestrion.yml b/internal/orchestrion/_.orchestrion.yml new file mode 100644 index 0000000000..91772988ce --- /dev/null +++ b/internal/orchestrion/_.orchestrion.yml @@ -0,0 +1,48 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/internal/orchestrion + description: Operations that interact with Go's runtime system. + caveats: |- + This configuration introduces a way to access the Goroutine Local Storage (GLS), which is not + meant to be used directly by end-users. This is intended to be used only by tracer internals to + enable trace context forwarding in places where a {{}} + value is not available. + +aspects: + - id: __dd_gls + join-point: + struct-definition: runtime.g + advice: + - add-struct-field: + name: __dd_gls + type: any + - add-blank-import: unsafe # Needed for go:linkname + - inject-declarations: + # Reference: https://github.com/golang/go/blob/6d89b38ed86e0bfa0ddaba08dc4071e6bb300eea/src/runtime/HACKING.md?plain=1#L44-L54 + template: |- + //go:linkname __dd_orchestrion_gls_get __dd_orchestrion_gls_get + var __dd_orchestrion_gls_get = func() any { + return getg().m.curg.__dd_gls + } + + //go:linkname __dd_orchestrion_gls_set __dd_orchestrion_gls_set + var __dd_orchestrion_gls_set = func(val any) { + getg().m.curg.__dd_gls = val + } + - id: goexit1 + join-point: + all-of: + - import-path: runtime + - function-body: + function: + # This is the function that finishes the execution of a goroutine. + # See: https://github.com/golang/go/blob/f38d42f2c4c6ad0d7cbdad5e1417cac3be2a5dcb/src/runtime/proc.go#L4264 + - name: goexit1 + advice: + - prepend-statements: + template: getg().__dd_gls = nil diff --git a/internal/orchestrion/context.go b/internal/orchestrion/context.go index f66d12c576..db7444208b 100644 --- a/internal/orchestrion/context.go +++ b/internal/orchestrion/context.go @@ -15,10 +15,8 @@ func WrapContext(ctx context.Context) context.Context { return ctx } - if ctx != nil { - if _, ok := ctx.(*glsContext); ok { // avoid (some) double wrapping - return ctx - } + if _, ok := ctx.(*glsContext); ok { // avoid (some) double wrapping + return ctx } if ctx == nil { diff --git a/internal/orchestrion/generator/main.go b/internal/orchestrion/generator/main.go new file mode 100644 index 0000000000..3ffe4904da --- /dev/null +++ b/internal/orchestrion/generator/main.go @@ -0,0 +1,185 @@ +// 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 2024 Datadog, Inc. + +package main + +import ( + "bytes" + "fmt" + "go/format" + "io/fs" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "slices" + "strings" + "text/template" + + _ "embed" // For go:embed + + "golang.org/x/tools/go/packages" +) + +const orchestrionToolGo = "orchestrion.tool.go" + +var ( + //go:embed orchestrion.tool.go.tmpl + templateText string + fileTemplate = template.Must(template.New(orchestrionToolGo).Parse(templateText)) +) + +func main() { + _, thisFile, _, _ := runtime.Caller(0) + rootDir := filepath.Join(thisFile, "..", "..", "..", "..") + + if err := generateRootYAML(rootDir); err != nil { + log.Fatalln(err) + } + + if err := validateValidConfig(rootDir); err != nil { + log.Fatalln(err) + } +} + +func generateRootYAML(rootDir string) error { + var paths []string + err := filepath.WalkDir(rootDir, func(path string, entry fs.DirEntry, err error) error { + if err != nil { + return err + } + + if entry.IsDir() { + // Ignore directories the go toolchain normally ignores. + if entry.Name() == "testdata" || strings.HasPrefix(entry.Name(), "_") || strings.HasPrefix(entry.Name(), ".") { + return filepath.SkipDir + } + return nil + } + + if entry.Name() != "orchestrion.yml" { + return nil + } + + rel, err := filepath.Rel(rootDir, filepath.Dir(path)) + if err != nil { + return fmt.Errorf("relative path of %q: %w", path, err) + } + + if rel == "." { + // We don't want to have the root file circular reference itself! + return nil + } + + pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName, Dir: rootDir}, "./"+rel) + if err != nil { + log.Fatalln(err) + } + + paths = append(paths, pkgs[0].PkgPath) + + return nil + }) + if err != nil { + return fmt.Errorf("listing YAML documents to extend: %w", err) + } + + // Sort to ensure consistent ordering... + slices.Sort(paths) + + var buf bytes.Buffer + if err := fileTemplate.Execute(&buf, paths); err != nil { + return fmt.Errorf("rendering YAML template: %w", err) + } + + src, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatalln(err) + } + + err = os.WriteFile( + filepath.Join(rootDir, orchestrionToolGo), + src, + 0o644, + ) + if err != nil { + return fmt.Errorf("writing YAML file: %w", err) + } + + return nil +} + +const ( + mainGo = `package main + +import ( + "log" + + "github.com/DataDog/orchestrion/runtime/built" +) + +func main(){ + if !built.WithOrchestrion { + log.Fatalln("Not built with orchestrion ☹️") + } +} +` + + orchestrionToolGoContent = `//go:build tools +package tools + +import ( + _ "github.com/DataDog/orchestrion" + _ "gopkg.in/DataDog/dd-trace-go.v1" // integration +) +` +) + +func validateValidConfig(rootDir string) error { + tmp, err := os.MkdirTemp("", "dd-trace-go.orchestrion-*") + if err != nil { + return fmt.Errorf("MkdirTemp: %w", err) + } + defer os.RemoveAll(tmp) + + if err := goCmd(tmp, "mod", "init", "github.com/DataDog/dd-trace-go.orchestrion"); err != nil { + return fmt.Errorf("init module: %w", err) + } + if err := goCmd(tmp, "mod", "edit", "-replace", "gopkg.in/DataDog/dd-trace-go.v1="+rootDir); err != nil { + return fmt.Errorf("replace gopkg.in/DataDog/dd-trace-go.v1: %w", err) + } + // TODO: Remove before shipping + if err := goCmd(tmp, "mod", "edit", "-replace", "github.com/DataDog/orchestrion=/Users/romain.marcadier/Development/Datadog/orchestrion"); err != nil { + return fmt.Errorf("replace gopkg.in/DataDog/dd-trace-go.v1: %w", err) + } + + if err := os.WriteFile(filepath.Join(tmp, "main.go"), []byte(mainGo), 0o644); err != nil { + return fmt.Errorf("writing main.go: %w", err) + } + if err := os.WriteFile(filepath.Join(tmp, orchestrionToolGo), []byte(orchestrionToolGoContent), 0o644); err != nil { + return fmt.Errorf("writing "+orchestrionToolGo+": %w", err) + } + + if err := goCmd(tmp, "mod", "tidy"); err != nil { + return fmt.Errorf("go mod tidy: %w", err) + } + + if err := goCmd(tmp, "run", "github.com/DataDog/orchestrion", "go", "run", "."); err != nil { + return fmt.Errorf("go run: %w", err) + } + + return nil +} + +func goCmd(dir string, command string, args ...string) error { + cmd := exec.Command("go", command) + cmd.Args = append(cmd.Args, args...) + cmd.Dir = dir + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} diff --git a/internal/orchestrion/generator/orchestrion.tool.go.tmpl b/internal/orchestrion/generator/orchestrion.tool.go.tmpl new file mode 100644 index 0000000000..4ad03917f6 --- /dev/null +++ b/internal/orchestrion/generator/orchestrion.tool.go.tmpl @@ -0,0 +1,23 @@ +// 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 2016 Datadog, Inc. + +// Code generated by 'go generate ./internal/orchestrion' DO NOT EDIT + +//go:build tools + +package ddtracego + +// Importing "gopkg.in/DataDog/dd-trace-go.v1" in an `orchestrion.tool.go` file +// causes the package to use _all_ available integrations of `dd-trace-go`. +// This makes it easy to ensure all available features of DataDog are enabled in +// your go application, but may cause your dependency closure (`go.mod` and +// `go.sum` files) to include a lot more packages than are stricly necessary for +// your application. If that is a problem, you should instead manually import +// only the specific integrations that are useful to your application. +import ( +{{- range . }} + _ {{ printf "%q" . }} // integration +{{- end }} +) diff --git a/internal/orchestrion/orchestrion.go b/internal/orchestrion/orchestrion.go index 679593652e..705b4e1176 100644 --- a/internal/orchestrion/orchestrion.go +++ b/internal/orchestrion/orchestrion.go @@ -5,6 +5,8 @@ package orchestrion +//go:generate go run ./generator + // Orchestrion will change this at build-time // //dd:orchestrion-enabled diff --git a/orchestrion.tool.go b/orchestrion.tool.go new file mode 100644 index 0000000000..59286fafc8 --- /dev/null +++ b/orchestrion.tool.go @@ -0,0 +1,61 @@ +// 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 2016 Datadog, Inc. + +// Code generated by 'go generate ./internal/orchestrion' DO NOT EDIT + +//go:build tools + +package ddtracego + +// Importing "gopkg.in/DataDog/dd-trace-go.v1" in an `orchestrion.tool.go` file +// causes the package to use _all_ available integrations of `dd-trace-go`. +// This makes it easy to ensure all available features of DataDog are enabled in +// your go application, but may cause your dependency closure (`go.mod` and +// `go.sum` files) to include a lot more packages than are stricly necessary for +// your application. If that is a problem, you should instead manually import +// only the specific integrations that are useful to your application. +import ( + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/99designs/gqlgen" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/IBM/sarama.v1" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/Shopify/sarama" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/cloud.google.com/go/pubsub.v1" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/kafka" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/confluentinc/confluent-kafka-go/kafka.v2" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/elastic/go-elasticsearch.v6" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/gin-gonic/gin" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v5" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v7" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis.v8" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/go.mongodb.org/mongo-driver/mongo" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/gocql/gocql" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/gofiber/fiber.v2" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorilla/mux" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/graph-gophers/graphql-go" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/graphql-go/graphql" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/hashicorp/vault" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/jackc/pgx.v5" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/jinzhu/gorm" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/k8s.io/client-go/kubernetes" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/labstack/echo.v4" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/log/slog" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/os" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/redis/go-redis.v9" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/segmentio/kafka.go.v0" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/sirupsen/logrus" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/contrib/twitchtv/twirp" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting" // integration + _ "gopkg.in/DataDog/dd-trace-go.v1/profiler" // integration +) diff --git a/profiler/orchestrion.yml b/profiler/orchestrion.yml new file mode 100644 index 0000000000..1464d2bebd --- /dev/null +++ b/profiler/orchestrion.yml @@ -0,0 +1,66 @@ +# 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. +--- +# yaml-language-server: $schema=https://datadoghq.dev/orchestrion/schema.json +meta: + name: gopkg.in/DataDog/dd-trace-go.v1/profiler + description: The entry point of a Go program. + +aspects: + - id: func main() + join-point: + all-of: + - package-name: main + - test-main: false + - function-body: + function: + - name: main + - signature: {} + advice: + - inject-declarations: + imports: + profiler: gopkg.in/DataDog/dd-trace-go.v1/profiler + log: log + os: os + # Note: it is valid to have multiple func init() in a single compile unit (e.g, `.go` file), in which case + # they get executed in declaration order. This means it's okay for us to add a new init function if there is + # already one in the file, but as it currently is appended AFTER all other declarations in the file, it means + # that it will be executed last (tracing contents of previous init functions will not be possible). + template: |- + func init() { + switch os.Getenv("DD_PROFILING_ENABLED") { + case "1", "true", "auto": + // The "auto" value can be set if profiling is enabled via the + // Datadog Admission Controller. We always turn on the profiler in + // the "auto" case since we only send profiles after at least a + // minute, and we assume anything running that long is worth + // profiling. + err := profiler.Start( + profiler.WithProfileTypes( + profiler.CPUProfile, + profiler.HeapProfile, + // Non-default profiles which are highly likely to be useful: + profiler.GoroutineProfile, + profiler.MutexProfile, + ), + profiler.WithTags("orchestrion:true"), + ) + if err != nil { + // TODO: is there a better reporting mechanism? + // The tracer and profiler already use the stdlib logger, so + // we're not adding anything new. But users might be using a + // different logger. + log.Printf("failed to start profiling: %s", err) + } + } + } + + # We need to stop the profiler at the end of `main` to ensure profiles are complete. + - prepend-statements: + imports: + profiler: gopkg.in/DataDog/dd-trace-go.v1/profiler + tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer + template: |- + defer profiler.Stop()