Skip to content

[OTel instrumentation] Add path params and endpoint in opts to perform_request #2179

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 48 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d119abd
Add request opts to perform_request method args
estolfo Aug 25, 2023
8b0c185
Update method template to handle create endpoint forwarding to index
estolfo Aug 25, 2023
d11af8b
Add unit test for perform request args
estolfo Aug 25, 2023
11f02e5
Update elasticsearch unit tests for additional perform_request arg
estolfo Aug 30, 2023
a63af83
Update cat specs
estolfo Aug 30, 2023
d7f1d5b
Update cluster specs
estolfo Aug 30, 2023
b25a90c
Update dangling_indices specs
estolfo Aug 30, 2023
7776328
Update data_frame specs
estolfo Aug 31, 2023
670e566
Update features specs
estolfo Aug 31, 2023
782dfb9
Update fleet specs
estolfo Aug 31, 2023
8a01b4d
Update index_lifecycle_management specs
estolfo Aug 31, 2023
25c7f86
Update indices specs
estolfo Sep 4, 2023
09e40f5
Update ingest specs
estolfo Sep 4, 2023
1599f1b
Update machine_learning specs
estolfo Sep 4, 2023
4d2e368
Update migration specs
estolfo Sep 4, 2023
94da052
Update info specs
estolfo Sep 4, 2023
aea3f4e
Update query_ruleset specs
estolfo Sep 4, 2023
b411561
Update search_application specs
estolfo Sep 4, 2023
69ee0e1
Update searchable_snapshots specs
estolfo Sep 4, 2023
f54f98e
Update security specs
estolfo Sep 4, 2023
ef5c561
Update shutdown specs
estolfo Sep 4, 2023
bb04fc6
Update snapshot specs
estolfo Sep 4, 2023
03fb742
Update sql specs
estolfo Sep 4, 2023
cf13de2
Update tasks specs
estolfo Sep 4, 2023
6c4d8d1
Delete synonyms specs
estolfo Sep 4, 2023
2eccb7c
Update transform specs
estolfo Sep 4, 2023
a9d16de
Update watcher specs
estolfo Sep 4, 2023
eb2afa5
Update top-level specs
estolfo Sep 4, 2023
56f642e
Update remaining node specs
estolfo Sep 4, 2023
d6bb5f7
Generate code using new template with additional perform_request arg
estolfo Sep 14, 2023
1133858
Add docs for native open telemetry instrumentation
estolfo Sep 26, 2023
20207a9
Change image extension
estolfo Sep 26, 2023
33ea7d7
Add valid options to capture search query option
estolfo Sep 26, 2023
bb7b612
Minor docs updates
estolfo Sep 26, 2023
938b196
Remove special handling for create and just use index endpoint
estolfo Sep 28, 2023
e7b0edd
Remove opts from args when calling perform_request directly
estolfo Sep 28, 2023
2633e80
Transport object is the http adapter so expected args don't change
estolfo Sep 28, 2023
06f85d2
Only remove the last arg if it has an endpoint key
estolfo Sep 28, 2023
b05036a
Temporarily disable unit tests
estolfo Sep 28, 2023
d994178
download artifacts for unit tests
estolfo Sep 28, 2023
28cbc84
endpoint for creating a doc is index
estolfo Sep 28, 2023
19676df
download_artifacts rake task is in root
estolfo Sep 28, 2023
527a74c
Further updates to create document spec
estolfo Sep 28, 2023
e6e9e59
Document client option opentelemetry_tracer_provider
estolfo Sep 28, 2023
10aafe6
Put OpenTelemetry::TracerProvider in quotes
estolfo Sep 28, 2023
77e2dd2
Inline documentation
estolfo Sep 28, 2023
66707fa
Require >= 8.3 of elastic-transport
estolfo Sep 28, 2023
55c8651
Add example of providing tracer provider to Client#new in the docs
estolfo Sep 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ jobs:
- name: elasticsearch
run: cd elasticsearch && bundle exec rake test:all
- name: elasticsearch-api
run: cd elasticsearch-api && bundle exec rake test:spec test:platinum:unit
run: rake elasticsearch:download_artifacts[8.11.0-SNAPSHOT] && cd elasticsearch-api && bundle exec rake test:spec test:platinum:unit
51 changes: 26 additions & 25 deletions docs/basic-config.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,30 @@ can use.
[cols="<,<,<"]
|===

