From 5f4ccd08f980a35a39e3658d226ea5c646284755 Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Wed, 19 Jun 2024 18:42:31 -0700 Subject: [PATCH 01/32] Document environment variables to disable instrumentation Signed-off-by: Marco Costa --- docs/GettingStarted.md | 265 ++++++++++++++++++++++++++--------------- 1 file changed, 172 insertions(+), 93 deletions(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 3169e33f0b7..e3511dbaa45 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -309,10 +309,16 @@ You can enable it through `Datadog.configure`: require 'datadog' Datadog.configure do |c| - c.tracing.instrument :action_cable + c.tracing.instrument :action_cable, **options end ``` +`options` are the following keyword arguments: + +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_ACTION_CABLE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | + ### Action Mailer The Action Mailer integration provides tracing for Rails 5 ActionMailer actions. @@ -329,9 +335,10 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| ------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| `email_data` | `Bool` | Whether or not to append additional email payload metadata to `action_mailer.deliver` spans. Fields include `['subject', 'to', 'from', 'bcc', 'cc', 'date', 'perform_deliveries']`. | `false` | +| Key | Env Var | Type | Description | Default | +| ------------ | - | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `email_data` | | `Bool` | Whether or not to append additional email payload metadata to `action_mailer.deliver` spans. Fields include `['subject', 'to', 'from', 'bcc', 'cc', 'date', 'perform_deliveries']`. | `false` | +| `enabled` | `DD_TRACE_ACTION_MAILER_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Action Pack @@ -342,10 +349,16 @@ require 'actionpack' require 'datadog' Datadog.configure do |c| - c.tracing.instrument :action_pack + c.tracing.instrument :action_pack, **options end ``` +`options` are the following keyword arguments: + +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_ACTION_PACK_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | + ### Action View Most of the time, Action View is set up as part of Rails, but it can be activated separately: @@ -361,9 +374,10 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| `template_base_path` | `String` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` | +| Key | Env Var | Type | Description | Default | +| -------------------- | - | ------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| `template_base_path` | | `String` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` | +| `enabled` | `DD_TRACE_ACTION_VIEW_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Active Job @@ -374,12 +388,18 @@ require 'active_job' require 'datadog' Datadog.configure do |c| - c.tracing.instrument :active_job + c.tracing.instrument :active_job, **options end ExampleJob.perform_later ``` +`options` are the following keyword arguments: + +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_ACTIVE_JOB_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | + ### Active Model Serializers The Active Model Serializers integration traces the `serialize` event for version 0.9+ and the `render` event for version 0.10+. @@ -389,13 +409,19 @@ require 'active_model_serializers' require 'datadog' Datadog.configure do |c| - c.tracing.instrument :active_model_serializers + c.tracing.instrument :active_model_serializers, **options end my_object = MyModel.new(name: 'my object') ActiveModelSerializers::SerializableResource.new(test_obj).serializable_hash ``` +`options` are the following keyword arguments: + +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_ACTIVE_MODEL_SERIALIZERS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | + ### Active Record Most of the time, Active Record is set up as part of a web framework (Rails, Sinatra...) however, it can be set up alone: @@ -419,9 +445,10 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -| `service_name` | `String` | Override the service name for the SQL query instrumentation. ActiveRecord instantiation instrumentation always uses the application's configured service name. | Name of database adapter (e.g. `'mysql2'`) | +| Key | Env Var | Type | Description | Default | +| -------------- | - | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | +| `service_name` | | `String` | Override the service name for the SQL query instrumentation. ActiveRecord instantiation instrumentation always uses the application's configured service name. | Name of database adapter (e.g. `'mysql2'`) | +| `enabled` | `DD_TRACE_ACTIVE_RECORD_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring trace settings per database** @@ -507,9 +534,10 @@ cache.read('city') `options` are the following keyword arguments: -| Key | Type | Description | Default | -| --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -| `cache_service` | `String` | Name of application running the `active_support` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `active_support-cache` | +| Key | Env Var | Type | Description | Default | +| --------------- | - | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | +| `cache_service` | | `String` | Name of application running the `active_support` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `active_support-cache` | +| `enabled` | `DD_TRACE_ACTIVE_SUPPORT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### AWS @@ -533,6 +561,7 @@ Aws::S3::Client.new.list_buckets | -------------- | --------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `service_name` | `DD_TRACE_AWS_SERVICE_NAME` | `String` | Name of application running the `aws` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `aws` | | `peer_service` | `DD_TRACE_AWS_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | +| `enabled` | `DD_TRACE_AWS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Concurrent Ruby @@ -544,7 +573,7 @@ To activate your integration, use the `Datadog.configure` method: # Inside Rails initializer or equivalent Datadog.configure do |c| # Patches ::Concurrent::Future to use ExecutorService that propagates context - c.tracing.instrument :concurrent_ruby + c.tracing.instrument :concurrent_ruby, **options end # Pass context into code executed within Concurrent::Future @@ -566,6 +595,12 @@ Datadog::Tracing.trace('outer') do end ``` +`options` are the following keyword arguments: + +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_CONCURRENT_RUBY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | + ### Dalli Dalli integration will trace all calls to your `memcached` server: @@ -591,6 +626,7 @@ client.set('abc', 123) | `command_enabled` | `DD_TRACE_MEMCACHED_COMMAND_ENABLED` | `Bool` | Collect commands as the `memcached.command` tag. Command `keys` can potentially contain sensitive information. | `false` | | `service_name` | `DD_TRACE_DALLI_SERVICE_NAME` | `String` | Name of application running the `dalli` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `memcached` | | `peer_service` | `DD_TRACE_DALLI_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | +| `enabled` | `DD_TRACE_DALLI_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### DelayedJob @@ -608,9 +644,10 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| ---------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `on_error` | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| Key | Env Var | Type | Description | Default | +| ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_DELAYED_JOB_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Elasticsearch @@ -639,6 +676,7 @@ Datadog.configure_onto(client.transport, **options) | `service_name` | `DD_TRACE_ELASTICSEARCH_SERVICE_NAME` | `String` | Name of application running the `elasticsearch` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `elasticsearch` | | `peer_service` | `DD_TRACE_ELASTICSEARCH_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `quantize` | | `Hash` | Hash containing options for quantization. May include `:show` with an Array of keys to not quantize (or `:all` to skip quantization), or `:exclude` with Array of keys to exclude entirely. | `{}` | +| `enabled` | `DD_TRACE_ELASTICSEARCH_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Ethon @@ -666,6 +704,7 @@ end | `peer_service` | `DD_TRACE_ETHON_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | +| `enabled` | `DD_TRACE_ETHON_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Excon @@ -700,6 +739,7 @@ connection.get | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a request raises an error. Provided `span` and `error` as arguments. Sets error on the span by deault. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | | `error_status_codes` | `DD_TRACE_EXCON_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | +| `enabled` | `DD_TRACE_EXCON_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring connections to use different settings** @@ -764,6 +804,7 @@ connection.get('/foo') | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a request raises an error. Provided `span` and `error` as arguments. Sets an error on the span by deault. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | | `error_status_codes` | `DD_TRACE_FARADAY_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | +| `enabled` | `DD_TRACE_FARADAY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Grape @@ -793,7 +834,7 @@ end | Key | Env Var | Type | Description | Default | | -------------------- | ----------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| `enabled` | `DD_TRACE_GRAPE_ENABLED` | `Bool` | Defines whether Grape should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | +| `enabled` | `DD_TRACE_GRAPE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `error_status_codes` | `DD_TRACE_GRAPE_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `500...600` | ### GraphQL @@ -814,11 +855,12 @@ YourSchema.execute(query, variables: {}, context: {}, operation_name: nil) The `instrument :graphql` method accepts the following parameters. Additional options can be substituted in for `options`: -| Key | Type | Description | Default | -| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- | -| `schemas` | `Array` | Array of `GraphQL::Schema` objects (that support class-based schema only) to trace. If you do not provide any, then tracing will applied to all the schemas. | `[]` | -| `with_deprecated_tracer` | `Bool` | Enable to instrument with deprecated `GraphQL::Tracing::DataDogTracing`. Default is `false`, using `GraphQL::Tracing::DataDogTrace` | `false` | -| `service_name` | `String` | Service name used for graphql instrumentation | `'ruby-graphql'` | +| Key | Env Var | Type | Description | Default | +| ------------------------ | - | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- | +| `schemas` | | `Array` | Array of `GraphQL::Schema` objects (that support class-based schema only) to trace. If you do not provide any, then tracing will applied to all the schemas. | `[]` | +| `with_deprecated_tracer` | | `Bool` | Enable to instrument with deprecated `GraphQL::Tracing::DataDogTracing`. Default is `false`, using `GraphQL::Tracing::DataDogTrace` | `false` | +| `service_name` | | `String` | Service name used for graphql instrumentation | `'ruby-graphql'` | +| `enabled` | `DD_TRACE_GRAPHQL_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Manually configuring GraphQL schemas** @@ -877,6 +919,7 @@ client.my_endpoint(DemoMessage.new(contents: 'hello!')) | `peer_service` | `DD_TRACE_GRPC_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `on_error` | | `Proc` | Custom error handler invoked when there is an error. A `Proc` that accepts `span` and `error` parameters. Sets error on the span by default. | `proc { \|span, error \| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_GRPC_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring clients to use different settings** @@ -915,9 +958,10 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| -------------- | -------- | ------------------------------------------ | ------- | -| `service_name` | `String` | Service name for `hanami` instrumentation. | `nil` | +| Key | Env Var | Type | Description | Default | +| -------------- | - | ------- | ------------------------------------------ | ------- | +| `service_name` | | `String` | Service name for `hanami` instrumentation. | `nil` | +| `enabled` | `DD_TRACE_HANAMI_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### http.rb @@ -945,6 +989,7 @@ end | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `error_status_codes` | `DD_TRACE_HTTPRB_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...**600**]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | +| `enabled` | `DD_TRACE_HTTPRB_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### httpclient @@ -972,6 +1017,7 @@ end | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `error_status_codes` | `DD_TRACE_HTTPCLIENT_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | +| `enabled` | `DD_TRACE_HTTPCLIENT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### httpx @@ -1004,10 +1050,16 @@ require 'kafka' require 'datadog' Datadog.configure do |c| - c.tracing.instrument :kafka + c.tracing.instrument :kafka, **options end ``` +`options` are the following keyword arguments: + +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_KAFKA_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | + ### MongoDB The integration traces any `Command` that is sent from the [MongoDB Ruby Driver](https://github.com/mongodb/mongo-ruby-driver) to a MongoDB cluster. By extension, Object Document Mappers (ODM) such as Mongoid are automatically instrumented if they use the official Ruby driver. To activate the integration, simply: @@ -1036,6 +1088,7 @@ Datadog.configure_onto(client, **options) | `service_name` | `DD_TRACE_MONGO_SERVICE_NAME` | `String` | Name of application running the `mongo` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `mongodb` | | `peer_service` | `DD_TRACE_MONGO_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `quantize` | | `Hash` | Hash containing options for quantization. May include `:show` with an Array of keys to not quantize (or `:all` to skip quantization), or `:exclude` with Array of keys to exclude entirely. | `{ show: [:collection, :database, :operation] }` | +| `enabled` | `DD_TRACE_MONGO_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring trace settings per connection** @@ -1092,6 +1145,7 @@ client.query("SELECT * FROM users WHERE group='x'") | `peer_service` | `DD_TRACE_MYSQL2_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `comment_propagation` | `DD_DBM_PROPAGATION_MODE` | `String` | SQL comment propagation mode for database monitoring.
(example: `disabled` \| `service`\| `full`).

**Important**: _Note that enabling SQL comment propagation results in potentially confidential data (service names) being stored in the databases which can then be accessed by other third parties that have been granted access to the database._ | `'disabled'` | | `on_error` | | `Proc` | Custom error handler invoked when MySQL raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring errors that are handled at the application level. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_MYSQL2_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Net/HTTP @@ -1128,6 +1182,7 @@ content = Net::HTTP.get(URI('http://127.0.0.1/index.html')) | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `error_status_codes` | `DD_TRACE_HTTP_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | +| `enabled` | `DD_TRACE_HTTP_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | If you wish to configure each connection object individually, you may use the `Datadog.configure_onto` as it follows: @@ -1164,6 +1219,7 @@ client.cluster.health | `service_name` | `DD_TRACE_OPENSEARCH_SERVICE_NAME` | `String` | Name of application running the `opensearch` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `opensearch` | | `peer_service` | `DD_TRACE_OPENSEARCH_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `quantize` | | `Hash` | Hash containing options for quantization. May include `:show` with an Array of keys to not quantize (or `:all` to skip quantization), or `:exclude` with Array of keys to exclude entirely. | `{}` | +| `enabled` | `DD_TRACE_OPENSEARCH_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Postgres @@ -1191,6 +1247,7 @@ end | `peer_service` | `DD_TRACE_PG_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `comment_propagation` | `DD_DBM_PROPAGATION_MODE` | `String` | SQL comment propagation mode for database monitoring.
(example: `disabled` \| `service`\| `full`).

**Important**: _Note that enabling sql comment propagation results in potentially confidential data (service names) being stored in the databases which can then be accessed by other 3rd parties that have been granted access to the database._ | `'disabled'` | | `on_error` | | `Proc` | Custom error handler invoked when PG raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring errors from Postgres that are handled at the application level. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_PG_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Presto @@ -1223,6 +1280,7 @@ client.run("select * from system.nodes") | -------------- | ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | | `service_name` | `DD_TRACE_PRESTO_SERVICE_NAME` | `String` | Name of application running the `presto` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `presto` | | `peer_service` | `DD_TRACE_PRESTO_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | +| `enabled` | `DD_TRACE_PRESTO_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Que @@ -1246,6 +1304,7 @@ end | `tag_args` | `DD_TRACE_QUE_TAG_ARGS_ENABLED` | `Bool` | Enable tagging of a job's args field. `true` for on, `false` for off. | `false` | | `tag_data` | `DD_TRACE_QUE_TAG_DATA_ENABLED` | `Bool` | Enable tagging of a job's data field. `true` for on, `false` for off. | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error \| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_QUE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Racecar @@ -1266,6 +1325,7 @@ end | Key | Env Var | Type | Description | Default | | -------------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | `service_name` | `DD_TRACE_RACECAR_SERVICE_NAME` | `String` | Name of application running the `racecar` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `racecar` | +| `enabled` | `DD_TRACE_RACECAR_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Rack @@ -1292,23 +1352,24 @@ run app `options` are the following keyword arguments: -| Key | Type | Description | Default | -| -------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------ | -| `application` | ??? | Your Rack application. Required for `middleware_names`. | `nil` | -| `distributed_tracing` | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | -| `headers` | `Hash` | Hash of HTTP request or response headers to add as tags to the `rack.request`. Accepts `request` and `response` keys with Array values e.g. `['Last-Modified']`. Adds `http.request.headers.*` and `http.response.headers.*` tags respectively. This option overrides the global `DD_TRACE_HEADER_TAGS`, see [Applying header tags to root spans][header tags] for more information. | `{ response: ['Content-Type', 'X-Request-ID'] }` | -| `middleware_names` | `Bool` | Enable this if you want to use the last executed middleware class as the resource name for the `rack` span. If enabled alongside the `rails` instrumention, `rails` takes precedence by setting the `rack` resource name to the active `rails` controller when applicable. Requires `application` option to use. | `false` | -| `quantize` | `Hash` | Hash containing options for quantization. May include `:query` or `:fragment`. | `{}` | -| `quantize.base` | | Defines behavior for URL base (scheme, host, port). May be `:show` to keep URL base in `http.url` tag and not set `http.base_url` tag, or `nil` to remove URL base from `http.url` tag by default, leaving a path and setting `http.base_url`. Option must be nested inside the `quantize` option. | `nil` | -| `quantize.query` | | Hash containing options for query portion of URL quantization. May include `:show` or `:exclude`. See options below. Option must be nested inside the `quantize` option. | `{}` | -| `quantize.query.show` | | Defines which values should always be shown. May be an Array of strings, `:all` to show all values, or `nil` to show no values. Option must be nested inside the `query` option. | `nil` | -| `quantize.query.exclude` | | Defines which values should be removed entirely. May be an Array of strings, `:all` to remove the query string entirely, or `nil` to exclude nothing. Option must be nested inside the `query` option. | `nil` | -| `quantize.query.obfuscate` | | Defines query string redaction behaviour. May be a hash of options, `:internal` to use the default internal obfuscation settings, or `nil` to disable obfuscation. Note that obfuscation is a string-wise operation, not a key-value operation. When enabled, `query.show` defaults to `:all` if otherwise unset. Option must be nested inside the `query` option. | `nil` | -| `quantize.query.obfuscate.with` | | Defines the string to replace obfuscated matches with. May be a String. Option must be nested inside the `query.obfuscate` option. | `''` | -| `quantize.query.obfuscate.regex` | | Defines the regex with which the query string will be redacted. May be a Regexp, or `:internal` to use the default internal Regexp, which redacts well-known sensitive data. Each match is redacted entirely by replacing it with `query.obfuscate.with`. Option must be nested inside the `query.obfuscate` option. | `:internal` | -| `quantize.fragment` | | Defines behavior for URL fragments. May be `:show` to show URL fragments, or `nil` to remove fragments. Option must be nested inside the `quantize` option. | `nil` | -| `request_queuing` | `Bool` | Track HTTP request time spent in the queue of the frontend server. See [HTTP request queuing](#http-request-queuing) for setup details. | `false` | -| `web_service_name` | `String` | Service name for frontend server request queuing spans. (e.g. `'nginx'`) | `'web-server'` | +| Key | Env Var | Type | Description | Default | +| -------------------------------- | - | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------ | +| `application` | | [`Rack Application`](https://github.com/rack/rack/blob/800e53fbe15b3424b7a8946b067bf6f2e648d5a8/SPEC.rdoc#label-Rack+applications) | Your Rack application. Required for `middleware_names`. | `nil` | +| `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | +| `headers` | | `Hash` | Hash of HTTP request or response headers to add as tags to the `rack.request`. Accepts `request` and `response` keys with Array values e.g. `['Last-Modified']`. Adds `http.request.headers.*` and `http.response.headers.*` tags respectively. This option overrides the global `DD_TRACE_HEADER_TAGS`, see [Applying header tags to root spans][header tags] for more information. | `{ response: ['Content-Type', 'X-Request-ID'] }` | +| `middleware_names` | | `Bool` | Enable this if you want to use the last executed middleware class as the resource name for the `rack` span. If enabled alongside the `rails` instrumention, `rails` takes precedence by setting the `rack` resource name to the active `rails` controller when applicable. Requires `application` option to use. | `false` | +| `quantize` | | `Hash` | Hash containing options for quantization. May include `:query` or `:fragment`. | `{}` | +| `quantize.base` | | | Defines behavior for URL base (scheme, host, port). May be `:show` to keep URL base in `http.url` tag and not set `http.base_url` tag, or `nil` to remove URL base from `http.url` tag by default, leaving a path and setting `http.base_url`. Option must be nested inside the `quantize` option. | `nil` | +| `quantize.query` | | | Hash containing options for query portion of URL quantization. May include `:show` or `:exclude`. See options below. Option must be nested inside the `quantize` option. | `{}` | +| `quantize.query.show` | | | Defines which values should always be shown. May be an Array of strings, `:all` to show all values, or `nil` to show no values. Option must be nested inside the `query` option. | `nil` | +| `quantize.query.exclude` | | | Defines which values should be removed entirely. May be an Array of strings, `:all` to remove the query string entirely, or `nil` to exclude nothing. Option must be nested inside the `query` option. | `nil` | +| `quantize.query.obfuscate` | | | Defines query string redaction behaviour. May be a hash of options, `:internal` to use the default internal obfuscation settings, or `nil` to disable obfuscation. Note that obfuscation is a string-wise operation, not a key-value operation. When enabled, `query.show` defaults to `:all` if otherwise unset. Option must be nested inside the `query` option. | `nil` | +| `quantize.query.obfuscate.with` | | | Defines the string to replace obfuscated matches with. May be a String. Option must be nested inside the `query.obfuscate` option. | `''` | +| `quantize.query.obfuscate.regex` | | | Defines the regex with which the query string will be redacted. May be a Regexp, or `:internal` to use the default internal Regexp, which redacts well-known sensitive data. Each match is redacted entirely by replacing it with `query.obfuscate.with`. Option must be nested inside the `query.obfuscate` option. | `:internal` | +| `quantize.fragment` | | | Defines behavior for URL fragments. May be `:show` to show URL fragments, or `nil` to remove fragments. Option must be nested inside the `quantize` option. | `nil` | +| `request_queuing` | | `Bool` | Track HTTP request time spent in the queue of the frontend server. See [HTTP request queuing](#http-request-queuing) for setup details. | `false` | +| `web_service_name` | | `String` | Service name for frontend server request queuing spans. (e.g. `'nginx'`) | `'web-server'` | +| `enabled` | `DD_TRACE_RACK_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | Deprecation notice: @@ -1382,14 +1443,15 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| --------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -| `distributed_tracing` | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | -| `request_queuing` | `Bool` | Track HTTP request time spent in the queue of the frontend server. See [HTTP request queuing](#http-request-queuing) for setup details. | `false` | -| `middleware` | `Bool` | Add the trace middleware to the Rails application. Set to `false` if you don't want the middleware to load. | `true` | -| `middleware_names` | `Bool` | Enables any short-circuited middleware requests to display the middleware name as a resource for the trace. | `false` | -| `service_name` | `String` | Service name used when tracing application requests (on the `rack` level) | `''` (inferred from your Rails application namespace) | -| `template_base_path` | `String` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` | +| Key | Env Var | Type | Description | Default | +| --------------------- | - | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | +| `request_queuing` | | `Bool` | Track HTTP request time spent in the queue of the frontend server. See [HTTP request queuing](#http-request-queuing) for setup details. | `false` | +| `middleware` | | `Bool` | Add the trace middleware to the Rails application. Set to `false` if you don't want the middleware to load. | `true` | +| `middleware_names` | | `Bool` | Enables any short-circuited middleware requests to display the middleware name as a resource for the trace. | `false` | +| `service_name` | | `String` | Service name used when tracing application requests (on the `rack` level) | `''` (inferred from your Rails application namespace) | +| `template_base_path` | | `String` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` | +| `enabled` | `DD_TRACE_RAILS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Supported versions** @@ -1429,12 +1491,13 @@ Rake::Task['my_task'].invoke `options` are the following keyword arguments: -| Key | Type | Description | Default | -| -------------- | -------- | -------------------------------------------------------------------------------------------------------- | -------- | -| `enabled` | `Bool` | Defines whether Rake tasks should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | -| `quantize` | `Hash` | Hash containing options for quantization of task arguments. See below for more details and examples. | `{}` | -| `service_name` | `String` | Service name used for `rake` instrumentation | `'rake'` | -| `tasks` | `Array` | Names of the Rake tasks to instrument | `[]` | +| Key | Env Var| Type | Description | Default | +| -------------- | - | ------- | -------------------------------------------------------------------------------------------------------- | -------- | +| `enabled` | | `Bool` | Defines whether Rake tasks should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | +| `quantize` | | `Hash` | Hash containing options for quantization of task arguments. See below for more details and examples. | `{}` | +| `service_name` | | `String` | Service name used for `rake` instrumentation | `'rake'` | +| `tasks` | | `Array` | Names of the Rake tasks to instrument | `[]` | +| `enabled` | `DD_TRACE_RAKE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring task quantization behavior** @@ -1494,6 +1557,7 @@ redis.set 'foo', 'bar' | `service_name` | `DD_TRACE_REDIS_SERVICE_NAME` | `String` | Name of application running the `redis` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `redis` | | `peer_service` | `DD_TRACE_REDIS_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `command_args` | `DD_REDIS_COMMAND_ARGS` | `Bool` | Show the command arguments (for example, `key` in `GET key`) as resource name and tag. If `false`, only the command name is shown (for example, `GET`). | false | +| `enabled` | `DD_TRACE_REDIS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring trace settings per instance** @@ -1604,9 +1668,10 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| ---------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `on_error` | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| Key | Env Var | Type | Description | Default | +| ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_RESQUE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Rest Client @@ -1629,6 +1694,7 @@ end | `peer_service` | `DD_TRACE_REST_CLIENT_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | +| `enabled` | `DD_TRACE_REST_CLIENT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Roda @@ -1659,9 +1725,10 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| -------------- | -------- | ---------------------------------------- | ------- | -| `service_name` | `String` | Service name for `roda` instrumentation. | `nil` | +| Key | Env Var | Type | Description | Default | +| -------------- | - | ------- | ---------------------------------------- | ------- | +| `service_name` | | `String` | Service name for `roda` instrumentation. | `nil` | +| `enabled` | `DD_TRACE_RODA_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sequel @@ -1691,9 +1758,10 @@ articles.all `options` are the following keyword arguments: -| Key | Type | Description | Default | -| -------------- | -------- | ----------------------------------------- | ------------------------------------------ | -| `service_name` | `String` | Service name for `sequel` instrumentation | Name of database adapter (e.g. `'mysql2'`) | +| Key | Env Var | Type | Description | Default | +| -------------- | - | ------- | ----------------------------------------- | ------------------------------------------ | +| `service_name` | | `String` | Service name for `sequel` instrumentation | Name of database adapter (e.g. `'mysql2'`) | +| `enabled` | `DD_TRACE_SEQUEL_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring databases to use different settings** @@ -1724,10 +1792,11 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| ---------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `tag_body` | `Bool` | Tag spans with the SQS message body `true` or `false` | `false` | -| `on_error` | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| Key | Env Var | Type | Description | Default | +| ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `tag_body` | | `Bool` | Tag spans with the SQS message body `true` or `false` | `false` | +| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_SHORYUKEN_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sidekiq @@ -1745,11 +1814,12 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| --------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `distributed_tracing` | `Bool` | Enabling [distributed tracing](#distributed-tracing) creates a parent-child relationship between the `sidekiq.push` span and the `sidekiq.job` span.

**Important**: _Enabling distributed_tracing for asynchronous processing can result in drastic changes in your trace graph. Such cases include long running jobs, retried jobs, and jobs scheduled in the far future. Make sure to inspect your traces after enabling this feature._ | `false` | -| `on_error` | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | -| `quantize` | `Hash` | Hash containing options for quantization of job arguments. | `{}` | +| Key | Env Var | Type | Description | Default | +| --------------------- | - | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `distributed_tracing` | | `Bool` | Enabling [distributed tracing](#distributed-tracing) creates a parent-child relationship between the `sidekiq.push` span and the `sidekiq.job` span.

**Important**: _Enabling distributed_tracing for asynchronous processing can result in drastic changes in your trace graph. Such cases include long running jobs, retried jobs, and jobs scheduled in the far future. Make sure to inspect your traces after enabling this feature._ | `false` | +| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| `quantize` | | `Hash` | Hash containing options for quantization of job arguments. | `{}` | +| `enabled` | `DD_TRACE_SIDEKIQ_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sinatra @@ -1801,11 +1871,12 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| ----------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | -| `distributed_tracing` | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | -| `headers` | `Hash` | Hash of HTTP request or response headers to add as tags to the `sinatra.request`. Accepts `request` and `response` keys with Array values e.g. `['Last-Modified']`. Adds `http.request.headers.*` and `http.response.headers.*` tags respectively. This option overrides the global `DD_TRACE_HEADER_TAGS`, see [Applying header tags to root spans][header tags] for more information. | `{ response: ['Content-Type', 'X-Request-ID'] }` | -| `resource_script_names` | `Bool` | Prepend resource names with script name | `false` | +| Key | Env Var | Type | Description | Default | +| ----------------------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | +| `headers` | | `Hash` | Hash of HTTP request or response headers to add as tags to the `sinatra.request`. Accepts `request` and `response` keys with Array values e.g. `['Last-Modified']`. Adds `http.request.headers.*` and `http.response.headers.*` tags respectively. This option overrides the global `DD_TRACE_HEADER_TAGS`, see [Applying header tags to root spans][header tags] for more information. | `{ response: ['Content-Type', 'X-Request-ID'] }` | +| `resource_script_names` | | `Bool` | Prepend resource names with script name | `false` | +| `enabled` | `DD_TRACE_SINATRA_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sneakers @@ -1823,11 +1894,12 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| ---------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `enabled` | `Bool` | Defines whether Sneakers should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | -| `tag_body` | `Bool` | Enable tagging of job message. `true` for on, `false` for off. | `false` | -| `on_error` | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| Key | Env Var | Type | Description | Default | +| ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `enabled` | | `Bool` | Defines whether Sneakers should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | +| `tag_body` | | `Bool` | Enable tagging of job message. `true` for on, `false` for off. | `false` | +| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | +| `enabled` | `DD_TRACE_SNEAKERS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Stripe @@ -1845,9 +1917,9 @@ end `options` are the following keyword arguments: -| Key | Type | Description | Default | -| --------- | ------ | ---------------------------------------------------------------------------------------------------- | ------- | -| `enabled` | `Bool` | Defines whether Stripe should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_STRIPE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sucker Punch @@ -1857,13 +1929,19 @@ The `sucker_punch` integration traces all scheduled jobs: require 'datadog' Datadog.configure do |c| - c.tracing.instrument :sucker_punch + c.tracing.instrument :sucker_punch, **options end # Execution of this job is traced LogJob.perform_async('login') ``` +`options` are the following keyword arguments: + +| Key | Env Var | Type | Description | Default | +| --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_SUCKER_PUNCH_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | + ### Trilogy The trilogy integration traces any SQL command sent through the `trilogy` gem. @@ -1886,6 +1964,7 @@ client.query("SELECT * FROM users WHERE group='x'") | -------------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | `service_name` | `DD_TRACE_TRILOGY_SERVICE_NAME` | `String` | Name of application running the `trilogy` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `trilogy` | | `peer_service` | `DD_TRACE_TRILOGY_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | +| `enabled` | `DD_TRACE_TRILOGY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ## Additional configuration From 6a658436a4fc40084f15481d029a5bcf26ee6a17 Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Fri, 21 Jun 2024 11:42:25 -0700 Subject: [PATCH 02/32] Remove a few duplicated entries --- docs/GettingStarted.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index e3511dbaa45..92b84e00b29 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -1242,12 +1242,11 @@ end | Key | Env Var | Type | Description | Default | | --------------------- | -------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `enabled` | | Defines whether Postgres should be traced. | `true` | +| `enabled` | `DD_TRACE_PG_ENABLED` | `true` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_PG_SERVICE_NAME` | `String` | Name of application running the `pg` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `pg` | | `peer_service` | `DD_TRACE_PG_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `comment_propagation` | `DD_DBM_PROPAGATION_MODE` | `String` | SQL comment propagation mode for database monitoring.
(example: `disabled` \| `service`\| `full`).

**Important**: _Note that enabling sql comment propagation results in potentially confidential data (service names) being stored in the databases which can then be accessed by other 3rd parties that have been granted access to the database._ | `'disabled'` | | `on_error` | | `Proc` | Custom error handler invoked when PG raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring errors from Postgres that are handled at the application level. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | -| `enabled` | `DD_TRACE_PG_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Presto @@ -1300,11 +1299,10 @@ end | Key | Env Var | Type | Description | Default | | ---------- | ------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| `enabled` | `DD_TRACE_QUE_ENABLED` | `Bool` | Defines whether Que should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | +| `enabled` | `DD_TRACE_QUE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `tag_args` | `DD_TRACE_QUE_TAG_ARGS_ENABLED` | `Bool` | Enable tagging of a job's args field. `true` for on, `false` for off. | `false` | | `tag_data` | `DD_TRACE_QUE_TAG_DATA_ENABLED` | `Bool` | Enable tagging of a job's data field. `true` for on, `false` for off. | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error \| span.set_error(error) unless span.nil? }` | -| `enabled` | `DD_TRACE_QUE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Racecar @@ -1896,10 +1894,9 @@ end | Key | Env Var | Type | Description | Default | | ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `enabled` | | `Bool` | Defines whether Sneakers should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` | +| `enabled` | `DD_TRACE_SNEAKERS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `tag_body` | | `Bool` | Enable tagging of job message. `true` for on, `false` for off. | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | -| `enabled` | `DD_TRACE_SNEAKERS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Stripe From f683d5e8690254421b1d7c0e0c448c26d3d5305b Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Fri, 21 Jun 2024 11:43:31 -0700 Subject: [PATCH 03/32] Fix Concurrent Ruby description --- docs/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 92b84e00b29..a5294d96265 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -599,7 +599,7 @@ end | Key | Env Var | Type | Description | Default | | --------- | ------------------------------- | ------ | -------------------------------------------- | ------- | -| `enabled` | `DD_TRACE_CONCURRENT_RUBY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `enabled` | `DD_TRACE_CONCURRENT_RUBY_ENABLED` | `Bool` | Whether the integration propagates contexts. | `true` | ### Dalli From 8d3d303a45850fa2a84590ca8b490e9e7dc6635b Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Fri, 21 Jun 2024 11:59:23 -0700 Subject: [PATCH 04/32] Move enabled to the top of table --- docs/GettingStarted.md | 66 +++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index a5294d96265..09033778b31 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -337,8 +337,8 @@ end | Key | Env Var | Type | Description | Default | | ------------ | - | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| `email_data` | | `Bool` | Whether or not to append additional email payload metadata to `action_mailer.deliver` spans. Fields include `['subject', 'to', 'from', 'bcc', 'cc', 'date', 'perform_deliveries']`. | `false` | | `enabled` | `DD_TRACE_ACTION_MAILER_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `email_data` | | `Bool` | Whether or not to append additional email payload metadata to `action_mailer.deliver` spans. Fields include `['subject', 'to', 'from', 'bcc', 'cc', 'date', 'perform_deliveries']`. | `false` | ### Action Pack @@ -376,8 +376,8 @@ end | Key | Env Var | Type | Description | Default | | -------------------- | - | ------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| `template_base_path` | | `String` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` | | `enabled` | `DD_TRACE_ACTION_VIEW_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `template_base_path` | | `String` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` | ### Active Job @@ -447,8 +447,8 @@ end | Key | Env Var | Type | Description | Default | | -------------- | - | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -| `service_name` | | `String` | Override the service name for the SQL query instrumentation. ActiveRecord instantiation instrumentation always uses the application's configured service name. | Name of database adapter (e.g. `'mysql2'`) | | `enabled` | `DD_TRACE_ACTIVE_RECORD_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `service_name` | | `String` | Override the service name for the SQL query instrumentation. ActiveRecord instantiation instrumentation always uses the application's configured service name. | Name of database adapter (e.g. `'mysql2'`) | **Configuring trace settings per database** @@ -536,8 +536,8 @@ cache.read('city') | Key | Env Var | Type | Description | Default | | --------------- | - | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -| `cache_service` | | `String` | Name of application running the `active_support` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `active_support-cache` | | `enabled` | `DD_TRACE_ACTIVE_SUPPORT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `cache_service` | | `String` | Name of application running the `active_support` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `active_support-cache` | ### AWS @@ -559,9 +559,9 @@ Aws::S3::Client.new.list_buckets | Key | Env Var | Type | Description | Default | | -------------- | --------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_AWS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_AWS_SERVICE_NAME` | `String` | Name of application running the `aws` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `aws` | | `peer_service` | `DD_TRACE_AWS_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | -| `enabled` | `DD_TRACE_AWS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Concurrent Ruby @@ -623,10 +623,10 @@ client.set('abc', 123) | Key | Env Var | Type | Description | Default | | ----------------- | ------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `enabled` | `DD_TRACE_DALLI_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `command_enabled` | `DD_TRACE_MEMCACHED_COMMAND_ENABLED` | `Bool` | Collect commands as the `memcached.command` tag. Command `keys` can potentially contain sensitive information. | `false` | | `service_name` | `DD_TRACE_DALLI_SERVICE_NAME` | `String` | Name of application running the `dalli` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `memcached` | | `peer_service` | `DD_TRACE_DALLI_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | -| `enabled` | `DD_TRACE_DALLI_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### DelayedJob @@ -646,8 +646,8 @@ end | Key | Env Var | Type | Description | Default | | ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | | `enabled` | `DD_TRACE_DELAYED_JOB_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | ### Elasticsearch @@ -673,10 +673,10 @@ Datadog.configure_onto(client.transport, **options) | Key | Env Var | Type | Description | Default | | -------------- | ------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `enabled` | `DD_TRACE_ELASTICSEARCH_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_ELASTICSEARCH_SERVICE_NAME` | `String` | Name of application running the `elasticsearch` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `elasticsearch` | | `peer_service` | `DD_TRACE_ELASTICSEARCH_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `quantize` | | `Hash` | Hash containing options for quantization. May include `:show` with an Array of keys to not quantize (or `:all` to skip quantization), or `:exclude` with Array of keys to exclude entirely. | `{}` | -| `enabled` | `DD_TRACE_ELASTICSEARCH_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Ethon @@ -700,11 +700,11 @@ end | Key | Env Var | Type | Description | Default | | --------------------- | ----------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_ETHON_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_ETHON_SERVICE_NAME` | `String` | Name of application running the `ethon` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `ethon` | | `peer_service` | `DD_TRACE_ETHON_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | -| `enabled` | `DD_TRACE_ETHON_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Excon @@ -733,13 +733,13 @@ connection.get | Key | Env Var | Type | Description | Default | | --------------------- | ----------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `enabled` | `DD_TRACE_EXCON_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_EXCON_SERVICE_NAME` | `String` | Name of application running the `excon` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `excon` | | `peer_service` | `DD_TRACE_EXCON_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a request raises an error. Provided `span` and `error` as arguments. Sets error on the span by deault. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | | `error_status_codes` | `DD_TRACE_EXCON_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | -| `enabled` | `DD_TRACE_EXCON_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring connections to use different settings** @@ -798,13 +798,13 @@ connection.get('/foo') | Key | Env Var | Type | Description | Default | | --------------------- | ------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `enabled` | `DD_TRACE_FARADAY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_FARADAY_SERVICE_NAME` | `String` | Name of application running the `faraday` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `faraday` | | `peer_service` | `DD_TRACE_FARADAY_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a request raises an error. Provided `span` and `error` as arguments. Sets an error on the span by deault. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | | `error_status_codes` | `DD_TRACE_FARADAY_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | -| `enabled` | `DD_TRACE_FARADAY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Grape @@ -857,10 +857,10 @@ The `instrument :graphql` method accepts the following parameters. Additional op | Key | Env Var | Type | Description | Default | | ------------------------ | - | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- | +| `enabled` | `DD_TRACE_GRAPHQL_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `schemas` | | `Array` | Array of `GraphQL::Schema` objects (that support class-based schema only) to trace. If you do not provide any, then tracing will applied to all the schemas. | `[]` | | `with_deprecated_tracer` | | `Bool` | Enable to instrument with deprecated `GraphQL::Tracing::DataDogTracing`. Default is `false`, using `GraphQL::Tracing::DataDogTrace` | `false` | | `service_name` | | `String` | Service name used for graphql instrumentation | `'ruby-graphql'` | -| `enabled` | `DD_TRACE_GRAPHQL_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Manually configuring GraphQL schemas** @@ -915,11 +915,11 @@ client.my_endpoint(DemoMessage.new(contents: 'hello!')) | Key | Env Var | Type | Description | Default | | --------------------- | ---------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| `enabled` | `DD_TRACE_GRPC_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_GRPC_SERVICE_NAME` | `String` | Name of application running the `grpc` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `grpc` | | `peer_service` | `DD_TRACE_GRPC_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `on_error` | | `Proc` | Custom error handler invoked when there is an error. A `Proc` that accepts `span` and `error` parameters. Sets error on the span by default. | `proc { \|span, error \| span.set_error(error) unless span.nil? }` | -| `enabled` | `DD_TRACE_GRPC_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring clients to use different settings** @@ -960,8 +960,8 @@ end | Key | Env Var | Type | Description | Default | | -------------- | - | ------- | ------------------------------------------ | ------- | -| `service_name` | | `String` | Service name for `hanami` instrumentation. | `nil` | | `enabled` | `DD_TRACE_HANAMI_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `service_name` | | `String` | Service name for `hanami` instrumentation. | `nil` | ### http.rb @@ -984,12 +984,12 @@ end | Key | Env Var | Type | Description | Default | | --------------------- | ------------------------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `enabled` | `DD_TRACE_HTTPRB_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_HTTPRB_SERVICE_NAME` | `String` | Name of application running the `httprb` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `httprb` | | `peer_service` | `DD_TRACE_HTTPRB_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `error_status_codes` | `DD_TRACE_HTTPRB_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...**600**]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | -| `enabled` | `DD_TRACE_HTTPRB_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### httpclient @@ -1012,12 +1012,12 @@ end | Key | Env Var | Type | Description | Default | | --------------------- | ---------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| `enabled` | `DD_TRACE_HTTPCLIENT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_HTTPCLIENT_SERVICE_NAME` | `String` | Name of application running the `httpclient` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `httpclient` | | `peer_service` | `DD_TRACE_HTTPCLIENT_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `error_status_codes` | `DD_TRACE_HTTPCLIENT_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | -| `enabled` | `DD_TRACE_HTTPCLIENT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### httpx @@ -1085,10 +1085,10 @@ Datadog.configure_onto(client, **options) | Key | Env Var | Type | Description | Default | | -------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `enabled` | `DD_TRACE_MONGO_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_MONGO_SERVICE_NAME` | `String` | Name of application running the `mongo` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `mongodb` | | `peer_service` | `DD_TRACE_MONGO_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `quantize` | | `Hash` | Hash containing options for quantization. May include `:show` with an Array of keys to not quantize (or `:all` to skip quantization), or `:exclude` with Array of keys to exclude entirely. | `{ show: [:collection, :database, :operation] }` | -| `enabled` | `DD_TRACE_MONGO_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring trace settings per connection** @@ -1141,11 +1141,11 @@ client.query("SELECT * FROM users WHERE group='x'") | Key | Env Var | Type | Description | Default | | --------------------- | ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | +| `enabled` | `DD_TRACE_MYSQL2_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_MYSQL2_SERVICE_NAME` | `String` | Name of application running the `mysql2` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `mysql2` | | `peer_service` | `DD_TRACE_MYSQL2_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `comment_propagation` | `DD_DBM_PROPAGATION_MODE` | `String` | SQL comment propagation mode for database monitoring.
(example: `disabled` \| `service`\| `full`).

**Important**: _Note that enabling SQL comment propagation results in potentially confidential data (service names) being stored in the databases which can then be accessed by other third parties that have been granted access to the database._ | `'disabled'` | | `on_error` | | `Proc` | Custom error handler invoked when MySQL raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring errors that are handled at the application level. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | -| `enabled` | `DD_TRACE_MYSQL2_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Net/HTTP @@ -1177,12 +1177,12 @@ content = Net::HTTP.get(URI('http://127.0.0.1/index.html')) | Key | Env Var | Type | Description | Default | | --------------------- | ---------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `enabled` | `DD_TRACE_HTTP_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_NET_HTTP_SERVICE_NAME` | `String` | Name of application running the `net/http` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `net/http` | | `peer_service` | `DD_TRACE_NET_HTTP_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | | `error_status_codes` | `DD_TRACE_HTTP_ERROR_STATUS_CODES` | `Array`\|`Range` | Defines HTTP status codes that are traced as errors. Value can be a range (`400...600`), or an array of ranges/integers `[403, 500...600]`. If configured with environment variable, use dash for range (`'400-599'`) and comma for adding element into an array (`'403,500-599'`) | `400...600` | -| `enabled` | `DD_TRACE_HTTP_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | If you wish to configure each connection object individually, you may use the `Datadog.configure_onto` as it follows: @@ -1216,10 +1216,10 @@ client.cluster.health | Key | Env Var | Type | Description | Default | | -------------- | ---------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| `enabled` | `DD_TRACE_OPENSEARCH_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_OPENSEARCH_SERVICE_NAME` | `String` | Name of application running the `opensearch` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `opensearch` | | `peer_service` | `DD_TRACE_OPENSEARCH_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `quantize` | | `Hash` | Hash containing options for quantization. May include `:show` with an Array of keys to not quantize (or `:all` to skip quantization), or `:exclude` with Array of keys to exclude entirely. | `{}` | -| `enabled` | `DD_TRACE_OPENSEARCH_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Postgres @@ -1277,9 +1277,9 @@ client.run("select * from system.nodes") | Key | Env Var | Type | Description | Default | | -------------- | ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | +| `enabled` | `DD_TRACE_PRESTO_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_PRESTO_SERVICE_NAME` | `String` | Name of application running the `presto` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `presto` | | `peer_service` | `DD_TRACE_PRESTO_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | -| `enabled` | `DD_TRACE_PRESTO_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Que @@ -1322,8 +1322,8 @@ end | Key | Env Var | Type | Description | Default | | -------------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `service_name` | `DD_TRACE_RACECAR_SERVICE_NAME` | `String` | Name of application running the `racecar` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `racecar` | | `enabled` | `DD_TRACE_RACECAR_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `service_name` | `DD_TRACE_RACECAR_SERVICE_NAME` | `String` | Name of application running the `racecar` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `racecar` | ### Rack @@ -1352,6 +1352,7 @@ run app | Key | Env Var | Type | Description | Default | | -------------------------------- | - | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------ | +| `enabled` | `DD_TRACE_RACK_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `application` | | [`Rack Application`](https://github.com/rack/rack/blob/800e53fbe15b3424b7a8946b067bf6f2e648d5a8/SPEC.rdoc#label-Rack+applications) | Your Rack application. Required for `middleware_names`. | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | | `headers` | | `Hash` | Hash of HTTP request or response headers to add as tags to the `rack.request`. Accepts `request` and `response` keys with Array values e.g. `['Last-Modified']`. Adds `http.request.headers.*` and `http.response.headers.*` tags respectively. This option overrides the global `DD_TRACE_HEADER_TAGS`, see [Applying header tags to root spans][header tags] for more information. | `{ response: ['Content-Type', 'X-Request-ID'] }` | @@ -1367,7 +1368,6 @@ run app | `quantize.fragment` | | | Defines behavior for URL fragments. May be `:show` to show URL fragments, or `nil` to remove fragments. Option must be nested inside the `quantize` option. | `nil` | | `request_queuing` | | `Bool` | Track HTTP request time spent in the queue of the frontend server. See [HTTP request queuing](#http-request-queuing) for setup details. | `false` | | `web_service_name` | | `String` | Service name for frontend server request queuing spans. (e.g. `'nginx'`) | `'web-server'` | -| `enabled` | `DD_TRACE_RACK_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | Deprecation notice: @@ -1443,13 +1443,13 @@ end | Key | Env Var | Type | Description | Default | | --------------------- | - | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| `enabled` | `DD_TRACE_RAILS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | | `request_queuing` | | `Bool` | Track HTTP request time spent in the queue of the frontend server. See [HTTP request queuing](#http-request-queuing) for setup details. | `false` | | `middleware` | | `Bool` | Add the trace middleware to the Rails application. Set to `false` if you don't want the middleware to load. | `true` | | `middleware_names` | | `Bool` | Enables any short-circuited middleware requests to display the middleware name as a resource for the trace. | `false` | | `service_name` | | `String` | Service name used when tracing application requests (on the `rack` level) | `''` (inferred from your Rails application namespace) | | `template_base_path` | | `String` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` | -| `enabled` | `DD_TRACE_RAILS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Supported versions** @@ -1552,10 +1552,10 @@ redis.set 'foo', 'bar' | Key | Env Var | Type | Description | Default | | -------------- | ----------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `enabled` | `DD_TRACE_REDIS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_REDIS_SERVICE_NAME` | `String` | Name of application running the `redis` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `redis` | | `peer_service` | `DD_TRACE_REDIS_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `command_args` | `DD_REDIS_COMMAND_ARGS` | `Bool` | Show the command arguments (for example, `key` in `GET key`) as resource name and tag. If `false`, only the command name is shown (for example, `GET`). | false | -| `enabled` | `DD_TRACE_REDIS_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | **Configuring trace settings per instance** @@ -1668,8 +1668,8 @@ end | Key | Env Var | Type | Description | Default | | ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | | `enabled` | `DD_TRACE_RESQUE_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | ### Rest Client @@ -1688,11 +1688,11 @@ end | Key | Env Var | Type | Description | Default | | --------------------- | ----------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `enabled` | `DD_TRACE_REST_CLIENT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_REST_CLIENT_SERVICE_NAME` | `String` | Name of application running the `rest_client` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `rest_client` | | `peer_service` | `DD_TRACE_REST_CLIENT_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) | `true` | | `split_by_domain` | | `Bool` | Uses the request domain as the service name when set to `true`. | `false` | -| `enabled` | `DD_TRACE_REST_CLIENT_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Roda @@ -1725,8 +1725,8 @@ end | Key | Env Var | Type | Description | Default | | -------------- | - | ------- | ---------------------------------------- | ------- | -| `service_name` | | `String` | Service name for `roda` instrumentation. | `nil` | | `enabled` | `DD_TRACE_RODA_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `service_name` | | `String` | Service name for `roda` instrumentation. | `nil` | ### Sequel @@ -1758,8 +1758,8 @@ articles.all | Key | Env Var | Type | Description | Default | | -------------- | - | ------- | ----------------------------------------- | ------------------------------------------ | -| `service_name` | | `String` | Service name for `sequel` instrumentation | Name of database adapter (e.g. `'mysql2'`) | | `enabled` | `DD_TRACE_SEQUEL_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | +| `service_name` | | `String` | Service name for `sequel` instrumentation | Name of database adapter (e.g. `'mysql2'`) | **Configuring databases to use different settings** @@ -1792,9 +1792,9 @@ end | Key | Env Var | Type | Description | Default | | ---------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `enabled` | `DD_TRACE_SHORYUKEN_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `tag_body` | | `Bool` | Tag spans with the SQS message body `true` or `false` | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | -| `enabled` | `DD_TRACE_SHORYUKEN_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sidekiq @@ -1814,10 +1814,10 @@ end | Key | Env Var | Type | Description | Default | | --------------------- | - | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `enabled` | `DD_TRACE_SIDEKIQ_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `distributed_tracing` | | `Bool` | Enabling [distributed tracing](#distributed-tracing) creates a parent-child relationship between the `sidekiq.push` span and the `sidekiq.job` span.

**Important**: _Enabling distributed_tracing for asynchronous processing can result in drastic changes in your trace graph. Such cases include long running jobs, retried jobs, and jobs scheduled in the far future. Make sure to inspect your traces after enabling this feature._ | `false` | | `on_error` | | `Proc` | Custom error handler invoked when a job raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. Useful for ignoring transient errors. | `proc { \|span, error\| span.set_error(error) unless span.nil? }` | | `quantize` | | `Hash` | Hash containing options for quantization of job arguments. | `{}` | -| `enabled` | `DD_TRACE_SIDEKIQ_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sinatra @@ -1871,10 +1871,10 @@ end | Key | Env Var | Type | Description | Default | | ----------------------- | - | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `enabled` | `DD_TRACE_SINATRA_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `distributed_tracing` | | `Bool` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `true` | | `headers` | | `Hash` | Hash of HTTP request or response headers to add as tags to the `sinatra.request`. Accepts `request` and `response` keys with Array values e.g. `['Last-Modified']`. Adds `http.request.headers.*` and `http.response.headers.*` tags respectively. This option overrides the global `DD_TRACE_HEADER_TAGS`, see [Applying header tags to root spans][header tags] for more information. | `{ response: ['Content-Type', 'X-Request-ID'] }` | | `resource_script_names` | | `Bool` | Prepend resource names with script name | `false` | -| `enabled` | `DD_TRACE_SINATRA_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ### Sneakers @@ -1959,9 +1959,9 @@ client.query("SELECT * FROM users WHERE group='x'") | Key | Env Var | Type | Description | Default | | -------------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| `enabled` | `DD_TRACE_TRILOGY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `service_name` | `DD_TRACE_TRILOGY_SERVICE_NAME` | `String` | Name of application running the `trilogy` instrumentation. May be overridden by `global_default_service_name`. [See _Additional Configuration_ for more details](#additional-configuration) | `trilogy` | | `peer_service` | `DD_TRACE_TRILOGY_PEER_SERVICE` | `String` | Name of external service the application connects to | `nil` | -| `enabled` | `DD_TRACE_TRILOGY_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | ## Additional configuration From 3db289f1af971740b02b31d3a23579bee71ddaa1 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Wed, 12 Jun 2024 16:42:36 +0200 Subject: [PATCH 05/32] rename Telemetry::Heartbeat to Telemetry::Worker --- lib/datadog/core/telemetry/client.rb | 4 ++-- .../core/telemetry/{heartbeat.rb => worker.rb} | 2 +- sig/datadog/core/telemetry/client.rbs | 2 +- sig/datadog/core/telemetry/http/ext.rbs | 2 +- .../telemetry/{heartbeat.rbs => worker.rbs} | 2 +- spec/datadog/core/telemetry/client_spec.rb | 4 ++-- .../{heartbeat_spec.rb => worker_spec.rb} | 18 +++++++++--------- 7 files changed, 17 insertions(+), 17 deletions(-) rename lib/datadog/core/telemetry/{heartbeat.rb => worker.rb} (95%) rename sig/datadog/core/telemetry/{heartbeat.rbs => worker.rbs} (92%) rename spec/datadog/core/telemetry/{heartbeat_spec.rb => worker_spec.rb} (63%) diff --git a/lib/datadog/core/telemetry/client.rb b/lib/datadog/core/telemetry/client.rb index 172145c9342..db5c01673ee 100644 --- a/lib/datadog/core/telemetry/client.rb +++ b/lib/datadog/core/telemetry/client.rb @@ -2,7 +2,7 @@ require_relative 'emitter' require_relative 'event' -require_relative 'heartbeat' +require_relative 'worker' require_relative '../utils/forking' module Datadog @@ -27,7 +27,7 @@ def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: tru @started = false @dependency_collection = dependency_collection - @worker = Telemetry::Heartbeat.new(enabled: @enabled, heartbeat_interval_seconds: heartbeat_interval_seconds) do + @worker = Telemetry::Worker.new(enabled: @enabled, heartbeat_interval_seconds: heartbeat_interval_seconds) do next unless @started # `started!` should be the first event, thus ensure that `heartbeat!` is not sent first. heartbeat! diff --git a/lib/datadog/core/telemetry/heartbeat.rb b/lib/datadog/core/telemetry/worker.rb similarity index 95% rename from lib/datadog/core/telemetry/heartbeat.rb rename to lib/datadog/core/telemetry/worker.rb index b2129504e68..8e0f106d0bd 100644 --- a/lib/datadog/core/telemetry/heartbeat.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -7,7 +7,7 @@ module Datadog module Core module Telemetry # Periodically sends a heartbeat event to the telemetry API. - class Heartbeat < Core::Worker + class Worker < Core::Worker include Core::Workers::Polling def initialize(heartbeat_interval_seconds:, enabled: true, &block) diff --git a/sig/datadog/core/telemetry/client.rbs b/sig/datadog/core/telemetry/client.rbs index 9bd2f4a97cc..79aa3850b1c 100644 --- a/sig/datadog/core/telemetry/client.rbs +++ b/sig/datadog/core/telemetry/client.rbs @@ -8,7 +8,7 @@ module Datadog @stopped: bool @emitter: Datadog::Core::Telemetry::Emitter @unsupported: bool - @worker: Datadog::Core::Telemetry::Heartbeat + @worker: Datadog::Core::Telemetry::Worker attr_reader enabled: bool diff --git a/sig/datadog/core/telemetry/http/ext.rbs b/sig/datadog/core/telemetry/http/ext.rbs index 22cea7d1fd0..11271822da3 100644 --- a/sig/datadog/core/telemetry/http/ext.rbs +++ b/sig/datadog/core/telemetry/http/ext.rbs @@ -21,7 +21,7 @@ module Datadog CONTENT_TYPE_APPLICATION_JSON: "application/json" - API_VERSION: "v1" + API_VERSION: "v2" AGENT_ENDPOINT: "/telemetry/proxy/api/v2/apmtelemetry" end diff --git a/sig/datadog/core/telemetry/heartbeat.rbs b/sig/datadog/core/telemetry/worker.rbs similarity index 92% rename from sig/datadog/core/telemetry/heartbeat.rbs rename to sig/datadog/core/telemetry/worker.rbs index b89aeedca8b..779cbd7aa6c 100644 --- a/sig/datadog/core/telemetry/heartbeat.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -1,7 +1,7 @@ module Datadog module Core module Telemetry - class Heartbeat < Core::Worker + class Worker < Core::Worker include Core::Workers::Polling include Core::Workers::Async::Thread include Core::Workers::Async::Thread::PrependedMethods diff --git a/spec/datadog/core/telemetry/client_spec.rb b/spec/datadog/core/telemetry/client_spec.rb index 5618d4ea65e..ebd59516a42 100644 --- a/spec/datadog/core/telemetry/client_spec.rb +++ b/spec/datadog/core/telemetry/client_spec.rb @@ -213,10 +213,10 @@ describe '#stop!' do subject(:stop!) { client.stop! } - let(:worker) { instance_double(Datadog::Core::Telemetry::Heartbeat) } + let(:worker) { instance_double(Datadog::Core::Telemetry::Worker) } before do - allow(Datadog::Core::Telemetry::Heartbeat).to receive(:new) + allow(Datadog::Core::Telemetry::Worker).to receive(:new) .with(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds).and_return(worker) allow(worker).to receive(:start) allow(worker).to receive(:stop) diff --git a/spec/datadog/core/telemetry/heartbeat_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb similarity index 63% rename from spec/datadog/core/telemetry/heartbeat_spec.rb rename to spec/datadog/core/telemetry/worker_spec.rb index 645120267e0..532e2873375 100644 --- a/spec/datadog/core/telemetry/heartbeat_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -require 'datadog/core/telemetry/heartbeat' +require 'datadog/core/telemetry/worker' -RSpec.describe Datadog::Core::Telemetry::Heartbeat do - subject(:heartbeat) do +RSpec.describe Datadog::Core::Telemetry::Worker do + subject(:worker) do described_class.new(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, &block) end @@ -12,13 +12,13 @@ let(:block) { proc {} } after do - heartbeat.stop(true, 0) - heartbeat.join + worker.stop(true, 0) + worker.join end describe '.new' do context 'when using default settings' do - subject(:heartbeat) { described_class.new(heartbeat_interval_seconds: heartbeat_interval_seconds, &block) } + subject(:worker) { described_class.new(heartbeat_interval_seconds: heartbeat_interval_seconds, &block) } it do is_expected.to have_attributes( enabled?: true, @@ -32,10 +32,10 @@ let(:enabled) { true } it do - heartbeat + worker - try_wait_until { heartbeat.running? } - expect(heartbeat).to have_attributes( + try_wait_until { worker.running? } + expect(worker).to have_attributes( run_async?: true, running?: true, started?: true From b1061efa78d28be2f799c25224033232fbbb55bf Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Thu, 13 Jun 2024 13:33:40 +0200 Subject: [PATCH 06/32] Add queue to telemetry worker. Move sending heartbeat logic to the telemetry worker. --- lib/datadog/core/telemetry/client.rb | 20 ++++----- lib/datadog/core/telemetry/worker.rb | 31 +++++++++----- sig/datadog/core/telemetry/worker.rbs | 11 +++-- spec/datadog/core/telemetry/client_spec.rb | 3 +- spec/datadog/core/telemetry/worker_spec.rb | 47 +++++++++++++++------- 5 files changed, 70 insertions(+), 42 deletions(-) diff --git a/lib/datadog/core/telemetry/client.rb b/lib/datadog/core/telemetry/client.rb index db5c01673ee..eeeae58556e 100644 --- a/lib/datadog/core/telemetry/client.rb +++ b/lib/datadog/core/telemetry/client.rb @@ -27,11 +27,11 @@ def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: tru @started = false @dependency_collection = dependency_collection - @worker = Telemetry::Worker.new(enabled: @enabled, heartbeat_interval_seconds: heartbeat_interval_seconds) do - next unless @started # `started!` should be the first event, thus ensure that `heartbeat!` is not sent first. - - heartbeat! - end + @worker = Telemetry::Worker.new( + enabled: @enabled, + heartbeat_interval_seconds: heartbeat_interval_seconds, + emitter: @emitter + ) end def disable! @@ -51,6 +51,8 @@ def started! return res end + @worker.start + @emitter.request(Event::AppDependenciesLoaded.new) if @dependency_collection @started = true @@ -81,14 +83,6 @@ def client_configuration_change!(changes) @emitter.request(Event::AppClientConfigurationChange.new(changes, 'remote_config')) end - - private - - def heartbeat! - return if !@enabled || forked? - - @emitter.request(Event::AppHeartbeat.new) - end end end end diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 8e0f106d0bd..5683f2028b7 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -1,31 +1,44 @@ # frozen_string_literal: true -require_relative '../worker' +require_relative 'event' + require_relative '../workers/polling' +require_relative '../workers/queue' module Datadog module Core module Telemetry - # Periodically sends a heartbeat event to the telemetry API. - class Worker < Core::Worker + # Accumulates events and sends them to the API at a regular interval, including heartbeat event. + class Worker + include Core::Workers::Queue include Core::Workers::Polling - def initialize(heartbeat_interval_seconds:, enabled: true, &block) + def initialize(heartbeat_interval_seconds:, emitter:, enabled: true) + @emitter = emitter + # Workers::Polling settings self.enabled = enabled # Workers::IntervalLoop settings self.loop_base_interval = heartbeat_interval_seconds self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_STOP - super(&block) - start end - def loop_wait_before_first_iteration?; end + def start + return if !enabled? || forked? + + perform + end private - def start - perform + def perform(*_events) + return if !enabled? || forked? + + heartbeat! + end + + def heartbeat! + @emitter.request(Event::AppHeartbeat.new) end end end diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index 779cbd7aa6c..0fa008ebca9 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -1,19 +1,22 @@ module Datadog module Core module Telemetry - class Worker < Core::Worker + class Worker include Core::Workers::Polling include Core::Workers::Async::Thread include Core::Workers::Async::Thread::PrependedMethods include Core::Workers::IntervalLoop + include Core::Workers::Queue - def initialize: (?enabled: bool, heartbeat_interval_seconds: Numeric) ?{ () -> void } -> void + @emitter: Emitter - def loop_wait_before_first_iteration?: () -> bool? + def initialize: (?enabled: bool, heartbeat_interval_seconds: Numeric, emitter: Emitter) -> void + + def start: () -> void private - def start: () -> void + def heartbeat!: () -> void end end end diff --git a/spec/datadog/core/telemetry/client_spec.rb b/spec/datadog/core/telemetry/client_spec.rb index ebd59516a42..c8a89f6264f 100644 --- a/spec/datadog/core/telemetry/client_spec.rb +++ b/spec/datadog/core/telemetry/client_spec.rb @@ -217,7 +217,8 @@ before do allow(Datadog::Core::Telemetry::Worker).to receive(:new) - .with(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds).and_return(worker) + .with(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, emitter: emitter) + .and_return(worker) allow(worker).to receive(:start) allow(worker).to receive(:stop) end diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 532e2873375..0a874112993 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -4,12 +4,16 @@ RSpec.describe Datadog::Core::Telemetry::Worker do subject(:worker) do - described_class.new(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, &block) + described_class.new(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, emitter: emitter) end let(:enabled) { true } let(:heartbeat_interval_seconds) { 1.2 } - let(:block) { proc {} } + let(:emitter) { double(Datadog::Core::Telemetry::Emitter) } + + before do + allow(emitter).to receive(:request) + end after do worker.stop(true, 0) @@ -17,29 +21,42 @@ end describe '.new' do - context 'when using default settings' do - subject(:worker) { described_class.new(heartbeat_interval_seconds: heartbeat_interval_seconds, &block) } - it do - is_expected.to have_attributes( - enabled?: true, - loop_base_interval: 1.2, # seconds - task: block - ) - end + it 'creates a new worker in stopped state' do + expect(worker).to have_attributes( + enabled?: true, + loop_base_interval: 1.2, # seconds + run_async?: false, + running?: false, + started?: false + ) end + end + describe '#start' do context 'when enabled' do - let(:enabled) { true } - - it do - worker + it 'starts the worker and sends heartbeat event' do + worker.start try_wait_until { worker.running? } + expect(worker).to have_attributes( + enabled?: true, + loop_base_interval: 1.2, # seconds run_async?: true, running?: true, started?: true ) + expect(emitter).to have_received(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) + end + end + + context 'when disabled' do + let(:enabled) { false } + + it 'does not start the worker' do + expect(worker).not_to receive(:perform) + + worker.start end end end From d4350cab60dbe80d0959bb741b12d2d0e97b83b3 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Thu, 13 Jun 2024 15:07:16 +0200 Subject: [PATCH 07/32] move AppStarted telemetry event out of the critical path --- lib/datadog/core/telemetry/client.rb | 14 +--- lib/datadog/core/telemetry/worker.rb | 21 ++++++ lib/datadog/core/workers/polling.rb | 1 + sig/datadog/core/telemetry/client.rbs | 7 -- sig/datadog/core/telemetry/worker.rbs | 5 ++ spec/datadog/core/telemetry/client_spec.rb | 55 +------------- spec/datadog/core/telemetry/worker_spec.rb | 86 ++++++++++++++++++---- 7 files changed, 103 insertions(+), 86 deletions(-) diff --git a/lib/datadog/core/telemetry/client.rb b/lib/datadog/core/telemetry/client.rb index eeeae58556e..07028e96ad5 100644 --- a/lib/datadog/core/telemetry/client.rb +++ b/lib/datadog/core/telemetry/client.rb @@ -10,9 +10,7 @@ module Core module Telemetry # Telemetry entrypoint, coordinates sending telemetry events at various points in app lifecycle. class Client - attr_reader \ - :enabled, - :unsupported + attr_reader :enabled include Core::Utils::Forking @@ -23,7 +21,6 @@ def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: tru @enabled = enabled @emitter = Emitter.new @stopped = false - @unsupported = false @started = false @dependency_collection = dependency_collection @@ -42,15 +39,6 @@ def disable! def started! return if !@enabled || forked? - res = @emitter.request(Event::AppStarted.new) - - if res.not_found? # Telemetry is only supported by agent versions 7.34 and up - Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.') - disable! - @unsupported = true # Prevent telemetry from getting re-enabled - return res - end - @worker.start @emitter.request(Event::AppDependenciesLoaded.new) if @dependency_collection diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 5683f2028b7..9ff7702e747 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -16,6 +16,8 @@ class Worker def initialize(heartbeat_interval_seconds:, emitter:, enabled: true) @emitter = emitter + @sent_started_event = false + # Workers::Polling settings self.enabled = enabled # Workers::IntervalLoop settings @@ -26,20 +28,39 @@ def initialize(heartbeat_interval_seconds:, emitter:, enabled: true) def start return if !enabled? || forked? + # starts async worker perform end + def sent_started_event? + @sent_started_event + end + private def perform(*_events) return if !enabled? || forked? + unless @sent_started_event + started! + @sent_started_event = true + end + heartbeat! end def heartbeat! @emitter.request(Event::AppHeartbeat.new) end + + def started! + res = @emitter.request(Event::AppStarted.new) + + if res.not_found? # Telemetry is only supported by agent versions 7.34 and up + Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.') + self.enabled = false + end + end end end end diff --git a/lib/datadog/core/workers/polling.rb b/lib/datadog/core/workers/polling.rb index cc4318f9afa..1d563cdbf4a 100644 --- a/lib/datadog/core/workers/polling.rb +++ b/lib/datadog/core/workers/polling.rb @@ -24,6 +24,7 @@ def perform(*args) end def stop(force_stop = false, timeout = DEFAULT_SHUTDOWN_TIMEOUT) + # p "in stop" if running? # Attempt graceful stop and wait stop_loop diff --git a/sig/datadog/core/telemetry/client.rbs b/sig/datadog/core/telemetry/client.rbs index 79aa3850b1c..e1646cb1eb8 100644 --- a/sig/datadog/core/telemetry/client.rbs +++ b/sig/datadog/core/telemetry/client.rbs @@ -7,13 +7,10 @@ module Datadog @started: bool @stopped: bool @emitter: Datadog::Core::Telemetry::Emitter - @unsupported: bool @worker: Datadog::Core::Telemetry::Worker attr_reader enabled: bool - attr_reader unsupported: bool - include Core::Utils::Forking def initialize: (heartbeat_interval_seconds: Numeric, dependency_collection: bool, enabled: bool) -> void @@ -29,10 +26,6 @@ module Datadog def stop!: () -> void def integrations_change!: () -> void - - private - - def heartbeat!: () -> void end end end diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index 0fa008ebca9..e1c5c09cf70 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -9,14 +9,19 @@ module Datadog include Core::Workers::Queue @emitter: Emitter + @sent_started_event: bool def initialize: (?enabled: bool, heartbeat_interval_seconds: Numeric, emitter: Emitter) -> void def start: () -> void + def sent_started_event?: () -> bool + private def heartbeat!: () -> void + + def started!: () -> void end end end diff --git a/spec/datadog/core/telemetry/client_spec.rb b/spec/datadog/core/telemetry/client_spec.rb index c8a89f6264f..8532ad80185 100644 --- a/spec/datadog/core/telemetry/client_spec.rb +++ b/spec/datadog/core/telemetry/client_spec.rb @@ -12,7 +12,7 @@ end let(:enabled) { true } - let(:heartbeat_interval_seconds) { 1.3 } + let(:heartbeat_interval_seconds) { 0 } let(:dependency_collection) { true } let(:emitter) { double(Datadog::Core::Telemetry::Emitter) } let(:response) { double(Datadog::Core::Telemetry::Http::Adapters::Net::Response) } @@ -83,15 +83,12 @@ context 'when dependency_collection is true' do it do - app_started = double - allow(Datadog::Core::Telemetry::Event::AppStarted).to receive(:new).with(no_args).and_return(app_started) - dependencies = double allow(Datadog::Core::Telemetry::Event::AppDependenciesLoaded) .to receive(:new).with(no_args).and_return(dependencies) started! - expect(emitter).to have_received(:request).with(app_started) + expect(emitter).to have_received(:request).with(dependencies) end end @@ -100,63 +97,17 @@ let(:dependency_collection) { false } it do - app_started = double - allow(Datadog::Core::Telemetry::Event::AppStarted).to receive(:new).with(no_args).and_return(app_started) - dependencies = double allow(Datadog::Core::Telemetry::Event::AppDependenciesLoaded) .to receive(:new).with(no_args).and_return(dependencies) started! - expect(emitter).to have_received(:request).with(app_started) - expect(emitter).to_not have_received(:request).with(dependencies) - end - - context 'with heartbeat' do - let(:heartbeat_interval_seconds) { 0 } - it 'sends a heartbeat strictly after app-started' do - @sent_hearbeat = false - allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do - # Ensure app-started was already sent by now - expect(emitter).to have_received(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppStarted)) - @sent_hearbeat = true - response - end - - client.started! - - try_wait_until { @sent_hearbeat } - end + expect(emitter).to_not have_received(:request).with(dependencies) end end end - context 'when internal error returned by emitter' do - let(:response) { Datadog::Core::Telemetry::Http::InternalErrorResponse.new('error') } - - it { expect { started! }.to_not raise_error } - end - - context 'when response returns 404' do - let(:not_found) { true } - - before do - logger = double(Datadog::Core::Logger) - allow(logger).to receive(:debug).with(any_args) - allow(Datadog).to receive(:logger).and_return(logger) - end - - it do - started! - expect(client.enabled).to be(false) - expect(client.unsupported).to be(true) - expect(Datadog.logger).to have_received(:debug).with( - 'Agent does not support telemetry; disabling future telemetry events.' - ) - end - end - context 'when in fork' do before { skip 'Fork not supported on current platform' unless Process.respond_to?(:fork) } diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 0a874112993..b2000f502a3 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -8,11 +8,13 @@ end let(:enabled) { true } - let(:heartbeat_interval_seconds) { 1.2 } + let(:heartbeat_interval_seconds) { 0.5 } let(:emitter) { double(Datadog::Core::Telemetry::Emitter) } before do - allow(emitter).to receive(:request) + logger = double(Datadog::Core::Logger) + allow(logger).to receive(:debug).with(any_args) + allow(Datadog).to receive(:logger).and_return(logger) end after do @@ -24,7 +26,7 @@ it 'creates a new worker in stopped state' do expect(worker).to have_attributes( enabled?: true, - loop_base_interval: 1.2, # seconds + loop_base_interval: heartbeat_interval_seconds, run_async?: false, running?: false, started?: false @@ -34,19 +36,75 @@ describe '#start' do context 'when enabled' do - it 'starts the worker and sends heartbeat event' do - worker.start + let(:response) do + double(Datadog::Core::Telemetry::Http::Adapters::Net::Response, not_found?: !backend_supports_telemetry?) + end + + before do + expect(emitter).to receive(:request) + .with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) + .and_return(response) + end + + context "when backend doesn't support telemetry" do + let(:backend_supports_telemetry?) { false } + + it 'disables the worker' do + worker.start + + try_wait_until { worker.sent_started_event? } + + expect(worker).to have_attributes( + enabled?: false, + loop_base_interval: heartbeat_interval_seconds, + ) + expect(Datadog.logger).to have_received(:debug).with( + 'Agent does not support telemetry; disabling future telemetry events.' + ) + end + end + + context 'when backend supports telemetry' do + let(:backend_supports_telemetry?) { true } + + it 'starts the worker and sends heartbeat event' do + expect(emitter).to receive(:request) + .with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) + + worker.start + + try_wait_until { worker.sent_started_event? } + + expect(worker).to have_attributes( + enabled?: true, + loop_base_interval: heartbeat_interval_seconds, + run_async?: true, + running?: true, + started?: true + ) + end + + it 'always sends heartbeat event after started event' do + @sent_hearbeat = false + allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do + # app-started was already sent by now + expect(worker.sent_started_event?).to be(true) + + @sent_hearbeat = true + + response + end + + worker.start + + try_wait_until { @sent_hearbeat } + end + end - try_wait_until { worker.running? } + context 'when internal error returned by emitter' do + let(:response) { Datadog::Core::Telemetry::Http::InternalErrorResponse.new('error') } - expect(worker).to have_attributes( - enabled?: true, - loop_base_interval: 1.2, # seconds - run_async?: true, - running?: true, - started?: true - ) - expect(emitter).to have_received(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) + it { expect { worker.start }.to_not raise_error } end end From bb9f888de3c93ba52f8ca5ce2e32944697277947 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 14 Jun 2024 10:38:59 +0200 Subject: [PATCH 08/32] fix failing tests by waiting for worker startup --- lib/datadog/core/workers/polling.rb | 1 - spec/datadog/core/telemetry/worker_spec.rb | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/datadog/core/workers/polling.rb b/lib/datadog/core/workers/polling.rb index 1d563cdbf4a..cc4318f9afa 100644 --- a/lib/datadog/core/workers/polling.rb +++ b/lib/datadog/core/workers/polling.rb @@ -24,7 +24,6 @@ def perform(*args) end def stop(force_stop = false, timeout = DEFAULT_SHUTDOWN_TIMEOUT) - # p "in stop" if running? # Attempt graceful stop and wait stop_loop diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index b2000f502a3..66dd3ebb416 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -104,7 +104,21 @@ context 'when internal error returned by emitter' do let(:response) { Datadog::Core::Telemetry::Http::InternalErrorResponse.new('error') } - it { expect { worker.start }.to_not raise_error } + it do + expect do + worker.start + + try_wait_until { worker.sent_started_event? } + + expect(worker).to have_attributes( + enabled?: true, + loop_base_interval: heartbeat_interval_seconds, + run_async?: true, + running?: true, + started?: true + ) + end.to_not raise_error + end end end From a11e48d566c810cb9a2fd4f4db2291240002a3dc Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 14 Jun 2024 10:59:36 +0200 Subject: [PATCH 09/32] debug logging, flushing events, attempt at fixing failing test for worker --- lib/datadog/core/telemetry/worker.rb | 28 +++++++++++++++++++--- sig/datadog/core/telemetry/worker.rbs | 4 ++++ spec/datadog/core/telemetry/worker_spec.rb | 3 +++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 9ff7702e747..4e7ca78936e 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -38,7 +38,7 @@ def sent_started_event? private - def perform(*_events) + def perform(*events) return if !enabled? || forked? unless @sent_started_event @@ -47,20 +47,42 @@ def perform(*_events) end heartbeat! + + send_events(events) + end + + def send_events(events) + return unless enabled? + + Datadog.logger.debug { "Sending #{events&.count} telemetry events" } + (events || []).each do |event| + send_event(event) + end end def heartbeat! - @emitter.request(Event::AppHeartbeat.new) + return unless enabled? + + send_event(Event::AppHeartbeat.new) end def started! - res = @emitter.request(Event::AppStarted.new) + return unless enabled? + + res = send_event(Event::AppStarted.new) if res.not_found? # Telemetry is only supported by agent versions 7.34 and up Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.') self.enabled = false end end + + def send_event(event) + Datadog.logger.debug { "Sending telemetry event: #{event}" } + response = @emitter.request(event) + Datadog.logger.debug { "Received response: #{response}" } + response + end end end end diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index e1c5c09cf70..e8855724a6d 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -22,6 +22,10 @@ module Datadog def heartbeat!: () -> void def started!: () -> void + + def send_events: (Array[Event::Base] events) -> void + + def send_event: (Event::Base event) -> Datadog::Core::Telemetry::Http::Adapters::Net::Response end end end diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 66dd3ebb416..0f4d9919e9a 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -105,6 +105,9 @@ let(:response) { Datadog::Core::Telemetry::Http::InternalErrorResponse.new('error') } it do + expect(emitter).to receive(:request) + .with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) + expect do worker.start From 6b9204ee9b137b880c8b18f1652fbbe7c13fc635 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 14 Jun 2024 11:34:25 +0200 Subject: [PATCH 10/32] don't send heartbeat event if started event wasn't successfully sent --- lib/datadog/core/telemetry/worker.rb | 18 ++++---- sig/datadog/core/telemetry/worker.rbs | 2 +- spec/datadog/core/telemetry/worker_spec.rb | 50 +++++++++++----------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 4e7ca78936e..a837259c3f9 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -41,18 +41,15 @@ def sent_started_event? def perform(*events) return if !enabled? || forked? - unless @sent_started_event - started! - @sent_started_event = true - end + started! unless sent_started_event? heartbeat! - send_events(events) + flush_events(events) end - def send_events(events) - return unless enabled? + def flush_events(events) + return if !enabled? || !sent_started_event? Datadog.logger.debug { "Sending #{events&.count} telemetry events" } (events || []).each do |event| @@ -61,7 +58,7 @@ def send_events(events) end def heartbeat! - return unless enabled? + return if !enabled? || !sent_started_event? send_event(Event::AppHeartbeat.new) end @@ -75,6 +72,11 @@ def started! Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.') self.enabled = false end + + if res.ok? + Datadog.logger.debug('Telemetry app-started event is successfully sent') + @sent_started_event = true + end end def send_event(event) diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index e8855724a6d..64b3de884d3 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -23,7 +23,7 @@ module Datadog def started!: () -> void - def send_events: (Array[Event::Base] events) -> void + def flush_events: (Array[Event::Base] events) -> void def send_event: (Event::Base event) -> Datadog::Core::Telemetry::Http::Adapters::Net::Response end diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 0f4d9919e9a..8b8aba31784 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -37,13 +37,28 @@ describe '#start' do context 'when enabled' do let(:response) do - double(Datadog::Core::Telemetry::Http::Adapters::Net::Response, not_found?: !backend_supports_telemetry?) + double( + Datadog::Core::Telemetry::Http::Adapters::Net::Response, + not_found?: !backend_supports_telemetry?, + ok?: backend_supports_telemetry? + ) end before do - expect(emitter).to receive(:request) - .with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) - .and_return(response) + @received_started = false + @received_heartbeat = false + + allow(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) do + @received_started = true + + response + end + + allow(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do + @received_heartbeat = true + + response + end end context "when backend doesn't support telemetry" do @@ -52,7 +67,7 @@ it 'disables the worker' do worker.start - try_wait_until { worker.sent_started_event? } + try_wait_until { @received_started } expect(worker).to have_attributes( enabled?: false, @@ -61,6 +76,7 @@ expect(Datadog.logger).to have_received(:debug).with( 'Agent does not support telemetry; disabling future telemetry events.' ) + expect(@received_heartbeat).to be(false) end end @@ -68,12 +84,9 @@ let(:backend_supports_telemetry?) { true } it 'starts the worker and sends heartbeat event' do - expect(emitter).to receive(:request) - .with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) - worker.start - try_wait_until { worker.sent_started_event? } + try_wait_until { @received_heartbeat } expect(worker).to have_attributes( enabled?: true, @@ -104,23 +117,12 @@ context 'when internal error returned by emitter' do let(:response) { Datadog::Core::Telemetry::Http::InternalErrorResponse.new('error') } - it do - expect(emitter).to receive(:request) - .with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) - - expect do - worker.start + it 'does not send heartbeat event' do + worker.start - try_wait_until { worker.sent_started_event? } + try_wait_until { @received_started } - expect(worker).to have_attributes( - enabled?: true, - loop_base_interval: heartbeat_interval_seconds, - run_async?: true, - running?: true, - started?: true - ) - end.to_not raise_error + expect(@received_heartbeat).to be(false) end end end From dcedde62cb73432d6d0421229b37fee977883315 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 14 Jun 2024 11:42:22 +0200 Subject: [PATCH 11/32] fix client_spec --- spec/datadog/core/telemetry/client_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/datadog/core/telemetry/client_spec.rb b/spec/datadog/core/telemetry/client_spec.rb index 8532ad80185..ed5d991feb7 100644 --- a/spec/datadog/core/telemetry/client_spec.rb +++ b/spec/datadog/core/telemetry/client_spec.rb @@ -22,6 +22,7 @@ allow(Datadog::Core::Telemetry::Emitter).to receive(:new).and_return(emitter) allow(emitter).to receive(:request).and_return(response) allow(response).to receive(:not_found?).and_return(not_found) + allow(response).to receive(:ok?).and_return(!not_found) end describe '#initialize' do From 3a0256fc40933aaea99638cde6ee4ae49bf1b6f5 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 14 Jun 2024 13:41:01 +0200 Subject: [PATCH 12/32] enqueue events to be sent later by worker instead of sending them synchronously --- lib/datadog/core/configuration/components.rb | 3 +- lib/datadog/core/telemetry/client.rb | 14 +-- lib/datadog/core/telemetry/worker.rb | 37 +++++- sig/datadog/core/telemetry/client.rbs | 3 +- sig/datadog/core/telemetry/worker.rbs | 10 +- sig/datadog/core/workers/polling.rbs | 2 +- spec/datadog/core/telemetry/client_spec.rb | 117 ++++++++----------- spec/datadog/core/telemetry/worker_spec.rb | 69 +++++++---- 8 files changed, 148 insertions(+), 107 deletions(-) diff --git a/lib/datadog/core/configuration/components.rb b/lib/datadog/core/configuration/components.rb index f7e00d7a981..46106accc12 100644 --- a/lib/datadog/core/configuration/components.rb +++ b/lib/datadog/core/configuration/components.rb @@ -169,8 +169,9 @@ def shutdown!(replacement = nil) unused_statsd = (old_statsd - (old_statsd & new_statsd)) unused_statsd.each(&:close) - telemetry.stop! + # enqueue closing event before stopping telemetry so it will be send out on shutdown telemetry.emit_closing! unless replacement + telemetry.stop! end end end diff --git a/lib/datadog/core/telemetry/client.rb b/lib/datadog/core/telemetry/client.rb index 07028e96ad5..05a96e39239 100644 --- a/lib/datadog/core/telemetry/client.rb +++ b/lib/datadog/core/telemetry/client.rb @@ -19,7 +19,6 @@ class Client # @param [Boolean] dependency_collection Whether to send the `app-dependencies-loaded` event def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: true) @enabled = enabled - @emitter = Emitter.new @stopped = false @started = false @dependency_collection = dependency_collection @@ -27,7 +26,7 @@ def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: tru @worker = Telemetry::Worker.new( enabled: @enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, - emitter: @emitter + emitter: Emitter.new ) end @@ -41,7 +40,7 @@ def started! @worker.start - @emitter.request(Event::AppDependenciesLoaded.new) if @dependency_collection + @worker.enqueue(Event::AppDependenciesLoaded.new) if @dependency_collection @started = true end @@ -49,27 +48,28 @@ def started! def emit_closing! return if !@enabled || forked? - @emitter.request(Event::AppClosing.new) + @worker.enqueue(Event::AppClosing.new) end def stop! return if @stopped - @worker.stop(true, 0) + # gracefully stop the worker and send leftover events + @worker.stop @stopped = true end def integrations_change! return if !@enabled || forked? - @emitter.request(Event::AppIntegrationsChange.new) + @worker.enqueue(Event::AppIntegrationsChange.new) end # Report configuration changes caused by Remote Configuration. def client_configuration_change!(changes) return if !@enabled || forked? - @emitter.request(Event::AppClientConfigurationChange.new(changes, 'remote_config')) + @worker.enqueue(Event::AppClientConfigurationChange.new(changes, 'remote_config')) end end end diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index a837259c3f9..93fccab9a6c 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -13,7 +13,15 @@ class Worker include Core::Workers::Queue include Core::Workers::Polling - def initialize(heartbeat_interval_seconds:, emitter:, enabled: true) + DEFAULT_BUFFER_MAX_SIZE = 1000 + + def initialize( + heartbeat_interval_seconds:, + emitter:, + enabled: true, + shutdown_timeout: Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT, + buffer_size: DEFAULT_BUFFER_MAX_SIZE + ) @emitter = emitter @sent_started_event = false @@ -23,6 +31,11 @@ def initialize(heartbeat_interval_seconds:, emitter:, enabled: true) # Workers::IntervalLoop settings self.loop_base_interval = heartbeat_interval_seconds self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_STOP + + @shutdown_timeout = shutdown_timeout + @buffer_size = buffer_size + + self.buffer = buffer_klass.new(@buffer_size) end def start @@ -32,6 +45,16 @@ def start perform end + def stop(force_stop = false, timeout = @shutdown_timeout) + buffer.close if running? + + super + end + + def enqueue(event) + buffer.push(event) + end + def sent_started_event? @sent_started_event end @@ -85,6 +108,18 @@ def send_event(event) Datadog.logger.debug { "Received response: #{response}" } response end + + def dequeue + buffer.pop + end + + def buffer_klass + if Core::Environment::Ext::RUBY_ENGINE == 'ruby' + Core::Buffer::CRuby + else + Core::Buffer::ThreadSafe + end + end end end end diff --git a/sig/datadog/core/telemetry/client.rbs b/sig/datadog/core/telemetry/client.rbs index e1646cb1eb8..007050163a7 100644 --- a/sig/datadog/core/telemetry/client.rbs +++ b/sig/datadog/core/telemetry/client.rbs @@ -6,14 +6,13 @@ module Datadog @dependency_collection: bool @started: bool @stopped: bool - @emitter: Datadog::Core::Telemetry::Emitter @worker: Datadog::Core::Telemetry::Worker attr_reader enabled: bool include Core::Utils::Forking - def initialize: (heartbeat_interval_seconds: Numeric, dependency_collection: bool, enabled: bool) -> void + def initialize: (heartbeat_interval_seconds: Numeric, dependency_collection: bool, ?enabled: bool) -> void def disable!: () -> void diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index 64b3de884d3..9220dfeea09 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -8,15 +8,21 @@ module Datadog include Core::Workers::IntervalLoop include Core::Workers::Queue + DEFAULT_BUFFER_MAX_SIZE: 1000 + @emitter: Emitter @sent_started_event: bool + @shutdown_timeout: Integer + @buffer_size: Integer - def initialize: (?enabled: bool, heartbeat_interval_seconds: Numeric, emitter: Emitter) -> void + def initialize: (?enabled: bool, heartbeat_interval_seconds: Numeric, emitter: Emitter, ?shutdown_timeout: Integer, ?buffer_size: Integer) -> void def start: () -> void def sent_started_event?: () -> bool + def enqueue: (Event::Base event) -> void + private def heartbeat!: () -> void @@ -26,6 +32,8 @@ module Datadog def flush_events: (Array[Event::Base] events) -> void def send_event: (Event::Base event) -> Datadog::Core::Telemetry::Http::Adapters::Net::Response + + def buffer_klass: () -> untyped end end end diff --git a/sig/datadog/core/workers/polling.rbs b/sig/datadog/core/workers/polling.rbs index 7f4d8f9c55b..43c1360a92c 100644 --- a/sig/datadog/core/workers/polling.rbs +++ b/sig/datadog/core/workers/polling.rbs @@ -2,7 +2,7 @@ module Datadog module Core module Workers module Polling - SHUTDOWN_TIMEOUT: 1 + DEFAULT_SHUTDOWN_TIMEOUT: 1 def self.included: (Class | Module base) -> void diff --git a/spec/datadog/core/telemetry/client_spec.rb b/spec/datadog/core/telemetry/client_spec.rb index ed5d991feb7..2e1a0a34bcf 100644 --- a/spec/datadog/core/telemetry/client_spec.rb +++ b/spec/datadog/core/telemetry/client_spec.rb @@ -14,15 +14,15 @@ let(:enabled) { true } let(:heartbeat_interval_seconds) { 0 } let(:dependency_collection) { true } - let(:emitter) { double(Datadog::Core::Telemetry::Emitter) } - let(:response) { double(Datadog::Core::Telemetry::Http::Adapters::Net::Response) } + let(:worker) { double(Datadog::Core::Telemetry::Worker) } let(:not_found) { false } before do - allow(Datadog::Core::Telemetry::Emitter).to receive(:new).and_return(emitter) - allow(emitter).to receive(:request).and_return(response) - allow(response).to receive(:not_found?).and_return(not_found) - allow(response).to receive(:ok?).and_return(!not_found) + allow(Datadog::Core::Telemetry::Worker).to receive(:new).and_return(worker) + allow(worker).to receive(:start) + allow(worker).to receive(:enqueue) + allow(worker).to receive(:stop) + allow(worker).to receive(:"enabled=") end describe '#initialize' do @@ -62,6 +62,12 @@ end it { expect { client.disable! }.to change { client.enabled }.from(true).to(false) } + + it 'disables worker' do + client.disable! + + expect(worker).to have_received(:"enabled=").with(false) + end end describe '#started!' do @@ -75,7 +81,8 @@ let(:enabled) { false } it do started! - expect(emitter).to_not have_received(:request) + + expect(worker).to_not have_received(:start) end end @@ -84,13 +91,11 @@ context 'when dependency_collection is true' do it do - dependencies = double - allow(Datadog::Core::Telemetry::Event::AppDependenciesLoaded) - .to receive(:new).with(no_args).and_return(dependencies) - started! - expect(emitter).to have_received(:request).with(dependencies) + expect(worker).to have_received(:enqueue).with( + an_instance_of(Datadog::Core::Telemetry::Event::AppDependenciesLoaded) + ) end end @@ -98,13 +103,9 @@ let(:dependency_collection) { false } it do - dependencies = double - allow(Datadog::Core::Telemetry::Event::AppDependenciesLoaded) - .to receive(:new).with(no_args).and_return(dependencies) - started! - expect(emitter).to_not have_received(:request).with(dependencies) + expect(worker).not_to have_received(:enqueue) end end end @@ -115,8 +116,9 @@ it do client expect_in_fork do - expect(emitter).to_not receive(:request) client.started! + + expect(worker).to_not have_received(:start) end end end @@ -133,21 +135,20 @@ let(:enabled) { false } it do emit_closing! - expect(emitter).to_not have_received(:request) + + expect(worker).not_to have_received(:enqueue) end end context 'when enabled' do let(:enabled) { true } it do - double = double() - allow(Datadog::Core::Telemetry::Event::AppClosing).to receive(:new).with(no_args).and_return(double) - emit_closing! - expect(emitter).to have_received(:request).with(double) - end - it { is_expected.to be(response) } + expect(worker).to have_received(:enqueue).with( + an_instance_of(Datadog::Core::Telemetry::Event::AppClosing) + ) + end end context 'when in fork' do @@ -156,8 +157,9 @@ it do client expect_in_fork do - expect(emitter).to_not receive(:request) client.started! + + expect(worker).not_to have_received(:enqueue) end end end @@ -165,32 +167,12 @@ describe '#stop!' do subject(:stop!) { client.stop! } - let(:worker) { instance_double(Datadog::Core::Telemetry::Worker) } - - before do - allow(Datadog::Core::Telemetry::Worker).to receive(:new) - .with(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, emitter: emitter) - .and_return(worker) - allow(worker).to receive(:start) - allow(worker).to receive(:stop) - end - context 'when disabled' do - let(:enabled) { false } - it 'does not raise error' do - stop! - end - end + it 'stops worker once' do + stop! + stop! - context 'when enabled' do - let(:enabled) { true } - - context 'when stop! has been called already' do - it 'does not raise error' do - stop! - stop! - end - end + expect(worker).to have_received(:stop).once end end @@ -205,21 +187,20 @@ let(:enabled) { false } it do integrations_change! - expect(emitter).to_not have_received(:request) + + expect(worker).not_to have_received(:enqueue) end end context 'when enabled' do let(:enabled) { true } it do - double = double() - allow(Datadog::Core::Telemetry::Event::AppIntegrationsChange).to receive(:new).with(no_args).and_return(double) - integrations_change! - expect(emitter).to have_received(:request).with(double) - end - it { is_expected.to be(response) } + expect(worker).to have_received(:enqueue).with( + an_instance_of(Datadog::Core::Telemetry::Event::AppIntegrationsChange) + ) + end end context 'when in fork' do @@ -228,8 +209,9 @@ it do client expect_in_fork do - expect(emitter).to_not receive(:request) client.started! + + expect(worker).not_to have_received(:enqueue) end end end @@ -247,24 +229,20 @@ let(:enabled) { false } it do client_configuration_change! - expect(emitter).to_not have_received(:request) + + expect(worker).not_to have_received(:enqueue) end end context 'when enabled' do let(:enabled) { true } it do - double = double() - allow(Datadog::Core::Telemetry::Event::AppClientConfigurationChange).to receive(:new).with( - changes, - 'remote_config' - ).and_return(double) - client_configuration_change! - expect(emitter).to have_received(:request).with(double) - end - it { is_expected.to be(response) } + expect(worker).to have_received(:enqueue).with( + an_instance_of(Datadog::Core::Telemetry::Event::AppClientConfigurationChange) + ) + end end context 'when in fork' do @@ -273,8 +251,9 @@ it do client expect_in_fork do - expect(emitter).to_not receive(:request) client.started! + + expect(worker).not_to have_received(:enqueue) end end end diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 8b8aba31784..ba389c23801 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -11,10 +11,34 @@ let(:heartbeat_interval_seconds) { 0.5 } let(:emitter) { double(Datadog::Core::Telemetry::Emitter) } + let(:backend_supports_telemetry?) { true } + let(:response) do + double( + Datadog::Core::Telemetry::Http::Adapters::Net::Response, + not_found?: !backend_supports_telemetry?, + ok?: backend_supports_telemetry? + ) + end + before do logger = double(Datadog::Core::Logger) allow(logger).to receive(:debug).with(any_args) allow(Datadog).to receive(:logger).and_return(logger) + + @received_started = false + @received_heartbeat = false + + allow(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) do + @received_started = true + + response + end + + allow(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do + @received_heartbeat = true + + response + end end after do @@ -36,31 +60,6 @@ describe '#start' do context 'when enabled' do - let(:response) do - double( - Datadog::Core::Telemetry::Http::Adapters::Net::Response, - not_found?: !backend_supports_telemetry?, - ok?: backend_supports_telemetry? - ) - end - - before do - @received_started = false - @received_heartbeat = false - - allow(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) do - @received_started = true - - response - end - - allow(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do - @received_heartbeat = true - - response - end - end - context "when backend doesn't support telemetry" do let(:backend_supports_telemetry?) { false } @@ -137,4 +136,24 @@ end end end + + describe '#enqueue' do + it 'adds events to the buffer and flushes them later' do + events_received = 0 + allow(emitter).to receive(:request).with( + an_instance_of(Datadog::Core::Telemetry::Event::AppIntegrationsChange) + ) do + events_received += 1 + end + + worker.start + + events_sent = 3 + events_sent.times do + worker.enqueue(Datadog::Core::Telemetry::Event::AppIntegrationsChange.new) + end + + try_wait_until { events_received == events_sent } + end + end end From 06293a4b0c39b2ff5fba48b70675425f0e9baf62 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 14 Jun 2024 14:14:30 +0200 Subject: [PATCH 13/32] rename Telemetry::Client to Telemetry::Component to better reflect its purpose --- lib/datadog/core/configuration/components.rb | 4 +- .../telemetry/{client.rb => component.rb} | 3 +- lib/datadog/core/telemetry/worker.rb | 6 +- .../telemetry/{client.rbs => component.rbs} | 2 +- .../core/configuration/components_spec.rb | 18 +++--- spec/datadog/core/configuration_spec.rb | 20 +++---- .../{client_spec.rb => component_spec.rb} | 56 +++++++++---------- 7 files changed, 54 insertions(+), 55 deletions(-) rename lib/datadog/core/telemetry/{client.rb => component.rb} (98%) rename sig/datadog/core/telemetry/{client.rbs => component.rbs} (97%) rename spec/datadog/core/telemetry/{client_spec.rb => component_spec.rb} (82%) diff --git a/lib/datadog/core/configuration/components.rb b/lib/datadog/core/configuration/components.rb index 46106accc12..665485c6f0c 100644 --- a/lib/datadog/core/configuration/components.rb +++ b/lib/datadog/core/configuration/components.rb @@ -6,7 +6,7 @@ require_relative '../diagnostics/health' require_relative '../logger' require_relative '../runtime/metrics' -require_relative '../telemetry/client' +require_relative '../telemetry/component' require_relative '../workers/runtime_metrics' require_relative '../remote/component' @@ -62,7 +62,7 @@ def build_telemetry(settings, agent_settings, logger) logger.debug { "Telemetry disabled. Agent network adapter not supported: #{agent_settings.adapter}" } end - Telemetry::Client.new( + Telemetry::Component.new( enabled: enabled, heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds, dependency_collection: settings.telemetry.dependency_collection diff --git a/lib/datadog/core/telemetry/client.rb b/lib/datadog/core/telemetry/component.rb similarity index 98% rename from lib/datadog/core/telemetry/client.rb rename to lib/datadog/core/telemetry/component.rb index 05a96e39239..6324f8ac795 100644 --- a/lib/datadog/core/telemetry/client.rb +++ b/lib/datadog/core/telemetry/component.rb @@ -9,7 +9,7 @@ module Datadog module Core module Telemetry # Telemetry entrypoint, coordinates sending telemetry events at various points in app lifecycle. - class Client + class Component attr_reader :enabled include Core::Utils::Forking @@ -39,7 +39,6 @@ def started! return if !@enabled || forked? @worker.start - @worker.enqueue(Event::AppDependenciesLoaded.new) if @dependency_collection @started = true diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 93fccab9a6c..da0bc7dd455 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -94,11 +94,11 @@ def started! if res.not_found? # Telemetry is only supported by agent versions 7.34 and up Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.') self.enabled = false - end - - if res.ok? + elsif res.ok? Datadog.logger.debug('Telemetry app-started event is successfully sent') @sent_started_event = true + else + Datadog.logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...') end end diff --git a/sig/datadog/core/telemetry/client.rbs b/sig/datadog/core/telemetry/component.rbs similarity index 97% rename from sig/datadog/core/telemetry/client.rbs rename to sig/datadog/core/telemetry/component.rbs index 007050163a7..614ac20d691 100644 --- a/sig/datadog/core/telemetry/client.rbs +++ b/sig/datadog/core/telemetry/component.rbs @@ -1,7 +1,7 @@ module Datadog module Core module Telemetry - class Client + class Component @enabled: bool @dependency_collection: bool @started: bool diff --git a/spec/datadog/core/configuration/components_spec.rb b/spec/datadog/core/configuration/components_spec.rb index e04fad93fcf..79c65637f91 100644 --- a/spec/datadog/core/configuration/components_spec.rb +++ b/spec/datadog/core/configuration/components_spec.rb @@ -7,7 +7,7 @@ require 'datadog/core/diagnostics/environment_logger' require 'datadog/core/diagnostics/health' require 'datadog/core/logger' -require 'datadog/core/telemetry/client' +require 'datadog/core/telemetry/component' require 'datadog/core/runtime/metrics' require 'datadog/core/workers/runtime_metrics' require 'datadog/statsd' @@ -33,7 +33,7 @@ let(:profiler_setup_task) { Datadog::Profiling.supported? ? instance_double(Datadog::Profiling::Tasks::Setup) : nil } let(:remote) { instance_double(Datadog::Core::Remote::Component, start: nil, shutdown!: nil) } - let(:telemetry) { instance_double(Datadog::Core::Telemetry::Client) } + let(:telemetry) { instance_double(Datadog::Core::Telemetry::Component) } let(:environment_logger_extra) { { hello: 123, world: '456' } } @@ -46,7 +46,7 @@ end allow(Datadog::Statsd).to receive(:new) { instance_double(Datadog::Statsd) } allow(Datadog::Core::Remote::Component).to receive(:new).and_return(remote) - allow(Datadog::Core::Telemetry::Client).to receive(:new).and_return(telemetry) + allow(Datadog::Core::Telemetry::Component).to receive(:new).and_return(telemetry) end around do |example| @@ -223,7 +223,7 @@ let(:logger) { instance_double(Logger) } context 'given settings' do - let(:telemetry_client) { instance_double(Datadog::Core::Telemetry::Client) } + let(:telemetry) { instance_double(Datadog::Core::Telemetry::Component) } let(:expected_options) do { enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, dependency_collection: dependency_collection } @@ -233,16 +233,16 @@ let(:dependency_collection) { true } before do - expect(Datadog::Core::Telemetry::Client).to receive(:new).with(expected_options).and_return(telemetry_client) + expect(Datadog::Core::Telemetry::Component).to receive(:new).with(expected_options).and_return(telemetry) allow(settings.telemetry).to receive(:enabled).and_return(enabled) end - it { is_expected.to be(telemetry_client) } + it { is_expected.to be(telemetry) } context 'with :enabled true' do let(:enabled) { double('enabled') } - it { is_expected.to be(telemetry_client) } + it { is_expected.to be(telemetry) } context 'and :unix agent adapter' do let(:expected_options) do @@ -255,7 +255,7 @@ it 'does not enable telemetry for unsupported non-http transport' do expect(logger).to receive(:debug) - is_expected.to be(telemetry_client) + is_expected.to be(telemetry) end end end @@ -1108,7 +1108,7 @@ let(:runtime_metrics) { instance_double(Datadog::Core::Runtime::Metrics, statsd: statsd) } let(:health_metrics) { instance_double(Datadog::Core::Diagnostics::Health::Metrics, statsd: statsd) } let(:statsd) { instance_double(::Datadog::Statsd) } - let(:telemetry) { instance_double(Datadog::Core::Telemetry::Client) } + let(:telemetry) { instance_double(Datadog::Core::Telemetry::Component) } before do allow(replacement).to receive(:tracer).and_return(tracer) diff --git a/spec/datadog/core/configuration_spec.rb b/spec/datadog/core/configuration_spec.rb index 7873d262f70..ace1b5066b9 100644 --- a/spec/datadog/core/configuration_spec.rb +++ b/spec/datadog/core/configuration_spec.rb @@ -8,13 +8,13 @@ RSpec.describe Datadog::Core::Configuration do let(:default_log_level) { ::Logger::INFO } - let(:telemetry_client) { instance_double(Datadog::Core::Telemetry::Client) } + let(:telemetry) { instance_double(Datadog::Core::Telemetry::Component) } before do - allow(telemetry_client).to receive(:started!) - allow(telemetry_client).to receive(:stop!) - allow(telemetry_client).to receive(:emit_closing!) - allow(Datadog::Core::Telemetry::Client).to receive(:new).and_return(telemetry_client) + allow(telemetry).to receive(:started!) + allow(telemetry).to receive(:stop!) + allow(telemetry).to receive(:emit_closing!) + allow(Datadog::Core::Telemetry::Component).to receive(:new).and_return(telemetry) allow(Datadog::Core::Remote::Component).to receive(:build) end @@ -43,7 +43,7 @@ it do # We cannot mix `expect().to_not` with `expect().to(...).ordered`. # One way around that is to force the method to raise an error if it's ever called. - allow(telemetry_client).to receive(:started!).and_raise('Should not be called') + allow(telemetry).to receive(:started!).and_raise('Should not be called') # Components should have changed expect { configure } @@ -84,7 +84,7 @@ .with(test_class.configuration) expect(new_components).to_not have_received(:shutdown!) - expect(telemetry_client).to have_received(:started!) + expect(telemetry).to have_received(:started!) end end end @@ -501,7 +501,7 @@ describe '#components' do context 'when components are not initialized' do it 'initializes the components' do - expect(telemetry_client).to receive(:started!) + expect(telemetry).to receive(:started!) test_class.send(:components) @@ -510,7 +510,7 @@ context 'when allow_initialization is false' do it 'does not initialize the components' do - expect(telemetry_client).to_not receive(:started!) + expect(telemetry).to_not receive(:started!) test_class.send(:components, allow_initialization: false) @@ -527,7 +527,7 @@ it 'returns the components without touching the COMPONENTS_WRITE_LOCK' do described_class.const_get(:COMPONENTS_WRITE_LOCK).lock - expect(telemetry_client).to_not receive(:started!) + expect(telemetry).to_not receive(:started!) expect(test_class.send(:components)).to_not be_nil end end diff --git a/spec/datadog/core/telemetry/client_spec.rb b/spec/datadog/core/telemetry/component_spec.rb similarity index 82% rename from spec/datadog/core/telemetry/client_spec.rb rename to spec/datadog/core/telemetry/component_spec.rb index 2e1a0a34bcf..5d1f7f013f7 100644 --- a/spec/datadog/core/telemetry/client_spec.rb +++ b/spec/datadog/core/telemetry/component_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -require 'datadog/core/telemetry/client' +require 'datadog/core/telemetry/component' -RSpec.describe Datadog::Core::Telemetry::Client do - subject(:client) do +RSpec.describe Datadog::Core::Telemetry::Component do + subject(:telemetry) do described_class.new( enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, @@ -27,11 +27,11 @@ describe '#initialize' do after do - client.stop! + telemetry.stop! end context 'with default parameters' do - subject(:client) do + subject(:telemetry) do described_class.new( heartbeat_interval_seconds: heartbeat_interval_seconds, dependency_collection: dependency_collection @@ -39,42 +39,42 @@ end it { is_expected.to be_a_kind_of(described_class) } - it { expect(client.enabled).to be(true) } + it { expect(telemetry.enabled).to be(true) } end context 'when :enabled is false' do let(:enabled) { false } it { is_expected.to be_a_kind_of(described_class) } - it { expect(client.enabled).to be(false) } + it { expect(telemetry.enabled).to be(false) } end context 'when enabled' do let(:enabled) { true } it { is_expected.to be_a_kind_of(described_class) } - it { expect(client.enabled).to be(true) } + it { expect(telemetry.enabled).to be(true) } end end describe '#disable!' do after do - client.stop! + telemetry.stop! end - it { expect { client.disable! }.to change { client.enabled }.from(true).to(false) } + it { expect { telemetry.disable! }.to change { telemetry.enabled }.from(true).to(false) } it 'disables worker' do - client.disable! + telemetry.disable! expect(worker).to have_received(:"enabled=").with(false) end end describe '#started!' do - subject(:started!) { client.started! } + subject(:started!) { telemetry.started! } after do - client.stop! + telemetry.stop! end context 'when disabled' do @@ -114,9 +114,9 @@ before { skip 'Fork not supported on current platform' unless Process.respond_to?(:fork) } it do - client + telemetry expect_in_fork do - client.started! + telemetry.started! expect(worker).to_not have_received(:start) end @@ -125,10 +125,10 @@ end describe '#emit_closing!' do - subject(:emit_closing!) { client.emit_closing! } + subject(:emit_closing!) { telemetry.emit_closing! } after do - client.stop! + telemetry.stop! end context 'when disabled' do @@ -155,9 +155,9 @@ before { skip 'Fork not supported on current platform' unless Process.respond_to?(:fork) } it do - client + telemetry expect_in_fork do - client.started! + telemetry.started! expect(worker).not_to have_received(:enqueue) end @@ -166,7 +166,7 @@ end describe '#stop!' do - subject(:stop!) { client.stop! } + subject(:stop!) { telemetry.stop! } it 'stops worker once' do stop! @@ -177,10 +177,10 @@ end describe '#integrations_change!' do - subject(:integrations_change!) { client.integrations_change! } + subject(:integrations_change!) { telemetry.integrations_change! } after do - client.stop! + telemetry.stop! end context 'when disabled' do @@ -207,9 +207,9 @@ before { skip 'Fork not supported on current platform' unless Process.respond_to?(:fork) } it do - client + telemetry expect_in_fork do - client.started! + telemetry.started! expect(worker).not_to have_received(:enqueue) end @@ -218,11 +218,11 @@ end describe '#client_configuration_change!' do - subject(:client_configuration_change!) { client.client_configuration_change!(changes) } + subject(:client_configuration_change!) { telemetry.client_configuration_change!(changes) } let(:changes) { double('changes') } after do - client.stop! + telemetry.stop! end context 'when disabled' do @@ -249,9 +249,9 @@ before { skip 'Fork not supported on current platform' unless Process.respond_to?(:fork) } it do - client + telemetry expect_in_fork do - client.started! + telemetry.started! expect(worker).not_to have_received(:enqueue) end From d31a0883668505b78b92a5077e19730677f5b42c Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 14 Jun 2024 14:24:44 +0200 Subject: [PATCH 14/32] leftover of telemetry component rename --- spec/datadog/tracing/contrib/extensions_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/datadog/tracing/contrib/extensions_spec.rb b/spec/datadog/tracing/contrib/extensions_spec.rb index d53dd66c0e8..461fd09852f 100644 --- a/spec/datadog/tracing/contrib/extensions_spec.rb +++ b/spec/datadog/tracing/contrib/extensions_spec.rb @@ -46,7 +46,7 @@ end it 'sends a telemetry integrations change event' do - expect_any_instance_of(Datadog::Core::Telemetry::Client).to receive(:integrations_change!) + expect_any_instance_of(Datadog::Core::Telemetry::Component).to receive(:integrations_change!) configure end end From 4f2aab8dbd215c061fb25d96a1d9a35f96783662 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Mon, 17 Jun 2024 12:37:58 +0200 Subject: [PATCH 15/32] add Core::Utils::OnlyOnceSuccessful to execute code with only one success --- .../core/utils/only_once_successful.rb | 34 +++++++ sig/datadog/core/utils/only_once.rbs | 3 + .../core/utils/only_once_successful.rbs | 8 ++ .../core/utils/only_once_successful_spec.rb | 95 +++++++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 lib/datadog/core/utils/only_once_successful.rb create mode 100644 sig/datadog/core/utils/only_once_successful.rbs create mode 100644 spec/datadog/core/utils/only_once_successful_spec.rb diff --git a/lib/datadog/core/utils/only_once_successful.rb b/lib/datadog/core/utils/only_once_successful.rb new file mode 100644 index 00000000000..e5ac05d304b --- /dev/null +++ b/lib/datadog/core/utils/only_once_successful.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative 'only_once' + +module Datadog + module Core + module Utils + # Helper class to execute something with only one success. + # + # This is useful for cases where we want to ensure that a block of code is only executed once, and only if it + # succeeds. One such example is sending app-started telemetry event. + # + # Successful execution is determined by the return value of the block: any truthy value is considered success. + # + # Thread-safe when used correctly (e.g. be careful of races when lazily initializing instances of this class). + # + # Note: In its current state, this class is not Ractor-safe. + # In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives, + # including an alternative implementation that is Ractor-safe once spent. + class OnlyOnceSuccessful < OnlyOnce + def run + @mutex.synchronize do + return if @ran_once + + result = yield + @ran_once = !!result + + result + end + end + end + end + end +end diff --git a/sig/datadog/core/utils/only_once.rbs b/sig/datadog/core/utils/only_once.rbs index 324b1ba72e7..354334c71e1 100644 --- a/sig/datadog/core/utils/only_once.rbs +++ b/sig/datadog/core/utils/only_once.rbs @@ -2,6 +2,9 @@ module Datadog module Core module Utils class OnlyOnce + @ran_once: bool + @mutex: Thread::Mutex + def initialize: () -> untyped def run: () { () -> untyped } -> untyped diff --git a/sig/datadog/core/utils/only_once_successful.rbs b/sig/datadog/core/utils/only_once_successful.rbs new file mode 100644 index 00000000000..81d24fc9094 --- /dev/null +++ b/sig/datadog/core/utils/only_once_successful.rbs @@ -0,0 +1,8 @@ +module Datadog + module Core + module Utils + class OnlyOnceSuccessful < Datadog::Core::Utils::OnlyOnce + end + end + end +end diff --git a/spec/datadog/core/utils/only_once_successful_spec.rb b/spec/datadog/core/utils/only_once_successful_spec.rb new file mode 100644 index 00000000000..bf06ff5d60c --- /dev/null +++ b/spec/datadog/core/utils/only_once_successful_spec.rb @@ -0,0 +1,95 @@ +require 'datadog/core/utils/only_once_successful' + +RSpec.describe Datadog::Core::Utils::OnlyOnceSuccessful do + subject(:only_once_successful) { described_class.new } + + describe '#run' do + context 'before running once' do + it do + expect { |block| only_once_successful.run(&block) }.to yield_control + end + + it 'returns the result of the block ran' do + expect(only_once_successful.run { :result }).to be :result + end + end + + context 'after running once' do + let(:result) { nil } + + before do + only_once_successful.run { result } + end + + context 'when block returns truthy value' do + let(:result) { true } + + it do + expect { |block| only_once_successful.run(&block) }.to_not yield_control + end + + it do + expect(only_once_successful.run { :result }).to be nil + end + end + + context 'when block returns falsey value' do + let(:result) { false } + + it do + expect { |block| only_once_successful.run(&block) }.to yield_control + end + + it 'runs again until block returns truthy value' do + expect(only_once_successful.run { :result }).to be :result + + expect(only_once_successful.run { :result }).to be nil + end + end + end + + context 'when run throws an exception' do + it 'propagates the exception out' do + exception = RuntimeError.new('boom') + + expect { only_once_successful.run { raise exception } }.to raise_exception(exception) + end + + it 'runs again' do + only_once_successful.run { raise 'boom' } rescue nil + + expect { |block| only_once_successful.run(&block) }.to yield_control + end + end + end + + describe '#ran?' do + context 'before running once' do + it do + expect(only_once_successful.ran?).to be false + end + end + + context 'after running once' do + let(:result) { nil } + + before do + only_once_successful.run { result } + end + + context 'when block returns truthy value' do + let(:result) { true } + + it do + expect(only_once_successful.ran?).to be true + end + end + + context 'when block returns falsey value' do + it do + expect(only_once_successful.ran?).to be false + end + end + end + end +end From 8b7a50e7c3203dfd65da9c54fc7856a850857997 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Mon, 17 Jun 2024 14:09:57 +0200 Subject: [PATCH 16/32] ensure that app-started event is sent at most once, flush events before stopping worker --- lib/datadog/core/telemetry/component.rb | 17 ++-- lib/datadog/core/telemetry/worker.rb | 43 ++++++---- sig/datadog/core/telemetry/worker.rbs | 5 ++ spec/datadog/core/telemetry/component_spec.rb | 4 +- spec/datadog/core/telemetry/worker_spec.rb | 78 ++++++++++++++++++- 5 files changed, 116 insertions(+), 31 deletions(-) diff --git a/lib/datadog/core/telemetry/component.rb b/lib/datadog/core/telemetry/component.rb index 6324f8ac795..f457a434ce4 100644 --- a/lib/datadog/core/telemetry/component.rb +++ b/lib/datadog/core/telemetry/component.rb @@ -28,6 +28,7 @@ def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: tru heartbeat_interval_seconds: heartbeat_interval_seconds, emitter: Emitter.new ) + @worker.start end def disable! @@ -38,26 +39,24 @@ def disable! def started! return if !@enabled || forked? - @worker.start @worker.enqueue(Event::AppDependenciesLoaded.new) if @dependency_collection @started = true end - def emit_closing! - return if !@enabled || forked? - - @worker.enqueue(Event::AppClosing.new) - end - def stop! return if @stopped - # gracefully stop the worker and send leftover events - @worker.stop + @worker.stop(true) @stopped = true end + def emit_closing! + return if !@enabled || forked? + + @worker.enqueue(Event::AppClosing.new) + end + def integrations_change! return if !@enabled || forked? diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index da0bc7dd455..8ba8e8723b1 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -2,6 +2,7 @@ require_relative 'event' +require_relative '../utils/only_once_successful' require_relative '../workers/polling' require_relative '../workers/queue' @@ -15,6 +16,8 @@ class Worker DEFAULT_BUFFER_MAX_SIZE = 1000 + TELEMETRY_STARTED_ONCE = Utils::OnlyOnceSuccessful.new + def initialize( heartbeat_interval_seconds:, emitter:, @@ -24,8 +27,6 @@ def initialize( ) @emitter = emitter - @sent_started_event = false - # Workers::Polling settings self.enabled = enabled # Workers::IntervalLoop settings @@ -48,6 +49,8 @@ def start def stop(force_stop = false, timeout = @shutdown_timeout) buffer.close if running? + flush_events(dequeue) if work_pending? + super end @@ -56,7 +59,7 @@ def enqueue(event) end def sent_started_event? - @sent_started_event + TELEMETRY_STARTED_ONCE.ran? end private @@ -89,24 +92,25 @@ def heartbeat! def started! return unless enabled? - res = send_event(Event::AppStarted.new) + TELEMETRY_STARTED_ONCE.run do + res = send_event(Event::AppStarted.new) - if res.not_found? # Telemetry is only supported by agent versions 7.34 and up - Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.') - self.enabled = false - elsif res.ok? - Datadog.logger.debug('Telemetry app-started event is successfully sent') - @sent_started_event = true - else - Datadog.logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...') + if res.ok? + Datadog.logger.debug('Telemetry app-started event is successfully sent') + true + else + Datadog.logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...') + false + end end end def send_event(event) - Datadog.logger.debug { "Sending telemetry event: #{event}" } - response = @emitter.request(event) - Datadog.logger.debug { "Received response: #{response}" } - response + res = @emitter.request(event) + + disable_on_not_found!(res) + + res end def dequeue @@ -120,6 +124,13 @@ def buffer_klass Core::Buffer::ThreadSafe end end + + def disable_on_not_found!(response) + return unless response.not_found? + + Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.') + self.enabled = false + end end end end diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index 9220dfeea09..b804a19664d 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -8,6 +8,7 @@ module Datadog include Core::Workers::IntervalLoop include Core::Workers::Queue + TELEMETRY_STARTED_ONCE: Datadog::Core::Utils::OnlyOnceSuccessful DEFAULT_BUFFER_MAX_SIZE: 1000 @emitter: Emitter @@ -23,6 +24,8 @@ module Datadog def enqueue: (Event::Base event) -> void + def dequeue: () -> Array[Event::Base] + private def heartbeat!: () -> void @@ -33,6 +36,8 @@ module Datadog def send_event: (Event::Base event) -> Datadog::Core::Telemetry::Http::Adapters::Net::Response + def disable_on_not_found!: (Datadog::Core::Telemetry::Http::Adapters::Net::Response response) -> void + def buffer_klass: () -> untyped end end diff --git a/spec/datadog/core/telemetry/component_spec.rb b/spec/datadog/core/telemetry/component_spec.rb index 5d1f7f013f7..33f4544ec6e 100644 --- a/spec/datadog/core/telemetry/component_spec.rb +++ b/spec/datadog/core/telemetry/component_spec.rb @@ -82,7 +82,7 @@ it do started! - expect(worker).to_not have_received(:start) + expect(worker).to_not have_received(:enqueue) end end @@ -118,7 +118,7 @@ expect_in_fork do telemetry.started! - expect(worker).to_not have_received(:start) + expect(worker).to_not have_received(:enqueue) end end end diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index ba389c23801..6008cefd892 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -42,8 +42,10 @@ end after do - worker.stop(true, 0) + worker.stop(true) worker.join + + Datadog::Core::Telemetry::Worker::TELEMETRY_STARTED_ONCE.send(:reset_ran_once_state_for_tests) end describe '.new' do @@ -97,19 +99,19 @@ end it 'always sends heartbeat event after started event' do - @sent_hearbeat = false + sent_hearbeat = false allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do # app-started was already sent by now expect(worker.sent_started_event?).to be(true) - @sent_hearbeat = true + sent_hearbeat = true response end worker.start - try_wait_until { @sent_hearbeat } + try_wait_until { sent_hearbeat } end end @@ -124,6 +126,42 @@ expect(@received_heartbeat).to be(false) end end + + context 'several workers running' do + it 'sends single started event' do + started_events = 0 + allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppStarted)) do + started_events += 1 + + response + end + + heartbeat_events = 0 + allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do + heartbeat_events += 1 + + response + end + + workers = Array.new(3) do + described_class.new( + enabled: enabled, + heartbeat_interval_seconds: heartbeat_interval_seconds, + emitter: emitter + ) + end + workers.each(&:start) + + try_wait_until { heartbeat_events >= 3 } + + expect(started_events).to be(1) + + workers.each do |w| + w.stop(true, 0) + w.join + end + end + end end context 'when disabled' do @@ -137,6 +175,36 @@ end end + describe '#stop' do + let(:heartbeat_interval_seconds) { 3 } + + it 'flushes events and stops the worker' do + events_received = 0 + allow(emitter).to receive(:request).with( + an_instance_of(Datadog::Core::Telemetry::Event::AppIntegrationsChange) + ) do + events_received += 1 + + response + end + + worker.start + + worker.enqueue(Datadog::Core::Telemetry::Event::AppIntegrationsChange.new) + worker.stop(true) + + try_wait_until { !worker.running? } + + expect(worker).to have_attributes( + enabled?: true, + loop_base_interval: heartbeat_interval_seconds, + run_async?: false, + running?: false, + started?: true + ) + end + end + describe '#enqueue' do it 'adds events to the buffer and flushes them later' do events_received = 0 @@ -144,6 +212,8 @@ an_instance_of(Datadog::Core::Telemetry::Event::AppIntegrationsChange) ) do events_received += 1 + + response end worker.start From 69adca1242c97f25a502feb2b144974d2dabe8b8 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Mon, 17 Jun 2024 16:04:27 +0200 Subject: [PATCH 17/32] remove Telemetry::Component.started! as right now it just contains dependency collection event logic, move it to the Worker --- lib/datadog/core/configuration.rb | 20 +----- lib/datadog/core/telemetry/component.rb | 13 +--- lib/datadog/core/telemetry/worker.rb | 5 ++ sig/datadog/core/telemetry/component.rbs | 4 -- sig/datadog/core/telemetry/worker.rbs | 3 +- spec/datadog/core/configuration_spec.rb | 11 --- spec/datadog/core/telemetry/component_spec.rb | 68 ++----------------- spec/datadog/core/telemetry/worker_spec.rb | 31 ++++++++- 8 files changed, 48 insertions(+), 107 deletions(-) diff --git a/lib/datadog/core/configuration.rb b/lib/datadog/core/configuration.rb index bcaa6432564..19d3ff27110 100644 --- a/lib/datadog/core/configuration.rb +++ b/lib/datadog/core/configuration.rb @@ -84,23 +84,16 @@ def configure configuration = self.configuration yield(configuration) - built_components = false - - components = safely_synchronize do |write_components| + safely_synchronize do |write_components| write_components.call( if components? replace_components!(configuration, @components) else - components = build_components(configuration) - built_components = true - components + build_components(configuration) end ) end - # Should only be called the first time components are built - components.telemetry.started! if built_components - configuration end @@ -200,20 +193,13 @@ def components(allow_initialization: true) current_components = COMPONENTS_READ_LOCK.synchronize { defined?(@components) && @components } return current_components if current_components || !allow_initialization - built_components = false - - components = safely_synchronize do |write_components| + safely_synchronize do |write_components| if defined?(@components) && @components @components else - built_components = true write_components.call(build_components(configuration)) end end - - # Should only be called the first time components are built - components&.telemetry&.started! if built_components - components end private diff --git a/lib/datadog/core/telemetry/component.rb b/lib/datadog/core/telemetry/component.rb index f457a434ce4..0d5046e4391 100644 --- a/lib/datadog/core/telemetry/component.rb +++ b/lib/datadog/core/telemetry/component.rb @@ -20,13 +20,12 @@ class Component def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: true) @enabled = enabled @stopped = false - @started = false - @dependency_collection = dependency_collection @worker = Telemetry::Worker.new( enabled: @enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, - emitter: Emitter.new + emitter: Emitter.new, + dependency_collection: dependency_collection ) @worker.start end @@ -36,14 +35,6 @@ def disable! @worker.enabled = false end - def started! - return if !@enabled || forked? - - @worker.enqueue(Event::AppDependenciesLoaded.new) if @dependency_collection - - @started = true - end - def stop! return if @stopped diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 8ba8e8723b1..f2bd7821b4c 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -21,11 +21,13 @@ class Worker def initialize( heartbeat_interval_seconds:, emitter:, + dependency_collection:, enabled: true, shutdown_timeout: Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT, buffer_size: DEFAULT_BUFFER_MAX_SIZE ) @emitter = emitter + @dependency_collection = dependency_collection # Workers::Polling settings self.enabled = enabled @@ -97,6 +99,9 @@ def started! if res.ok? Datadog.logger.debug('Telemetry app-started event is successfully sent') + + enqueue(Event::AppDependenciesLoaded.new) if @dependency_collection + true else Datadog.logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...') diff --git a/sig/datadog/core/telemetry/component.rbs b/sig/datadog/core/telemetry/component.rbs index 614ac20d691..4d411d32578 100644 --- a/sig/datadog/core/telemetry/component.rbs +++ b/sig/datadog/core/telemetry/component.rbs @@ -3,8 +3,6 @@ module Datadog module Telemetry class Component @enabled: bool - @dependency_collection: bool - @started: bool @stopped: bool @worker: Datadog::Core::Telemetry::Worker @@ -18,8 +16,6 @@ module Datadog def client_configuration_change!: (Enumerable[[String, Numeric | bool | String]] changes) -> void - def started!: () -> void - def emit_closing!: () -> void def stop!: () -> void diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index b804a19664d..01c5107e990 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -15,8 +15,9 @@ module Datadog @sent_started_event: bool @shutdown_timeout: Integer @buffer_size: Integer + @dependency_collection: bool - def initialize: (?enabled: bool, heartbeat_interval_seconds: Numeric, emitter: Emitter, ?shutdown_timeout: Integer, ?buffer_size: Integer) -> void + def initialize: (?enabled: bool, heartbeat_interval_seconds: Numeric, emitter: Emitter, ?shutdown_timeout: Integer, ?buffer_size: Integer, dependency_collection: bool) -> void def start: () -> void diff --git a/spec/datadog/core/configuration_spec.rb b/spec/datadog/core/configuration_spec.rb index ace1b5066b9..51a2b44cb01 100644 --- a/spec/datadog/core/configuration_spec.rb +++ b/spec/datadog/core/configuration_spec.rb @@ -11,7 +11,6 @@ let(:telemetry) { instance_double(Datadog::Core::Telemetry::Component) } before do - allow(telemetry).to receive(:started!) allow(telemetry).to receive(:stop!) allow(telemetry).to receive(:emit_closing!) allow(Datadog::Core::Telemetry::Component).to receive(:new).and_return(telemetry) @@ -41,10 +40,6 @@ end it do - # We cannot mix `expect().to_not` with `expect().to(...).ordered`. - # One way around that is to force the method to raise an error if it's ever called. - allow(telemetry).to receive(:started!).and_raise('Should not be called') - # Components should have changed expect { configure } .to change { test_class.send(:components) } @@ -84,7 +79,6 @@ .with(test_class.configuration) expect(new_components).to_not have_received(:shutdown!) - expect(telemetry).to have_received(:started!) end end end @@ -501,8 +495,6 @@ describe '#components' do context 'when components are not initialized' do it 'initializes the components' do - expect(telemetry).to receive(:started!) - test_class.send(:components) expect(test_class.send(:components?)).to be true @@ -510,8 +502,6 @@ context 'when allow_initialization is false' do it 'does not initialize the components' do - expect(telemetry).to_not receive(:started!) - test_class.send(:components, allow_initialization: false) expect(test_class.send(:components?)).to be false @@ -527,7 +517,6 @@ it 'returns the components without touching the COMPONENTS_WRITE_LOCK' do described_class.const_get(:COMPONENTS_WRITE_LOCK).lock - expect(telemetry).to_not receive(:started!) expect(test_class.send(:components)).to_not be_nil end end diff --git a/spec/datadog/core/telemetry/component_spec.rb b/spec/datadog/core/telemetry/component_spec.rb index 33f4544ec6e..ba17d37c2f4 100644 --- a/spec/datadog/core/telemetry/component_spec.rb +++ b/spec/datadog/core/telemetry/component_spec.rb @@ -18,7 +18,13 @@ let(:not_found) { false } before do - allow(Datadog::Core::Telemetry::Worker).to receive(:new).and_return(worker) + allow(Datadog::Core::Telemetry::Worker).to receive(:new).with( + heartbeat_interval_seconds: heartbeat_interval_seconds, + dependency_collection: dependency_collection, + enabled: enabled, + emitter: an_instance_of(Datadog::Core::Telemetry::Emitter) + ).and_return(worker) + allow(worker).to receive(:start) allow(worker).to receive(:enqueue) allow(worker).to receive(:stop) @@ -70,60 +76,6 @@ end end - describe '#started!' do - subject(:started!) { telemetry.started! } - - after do - telemetry.stop! - end - - context 'when disabled' do - let(:enabled) { false } - it do - started! - - expect(worker).to_not have_received(:enqueue) - end - end - - context 'when enabled' do - let(:enabled) { true } - - context 'when dependency_collection is true' do - it do - started! - - expect(worker).to have_received(:enqueue).with( - an_instance_of(Datadog::Core::Telemetry::Event::AppDependenciesLoaded) - ) - end - end - - context 'when dependency_collection is false' do - let(:dependency_collection) { false } - - it do - started! - - expect(worker).not_to have_received(:enqueue) - end - end - end - - context 'when in fork' do - before { skip 'Fork not supported on current platform' unless Process.respond_to?(:fork) } - - it do - telemetry - expect_in_fork do - telemetry.started! - - expect(worker).to_not have_received(:enqueue) - end - end - end - end - describe '#emit_closing!' do subject(:emit_closing!) { telemetry.emit_closing! } @@ -157,8 +109,6 @@ it do telemetry expect_in_fork do - telemetry.started! - expect(worker).not_to have_received(:enqueue) end end @@ -209,8 +159,6 @@ it do telemetry expect_in_fork do - telemetry.started! - expect(worker).not_to have_received(:enqueue) end end @@ -251,8 +199,6 @@ it do telemetry expect_in_fork do - telemetry.started! - expect(worker).not_to have_received(:enqueue) end end diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 6008cefd892..6a1d5ca055f 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -4,12 +4,18 @@ RSpec.describe Datadog::Core::Telemetry::Worker do subject(:worker) do - described_class.new(enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, emitter: emitter) + described_class.new( + enabled: enabled, + heartbeat_interval_seconds: heartbeat_interval_seconds, + emitter: emitter, + dependency_collection: dependency_collection + ) end let(:enabled) { true } let(:heartbeat_interval_seconds) { 0.5 } let(:emitter) { double(Datadog::Core::Telemetry::Emitter) } + let(:dependency_collection) { false } let(:backend_supports_telemetry?) { true } let(:response) do @@ -113,6 +119,26 @@ try_wait_until { sent_hearbeat } end + + context 'when dependencies collection enabled' do + let(:dependency_collection) { true } + + it 'sends dependencies loaded event after started event' do + sent_dependencies = false + allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppDependenciesLoaded)) do + # app-started was already sent by now + expect(worker.sent_started_event?).to be(true) + + sent_dependencies = true + + response + end + + worker.start + + try_wait_until { sent_dependencies } + end + end end context 'when internal error returned by emitter' do @@ -147,7 +173,8 @@ described_class.new( enabled: enabled, heartbeat_interval_seconds: heartbeat_interval_seconds, - emitter: emitter + emitter: emitter, + dependency_collection: dependency_collection ) end workers.each(&:start) From 440746abadf8404595a12f33b586c3003c7558ab Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Mon, 17 Jun 2024 16:12:35 +0200 Subject: [PATCH 18/32] change the wrong expectation in workers spec --- spec/datadog/core/telemetry/worker_spec.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 6a1d5ca055f..ad5885abca8 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -222,13 +222,7 @@ try_wait_until { !worker.running? } - expect(worker).to have_attributes( - enabled?: true, - loop_base_interval: heartbeat_interval_seconds, - run_async?: false, - running?: false, - started?: true - ) + expect(events_received).to eq(1) end end From 76bfd9fc5bd1cd162909629bc3f48b351acaa3e6 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Mon, 17 Jun 2024 16:48:53 +0200 Subject: [PATCH 19/32] send app-dependencies-loaded event right after app-started event in the same thread --- lib/datadog/core/telemetry/worker.rb | 2 +- spec/datadog/core/telemetry/worker_spec.rb | 18 +++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index f2bd7821b4c..2021e139b0a 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -100,7 +100,7 @@ def started! if res.ok? Datadog.logger.debug('Telemetry app-started event is successfully sent') - enqueue(Event::AppDependenciesLoaded.new) if @dependency_collection + send_event(Event::AppDependenciesLoaded.new) if @dependency_collection true else diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index ad5885abca8..35a01e46834 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -127,7 +127,8 @@ sent_dependencies = false allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppDependenciesLoaded)) do # app-started was already sent by now - expect(worker.sent_started_event?).to be(true) + # don't use worker.sent_started_event? because it uses the same lock + expect(@received_started).to be(true) sent_dependencies = true @@ -206,23 +207,10 @@ let(:heartbeat_interval_seconds) { 3 } it 'flushes events and stops the worker' do - events_received = 0 - allow(emitter).to receive(:request).with( - an_instance_of(Datadog::Core::Telemetry::Event::AppIntegrationsChange) - ) do - events_received += 1 - - response - end - worker.start - worker.enqueue(Datadog::Core::Telemetry::Event::AppIntegrationsChange.new) + expect(worker).to receive(:flush_events).at_least(:once) worker.stop(true) - - try_wait_until { !worker.running? } - - expect(events_received).to eq(1) end end From cd239cb88dd642866c2e93f1eb1c8f10681cc4eb Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 21 Jun 2024 09:48:03 +0200 Subject: [PATCH 20/32] add limit option to OnlyOnceSuccessful util --- .../core/utils/only_once_successful.rb | 34 ++++ .../core/utils/only_once_successful.rbs | 15 ++ .../core/utils/only_once_successful_spec.rb | 176 ++++++++++++++++-- 3 files changed, 206 insertions(+), 19 deletions(-) diff --git a/lib/datadog/core/utils/only_once_successful.rb b/lib/datadog/core/utils/only_once_successful.rb index e5ac05d304b..4209cafc1ef 100644 --- a/lib/datadog/core/utils/only_once_successful.rb +++ b/lib/datadog/core/utils/only_once_successful.rb @@ -18,6 +18,14 @@ module Utils # In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives, # including an alternative implementation that is Ractor-safe once spent. class OnlyOnceSuccessful < OnlyOnce + def initialize(limit = 0) + super() + + @limit = limit + @failed = false + @retries = 0 + end + def run @mutex.synchronize do return if @ran_once @@ -25,9 +33,35 @@ def run result = yield @ran_once = !!result + if !@ran_once && limited? + @retries += 1 + check_limit! + end + result end end + + def success? + @mutex.synchronize { @ran_once && !@failed } + end + + def failed? + @mutex.synchronize { @ran_once && @failed } + end + + private + + def check_limit! + if @retries >= @limit + @failed = true + @ran_once = true + end + end + + def limited? + !@limit.nil? && @limit.positive? + end end end end diff --git a/sig/datadog/core/utils/only_once_successful.rbs b/sig/datadog/core/utils/only_once_successful.rbs index 81d24fc9094..2236b5a66b5 100644 --- a/sig/datadog/core/utils/only_once_successful.rbs +++ b/sig/datadog/core/utils/only_once_successful.rbs @@ -2,6 +2,21 @@ module Datadog module Core module Utils class OnlyOnceSuccessful < Datadog::Core::Utils::OnlyOnce + @limit: Integer + @retries: Integer + @failed: bool + + def initialize: (?Integer limit) -> void + + def success?: () -> bool + + def failed?: () -> bool + + private + + def check_limit!: () -> void + + def limited?: () -> bool end end end diff --git a/spec/datadog/core/utils/only_once_successful_spec.rb b/spec/datadog/core/utils/only_once_successful_spec.rb index bf06ff5d60c..e0adb31b41a 100644 --- a/spec/datadog/core/utils/only_once_successful_spec.rb +++ b/spec/datadog/core/utils/only_once_successful_spec.rb @@ -1,28 +1,62 @@ require 'datadog/core/utils/only_once_successful' RSpec.describe Datadog::Core::Utils::OnlyOnceSuccessful do - subject(:only_once_successful) { described_class.new } + subject(:only_once_successful) { described_class.new(limit) } + + let(:limit) { 0 } describe '#run' do - context 'before running once' do - it do - expect { |block| only_once_successful.run(&block) }.to yield_control - end + context 'when limitless' do + context 'before running once' do + it do + expect { |block| only_once_successful.run(&block) }.to yield_control + end - it 'returns the result of the block ran' do - expect(only_once_successful.run { :result }).to be :result + it 'returns the result of the block ran' do + expect(only_once_successful.run { :result }).to be :result + end end - end - context 'after running once' do - let(:result) { nil } + context 'after running once' do + let(:result) { nil } - before do - only_once_successful.run { result } + before do + only_once_successful.run { result } + end + + context 'when block returns truthy value' do + let(:result) { true } + + it do + expect { |block| only_once_successful.run(&block) }.to_not yield_control + end + + it do + expect(only_once_successful.run { :result }).to be nil + end + end + + context 'when block returns falsey value' do + let(:result) { false } + + it do + expect { |block| only_once_successful.run(&block) }.to yield_control + end + + it 'runs again until block returns truthy value' do + expect(only_once_successful.run { :result }).to be :result + + expect(only_once_successful.run { :result }).to be nil + end + end end + end + + context 'when limited' do + let(:limit) { 2 } context 'when block returns truthy value' do - let(:result) { true } + before { only_once_successful.run { true } } it do expect { |block| only_once_successful.run(&block) }.to_not yield_control @@ -33,16 +67,18 @@ end end - context 'when block returns falsey value' do - let(:result) { false } + context 'when block returns falsey value "limit" times' do + before do + limit.times do + only_once_successful.run { false } + end + end it do - expect { |block| only_once_successful.run(&block) }.to yield_control + expect { |block| only_once_successful.run(&block) }.to_not yield_control end - it 'runs again until block returns truthy value' do - expect(only_once_successful.run { :result }).to be :result - + it do expect(only_once_successful.run { :result }).to be nil end end @@ -91,5 +127,107 @@ end end end + + context 'when limited and ran "limit" times' do + let(:limit) { 2 } + + before do + limit.times do + only_once_successful.run { false } + end + end + + it do + expect(only_once_successful.ran?).to be true + end + end + end + + describe '#success?' do + context 'before running once' do + it do + expect(only_once_successful.success?).to be false + end + end + + context 'after running once' do + let(:result) { nil } + + before do + only_once_successful.run { result } + end + + context 'when block returns truthy value' do + let(:result) { true } + + it do + expect(only_once_successful.success?).to be true + end + end + + context 'when block returns falsey value' do + it do + expect(only_once_successful.success?).to be false + end + end + end + + context 'when limited and ran "limit" times' do + let(:limit) { 2 } + + before do + limit.times do + only_once_successful.run { false } + end + end + + it do + expect(only_once_successful.success?).to be false + end + end + end + + describe '#failed?' do + context 'before running once' do + it do + expect(only_once_successful.failed?).to be false + end + end + + context 'after running once' do + let(:result) { nil } + + before do + only_once_successful.run { result } + end + + context 'when block returns truthy value' do + let(:result) { true } + + it do + expect(only_once_successful.failed?).to be false + end + end + + context 'when block returns falsey value' do + it do + expect(only_once_successful.failed?).to be false + end + end + end + + context 'when limited and ran "limit" times' do + let(:limit) { 2 } + + before do + limit.times do + only_once_successful.run { false } + end + end + + it do + expect(only_once_successful.failed?).to be true + end + end end end From 71c9e5cfc6ab2ea052c67919d8193d55a249f7c4 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 21 Jun 2024 10:37:14 +0200 Subject: [PATCH 21/32] limit telemetry app-started event retries --- lib/datadog/core/telemetry/worker.rb | 15 ++++- .../core/utils/only_once_successful.rb | 8 +++ sig/datadog/core/telemetry/worker.rbs | 3 + spec/datadog/core/telemetry/worker_spec.rb | 65 +++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 2021e139b0a..46d7721ee40 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -15,8 +15,9 @@ class Worker include Core::Workers::Polling DEFAULT_BUFFER_MAX_SIZE = 1000 + APP_STARTED_EVENT_RETRIES = 10 - TELEMETRY_STARTED_ONCE = Utils::OnlyOnceSuccessful.new + TELEMETRY_STARTED_ONCE = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES) def initialize( heartbeat_interval_seconds:, @@ -61,7 +62,11 @@ def enqueue(event) end def sent_started_event? - TELEMETRY_STARTED_ONCE.ran? + TELEMETRY_STARTED_ONCE.success? + end + + def failed_to_start? + TELEMETRY_STARTED_ONCE.failed? end private @@ -94,6 +99,12 @@ def heartbeat! def started! return unless enabled? + if failed_to_start? + Datadog.logger.debug('Telemetry app-started event exhausted retries, disabling telemetry worker') + self.enabled = false + return + end + TELEMETRY_STARTED_ONCE.run do res = send_event(Event::AppStarted.new) diff --git a/lib/datadog/core/utils/only_once_successful.rb b/lib/datadog/core/utils/only_once_successful.rb index 4209cafc1ef..ed8b4141963 100644 --- a/lib/datadog/core/utils/only_once_successful.rb +++ b/lib/datadog/core/utils/only_once_successful.rb @@ -62,6 +62,14 @@ def check_limit! def limited? !@limit.nil? && @limit.positive? end + + def reset_ran_once_state_for_tests + @mutex.synchronize do + @ran_once = false + @failed = false + @retries = 0 + end + end end end end diff --git a/sig/datadog/core/telemetry/worker.rbs b/sig/datadog/core/telemetry/worker.rbs index 01c5107e990..822b9fece95 100644 --- a/sig/datadog/core/telemetry/worker.rbs +++ b/sig/datadog/core/telemetry/worker.rbs @@ -9,6 +9,7 @@ module Datadog include Core::Workers::Queue TELEMETRY_STARTED_ONCE: Datadog::Core::Utils::OnlyOnceSuccessful + APP_STARTED_EVENT_RETRIES: 10 DEFAULT_BUFFER_MAX_SIZE: 1000 @emitter: Emitter @@ -23,6 +24,8 @@ module Datadog def sent_started_event?: () -> bool + def failed_to_start?: () -> bool + def enqueue: (Event::Base event) -> void def dequeue: () -> Array[Event::Base] diff --git a/spec/datadog/core/telemetry/worker_spec.rb b/spec/datadog/core/telemetry/worker_spec.rb index 35a01e46834..e73fec0888e 100644 --- a/spec/datadog/core/telemetry/worker_spec.rb +++ b/spec/datadog/core/telemetry/worker_spec.rb @@ -120,6 +120,71 @@ try_wait_until { sent_hearbeat } end + context 'when app-started event fails' do + it 'retries' do + expect(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) + .and_return( + double( + Datadog::Core::Telemetry::Http::Adapters::Net::Response, + not_found?: false, + ok?: false + ) + ).once + + expect(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) do + @received_started = true + + response + end + + sent_hearbeat = false + allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do + # app-started was already sent by now + expect(@received_started).to be(true) + + sent_hearbeat = true + + response + end + + worker.start + + try_wait_until { sent_hearbeat } + end + end + + context 'when app-started event exhausted retries' do + let(:heartbeat_interval_seconds) { 0.1 } + + it 'stops retrying, never sends heartbeat, and disables worker' do + expect(emitter).to receive(:request).with(an_instance_of(Datadog::Core::Telemetry::Event::AppStarted)) + .and_return( + double( + Datadog::Core::Telemetry::Http::Adapters::Net::Response, + not_found?: false, + ok?: false + ) + ).exactly(described_class::APP_STARTED_EVENT_RETRIES).times + + sent_hearbeat = false + allow(emitter).to receive(:request).with(kind_of(Datadog::Core::Telemetry::Event::AppHeartbeat)) do + # app-started was already sent by now + expect(@received_started).to be(true) + + sent_hearbeat = true + + response + end + + worker.start + + try_wait_until { !worker.enabled? } + + expect(sent_hearbeat).to be(false) + expect(worker.failed_to_start?).to be(true) + end + end + context 'when dependencies collection enabled' do let(:dependency_collection) { true } From f4d349a6d5b9e224618bb5faabe9a2400854eead Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Fri, 21 Jun 2024 10:56:40 +0200 Subject: [PATCH 22/32] do not instantiate empty array every time when sending events --- lib/datadog/core/telemetry/worker.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index 46d7721ee40..ed1ce11f0d8 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -82,10 +82,11 @@ def perform(*events) end def flush_events(events) + return if events.nil? return if !enabled? || !sent_started_event? Datadog.logger.debug { "Sending #{events&.count} telemetry events" } - (events || []).each do |event| + events.each do |event| send_event(event) end end From 3ccf58ba4dd64b4d716a9c5faeb433ccf3a8214b Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Tue, 25 Jun 2024 12:19:11 +0200 Subject: [PATCH 23/32] lower HTTP timeout for telemetry worker --- lib/datadog/core/telemetry/http/adapters/net.rb | 2 +- lib/datadog/core/telemetry/worker.rb | 2 ++ sig/datadog/core/telemetry/http/adapters/net.rbs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/datadog/core/telemetry/http/adapters/net.rb b/lib/datadog/core/telemetry/http/adapters/net.rb index e49b321c436..3aa65e6d49d 100644 --- a/lib/datadog/core/telemetry/http/adapters/net.rb +++ b/lib/datadog/core/telemetry/http/adapters/net.rb @@ -15,7 +15,7 @@ class Net :timeout, :ssl - DEFAULT_TIMEOUT = 30 + DEFAULT_TIMEOUT = 2 def initialize(hostname:, port: nil, timeout: DEFAULT_TIMEOUT, ssl: true) @hostname = hostname diff --git a/lib/datadog/core/telemetry/worker.rb b/lib/datadog/core/telemetry/worker.rb index ed1ce11f0d8..57633251ac7 100644 --- a/lib/datadog/core/telemetry/worker.rb +++ b/lib/datadog/core/telemetry/worker.rb @@ -58,6 +58,8 @@ def stop(force_stop = false, timeout = @shutdown_timeout) end def enqueue(event) + return if !enabled? || forked? + buffer.push(event) end diff --git a/sig/datadog/core/telemetry/http/adapters/net.rbs b/sig/datadog/core/telemetry/http/adapters/net.rbs index 5cf50e53adf..311c5989f95 100644 --- a/sig/datadog/core/telemetry/http/adapters/net.rbs +++ b/sig/datadog/core/telemetry/http/adapters/net.rbs @@ -14,7 +14,7 @@ module Datadog attr_reader ssl: bool - DEFAULT_TIMEOUT: 30 + DEFAULT_TIMEOUT: 2 def initialize: (hostname: String, ?port: Integer?, ?timeout: Float | Integer, ?ssl: bool?) -> void From 5729921839d6ff09f724fc4ff022ad21884fcc4e Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Tue, 25 Jun 2024 13:57:54 +0200 Subject: [PATCH 24/32] do not run httprb spec for jruby --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index ee43950a83a..919a85f6403 100644 --- a/Rakefile +++ b/Rakefile @@ -101,7 +101,7 @@ TEST_METADATA = { 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'httprb' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'kafka' => { 'activesupport' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' From 3b4e4d4007fe603daef835698ac9df4a93438be4 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Tue, 25 Jun 2024 14:12:26 +0200 Subject: [PATCH 25/32] skip more http specs for jruby --- Rakefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Rakefile b/Rakefile index 919a85f6403..f4ac22207b2 100644 --- a/Rakefile +++ b/Rakefile @@ -67,13 +67,13 @@ TEST_METADATA = { 'elasticsearch-8' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'ethon' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'excon' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'faraday' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby', + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby', 'contrib-old' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ❌ 3.0 / ❌ 3.1 / ❌ 3.2 / ❌ 3.3 / ❌ 3.4 / ✅ jruby' }, 'grape' => { @@ -95,10 +95,10 @@ TEST_METADATA = { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'http' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'httpclient' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'httprb' => { 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' @@ -146,7 +146,7 @@ TEST_METADATA = { 'resque2-redis4' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'rest_client' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'roda' => { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' @@ -167,7 +167,7 @@ TEST_METADATA = { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'stripe' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'sucker_punch' => { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' From 8b682b628b4a00d0d07b8eccb47bc01b0a1efba5 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Mon, 24 Jun 2024 13:34:15 +0200 Subject: [PATCH 26/32] add generate-metrics event --- lib/datadog/core/telemetry/event.rb | 20 ++++++++++++++++++++ sig/datadog/core/telemetry/event.rbs | 7 +++++++ spec/datadog/core/telemetry/event_spec.rb | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/lib/datadog/core/telemetry/event.rb b/lib/datadog/core/telemetry/event.rb index 49b292878bb..e04aaf0cc08 100644 --- a/lib/datadog/core/telemetry/event.rb +++ b/lib/datadog/core/telemetry/event.rb @@ -286,6 +286,26 @@ def type 'app-closing' end end + + # Telemetry class for the 'generate-metrics' event + class GenerateMetrics < Base + def type + 'generate-metrics' + end + + def initialize(namespace, metric_series) + super() + @namespace = namespace + @metric_series = metric_series + end + + def payload(_) + { + namespace: @namespace, + series: @metric_series.map(&:to_h) + } + end + end end end end diff --git a/sig/datadog/core/telemetry/event.rbs b/sig/datadog/core/telemetry/event.rbs index 4e0e3824109..ac432ef0488 100644 --- a/sig/datadog/core/telemetry/event.rbs +++ b/sig/datadog/core/telemetry/event.rbs @@ -55,6 +55,13 @@ module Datadog class AppClosing < Base end + + class GenerateMetrics < Base + @namespace: String + @metric_series: Enumerable[Hash[Symbol, untyped]] + + def initialize: (String namespace, Enumerable[Hash[Symbol, untyped]] metric_series) -> void + end end end end diff --git a/spec/datadog/core/telemetry/event_spec.rb b/spec/datadog/core/telemetry/event_spec.rb index 32d83e54fc4..798d1e91e73 100644 --- a/spec/datadog/core/telemetry/event_spec.rb +++ b/spec/datadog/core/telemetry/event_spec.rb @@ -207,4 +207,22 @@ def contain_configuration(*array) is_expected.to eq({}) end end + + context 'GenerateMetrics' do + let(:event) { described_class::GenerateMetrics.new(namespace, metric_series) } + + let(:namespace) { 'general' } + let(:metric_name) { 'request_count' } + let(:points) { [[123123123, 33]] } + let(:metric_series) { [{ metric: metric_name, points: points }] } + + it do + is_expected.to eq( + { + namespace: namespace, + series: metric_series + } + ) + end + end end From f103ed5b4eb14288f6246f3dbf897c1116455130 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Mon, 24 Jun 2024 14:54:31 +0200 Subject: [PATCH 27/32] add distributions event --- lib/datadog/core/telemetry/event.rb | 7 +++++++ sig/datadog/core/telemetry/event.rbs | 3 +++ spec/datadog/core/telemetry/event_spec.rb | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/lib/datadog/core/telemetry/event.rb b/lib/datadog/core/telemetry/event.rb index e04aaf0cc08..60a9886eb1e 100644 --- a/lib/datadog/core/telemetry/event.rb +++ b/lib/datadog/core/telemetry/event.rb @@ -306,6 +306,13 @@ def payload(_) } end end + + # Telemetry class for the 'distributions' event + class Distributions < GenerateMetrics + def type + 'distributions' + end + end end end end diff --git a/sig/datadog/core/telemetry/event.rbs b/sig/datadog/core/telemetry/event.rbs index ac432ef0488..61b2e34588d 100644 --- a/sig/datadog/core/telemetry/event.rbs +++ b/sig/datadog/core/telemetry/event.rbs @@ -62,6 +62,9 @@ module Datadog def initialize: (String namespace, Enumerable[Hash[Symbol, untyped]] metric_series) -> void end + + class Distributions < GenerateMetrics + end end end end diff --git a/spec/datadog/core/telemetry/event_spec.rb b/spec/datadog/core/telemetry/event_spec.rb index 798d1e91e73..50ae1265539 100644 --- a/spec/datadog/core/telemetry/event_spec.rb +++ b/spec/datadog/core/telemetry/event_spec.rb @@ -225,4 +225,22 @@ def contain_configuration(*array) ) end end + + context 'Distributions' do + let(:event) { described_class::Distributions.new(namespace, metric_series) } + + let(:namespace) { 'general' } + let(:metric_name) { 'request_duration' } + let(:points) { [13, 14, 15, 16] } + let(:metric_series) { [{ metric: metric_name, points: points }] } + + it do + is_expected.to eq( + { + namespace: namespace, + series: metric_series + } + ) + end + end end From b9ea6f14e32a502e4a3aaa9b0c6de7cd77dbd0fa Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Tue, 25 Jun 2024 12:00:21 +0200 Subject: [PATCH 28/32] first pass on metrics data model --- lib/datadog/core/telemetry/metric.rb | 145 ++++++++++++++++++++++++++ sig/datadog/core/telemetry/metric.rbs | 102 ++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 lib/datadog/core/telemetry/metric.rb create mode 100644 sig/datadog/core/telemetry/metric.rbs diff --git a/lib/datadog/core/telemetry/metric.rb b/lib/datadog/core/telemetry/metric.rb new file mode 100644 index 00000000000..6ad519d9156 --- /dev/null +++ b/lib/datadog/core/telemetry/metric.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +module Datadog + module Core + module Telemetry + # Telemetry metrics data model + module Metric + def self.metric_id(type, name, tags = []) + "#{type}:#{name}:#{tags.join(',')}" + end + + # Base class for all metric types + class Base + attr_reader :name, :tags, :values, :common, :interval + + def initialize(name, tags: {}, common: true, interval: nil) + @name = name + @values = [] + @tags = tags_to_array(tags) + @common = common + @interval = interval + end + + def track(value); end + + def type; end + + def to_h + # @type var res: Hash[Symbol, untyped] + res = { + metric: name, + points: values, + type: type, + tags: tags, # move to method + common: common + } + res[:interval] = interval if interval + res + end + + private + + def tags_to_array(tags) + return tags if tags.is_a?(Array) + + tags.map { |k, v| "#{k}:#{v}" } + end + end + + # Count metric adds up all the submitted values in a time interval. This would be suitable for a + # metric tracking the number of website hits, for instance. + class Count < Base + TYPE = 'count' + + def type + TYPE + end + + def inc(value = 1) + track(value) + end + + def dec(value = 1) + track(-value) + end + + def track(value) + if values.empty? + values << [Time.now.to_i, value] + else + values[0][0] = Time.now.to_i + values[0][1] += value + end + end + end + + # A gauge type takes the last value reported during the interval. This type would make sense for tracking RAM or + # CPU usage, where taking the last value provides a representative picture of the host’s behavior during the time + # interval. + class Gauge < Base + TYPE = 'gauge' + + def type + TYPE + end + + def track(value) + if values.empty? + values << [Time.now.to_i, value] + else + values[0][0] = Time.now.to_i + values[0][1] = value + end + end + end + + # The rate type takes the count and divides it by the length of the time interval. This is useful if you’re + # interested in the number of hits per second. + class Rate < Base + TYPE = 'rate' + + def initialize(name, tags: {}, common: true, interval: nil) + super + + @value = 0 + end + + def type + TYPE + end + + def track(value = 1.0) + @value += value + + rate = interval ? @value.to_f / interval : 0.0 + @values = [[Time.now.to_i, rate]] + end + end + + # Distribution metric represents the global statistical distribution of a set of values. + class Distribution < Base + TYPE = 'distributions' + + def type + TYPE + end + + def track(value) + values << value + end + + # distribution metric data does not have type field + def to_h + { + metric: name, + points: values, + tags: tags, # move to method + common: common + } + end + end + end + end + end +end diff --git a/sig/datadog/core/telemetry/metric.rbs b/sig/datadog/core/telemetry/metric.rbs new file mode 100644 index 00000000000..3eda8cda131 --- /dev/null +++ b/sig/datadog/core/telemetry/metric.rbs @@ -0,0 +1,102 @@ +module Datadog + module Core + module Telemetry + module Metric + type metric_type = "count" | "gauge" | "rate" | "distributions" | nil + + type input_value = Integer | Float + + type metric_value = Array[input_value] + type distribution_value = input_value + + type tags_input = ::Hash[String, String] | Array[String] + + def self.metric_id: (metric_type type, String name, ?Array[String] tags) -> ::String + + class Base + @name: String + + @values: Array[untyped] + + @tags: Array[String] + + @common: bool + + @interval: Integer? + + attr_reader name: String + + attr_reader tags: Array[String] + + attr_reader values: Array[untyped] + + attr_reader common: bool + + attr_reader interval: Integer? + + def initialize: (String name, ?tags: tags_input, ?common: bool, ?interval: Integer?) -> void + + def track: (Numeric value) -> void + + def type: () -> metric_type + + def to_h: () -> Hash[Symbol, untyped] + + private + + def tags_to_array: (tags_input tags) -> Array[String] + end + + class Count < Base + TYPE: "count" + + @values: Array[metric_value] + attr_reader values: Array[metric_value] + + def type: () -> "count" + + def inc: (?::Integer value) -> void + + def dec: (?::Integer value) -> void + + def track: (Integer value) -> void + end + + class Gauge < Base + TYPE: "gauge" + + def type: () -> "gauge" + + def track: (input_value value) -> void + end + + class Rate < Base + @value: Float + + @values: Array[metric_value] + attr_reader values: Array[metric_value] + + TYPE: "rate" + + def initialize: (String name, ?tags: tags_input, ?common: bool, ?interval: Integer?) -> void + + def type: () -> "rate" + + def track: (?::Float value) -> void + end + + class Distribution < Base + TYPE: "distributions" + + @values: Array[distribution_value] + attr_reader values: Array[distribution_value] + + def type: () -> "distributions" + + def track: (input_value value) -> void + def to_h: () -> { metric: String, points: Array[distribution_value], tags: Array[String], common: bool } + end + end + end + end +end From 703a1dee10f29dd093a928298a11ff8ec10f40d4 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Tue, 25 Jun 2024 12:06:23 +0200 Subject: [PATCH 29/32] remove stale comments --- lib/datadog/core/telemetry/metric.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/datadog/core/telemetry/metric.rb b/lib/datadog/core/telemetry/metric.rb index 6ad519d9156..025a2b7b43d 100644 --- a/lib/datadog/core/telemetry/metric.rb +++ b/lib/datadog/core/telemetry/metric.rb @@ -31,7 +31,7 @@ def to_h metric: name, points: values, type: type, - tags: tags, # move to method + tags: tags, common: common } res[:interval] = interval if interval @@ -134,7 +134,7 @@ def to_h { metric: name, points: values, - tags: tags, # move to method + tags: tags, common: common } end From a4d67ec8b72021339cc2cef22b42ca8576ab8c40 Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Tue, 25 Jun 2024 13:18:57 +0200 Subject: [PATCH 30/32] specs for metric data model --- lib/datadog/core/telemetry/metric.rb | 8 +- spec/datadog/core/telemetry/metric_spec.rb | 277 +++++++++++++++++++++ 2 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 spec/datadog/core/telemetry/metric_spec.rb diff --git a/lib/datadog/core/telemetry/metric.rb b/lib/datadog/core/telemetry/metric.rb index 025a2b7b43d..ebe8ecbe533 100644 --- a/lib/datadog/core/telemetry/metric.rb +++ b/lib/datadog/core/telemetry/metric.rb @@ -3,10 +3,10 @@ module Datadog module Core module Telemetry - # Telemetry metrics data model + # Telemetry metrics data model (internal Datadog metrics for client libraries) module Metric def self.metric_id(type, name, tags = []) - "#{type}:#{name}:#{tags.join(',')}" + "#{type}::#{name}::#{tags.join(',')}" end # Base class for all metric types @@ -102,7 +102,7 @@ class Rate < Base def initialize(name, tags: {}, common: true, interval: nil) super - @value = 0 + @value = 0.0 end def type @@ -112,7 +112,7 @@ def type def track(value = 1.0) @value += value - rate = interval ? @value.to_f / interval : 0.0 + rate = interval ? @value / interval : 0.0 @values = [[Time.now.to_i, rate]] end end diff --git a/spec/datadog/core/telemetry/metric_spec.rb b/spec/datadog/core/telemetry/metric_spec.rb new file mode 100644 index 00000000000..8b77475a8f9 --- /dev/null +++ b/spec/datadog/core/telemetry/metric_spec.rb @@ -0,0 +1,277 @@ +require 'spec_helper' + +require 'datadog/core/telemetry/metric' + +RSpec.describe Datadog::Core::Telemetry::Metric do + let(:now) { 123123 } + before { allow(Time).to receive(:now).and_return(now, now + 1, now + 2, now + 3) } + + describe '.metric_id' do + subject(:metric_id) { described_class.metric_id(type, name, tags) } + + let(:type) { 'type' } + let(:name) { 'name' } + let(:tags) { ['tag1:val1', 'tag2:val2'] } + + it { is_expected.to eq('type::name::tag1:val1,tag2:val2') } + end + + describe Datadog::Core::Telemetry::Metric::Count do + subject(:metric) { described_class.new(name, tags: tags) } + + let(:name) { 'metric_name' } + let(:tags) { { tag1: 'val1', tag2: 'val2' } } + + it do + is_expected.to have_attributes( + name: name, + tags: ['tag1:val1', 'tag2:val2'], + interval: nil, + common: true, + values: [] + ) + end + + describe '#type' do + subject(:type) { metric.type } + + it { is_expected.to eq('count') } + end + + describe '#inc' do + subject(:inc) { metric.inc(value) } + + let(:value) { 5 } + + it 'tracks the value' do + expect { inc }.to change { metric.values }.from([]).to([[now, value]]) + end + + context 'incrementing again' do + it 'adds the value to the previous one and updates timestamp' do + metric.inc(value) + expect { inc }.to change { metric.values }.from([[now, value]]).to([[now + 1, value + value]]) + end + end + end + + describe '#dec' do + subject(:dec) { metric.dec(value) } + + let(:value) { 5 } + + it 'tracks the value' do + expect { dec }.to change { metric.values }.from([]).to([[now, -value]]) + end + end + + describe '#to_h' do + subject(:to_h) { metric.to_h } + let(:value) { 2 } + + before do + metric.inc(value) + end + + it do + is_expected.to eq( + metric: name, + points: [[now, 2]], + type: 'count', + tags: ['tag1:val1', 'tag2:val2'], + common: true + ) + end + end + end + + describe Datadog::Core::Telemetry::Metric::Gauge do + subject(:metric) { described_class.new(name, tags: tags, interval: interval) } + + let(:name) { 'metric_name' } + let(:tags) { { tag1: 'val1', tag2: 'val2' } } + let(:interval) { 10 } + + it do + is_expected.to have_attributes( + name: name, + tags: ['tag1:val1', 'tag2:val2'], + interval: interval, + common: true, + values: [] + ) + end + + describe '#type' do + subject(:type) { metric.type } + + it { is_expected.to eq('gauge') } + end + + describe '#track' do + subject(:track) { metric.track(value) } + + let(:value) { 5 } + + it 'tracks the value' do + expect { track }.to change { metric.values }.from([]).to([[now, value]]) + end + + context 'tracking again' do + it 'updates the value and timestamp' do + metric.track(value + 1) + expect { track }.to change { metric.values }.from([[now, value + 1]]).to([[now + 1, value]]) + end + end + end + + describe '#to_h' do + subject(:to_h) { metric.to_h } + let(:value) { 2 } + + before do + metric.track(value) + end + + it do + is_expected.to eq( + metric: name, + points: [[now, 2]], + type: 'gauge', + tags: ['tag1:val1', 'tag2:val2'], + common: true, + interval: interval + ) + end + end + end + + describe Datadog::Core::Telemetry::Metric::Rate do + subject(:metric) { described_class.new(name, tags: tags, interval: interval) } + + let(:name) { 'metric_name' } + let(:tags) { { tag1: 'val1', tag2: 'val2' } } + let(:interval) { 10 } + + it do + is_expected.to have_attributes( + name: name, + tags: ['tag1:val1', 'tag2:val2'], + interval: interval, + common: true, + values: [] + ) + end + + describe '#type' do + subject(:type) { metric.type } + + it { is_expected.to eq('rate') } + end + + describe '#track' do + subject(:track) { metric.track(value) } + + let(:value) { 5 } + + it 'tracks the rate value' do + expect { track }.to change { metric.values }.from([]).to([[now, value.to_f / interval]]) + end + + context 'tracking again' do + it 'updates the value and timestamp' do + metric.track(value) + expect { track }.to change { metric.values } + .from([[now, value.to_f / interval]]) + .to([[now + 1, (value + value).to_f / interval]]) + end + end + + context 'interval is nil' do + let(:interval) { nil } + + it 'sets rate to zero' do + expect { track }.to change { metric.values }.from([]).to([[now, 0.0]]) + end + end + end + + describe '#to_h' do + subject(:to_h) { metric.to_h } + let(:value) { 2 } + + before do + metric.track(value) + end + + it do + is_expected.to eq( + metric: name, + points: [[now, 0.2]], + type: 'rate', + tags: ['tag1:val1', 'tag2:val2'], + common: true, + interval: 10 + ) + end + end + end + + describe Datadog::Core::Telemetry::Metric::Distribution do + subject(:metric) { described_class.new(name, tags: tags) } + + let(:name) { 'metric_name' } + let(:tags) { { tag1: 'val1', tag2: 'val2' } } + + it do + is_expected.to have_attributes( + name: name, + tags: ['tag1:val1', 'tag2:val2'], + interval: nil, + common: true, + values: [] + ) + end + + describe '#type' do + subject(:type) { metric.type } + + it { is_expected.to eq('distributions') } + end + + describe '#track' do + subject(:track) { metric.track(value) } + + let(:value) { 5 } + + it 'tracks the value' do + expect { track }.to change { metric.values }.from([]).to([value]) + end + + context 'tracking again' do + it 'adds the value to the previous ones' do + metric.track(value) + expect { track }.to change { metric.values }.from([value]).to([value, value]) + end + end + end + + describe '#to_h' do + subject(:to_h) { metric.to_h } + let(:value) { 2 } + + before do + metric.track(value) + end + + it do + is_expected.to eq( + metric: name, + points: [2], + tags: ['tag1:val1', 'tag2:val2'], + common: true + ) + end + end + end +end From a7974f2dde89713cb7aa35b9fa21b820b351b9fe Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Tue, 25 Jun 2024 13:28:47 +0200 Subject: [PATCH 31/32] generate-metrics and distributions telemetry events expect collection of metrics as input --- lib/datadog/core/telemetry/metric.rb | 4 ++++ sig/datadog/core/telemetry/event.rbs | 4 ++-- spec/datadog/core/telemetry/event_spec.rb | 25 +++++++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/datadog/core/telemetry/metric.rb b/lib/datadog/core/telemetry/metric.rb index ebe8ecbe533..2c6ba8d97de 100644 --- a/lib/datadog/core/telemetry/metric.rb +++ b/lib/datadog/core/telemetry/metric.rb @@ -13,6 +13,10 @@ def self.metric_id(type, name, tags = []) class Base attr_reader :name, :tags, :values, :common, :interval + # @param name [String] metric name + # @param tags [Array|Hash{String=>String}] metric tags as hash of array of "tag:val" strings + # @param common [Boolean] true if the metric is common for all languages, false for Ruby-specific metric + # @param interval [Integer] metrics aggregation interval in seconds def initialize(name, tags: {}, common: true, interval: nil) @name = name @values = [] diff --git a/sig/datadog/core/telemetry/event.rbs b/sig/datadog/core/telemetry/event.rbs index 61b2e34588d..791f014f9a4 100644 --- a/sig/datadog/core/telemetry/event.rbs +++ b/sig/datadog/core/telemetry/event.rbs @@ -58,9 +58,9 @@ module Datadog class GenerateMetrics < Base @namespace: String - @metric_series: Enumerable[Hash[Symbol, untyped]] + @metric_series: Enumerable[Datadog::Core::Telemetry::Metric::Base] - def initialize: (String namespace, Enumerable[Hash[Symbol, untyped]] metric_series) -> void + def initialize: (String namespace, Enumerable[Datadog::Core::Telemetry::Metric::Base] metric_series) -> void end class Distributions < GenerateMetrics diff --git a/spec/datadog/core/telemetry/event_spec.rb b/spec/datadog/core/telemetry/event_spec.rb index 50ae1265539..f53bd9468ad 100644 --- a/spec/datadog/core/telemetry/event_spec.rb +++ b/spec/datadog/core/telemetry/event_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' require 'datadog/core/telemetry/event' +require 'datadog/core/telemetry/metric' RSpec.describe Datadog::Core::Telemetry::Event do let(:id) { double('seq_id') } @@ -209,36 +210,44 @@ def contain_configuration(*array) end context 'GenerateMetrics' do - let(:event) { described_class::GenerateMetrics.new(namespace, metric_series) } + let(:event) { described_class::GenerateMetrics.new(namespace, metrics) } let(:namespace) { 'general' } let(:metric_name) { 'request_count' } - let(:points) { [[123123123, 33]] } - let(:metric_series) { [{ metric: metric_name, points: points }] } + let(:metric) do + Datadog::Core::Telemetry::Metric::Count.new(metric_name, tags: { status: '200' }) + end + let(:metrics) { [metric] } + + let(:expected_metric_series) { [metric.to_h] } it do is_expected.to eq( { namespace: namespace, - series: metric_series + series: expected_metric_series } ) end end context 'Distributions' do - let(:event) { described_class::Distributions.new(namespace, metric_series) } + let(:event) { described_class::Distributions.new(namespace, metrics) } let(:namespace) { 'general' } let(:metric_name) { 'request_duration' } - let(:points) { [13, 14, 15, 16] } - let(:metric_series) { [{ metric: metric_name, points: points }] } + let(:metric) do + Datadog::Core::Telemetry::Metric::Distribution.new(metric_name, tags: { status: '200' }) + end + let(:metrics) { [metric] } + + let(:expected_metric_series) { [metric.to_h] } it do is_expected.to eq( { namespace: namespace, - series: metric_series + series: expected_metric_series } ) end From dfc9f8456a4525e440c506aa463e99c2c5e8df5d Mon Sep 17 00:00:00 2001 From: Tony Hsu Date: Wed, 26 Jun 2024 10:47:05 +0200 Subject: [PATCH 32/32] Disable ethon and httprb for jruby only --- Rakefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Rakefile b/Rakefile index f4ac22207b2..38034f2cfe3 100644 --- a/Rakefile +++ b/Rakefile @@ -70,10 +70,10 @@ TEST_METADATA = { 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'excon' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'faraday' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby', + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby', 'contrib-old' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ❌ 3.0 / ❌ 3.1 / ❌ 3.2 / ❌ 3.3 / ❌ 3.4 / ✅ jruby' }, 'grape' => { @@ -95,10 +95,10 @@ TEST_METADATA = { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' }, 'http' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'httpclient' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'httprb' => { 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' @@ -146,7 +146,7 @@ TEST_METADATA = { 'resque2-redis4' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'rest_client' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'roda' => { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' @@ -167,7 +167,7 @@ TEST_METADATA = { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'stripe' => { - 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ❌ jruby' + 'http' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby' }, 'sucker_punch' => { 'contrib' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby'