dd-trace 5.81.0 → 5.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +7 -0
- package/loader-hook.mjs +7 -2
- package/package.json +13 -18
- package/packages/datadog-core/src/utils/src/parse-tags.js +1 -1
- package/packages/datadog-esbuild/index.js +8 -7
- package/packages/datadog-esbuild/src/utils.js +14 -2
- package/packages/datadog-instrumentations/src/aerospike.js +3 -2
- package/packages/datadog-instrumentations/src/ai.js +2 -2
- package/packages/datadog-instrumentations/src/amqp10.js +1 -1
- package/packages/datadog-instrumentations/src/amqplib.js +4 -4
- package/packages/datadog-instrumentations/src/anthropic.js +2 -2
- package/packages/datadog-instrumentations/src/apollo-server-core.js +2 -2
- package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
- package/packages/datadog-instrumentations/src/apollo.js +3 -2
- package/packages/datadog-instrumentations/src/avsc.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/azure-event-hubs.js +4 -3
- package/packages/datadog-instrumentations/src/azure-functions.js +2 -2
- package/packages/datadog-instrumentations/src/azure-service-bus.js +3 -4
- package/packages/datadog-instrumentations/src/bluebird.js +1 -1
- package/packages/datadog-instrumentations/src/bullmq.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +1 -1
- package/packages/datadog-instrumentations/src/cassandra-driver.js +1 -1
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +3 -3
- package/packages/datadog-instrumentations/src/couchbase.js +1 -1
- package/packages/datadog-instrumentations/src/crypto.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +12 -13
- package/packages/datadog-instrumentations/src/cypress.js +1 -1
- package/packages/datadog-instrumentations/src/dns.js +1 -1
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -1
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +1 -1
- package/packages/datadog-instrumentations/src/express.js +1 -1
- package/packages/datadog-instrumentations/src/fs.js +1 -1
- package/packages/datadog-instrumentations/src/generic-pool.js +1 -1
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +137 -15
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -3
- package/packages/datadog-instrumentations/src/google-genai.js +3 -3
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +1 -1
- package/packages/datadog-instrumentations/src/grpc/server.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hook.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -1
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +106 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/shared-utils.js +9 -0
- package/packages/datadog-instrumentations/src/hono.js +55 -10
- package/packages/datadog-instrumentations/src/ioredis.js +1 -1
- package/packages/datadog-instrumentations/src/iovalkey.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +2 -2
- package/packages/datadog-instrumentations/src/kafkajs.js +3 -3
- package/packages/datadog-instrumentations/src/knex.js +1 -1
- package/packages/datadog-instrumentations/src/ldapjs.js +1 -1
- package/packages/datadog-instrumentations/src/light-my-request.js +93 -0
- package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
- package/packages/datadog-instrumentations/src/lodash.js +1 -2
- package/packages/datadog-instrumentations/src/mariadb.js +1 -2
- package/packages/datadog-instrumentations/src/memcached.js +1 -1
- package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
- package/packages/datadog-instrumentations/src/mongodb.js +1 -1
- package/packages/datadog-instrumentations/src/mongoose.js +1 -1
- package/packages/datadog-instrumentations/src/mquery.js +1 -1
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +1 -1
- package/packages/datadog-instrumentations/src/net.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +1 -1
- package/packages/datadog-instrumentations/src/nyc.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +2 -2
- package/packages/datadog-instrumentations/src/opensearch.js +1 -1
- package/packages/datadog-instrumentations/src/oracledb.js +1 -1
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +3 -3
- package/packages/datadog-instrumentations/src/pino.js +1 -1
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/prisma.js +52 -37
- package/packages/datadog-instrumentations/src/process.js +1 -1
- package/packages/datadog-instrumentations/src/promise-js.js +1 -1
- package/packages/datadog-instrumentations/src/promise.js +1 -1
- package/packages/datadog-instrumentations/src/protobufjs.js +1 -1
- package/packages/datadog-instrumentations/src/q.js +1 -1
- package/packages/datadog-instrumentations/src/redis.js +1 -1
- package/packages/datadog-instrumentations/src/rhea.js +1 -1
- package/packages/datadog-instrumentations/src/selenium.js +1 -1
- package/packages/datadog-instrumentations/src/sequelize.js +1 -2
- package/packages/datadog-instrumentations/src/sharedb.js +1 -1
- package/packages/datadog-instrumentations/src/tedious.js +1 -1
- package/packages/datadog-instrumentations/src/undici.js +4 -4
- package/packages/datadog-instrumentations/src/url.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +1 -1
- package/packages/datadog-instrumentations/src/vm.js +1 -1
- package/packages/datadog-instrumentations/src/when.js +1 -1
- package/packages/datadog-instrumentations/src/winston.js +1 -1
- package/packages/datadog-instrumentations/src/ws.js +3 -2
- package/packages/datadog-plugin-amqp10/src/index.js +1 -1
- package/packages/datadog-plugin-amqplib/src/index.js +1 -1
- package/packages/datadog-plugin-anthropic/src/index.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +4 -4
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/util.js +3 -3
- package/packages/datadog-plugin-azure-event-hubs/src/index.js +1 -1
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +19 -5
- package/packages/datadog-plugin-azure-service-bus/src/index.js +1 -1
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +4 -0
- package/packages/datadog-plugin-bullmq/src/consumer.js +60 -0
- package/packages/datadog-plugin-bullmq/src/index.js +18 -0
- package/packages/datadog-plugin-bullmq/src/producer.js +178 -0
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -1
- package/packages/datadog-plugin-express/src/index.js +1 -1
- package/packages/datadog-plugin-fastify/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +13 -3
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +171 -12
- package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +160 -13
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +217 -0
- package/packages/datadog-plugin-google-cloud-vertexai/src/index.js +1 -1
- package/packages/datadog-plugin-google-genai/src/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
- package/packages/datadog-plugin-grpc/src/index.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +2 -1
- package/packages/datadog-plugin-http/src/index.js +25 -5
- package/packages/datadog-plugin-http2/src/client.js +2 -2
- package/packages/datadog-plugin-http2/src/index.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/index.js +1 -1
- package/packages/datadog-plugin-langchain/src/index.js +1 -1
- package/packages/datadog-plugin-moleculer/src/index.js +1 -1
- package/packages/datadog-plugin-mongodb-core/src/index.js +6 -2
- package/packages/datadog-plugin-openai/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/stream-helpers.js +30 -10
- package/packages/datadog-plugin-openai/src/tracing.js +2 -2
- package/packages/datadog-plugin-rhea/src/index.js +1 -1
- package/packages/datadog-plugin-ws/src/close.js +56 -3
- package/packages/datadog-plugin-ws/src/index.js +4 -0
- package/packages/datadog-plugin-ws/src/producer.js +39 -4
- package/packages/datadog-plugin-ws/src/receiver.js +39 -3
- package/packages/datadog-plugin-ws/src/server.js +13 -1
- package/packages/datadog-plugin-ws/src/util.js +107 -0
- package/packages/datadog-shimmer/src/shimmer.js +2 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -3
- package/packages/dd-trace/src/appsec/graphql.js +2 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/untrusted-deserialization-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +3 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/index.js +5 -5
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +10 -14
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +3 -3
- package/packages/dd-trace/src/appsec/index.js +8 -8
- package/packages/dd-trace/src/appsec/rasp/command_injection.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/index.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/lfi.js +1 -1
- package/packages/dd-trace/src/appsec/rc-products.js +10 -0
- package/packages/dd-trace/src/appsec/recommended.json +230 -3
- package/packages/dd-trace/src/appsec/remote_config.js +177 -0
- package/packages/dd-trace/src/appsec/reporter.js +3 -3
- package/packages/dd-trace/src/appsec/rule_manager.js +37 -20
- package/packages/dd-trace/src/appsec/sdk/index.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/user_tracking.js +2 -2
- package/packages/dd-trace/src/appsec/waf/index.js +17 -3
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +11 -0
- package/packages/dd-trace/src/azure_metadata.js +8 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +6 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +1 -1
- package/packages/dd-trace/src/config/remote_config.js +34 -0
- package/packages/dd-trace/src/config.js +29 -28
- package/packages/dd-trace/src/config_defaults.js +2 -1
- package/packages/dd-trace/src/constants.js +5 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -1
- package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
- package/packages/dd-trace/src/datastreams/index.js +1 -1
- package/packages/dd-trace/src/datastreams/pathway.js +7 -7
- package/packages/dd-trace/src/datastreams/processor.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +2 -2
- package/packages/dd-trace/src/debugger/config.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +7 -2
- package/packages/dd-trace/src/debugger/devtools_client/send.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/session.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +5 -5
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/status.js +2 -2
- package/packages/dd-trace/src/debugger/index.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +3 -2
- package/packages/dd-trace/src/encode/0.4.js +1 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +2 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +1 -1
- package/packages/dd-trace/src/encode/span-stats.js +6 -1
- package/packages/dd-trace/src/exporter.js +2 -2
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +1 -1
- package/packages/dd-trace/src/exporters/common/request.js +2 -2
- package/packages/dd-trace/src/exporters/common/writer.js +1 -1
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -1
- package/packages/dd-trace/src/external-logger/src/index.js +1 -2
- package/packages/dd-trace/src/flare/index.js +1 -1
- package/packages/dd-trace/src/guardrails/index.js +6 -3
- package/packages/dd-trace/src/id.js +1 -1
- package/packages/dd-trace/src/index.js +1 -1
- package/packages/dd-trace/src/lambda/handler.js +4 -4
- package/packages/dd-trace/src/lambda/index.js +1 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +4 -4
- package/packages/dd-trace/src/lambda/runtime/ritm.js +1 -1
- package/packages/dd-trace/src/llmobs/constants/tags.js +7 -1
- package/packages/dd-trace/src/llmobs/index.js +8 -9
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +38 -7
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +30 -9
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/llm.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/vectorstore.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +16 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +16 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +22 -10
- package/packages/dd-trace/src/llmobs/plugins/vertexai.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +15 -22
- package/packages/dd-trace/src/llmobs/span_processor.js +9 -13
- package/packages/dd-trace/src/llmobs/telemetry.js +3 -4
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
- package/packages/dd-trace/src/llmobs/writers/spans.js +1 -2
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/noop/proxy.js +2 -2
- package/packages/dd-trace/src/noop/span.js +1 -1
- package/packages/dd-trace/src/openfeature/index.js +2 -2
- package/packages/dd-trace/src/openfeature/noop.js +14 -14
- package/packages/dd-trace/src/openfeature/remote_config.js +31 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +5 -5
- package/packages/dd-trace/src/openfeature/writers/exposures.js +9 -9
- package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -2
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +4 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +9 -8
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +3 -3
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +2 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +4 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +36 -11
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +11 -10
- package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +2 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +3 -3
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +24 -8
- package/packages/dd-trace/src/opentracing/span.js +3 -3
- package/packages/dd-trace/src/opentracing/tracer.js +5 -5
- package/packages/dd-trace/src/payload-tagging/index.js +6 -2
- package/packages/dd-trace/src/plugin_manager.js +1 -1
- package/packages/dd-trace/src/plugins/apollo.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +27 -27
- package/packages/dd-trace/src/plugins/database.js +1 -1
- package/packages/dd-trace/src/plugins/index.js +5 -1
- package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/tracing.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +1 -1
- package/packages/dd-trace/src/plugins/util/git.js +8 -8
- package/packages/dd-trace/src/plugins/util/stacktrace.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +24 -24
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +8 -5
- package/packages/dd-trace/src/priority_sampler.js +15 -16
- package/packages/dd-trace/src/process-tags/index.js +31 -29
- package/packages/dd-trace/src/profiling/config.js +32 -21
- package/packages/dd-trace/src/profiling/exporter_cli.js +4 -4
- package/packages/dd-trace/src/profiling/exporters/agent.js +5 -5
- package/packages/dd-trace/src/profiling/index.js +1 -1
- package/packages/dd-trace/src/profiling/libuv-size.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +4 -5
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +4 -4
- package/packages/dd-trace/src/proxy.js +12 -18
- package/packages/dd-trace/src/remote_config/index.js +541 -137
- package/packages/dd-trace/src/require-package-json.js +1 -1
- package/packages/dd-trace/src/ritm.js +50 -27
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
- package/packages/dd-trace/src/serverless.js +16 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/span_format.js +1 -1
- package/packages/dd-trace/src/span_processor.js +2 -2
- package/packages/dd-trace/src/span_stats.js +6 -4
- package/packages/dd-trace/src/standalone/index.js +1 -1
- package/packages/dd-trace/src/startup-log.js +3 -3
- package/packages/dd-trace/src/supported-configurations.json +3 -0
- package/packages/dd-trace/src/telemetry/dependencies.js +3 -3
- package/packages/dd-trace/src/telemetry/endpoints.js +8 -8
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +11 -6
- package/packages/dd-trace/src/tracer.js +3 -3
- package/packages/dd-trace/src/tracer_metadata.js +19 -15
- package/packages/dd-trace/src/remote_config/manager.js +0 -368
|
@@ -2,30 +2,188 @@
|
|
|
2
2
|
|
|
3
3
|
const { getMessageSize } = require('../../dd-trace/src/datastreams')
|
|
4
4
|
const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
|
|
5
|
+
const SpanContext = require('../../dd-trace/src/opentracing/span_context')
|
|
6
|
+
const id = require('../../dd-trace/src/id')
|
|
7
|
+
const { storage } = require('../../datadog-core')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* PULL SUBSCRIPTION: Service explicitly pulls messages from Pub/Sub and processes them.
|
|
11
|
+
* Async context storage for linking acknowledge() API calls back to original message spans.
|
|
12
|
+
*
|
|
13
|
+
* Problem: message.ack() is often called asynchronously outside the original handler,
|
|
14
|
+
* losing async context. The acknowledge() API only receives ackIds (strings), not Messages.
|
|
15
|
+
*
|
|
16
|
+
* Solution: Store context by Message (WeakMap), lookup by ackId (Map<ackId, WeakRef<Message>>).
|
|
17
|
+
* WeakRef allows Messages to be GC'd even if never acknowledged (network failures, crashes).
|
|
18
|
+
*/
|
|
19
|
+
const messageToContext = new WeakMap() // Message -> context (auto-cleanup on GC)
|
|
20
|
+
const ackIdToMessage = new Map() // ackId -> WeakRef<Message> (needs cleanup)
|
|
21
|
+
|
|
22
|
+
const ackMapCleanup = new FinalizationRegistry((ackId) => {
|
|
23
|
+
ackIdToMessage.delete(ackId) // Remove orphaned ackId when Message is GC'd
|
|
24
|
+
})
|
|
5
25
|
|
|
6
26
|
class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
7
27
|
static id = 'google-cloud-pubsub'
|
|
8
28
|
static operation = 'receive'
|
|
9
29
|
|
|
30
|
+
constructor (...args) {
|
|
31
|
+
super(...args)
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Channel: message:ack-store
|
|
35
|
+
* When message.ack() is called, capture and store the current async context.
|
|
36
|
+
* Use WeakRef to allow Message GC even if never acknowledged.
|
|
37
|
+
*/
|
|
38
|
+
this.addSub('apm:google-cloud-pubsub:message:ack-store', (ctx) => {
|
|
39
|
+
const { message, ackId } = ctx
|
|
40
|
+
const currentStore = storage('legacy').getStore()
|
|
41
|
+
|
|
42
|
+
if (currentStore) {
|
|
43
|
+
messageToContext.set(message, currentStore)
|
|
44
|
+
ackIdToMessage.set(ackId, new WeakRef(message))
|
|
45
|
+
ackMapCleanup.register(message, ackId, message)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Channel: message:ack-retrieve
|
|
51
|
+
* When acknowledge() API is called, retrieve stored context for the ackIds.
|
|
52
|
+
* Map: ackId -> WeakRef<Message> -> stored context.
|
|
53
|
+
* Clean up immediately after acknowledge (happy path), or via FinalizationRegistry (GC).
|
|
54
|
+
*/
|
|
55
|
+
this.addSub('apm:google-cloud-pubsub:message:ack-retrieve', ({ ackIds, api, ctx }) => {
|
|
56
|
+
for (const ackId of ackIds) {
|
|
57
|
+
const weakRef = ackIdToMessage.get(ackId)
|
|
58
|
+
if (weakRef) {
|
|
59
|
+
const message = weakRef.deref()
|
|
60
|
+
if (message) {
|
|
61
|
+
const storedContext = messageToContext.get(message)
|
|
62
|
+
if (storedContext) {
|
|
63
|
+
ctx.storedContext = storedContext
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (api === 'acknowledge') {
|
|
71
|
+
ackIds.forEach(ackId => ackIdToMessage.delete(ackId))
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#reconstructPubSubRequestContext (attrs) {
|
|
77
|
+
const traceIdLower = attrs['_dd.pubsub_request.trace_id']
|
|
78
|
+
const spanId = attrs['_dd.pubsub_request.span_id']
|
|
79
|
+
const traceIdUpper = attrs['_dd.pubsub_request.p.tid']
|
|
80
|
+
|
|
81
|
+
if (!traceIdLower || !spanId) return null
|
|
82
|
+
|
|
83
|
+
const traceId128 = traceIdUpper ? traceIdUpper + traceIdLower : traceIdLower.padStart(32, '0')
|
|
84
|
+
const traceId = id(traceId128, 16)
|
|
85
|
+
const parentId = id(spanId, 16)
|
|
86
|
+
|
|
87
|
+
const tags = {}
|
|
88
|
+
if (traceIdUpper) tags['_dd.p.tid'] = traceIdUpper
|
|
89
|
+
|
|
90
|
+
return new SpanContext({
|
|
91
|
+
traceId,
|
|
92
|
+
spanId: parentId,
|
|
93
|
+
tags
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
10
97
|
bindStart (ctx) {
|
|
11
98
|
const { message } = ctx
|
|
12
99
|
const subscription = message._subscriber._subscription
|
|
13
|
-
const topic = subscription
|
|
14
|
-
|
|
100
|
+
const topic = subscription?.metadata?.topic || message.attributes?.['pubsub.topic']
|
|
101
|
+
|
|
102
|
+
const batchRequestTraceId = message.attributes?.['_dd.pubsub_request.trace_id']
|
|
103
|
+
const batchRequestSpanId = message.attributes?.['_dd.pubsub_request.span_id']
|
|
104
|
+
|
|
105
|
+
// Parse batch metadata once upfront for reuse
|
|
106
|
+
const batchSizeStr = message.attributes?.['_dd.batch.size']
|
|
107
|
+
const batchIndexStr = message.attributes?.['_dd.batch.index']
|
|
108
|
+
const batchSize = batchSizeStr ? Number(batchSizeStr) : undefined
|
|
109
|
+
const batchIndex = batchIndexStr ? Number(batchIndexStr) : undefined
|
|
110
|
+
|
|
111
|
+
let childOf = this.tracer.extract('text_map', message.attributes)
|
|
112
|
+
|
|
113
|
+
const isFirstMessage = batchIndex === 0
|
|
114
|
+
if (isFirstMessage && batchRequestSpanId) {
|
|
115
|
+
const pubsubRequestContext = this.#reconstructPubSubRequestContext(message.attributes)
|
|
116
|
+
if (pubsubRequestContext) {
|
|
117
|
+
childOf = pubsubRequestContext
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const topicName = topic?.slice(topic.lastIndexOf('/') + 1) ??
|
|
122
|
+
subscription.name.slice(subscription.name.lastIndexOf('/') + 1)
|
|
123
|
+
const baseService = this.tracer._service || 'unknown'
|
|
124
|
+
const serviceName = this.config.service || `${baseService}-pubsub`
|
|
125
|
+
const meta = {
|
|
126
|
+
'gcloud.project_id': subscription.pubsub.projectId,
|
|
127
|
+
'pubsub.topic': topic,
|
|
128
|
+
'span.kind': 'consumer',
|
|
129
|
+
'pubsub.subscription_type': 'pull',
|
|
130
|
+
'pubsub.span_type': 'message_processing',
|
|
131
|
+
'messaging.operation': 'receive',
|
|
132
|
+
base_service: baseService,
|
|
133
|
+
service_override_type: 'custom'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (batchRequestTraceId && batchRequestSpanId) {
|
|
137
|
+
meta['pubsub.batch.request_trace_id'] = batchRequestTraceId
|
|
138
|
+
// Use JSON format like producer for proper span link parsing
|
|
139
|
+
meta['_dd.span_links'] = JSON.stringify([{
|
|
140
|
+
trace_id: batchRequestTraceId,
|
|
141
|
+
span_id: batchRequestSpanId,
|
|
142
|
+
flags: 0
|
|
143
|
+
}])
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const metrics = {
|
|
147
|
+
'pubsub.ack': 0
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (batchSize) {
|
|
151
|
+
metrics['pubsub.batch.message_count'] = batchSize
|
|
152
|
+
metrics['pubsub.batch.size'] = batchSize
|
|
153
|
+
}
|
|
154
|
+
if (batchIndex !== undefined) {
|
|
155
|
+
metrics['pubsub.batch.message_index'] = batchIndex
|
|
156
|
+
metrics['pubsub.batch.index'] = batchIndex
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (batchSize && batchIndex !== undefined) {
|
|
160
|
+
meta['pubsub.batch.description'] = `Message ${batchIndex + 1} of ${batchSize}`
|
|
161
|
+
}
|
|
15
162
|
|
|
16
163
|
const span = this.startSpan({
|
|
17
164
|
childOf,
|
|
18
|
-
resource:
|
|
165
|
+
resource: `Message from ${topicName}`,
|
|
19
166
|
type: 'worker',
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
metrics: {
|
|
25
|
-
'pubsub.ack': 0
|
|
26
|
-
}
|
|
167
|
+
service: serviceName,
|
|
168
|
+
meta,
|
|
169
|
+
metrics
|
|
27
170
|
}, ctx)
|
|
28
171
|
|
|
172
|
+
if (message.id) {
|
|
173
|
+
span.setTag('pubsub.message_id', message.id)
|
|
174
|
+
}
|
|
175
|
+
if (message.publishTime) {
|
|
176
|
+
span.setTag('pubsub.publish_time', message.publishTime.toISOString())
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (message.attributes) {
|
|
180
|
+
const publishStartTime = message.attributes['x-dd-publish-start-time']
|
|
181
|
+
if (publishStartTime) {
|
|
182
|
+
const deliveryDuration = Date.now() - Number(publishStartTime)
|
|
183
|
+
span.setTag('pubsub.delivery_duration_ms', deliveryDuration)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
29
187
|
if (this.config.dsmEnabled && message?.attributes) {
|
|
30
188
|
const payloadSize = getMessageSize(message)
|
|
31
189
|
this.tracer.decodeDataStreamsContext(message.attributes)
|
|
@@ -38,14 +196,15 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
|
38
196
|
|
|
39
197
|
bindFinish (ctx) {
|
|
40
198
|
const { message } = ctx
|
|
41
|
-
const span = ctx.currentStore
|
|
199
|
+
const span = ctx.currentStore?.span
|
|
200
|
+
|
|
201
|
+
if (!span) return ctx.parentStore
|
|
42
202
|
|
|
43
203
|
if (message?._handled) {
|
|
44
204
|
span.setTag('pubsub.ack', 1)
|
|
45
205
|
}
|
|
46
206
|
|
|
47
207
|
super.finish()
|
|
48
|
-
|
|
49
208
|
return ctx.parentStore
|
|
50
209
|
}
|
|
51
210
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
3
4
|
const ProducerPlugin = require('./producer')
|
|
4
5
|
const ConsumerPlugin = require('./consumer')
|
|
5
6
|
const ClientPlugin = require('./client')
|
|
6
|
-
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
7
7
|
|
|
8
|
-
// TODO: Consider splitting channels for publish/receive in the instrumentation.
|
|
9
8
|
class GoogleCloudPubsubPlugin extends CompositePlugin {
|
|
10
9
|
static id = 'google-cloud-pubsub'
|
|
11
10
|
static get plugins () {
|
|
@@ -2,42 +2,189 @@
|
|
|
2
2
|
|
|
3
3
|
const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
|
|
4
4
|
const { DsmPathwayCodec, getHeadersSize } = require('../../dd-trace/src/datastreams')
|
|
5
|
+
const id = require('../../dd-trace/src/id')
|
|
5
6
|
|
|
6
7
|
class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
|
|
7
8
|
static id = 'google-cloud-pubsub'
|
|
8
9
|
static operation = 'request'
|
|
9
10
|
|
|
11
|
+
constructor (...args) {
|
|
12
|
+
super(...args)
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Inject trace context into individual messages published via Topic.publish() or
|
|
16
|
+
* Topic.publishMessage(). This happens before the batch publish API call, allowing
|
|
17
|
+
* each message to propagate trace context to consumers.
|
|
18
|
+
*/
|
|
19
|
+
this.addSub('apm:google-cloud-pubsub:message:publish', this.handleMessagePublish.bind(this))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
handleMessagePublish ({ attributes, pubsub, topicName }) {
|
|
23
|
+
// Skip if message already has trace context from upstream
|
|
24
|
+
if (attributes['x-datadog-trace-id'] || attributes.traceparent) return
|
|
25
|
+
|
|
26
|
+
const activeSpan = this.tracer.scope().active()
|
|
27
|
+
if (!activeSpan) return
|
|
28
|
+
|
|
29
|
+
// Inject current span's trace context into message attributes
|
|
30
|
+
this.tracer.inject(activeSpan, 'text_map', attributes)
|
|
31
|
+
|
|
32
|
+
const traceIdUpperBits = activeSpan.context()._trace.tags['_dd.p.tid']
|
|
33
|
+
if (traceIdUpperBits) attributes['_dd.p.tid'] = traceIdUpperBits
|
|
34
|
+
|
|
35
|
+
if (pubsub) attributes['gcloud.project_id'] = pubsub.projectId
|
|
36
|
+
if (topicName) attributes['pubsub.topic'] = topicName
|
|
37
|
+
}
|
|
38
|
+
|
|
10
39
|
bindStart (ctx) {
|
|
11
40
|
const { request, api, projectId } = ctx
|
|
12
|
-
|
|
13
41
|
if (api !== 'publish') return
|
|
14
42
|
|
|
15
43
|
const messages = request.messages || []
|
|
16
44
|
const topic = request.topic
|
|
17
|
-
const
|
|
18
|
-
|
|
45
|
+
const messageCount = messages.length
|
|
46
|
+
const hasTraceContext = messages[0]?.attributes?.['x-datadog-trace-id']
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Batch Publishing Strategy:
|
|
50
|
+
* - Create one "batch span" representing the entire publish operation
|
|
51
|
+
* - If messages already have trace context (from upstream), use the first message's
|
|
52
|
+
* context as the parent, and create span links for messages 2-N
|
|
53
|
+
* - Otherwise, use the current active span as parent
|
|
54
|
+
* - Inject batch span context + metadata into all message attributes for downstream
|
|
55
|
+
* consumers to reconstruct the trace and understand batch relationships
|
|
56
|
+
*/
|
|
57
|
+
const spanLinkData = hasTraceContext
|
|
58
|
+
? messages.slice(1).map(msg => this.#extractSpanLink(msg.attributes)).filter(Boolean)
|
|
59
|
+
: []
|
|
60
|
+
|
|
61
|
+
const firstAttrs = messages[0]?.attributes
|
|
62
|
+
const parentData = firstAttrs?.['x-datadog-trace-id'] && firstAttrs['x-datadog-parent-id']
|
|
63
|
+
? {
|
|
64
|
+
traceId: firstAttrs['x-datadog-trace-id'],
|
|
65
|
+
spanId: firstAttrs['x-datadog-parent-id'],
|
|
66
|
+
traceIdUpper: firstAttrs['_dd.p.tid'],
|
|
67
|
+
samplingPriority: firstAttrs['x-datadog-sampling-priority']
|
|
68
|
+
}
|
|
69
|
+
: null
|
|
70
|
+
|
|
71
|
+
const lastSlash = topic.lastIndexOf('/')
|
|
72
|
+
const topicName = lastSlash === -1 ? topic : topic.slice(lastSlash + 1)
|
|
73
|
+
const batchSpan = this.startSpan({
|
|
74
|
+
childOf: parentData ? this.#extractParentContext(parentData) : undefined,
|
|
75
|
+
resource: `${api} to Topic ${topicName}`,
|
|
19
76
|
meta: {
|
|
20
77
|
'gcloud.project_id': projectId,
|
|
21
|
-
'pubsub.method': api,
|
|
22
|
-
'pubsub.topic': topic
|
|
78
|
+
'pubsub.method': api,
|
|
79
|
+
'pubsub.topic': topic,
|
|
80
|
+
'span.kind': 'producer',
|
|
81
|
+
'_dd.base_service': this.tracer._service,
|
|
82
|
+
'_dd.serviceoverride.type': 'integration',
|
|
83
|
+
'pubsub.linked_message_count': spanLinkData.length || undefined,
|
|
84
|
+
operation: messageCount > 1 ? 'batched.pubsub.request' : 'pubsub.request'
|
|
85
|
+
},
|
|
86
|
+
metrics: {
|
|
87
|
+
'pubsub.batch.message_count': messageCount,
|
|
88
|
+
'pubsub.batch': messageCount > 1 ? true : undefined
|
|
23
89
|
}
|
|
24
90
|
}, ctx)
|
|
25
91
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
92
|
+
const spanCtx = batchSpan.context()
|
|
93
|
+
// Get 128-bit trace ID and span ID as hex strings
|
|
94
|
+
const fullTraceIdHex = spanCtx.toTraceId(true)
|
|
95
|
+
const batchSpanIdHex = spanCtx.toSpanId(true)
|
|
96
|
+
// Extract lower 64 bits (last 16 hex chars) for trace ID
|
|
97
|
+
const batchTraceIdHex = fullTraceIdHex.slice(-16)
|
|
98
|
+
const batchTraceIdUpper = spanCtx._trace.tags['_dd.p.tid']
|
|
99
|
+
|
|
100
|
+
if (spanLinkData.length) {
|
|
101
|
+
batchSpan.setTag('_dd.span_links', JSON.stringify(
|
|
102
|
+
spanLinkData.map(link => ({
|
|
103
|
+
trace_id: link.traceId,
|
|
104
|
+
span_id: link.spanId,
|
|
105
|
+
flags: link.samplingPriority || 0
|
|
106
|
+
}))
|
|
107
|
+
))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
messages.forEach((msg, i) => {
|
|
111
|
+
msg.attributes ??= {}
|
|
112
|
+
|
|
113
|
+
if (!hasTraceContext) {
|
|
114
|
+
this.tracer.inject(batchSpan, 'text_map', msg.attributes)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
Object.assign(msg.attributes, {
|
|
118
|
+
'_dd.pubsub_request.trace_id': batchTraceIdHex,
|
|
119
|
+
'_dd.pubsub_request.span_id': batchSpanIdHex,
|
|
120
|
+
'_dd.batch.size': String(messageCount),
|
|
121
|
+
'_dd.batch.index': String(i),
|
|
122
|
+
'gcloud.project_id': projectId,
|
|
123
|
+
'pubsub.topic': topic,
|
|
124
|
+
'x-dd-publish-start-time': String(Math.floor(batchSpan._startTime))
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
if (batchTraceIdUpper) {
|
|
128
|
+
msg.attributes['_dd.pubsub_request.p.tid'] = batchTraceIdUpper
|
|
29
129
|
}
|
|
30
|
-
|
|
130
|
+
|
|
31
131
|
if (this.config.dsmEnabled) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
132
|
+
const dataStreamsContext = this.tracer.setCheckpoint(
|
|
133
|
+
['direction:out', `topic:${topic}`, 'type:google-pubsub'],
|
|
134
|
+
batchSpan,
|
|
135
|
+
getHeadersSize(msg)
|
|
136
|
+
)
|
|
35
137
|
DsmPathwayCodec.encode(dataStreamsContext, msg.attributes)
|
|
36
138
|
}
|
|
37
|
-
}
|
|
139
|
+
})
|
|
38
140
|
|
|
141
|
+
ctx.batchSpan = batchSpan
|
|
39
142
|
return ctx.currentStore
|
|
40
143
|
}
|
|
144
|
+
|
|
145
|
+
bindFinish (ctx) {
|
|
146
|
+
if (ctx.batchSpan && !ctx.batchSpan._duration) ctx.batchSpan.finish()
|
|
147
|
+
return super.bindFinish(ctx)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
bindError (ctx) {
|
|
151
|
+
if (ctx.error && ctx.batchSpan) {
|
|
152
|
+
ctx.batchSpan.setTag('error', ctx.error)
|
|
153
|
+
}
|
|
154
|
+
return ctx.parentStore
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#extractSpanLink (attrs) {
|
|
158
|
+
if (!attrs?.['x-datadog-trace-id'] || !attrs['x-datadog-parent-id']) return null
|
|
159
|
+
|
|
160
|
+
// Convert to hex strings
|
|
161
|
+
const lowerHex = id(attrs['x-datadog-trace-id']).toString(16)
|
|
162
|
+
const spanIdHex = id(attrs['x-datadog-parent-id']).toString(16)
|
|
163
|
+
|
|
164
|
+
// Build full 128-bit trace ID
|
|
165
|
+
const traceIdHex = attrs['_dd.p.tid']
|
|
166
|
+
? attrs['_dd.p.tid'] + lowerHex
|
|
167
|
+
: lowerHex.padStart(32, '0')
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
traceId: traceIdHex,
|
|
171
|
+
spanId: spanIdHex,
|
|
172
|
+
samplingPriority: attrs['x-datadog-sampling-priority']
|
|
173
|
+
? Number.parseInt(attrs['x-datadog-sampling-priority'], 10)
|
|
174
|
+
: undefined
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
#extractParentContext (data) {
|
|
179
|
+
const carrier = {
|
|
180
|
+
'x-datadog-trace-id': data.traceId,
|
|
181
|
+
'x-datadog-parent-id': data.spanId
|
|
182
|
+
}
|
|
183
|
+
if (data.traceIdUpper) carrier['_dd.p.tid'] = data.traceIdUpper
|
|
184
|
+
if (data.samplingPriority) carrier['x-datadog-sampling-priority'] = String(data.samplingPriority)
|
|
185
|
+
|
|
186
|
+
return this.tracer.extract('text_map', carrier)
|
|
187
|
+
}
|
|
41
188
|
}
|
|
42
189
|
|
|
43
190
|
module.exports = GoogleCloudPubsubProducerPlugin
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
+
const SpanContext = require('../../dd-trace/src/opentracing/span_context')
|
|
5
|
+
const id = require('../../dd-trace/src/id')
|
|
6
|
+
const log = require('../../dd-trace/src/log')
|
|
7
|
+
|
|
8
|
+
// WeakMap to track push receive spans by request
|
|
9
|
+
const pushReceiveSpans = new WeakMap()
|
|
10
|
+
|
|
11
|
+
class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
|
|
12
|
+
static get id () { return 'google-cloud-pubsub-push-subscription' }
|
|
13
|
+
|
|
14
|
+
constructor (...args) {
|
|
15
|
+
super(...args)
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* PUSH SUBSCRIPTION: GCP sends HTTP POST requests to our service with message data in headers.
|
|
19
|
+
* We intercept these requests to create a pubsub.push.receive span that wraps the HTTP request.
|
|
20
|
+
*
|
|
21
|
+
* Flow: Detect push request -> Extract trace context -> Create receive span -> Activate it
|
|
22
|
+
* Hierarchy: pubsub.push.receive (parent) -> http.request (child) -> express.middleware...
|
|
23
|
+
*
|
|
24
|
+
* Plugin load order (http/index.js) ensures we subscribe before HttpServerPlugin.
|
|
25
|
+
*/
|
|
26
|
+
this.addSub('apm:http:server:request:start', (ctx) => {
|
|
27
|
+
this.#handlePubSubRequest(ctx)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
this.addSub('apm:http:server:request:finish', ({ req }) => {
|
|
31
|
+
this.#finishPushReceiveSpan(req)
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#finishPushReceiveSpan (req) {
|
|
36
|
+
const pushReceiveSpan = pushReceiveSpans.get(req)
|
|
37
|
+
if (pushReceiveSpan && !pushReceiveSpan._duration) {
|
|
38
|
+
pushReceiveSpan.finish()
|
|
39
|
+
pushReceiveSpans.delete(req)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#handlePubSubRequest (ctx) {
|
|
44
|
+
const { req } = ctx
|
|
45
|
+
const userAgent = req.headers['user-agent'] || ''
|
|
46
|
+
if (req.method !== 'POST' || !userAgent.includes('APIs-Google')) {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (req.headers['x-goog-pubsub-message-id']) {
|
|
51
|
+
this.#createPushReceiveSpanAndActivate(ctx)
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
log.warn(
|
|
56
|
+
'[PubSub] No x-goog-pubsub-* headers detected. pubsub.push.receive spans will not be created. ' +
|
|
57
|
+
'Add --push-no-wrapper-write-metadata to your subscription.'
|
|
58
|
+
)
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#createPushReceiveSpanAndActivate (ctx) {
|
|
63
|
+
const { req, res } = ctx
|
|
64
|
+
const messageData = this.#parseMessage(req)
|
|
65
|
+
if (!messageData) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const originalContext = this.#extractContext(messageData)
|
|
70
|
+
const pubsubRequestContext = this.#reconstructPubSubContext(messageData.attrs) || originalContext
|
|
71
|
+
const isSameTrace = pubsubRequestContext &&
|
|
72
|
+
originalContext?.toTraceId() === pubsubRequestContext.toTraceId()
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create receive span, choosing parent context:
|
|
76
|
+
* - Same trace: use batch context (message is part of the batch trace)
|
|
77
|
+
* - Different trace: use message context as parent, link to batch for observability
|
|
78
|
+
*
|
|
79
|
+
* this.enter() activates the span so the HTTP request span becomes its child.
|
|
80
|
+
*/
|
|
81
|
+
const pushReceiveSpan = this.#createPushReceiveSpan(
|
|
82
|
+
messageData,
|
|
83
|
+
isSameTrace ? pubsubRequestContext : originalContext,
|
|
84
|
+
isSameTrace ? null : pubsubRequestContext
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if (!pushReceiveSpan) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.enter(pushReceiveSpan, { req, res })
|
|
92
|
+
pushReceiveSpans.set(req, pushReceiveSpan)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#parseMessage (req) {
|
|
96
|
+
const subscription = req.headers['x-goog-pubsub-subscription-name']
|
|
97
|
+
const message = {
|
|
98
|
+
messageId: req.headers['x-goog-pubsub-message-id'],
|
|
99
|
+
publishTime: req.headers['x-goog-pubsub-publish-time']
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const topicName = req.headers['pubsub.topic'] || 'push-subscription-topic'
|
|
103
|
+
return { message, subscription, attrs: req.headers, topicName }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#extractContext (messageData) {
|
|
107
|
+
return this.tracer.extract('text_map', messageData.attrs)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#reconstructPubSubContext (attrs) {
|
|
111
|
+
/**
|
|
112
|
+
* Reconstruct the batch publish span context from message attributes.
|
|
113
|
+
*
|
|
114
|
+
* When a batch is published, the producer injects:
|
|
115
|
+
* - _dd.pubsub_request.trace_id: lower 64 bits of the batch span's trace ID (hex)
|
|
116
|
+
* - _dd.pubsub_request.span_id: the batch span's span ID (hex)
|
|
117
|
+
* - _dd.pubsub_request.p.tid: upper 64 bits of trace ID (hex, optional for 128-bit traces)
|
|
118
|
+
*
|
|
119
|
+
* This context represents the "pubsub.request" span on the producer side.
|
|
120
|
+
* We use it to create span links, connecting each pubsub.push.receive span back to the original batch.
|
|
121
|
+
*/
|
|
122
|
+
const traceIdLower = attrs['_dd.pubsub_request.trace_id']
|
|
123
|
+
const spanId = attrs['_dd.pubsub_request.span_id']
|
|
124
|
+
const traceIdUpper = attrs['_dd.pubsub_request.p.tid']
|
|
125
|
+
|
|
126
|
+
if (!traceIdLower || !spanId) return null
|
|
127
|
+
|
|
128
|
+
// Reconstruct full 128-bit trace ID (or pad 64-bit to 128-bit)
|
|
129
|
+
const traceId128 = traceIdUpper ? traceIdUpper + traceIdLower : traceIdLower.padStart(32, '0')
|
|
130
|
+
const traceId = id(traceId128, 16)
|
|
131
|
+
const parentId = id(spanId, 16)
|
|
132
|
+
|
|
133
|
+
const tags = {}
|
|
134
|
+
if (traceIdUpper) tags['_dd.p.tid'] = traceIdUpper
|
|
135
|
+
|
|
136
|
+
return new SpanContext({
|
|
137
|
+
traceId,
|
|
138
|
+
spanId: parentId,
|
|
139
|
+
tags
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#createPushReceiveSpan (messageData, parentContext, linkContext) {
|
|
144
|
+
const { message, subscription, topicName, attrs } = messageData
|
|
145
|
+
const subscriptionName = subscription?.slice(subscription.lastIndexOf('/') + 1) ?? subscription
|
|
146
|
+
const publishStartTime = attrs['x-dd-publish-start-time']
|
|
147
|
+
const startTime = publishStartTime ? Number.parseInt(publishStartTime, 10) : undefined
|
|
148
|
+
|
|
149
|
+
// Get the base service name and construct the pubsub service override
|
|
150
|
+
const baseService = this.tracer._service
|
|
151
|
+
const serviceOverride = this.config.service ?? `${baseService}-pubsub`
|
|
152
|
+
|
|
153
|
+
// Use this.startSpan() which automatically activates the span
|
|
154
|
+
const span = this.startSpan('pubsub.push.receive', {
|
|
155
|
+
childOf: parentContext,
|
|
156
|
+
startTime,
|
|
157
|
+
kind: 'consumer',
|
|
158
|
+
service: serviceOverride,
|
|
159
|
+
meta: {
|
|
160
|
+
component: 'google-cloud-pubsub',
|
|
161
|
+
'pubsub.method': 'receive',
|
|
162
|
+
'pubsub.subscription': subscription,
|
|
163
|
+
'pubsub.message_id': message.messageId,
|
|
164
|
+
'pubsub.subscription_type': 'push',
|
|
165
|
+
'pubsub.topic': topicName,
|
|
166
|
+
'_dd.base_service': baseService,
|
|
167
|
+
'_dd.serviceoverride.type': 'integration',
|
|
168
|
+
'resource.name': `Push Subscription ${subscriptionName}`
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
if (!span) {
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
span._integrationName = 'google-cloud-pubsub'
|
|
177
|
+
// Calculate delivery latency (queue time from publish to delivery)
|
|
178
|
+
if (publishStartTime) {
|
|
179
|
+
const deliveryDuration = Date.now() - Number(publishStartTime)
|
|
180
|
+
span.setTag('pubsub.delivery_duration_ms', deliveryDuration)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.#addBatchMetadata(span, attrs)
|
|
184
|
+
|
|
185
|
+
if (linkContext) {
|
|
186
|
+
if (span.addLink) {
|
|
187
|
+
span.addLink(linkContext, {})
|
|
188
|
+
} else {
|
|
189
|
+
span._links ??= []
|
|
190
|
+
span._links.push({ context: linkContext, attributes: {} })
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return span
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
#addBatchMetadata (span, attrs) {
|
|
198
|
+
const batchSizeStr = attrs['_dd.batch.size']
|
|
199
|
+
const batchIndexStr = attrs['_dd.batch.index']
|
|
200
|
+
|
|
201
|
+
if (!batchSizeStr || batchIndexStr === undefined) return
|
|
202
|
+
|
|
203
|
+
const size = Number(batchSizeStr)
|
|
204
|
+
const index = Number(batchIndexStr)
|
|
205
|
+
|
|
206
|
+
span.setTag('pubsub.batch.message_count', size)
|
|
207
|
+
span.setTag('pubsub.batch.message_index', index)
|
|
208
|
+
span.setTag('pubsub.batch.description', `Message ${index + 1} of ${size}`)
|
|
209
|
+
|
|
210
|
+
const requestTraceId = attrs['_dd.pubsub_request.trace_id']
|
|
211
|
+
if (requestTraceId) {
|
|
212
|
+
span.setTag('pubsub.batch.request_trace_id', requestTraceId)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = GoogleCloudPubsubPushSubscriptionPlugin
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
4
|
-
const GoogleVertexAITracingPlugin = require('./tracing')
|
|
5
4
|
const VertexAILLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/vertexai')
|
|
5
|
+
const GoogleVertexAITracingPlugin = require('./tracing')
|
|
6
6
|
|
|
7
7
|
class GoogleCloudVertexAIPlugin extends CompositePlugin {
|
|
8
8
|
static id = 'google-cloud-vertexai'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
4
|
-
const GenAiTracingPlugin = require('./tracing')
|
|
5
4
|
const GenAiLLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/genai')
|
|
5
|
+
const GenAiTracingPlugin = require('./tracing')
|
|
6
6
|
|
|
7
7
|
class GenAiPlugin extends CompositePlugin {
|
|
8
8
|
static id = 'google-genai'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
3
4
|
const GrpcServerPlugin = require('./server')
|
|
4
5
|
const GrpcClientPlugin = require('./client')
|
|
5
|
-
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
6
6
|
|
|
7
7
|
class GrpcPlugin extends CompositePlugin {
|
|
8
8
|
static id = 'grpc'
|