| **Parameter** | **Data type** | **Description**
| `adapter` | Symbol | A specific adapter for Faraday (for example, `:patron`).
| `api_key` | String, Hash | For API key Authentication. Either the base64 encoding of `id` and `api_key` joined by a colon as a string, or a hash with the `id` and `api_key` values.
| `compression` | Boolean | Whether to compress requests. Gzip compression is used. Defaults to `false`. Responses are automatically inflated if they are compressed. If a custom transport object is used, it must handle the request compression and response inflation.
| `enable_meta_header` | Boolean | Whether to enable sending the meta data header to Cloud. Defaults to `true`.
| `hosts` | String, Array | Single host passed as a string or hash, or multiple hosts passed as an array; `host` or `url` keys are also valid.
| `log` | Boolean | Whether to use the default logger. Disabled by default.
| `logger` | Object | An instance of a Logger-compatible object.
| `opaque_id_prefix` | String | Sets a prefix for X-Opaque-Id when initializing the client. This is prepended to the id you set before each request if you're using X-Opaque-Id.
| `randomize_hosts` | Boolean | Whether to shuffle connections on initialization and reload. Defaults to `false`.
| `reload_connections` | Boolean, Number | Whether to reload connections after X requests. Defaults to `false`.
| `reload_on_failure` | Boolean | Whether to reload connections after failure. Defaults to `false`.
| `request_timeout` | Integer | The request timeout to be passed to transport in options.
| `resurrect_after` | Integer | Specifies after how many seconds a dead connection should be tried again.
| `retry_on_failure` | Boolean, Number | Whether to retry X times when request fails before raising and exception. Defaults to `false`.
| `retry_on_status` | Array, Number | Specifies which status code needs to be returned to retry.
| `selector` | Constant | An instance of selector strategy implemented with {Elastic::Transport::Transport::Connections::Selector::Base}.
| `send_get_body_as` | String | Specifies the HTTP method to use for GET requests with a body. Defaults to `GET`.
| `serializer_class` | Constant | Specifies a serializer class to use. It is initialized by the transport and passed the transport instance.
| `sniffer_timeout` | Integer | Specifieds the timeout for reloading connections in seconds. Defaults to `1`.
| `trace` | Boolean | Whether to use the default tracer. Disabled by default.
| `tracer` | Object | Specifies an instance of a Logger-compatible object.
| `transport` | Object | Specifies a transport instance.
| `transport_class` | Constant | Specifies a transport class to use. It is initialized by the client and passed hosts and all arguments.
| `transport_options` | Hash | Specifies the options to be passed to the `Faraday::Connection` constructor.
| **Parameter** | **Data type** | **Description**
| `adapter` | Symbol | A specific adapter for Faraday (for example, `:patron`).
| `api_key` | String, Hash | For API key Authentication. Either the base64 encoding of `id` and `api_key` joined by a colon as a string, or a hash with the `id` and `api_key` values.
| `compression` | Boolean | Whether to compress requests. Gzip compression is used. Defaults to `false`. Responses are automatically inflated if they are compressed. If a custom transport object is used, it must handle the request compression and response inflation.
| `enable_meta_header` | Boolean | Whether to enable sending the meta data header to Cloud. Defaults to `true`.
| `hosts` | String, Array | Single host passed as a string or hash, or multiple hosts passed as an array; `host` or `url` keys are also valid.
| `log` | Boolean | Whether to use the default logger. Disabled by default.
| `logger` | Object | An instance of a Logger-compatible object.
| `opaque_id_prefix` | String | Sets a prefix for X-Opaque-Id when initializing the client. This is prepended to the id you set before each request if you're using X-Opaque-Id.
| `opentelemetry_tracer_provider` | `OpenTelemetry::Trace::TracerProvider` | An explicit TracerProvider to use instead of the global one with OpenTelemetry. This enables better dependency injection and simplifies testing.
| `randomize_hosts` | Boolean | Whether to shuffle connections on initialization and reload. Defaults to `false`.
| `reload_connections` | Boolean, Number | Whether to reload connections after X requests. Defaults to `false`.
| `reload_on_failure` | Boolean | Whether to reload connections after failure. Defaults to `false`.
| `request_timeout` | Integer | The request timeout to be passed to transport in options.
| `resurrect_after` | Integer | Specifies after how many seconds a dead connection should be tried again.
| `retry_on_failure` | Boolean, Number | Whether to retry X times when request fails before raising and exception. Defaults to `false`.
| `retry_on_status` | Array, Number | Specifies which status code needs to be returned to retry.
| `selector` | Constant | An instance of selector strategy implemented with {Elastic::Transport::Transport::Connections::Selector::Base}.
| `send_get_body_as` | String | Specifies the HTTP method to use for GET requests with a body. Defaults to `GET`.
| `serializer_class` | Constant | Specifies a serializer class to use. It is initialized by the transport and passed the transport instance.
| `sniffer_timeout` | Integer | Specifies the timeout for reloading connections in seconds. Defaults to `1`.
| `trace` | Boolean | Whether to use the default tracer. Disabled by default.
| `tracer` | Object | Specifies an instance of a Logger-compatible object.
| `transport` | Object | Specifies a transport instance.
| `transport_class` | Constant | Specifies a transport class to use. It is initialized by the client and passed hosts and all arguments.
| `transport_options` | Hash | Specifies the options to be passed to the `Faraday::Connection` constructor.
|===
Binary file added docs/images/otel-waterfall-retry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/otel-waterfall-with-http.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/otel-waterfall-without-http.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions docs/open-telemetry.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
[[opentelemetry]]
=== Using OpenTelemetry

You can use https://opentelemetry.io/[OpenTelemetry] to monitor the performance and behavior of your {es} requests through the Ruby Client.
The Ruby Client comes with built-in OpenTelemetry instrumentation that emits https://www.elastic.co/guide/en/apm/guide/current/apm-distributed-tracing.html[distributed tracing spans] by default.
With that, applications https://opentelemetry.io/docs/instrumentation/ruby/manual/[instrumented with OpenTelemetry] or using the https://opentelemetry.io/docs/instrumentation/ruby/automatic/[OpenTelemetry Ruby SDK] are inherently enriched with additional spans that contain insightful information about the execution of the {es} requests.

The native instrumentation in the Ruby Client follows the https://opentelemetry.io/docs/specs/semconv/database/elasticsearch/[OpenTelemetry Semantic Conventions for {es}]. In particular, the instrumentation in the client covers the logical layer of {es} requests. A single span per request is created that is processed by the service through the Ruby Client. The following image shows a trace that records the handling of two different {es} requests: a `ping` request and a `search` request.

[role="screenshot"]
image::images/otel-waterfall-without-http.png[alt="Distributed trace with Elasticsearch spans",align="center"]

Usually, OpenTelemetry auto-instrumentation modules come with instrumentation support for HTTP-level communication. In this case, in addition to the logical {es} client requests, spans will be captured for the physical HTTP requests emitted by the client. The following image shows a trace with both, {es} spans (in blue) and the corresponding HTTP-level spans (in red):

[role="screenshot"]
image::images/otel-waterfall-with-http.png[alt="Distributed trace with Elasticsearch spans",align="center"]

Advanced Ruby Client behavior such as nodes round-robin and request retries are revealed through the combination of logical {es} spans and the physical HTTP spans. The following example shows a `search` request in a scenario with two nodes:

[role="screenshot"]
image::images/otel-waterfall-retry.png[alt="Distributed trace with Elasticsearch spans",align="center"]

The first node is unavailable and results in an HTTP error, while the retry to the second node succeeds. Both HTTP requests are subsumed by the logical {es} request span (in blue).

[discrete]
==== Setup the OpenTelemetry instrumentation

When using the https://opentelemetry.io/docs/instrumentation/ruby/manual[OpenTelemetry Ruby SDK manually] or using the https://opentelemetry.io/docs/instrumentation/ruby/automatic/[OpenTelemetry Ruby Auto-Instrumentations], the Ruby Client's OpenTelemetry instrumentation is enabled by default and uses the global OpenTelemetry SDK with the global tracer provider. You can provide a tracer provider via the Ruby Client configuration option `opentelemetry_tracer_provider` when instantiating the client. This is sometimes useful for testing or other specific use cases.

[source,ruby]
------------------------------------
client = Elasticsearch::Client.new(
cloud_id: '<CloudID>',
api_key: '<ApiKey>',
opentelemetry_tracer_provider: tracer_provider
)
------------------------------------

[discrete]
==== Configuring the OpenTelemetry instrumentation

You can configure the OpenTelemetry instrumentation through Environment Variables.
The following configuration options are available.

[discrete]
[[opentelemetry-config-enable]]
===== Enable / Disable the OpenTelemetry instrumentation

With this configuration option you can enable (default) or disable the built-in OpenTelemetry instrumentation.

**Default:** `true`

|============
| Environment Variable | `OTEL_RUBY_INSTRUMENTATION_ELASTICSEARCH_ENABLED`
|============

[discrete]
===== Capture search request bodies

Per default, the built-in OpenTelemetry instrumentation does not capture request bodies due to data privacy considerations. You can use this option to enable capturing of search queries from the request bodies of {es} search requests in case you wish to gather this information regardless. The options are to capture the raw search query, sanitize the query with a default list of sensitive keys, or not capture it at all.

**Default:** `omit`

**Valid Options:** `omit`, `sanitize`, `raw`

|============
| Environment Variable | `OTEL_INSTRUMENTATION_ELASTICSEARCH_CAPTURE_SEARCH_QUERY`
|============

[discrete]
===== Sanitize the {es} search request body

You can configure the list of keys whose values are redacted when the search query is captured. Values must be comma-separated.

**Default:** `nil`

|============
| Environment Variable | `OTEL_RUBY_INSTRUMENTATION_ELASTICSEARCH_SEARCH_QUERY_SANITIZE_KEYS`
|============

Example:

```bash
OTEL_RUBY_INSTRUMENTATION_ELASTICSEARCH_SEARCH_QUERY_SANITIZE_KEYS='sensitive-key,other-sensitive-key'
```

[discrete]
==== Overhead

The OpenTelemetry instrumentation (as any other monitoring approach) may come with a slight overhead on CPU, memory, and/or latency. The overhead may only occur when the instrumentation is enabled (default) and an OpenTelemetry SDK is active in the target application. When the instrumentation is disabled or no OpenTelemetry SDK is active within the target application, monitoring overhead is not expected when using the client.

Even in cases where the instrumentation is enabled and is actively used (by an OpenTelemetry SDK), the overhead is minimal and negligible in the vast majority of cases. In edge cases where there is a noticeable overhead, the <<opentelemetry-config-enable,instrumentation can be explicitly disabled>> to eliminate any potential impact on performance.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ module Actions
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html
#
def delete(arguments = {})
request_opts = { endpoint: arguments[:endpoint] || "async_search.delete" }

defined_params = [:id].inject({}) do |set_variables, variable|
set_variables[variable] = arguments[variable] if arguments.key?(variable)
set_variables
end
request_opts[:defined_params] = defined_params unless defined_params.empty?

raise ArgumentError, "Required argument 'id' missing" unless arguments[:id]

arguments = arguments.clone
Expand All @@ -44,7 +52,7 @@ def delete(arguments = {})
params = {}

Elasticsearch::API::Response.new(
perform_request(method, path, params, body, headers)
perform_request(method, path, params, body, headers, request_opts)
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ module Actions
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html
#
def get(arguments = {})
request_opts = { endpoint: arguments[:endpoint] || "async_search.get" }

defined_params = [:id].inject({}) do |set_variables, variable|
set_variables[variable] = arguments[variable] if arguments.key?(variable)
set_variables
end
request_opts[:defined_params] = defined_params unless defined_params.empty?

raise ArgumentError, "Required argument 'id' missing" unless arguments[:id]

arguments = arguments.clone
Expand All @@ -47,7 +55,7 @@ def get(arguments = {})
params = Utils.process_params(arguments)

Elasticsearch::API::Response.new(
perform_request(method, path, params, body, headers)
perform_request(method, path, params, body, headers, request_opts)
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ module Actions
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html
#
def status(arguments = {})
request_opts = { endpoint: arguments[:endpoint] || "async_search.status" }

defined_params = [:id].inject({}) do |set_variables, variable|
set_variables[variable] = arguments[variable] if arguments.key?(variable)
set_variables
end
request_opts[:defined_params] = defined_params unless defined_params.empty?

raise ArgumentError, "Required argument 'id' missing" unless arguments[:id]

arguments = arguments.clone
Expand All @@ -44,7 +52,7 @@ def status(arguments = {})
params = {}

Elasticsearch::API::Response.new(
perform_request(method, path, params, body, headers)
perform_request(method, path, params, body, headers, request_opts)
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ module Actions
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html
#
def submit(arguments = {})
request_opts = { endpoint: arguments[:endpoint] || "async_search.submit" }

defined_params = [:index].inject({}) do |set_variables, variable|
set_variables[variable] = arguments[variable] if arguments.key?(variable)
set_variables
end
request_opts[:defined_params] = defined_params unless defined_params.empty?

arguments = arguments.clone
headers = arguments.delete(:headers) || {}

Expand All @@ -88,7 +96,7 @@ def submit(arguments = {})
params = Utils.process_params(arguments)

Elasticsearch::API::Response.new(
perform_request(method, path, params, body, headers)
perform_request(method, path, params, body, headers, request_opts)
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ module Actions
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-delete-autoscaling-policy.html
#
def delete_autoscaling_policy(arguments = {})
request_opts = { endpoint: arguments[:endpoint] || "autoscaling.delete_autoscaling_policy" }

defined_params = [:name].inject({}) do |set_variables, variable|
set_variables[variable] = arguments[variable] if arguments.key?(variable)
set_variables
end
request_opts[:defined_params] = defined_params unless defined_params.empty?

raise ArgumentError, "Required argument 'name' missing" unless arguments[:name]

arguments = arguments.clone
Expand All @@ -44,7 +52,7 @@ def delete_autoscaling_policy(arguments = {})
params = {}

Elasticsearch::API::Response.new(
perform_request(method, path, params, body, headers)
perform_request(method, path, params, body, headers, request_opts)
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ module Actions
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-get-autoscaling-capacity.html
#
def get_autoscaling_capacity(arguments = {})
request_opts = { endpoint: arguments[:endpoint] || "autoscaling.get_autoscaling_capacity" }

arguments = arguments.clone
headers = arguments.delete(:headers) || {}

Expand All @@ -39,7 +41,7 @@ def get_autoscaling_capacity(arguments = {})
params = {}

Elasticsearch::API::Response.new(
perform_request(method, path, params, body, headers)
perform_request(method, path, params, body, headers, request_opts)
)
end
end
Expand Down
Loading