Elasticsearch instrumentation metadata (#14164)

This commit is contained in:
Jay DeLuca 2025-07-14 02:44:27 -04:00 committed by GitHub
parent 1190ca248b
commit 1d3f60300a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 821 additions and 321 deletions

View File

@ -1100,6 +1100,9 @@ libraries:
default: false
elasticsearch:
- name: elasticsearch-api-client-7.16
description: This instrumentation provides client spans for Elasticsearch API
client requests for version 7 of the client. Versions 8.10 and later have native
support for OpenTelemetry.
source_path: instrumentation/elasticsearch/elasticsearch-api-client-7.16
scope:
name: io.opentelemetry.elasticsearch-api-client-7.16
@ -1108,6 +1111,7 @@ libraries:
- co.elastic.clients:elasticsearch-java:[7.16,7.17.20)
- co.elastic.clients:elasticsearch-java:[8.0.0,8.10)
- name: elasticsearch-rest-5.0
description: This instrumentation provides tracing for Elasticsearch REST clients.
source_path: instrumentation/elasticsearch/elasticsearch-rest-5.0
scope:
name: io.opentelemetry.elasticsearch-rest-5.0
@ -1122,7 +1126,23 @@ libraries:
may contain personal or sensitive information.
type: boolean
default: false
telemetry:
- when: default
spans:
- span_kind: CLIENT
attributes:
- name: db.system
type: STRING
- name: http.request.method
type: STRING
- name: server.address
type: STRING
- name: server.port
type: LONG
- name: url.full
type: STRING
- name: elasticsearch-rest-6.4
description: This instrumentation provides tracing for Elasticsearch REST clients.
source_path: instrumentation/elasticsearch/elasticsearch-rest-6.4
scope:
name: io.opentelemetry.elasticsearch-rest-6.4
@ -1135,7 +1155,23 @@ libraries:
Enable the capture of search query bodies. It is important to note that Elasticsearch queries may contain personal or sensitive information.
type: boolean
default: false
telemetry:
- when: default
spans:
- span_kind: CLIENT
attributes:
- name: db.system
type: STRING
- name: http.request.method
type: STRING
- name: server.address
type: STRING
- name: server.port
type: LONG
- name: url.full
type: STRING
- name: elasticsearch-rest-7.0
description: This instrumentation provides tracing for Elasticsearch REST clients.
source_path: instrumentation/elasticsearch/elasticsearch-rest-7.0
scope:
name: io.opentelemetry.elasticsearch-rest-7.0
@ -1150,7 +1186,24 @@ libraries:
Enable the capture of search query bodies. It is important to note that Elasticsearch queries may contain personal or sensitive information.
type: boolean
default: false
telemetry:
- when: default
spans:
- span_kind: CLIENT
attributes:
- name: db.system
type: STRING
- name: http.request.method
type: STRING
- name: server.address
type: STRING
- name: server.port
type: LONG
- name: url.full
type: STRING
- name: elasticsearch-transport-5.0
description: |
This instrumentation provides client spans for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes.
source_path: instrumentation/elasticsearch/elasticsearch-transport-5.0
scope:
name: io.opentelemetry.elasticsearch-transport-5.0
@ -1158,7 +1211,75 @@ libraries:
javaagent:
- org.elasticsearch.client:transport:[5.0.0,5.3.0)
- org.elasticsearch:elasticsearch:[5.0.0,5.3.0)
configurations:
- name: otel.instrumentation.elasticsearch.experimental-span-attributes
description: Enable the capture of experimental span attributes.
type: boolean
default: false
telemetry:
- when: default
spans:
- span_kind: CLIENT
attributes:
- name: db.operation
type: STRING
- name: db.system
type: STRING
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- when: otel.instrumentation.elasticsearch.experimental-span-attributes=true
spans:
- span_kind: CLIENT
attributes:
- name: db.operation
type: STRING
- name: db.system
type: STRING
- name: elasticsearch.action
type: STRING
- name: elasticsearch.id
type: STRING
- name: elasticsearch.request
type: STRING
- name: elasticsearch.request.indices
type: STRING
- name: elasticsearch.request.write.type
type: STRING
- name: elasticsearch.response.status
type: LONG
- name: elasticsearch.shard.replication.failed
type: LONG
- name: elasticsearch.shard.replication.successful
type: LONG
- name: elasticsearch.shard.replication.total
type: LONG
- name: elasticsearch.type
type: STRING
- name: elasticsearch.version
type: LONG
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- when: otel.semconv-stability.opt-in=database
spans:
- span_kind: CLIENT
attributes:
- name: db.operation.name
type: STRING
- name: db.system.name
type: STRING
- name: error.type
type: STRING
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- name: elasticsearch-transport-5.3
description: |
This instrumentation provides client spans for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes.
source_path: instrumentation/elasticsearch/elasticsearch-transport-5.3
scope:
name: io.opentelemetry.elasticsearch-transport-5.3
@ -1166,7 +1287,85 @@ libraries:
javaagent:
- org.elasticsearch.client:transport:[5.3.0,6.0.0)
- org.elasticsearch:elasticsearch:[5.3.0,6.0.0)
configurations:
- name: otel.instrumentation.elasticsearch.experimental-span-attributes
description: Enable the capture of experimental span attributes.
type: boolean
default: false
telemetry:
- when: default
spans:
- span_kind: CLIENT
attributes:
- name: db.operation
type: STRING
- name: db.system
type: STRING
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- when: otel.instrumentation.elasticsearch.experimental-span-attributes=true
spans:
- span_kind: CLIENT
attributes:
- name: db.operation
type: STRING
- name: db.system
type: STRING
- name: elasticsearch.action
type: STRING
- name: elasticsearch.id
type: STRING
- name: elasticsearch.request
type: STRING
- name: elasticsearch.request.indices
type: STRING
- name: elasticsearch.request.search.types
type: STRING
- name: elasticsearch.request.write.type
type: STRING
- name: elasticsearch.request.write.version
type: LONG
- name: elasticsearch.response.status
type: LONG
- name: elasticsearch.shard.broadcast.failed
type: LONG
- name: elasticsearch.shard.broadcast.successful
type: LONG
- name: elasticsearch.shard.broadcast.total
type: LONG
- name: elasticsearch.shard.replication.failed
type: LONG
- name: elasticsearch.shard.replication.successful
type: LONG
- name: elasticsearch.shard.replication.total
type: LONG
- name: elasticsearch.type
type: STRING
- name: elasticsearch.version
type: LONG
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- when: otel.semconv-stability.opt-in=database
spans:
- span_kind: CLIENT
attributes:
- name: db.operation.name
type: STRING
- name: db.system.name
type: STRING
- name: error.type
type: STRING
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- name: elasticsearch-transport-6.0
description: |
This instrumentation provides client spans for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes.
source_path: instrumentation/elasticsearch/elasticsearch-transport-6.0
scope:
name: io.opentelemetry.elasticsearch-transport-6.0
@ -1174,6 +1373,80 @@ libraries:
javaagent:
- org.elasticsearch:elasticsearch:[6.0.0,8.0.0)
- org.elasticsearch.client:transport:[6.0.0,)
configurations:
- name: otel.instrumentation.elasticsearch.experimental-span-attributes
description: Enable the capture of experimental span attributes.
type: boolean
default: false
telemetry:
- when: default
spans:
- span_kind: CLIENT
attributes:
- name: db.operation
type: STRING
- name: db.system
type: STRING
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- name: network.type
type: STRING
- when: otel.instrumentation.elasticsearch.experimental-span-attributes=true
spans:
- span_kind: CLIENT
attributes:
- name: db.operation
type: STRING
- name: db.system
type: STRING
- name: elasticsearch.action
type: STRING
- name: elasticsearch.id
type: STRING
- name: elasticsearch.request
type: STRING
- name: elasticsearch.request.indices
type: STRING
- name: elasticsearch.request.write.type
type: STRING
- name: elasticsearch.request.write.version
type: LONG
- name: elasticsearch.response.status
type: LONG
- name: elasticsearch.shard.replication.failed
type: LONG
- name: elasticsearch.shard.replication.successful
type: LONG
- name: elasticsearch.shard.replication.total
type: LONG
- name: elasticsearch.type
type: STRING
- name: elasticsearch.version
type: LONG
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- name: network.type
type: STRING
- when: otel.semconv-stability.opt-in=database
spans:
- span_kind: CLIENT
attributes:
- name: db.operation.name
type: STRING
- name: db.system.name
type: STRING
- name: error.type
type: STRING
- name: network.peer.address
type: STRING
- name: network.peer.port
type: LONG
- name: network.type
type: STRING
executors:
- name: executors
description: |

View File

@ -1,17 +1,11 @@
#!/usr/bin/env bash
# Runs selected Gradle test tasks to regenerate *.telemetry output for
# individual OpenTelemetry Java agent instrumentations.
# individual OpenTelemetry Java agent instrumentations. Some instrumentation test suites don't run
# on Apple Silicon, so we use colima to run them in an x86_64 container.
set -euo pipefail
# Oracle UCP won't run on Apple Silicon, so we need to use colima to run as x86_64
if [[ "$(uname -m)" == "arm64" || "$(uname -m)" == "aarch64" ]]; then
colima start --arch x86_64 --memory 4
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export DOCKER_HOST="unix://${HOME}/.colima/docker.sock"
fi
readonly INSTRUMENTATIONS=(
# <module path (colon-separated)> : <javaagent|library> : [ gradle-task-suffix ]
"activej-http-6.0:javaagent:test"
@ -66,16 +60,49 @@ readonly INSTRUMENTATIONS=(
"hikaricp-3.0:javaagent:testStableSemconv"
"tomcat:tomcat-jdbc:javaagent:test"
"tomcat:tomcat-jdbc:javaagent:testStableSemconv"
"oracle-ucp-11.2:javaagent:test"
"oracle-ucp-11.2:javaagent:testStableSemconv"
"oshi:javaagent:test"
"oshi:javaagent:testExperimental"
"vibur-dbcp-11.0:javaagent:test"
"vibur-dbcp-11.0:javaagent:testStableSemconv"
"elasticsearch:elasticsearch-api-client-7.16:javaagent:test"
"elasticsearch:elasticsearch-api-client-7.16:javaagent:testStableSemconv"
"elasticsearch:elasticsearch-rest-7.0:javaagent:test"
"elasticsearch:elasticsearch-transport-5.0:javaagent:test"
"elasticsearch:elasticsearch-transport-5.0:javaagent:testStableSemconv"
"elasticsearch:elasticsearch-transport-5.0:javaagent:testExperimental"
"elasticsearch:elasticsearch-transport-5.3:javaagent:test"
"elasticsearch:elasticsearch-transport-5.3:javaagent:testStableSemconv"
"elasticsearch:elasticsearch-transport-5.3:javaagent:testExperimental"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch6Test"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch6TestStableSemconv"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch6TestExperimental"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch65Test"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch65TestStableSemconv"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch65TestExperimental"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch7Test"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch7TestStableSemconv"
"elasticsearch:elasticsearch-transport-6.0:javaagent:elasticsearch7TestExperimental"
)
readonly COLIMA_INSTRUMENTATIONS=(
"elasticsearch:elasticsearch-rest-6.4:javaagent:test"
"elasticsearch:elasticsearch-rest-5.0:javaagent:test"
"oracle-ucp-11.2:javaagent:test"
"oracle-ucp-11.2:javaagent:testStableSemconv"
)
readonly TELEMETRY_DIR_NAME=".telemetry"
# Sets up colima for x86_64 architecture if on ARM
setup_colima() {
if [[ "$(uname -m)" == "arm64" || "$(uname -m)" == "aarch64" ]]; then
echo "Setting up colima for x86_64 architecture..."
colima start --arch x86_64 --memory 4
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export DOCKER_HOST="unix://${HOME}/.colima/docker.sock"
fi
}
# Splits a single descriptor into its three logical parts.
# argument $1: descriptor string (ex: "foo:bar:baz:test")
# Outputs three variables via echo:
@ -144,39 +171,88 @@ build_gradle_task() {
echo "$task"
}
# Processes a list of descriptors and returns an array of Gradle tasks
# Args: array of descriptors
# Echoes the Gradle tasks, one per line
process_descriptors() {
local descriptors=("$@")
local -a tasks=()
for descriptor in "${descriptors[@]}"; do
# Parse the descriptor into its components
if ! read -r module_path task_type task_suffix \
< <(parse_descriptor "$descriptor"); then
continue # skip any badly formed descriptors
fi
# Make sure we're starting fresh for this instrumentation
delete_existing_telemetry "$module_path"
# Build the Gradle task string and add to array
tasks+=( "$(build_gradle_task "$module_path" "$task_type" "$task_suffix")" )
done
# Echo tasks, one per line
for t in "${tasks[@]}"; do
echo "$t"
done
}
run_gradle_tasks() {
local -a tasks=("$@")
if [[ ${#tasks[@]} -eq 0 ]]; then
echo "No tasks to run"
return 0
fi
echo
echo "Running Gradle tasks:"
printf ' %s\n' "${tasks[@]}"
echo
./gradlew "${tasks[@]}" \
-PcollectMetadata=true \
--rerun-tasks --continue
}
# Cleans any stray .telemetry directories left in the repo.
find_and_remove_all_telemetry() {
echo "Removing stray .telemetry directories..."
find . -type d -name "$TELEMETRY_DIR_NAME" -exec rm -rf {} +
}
# Main
declare -a gradle_tasks=()
for descriptor in "${INSTRUMENTATIONS[@]}"; do
# Parse the descriptor into its components
if ! read -r module_path task_type task_suffix \
< <(parse_descriptor "$descriptor"); then
continue # skip any badly formed descriptors
fi
# Main execution
main() {
colima stop
# Make sure were starting fresh for this instrumentation
delete_existing_telemetry "$module_path"
# Process regular instrumentations
echo "Processing standard instrumentations..."
gradle_tasks=()
while IFS= read -r line; do
gradle_tasks+=("$line")
done < <(process_descriptors "${INSTRUMENTATIONS[@]}")
run_gradle_tasks "${gradle_tasks[@]}"
# Build the Gradle task string and queue it
gradle_tasks+=( "$(build_gradle_task "$module_path" "$task_type" "$task_suffix")" )
done
# Setup colima if needed
setup_colima
echo
echo "Running Gradle tasks:"
printf ' %s\n' "${gradle_tasks[@]}"
echo
# Process colima-specific instrumentations
echo "Processing colima instrumentations..."
gradle_tasks=()
while IFS= read -r line; do
gradle_tasks+=("$line")
done < <(process_descriptors "${COLIMA_INSTRUMENTATIONS[@]}")
run_gradle_tasks "${gradle_tasks[@]}"
./gradlew "${gradle_tasks[@]}" \
-PcollectMetadata=true \
--rerun-tasks --continue
colima stop
# uncomment the next line to remove all .telemetry directories
#find_and_remove_all_telemetry
# uncomment the next line to remove all .telemetry directories
#find_and_remove_all_telemetry
echo "Telemetry file regeneration complete."
echo "Telemetry file regeneration complete."
}
# Run main function
main "$@"

View File

@ -77,10 +77,14 @@ tasks {
withType<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database")
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
}
check {

View File

@ -0,0 +1,2 @@
description: This instrumentation provides client spans for Elasticsearch API client requests for
version 7 of the client. Versions 8.10 and later have native support for OpenTelemetry.

View File

@ -39,5 +39,8 @@ tasks {
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -1,7 +1,8 @@
description: This instrumentation provides tracing for Elasticsearch REST clients.
configurations:
- name: otel.instrumentation.elasticsearch.capture-search-query
description: |
Enable the capture of search query bodies. It is important to note that Elasticsearch queries
may contain personal or sensitive information.
type: boolean
default: false
default: false

View File

@ -38,5 +38,8 @@ dependencies {
tasks {
test {
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -1,7 +1,8 @@
description: This instrumentation provides tracing for Elasticsearch REST clients.
configurations:
- name: otel.instrumentation.elasticsearch.capture-search-query
description: >
Enable the capture of search query bodies. It is important to note that Elasticsearch queries
may contain personal or sensitive information.
type: boolean
default: false
default: false

View File

@ -41,5 +41,8 @@ dependencies {
tasks {
test {
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -1,7 +1,8 @@
description: This instrumentation provides tracing for Elasticsearch REST clients.
configurations:
- name: otel.instrumentation.elasticsearch.capture-search-query
description: >
Enable the capture of search query bodies. It is important to note that Elasticsearch queries
may contain personal or sensitive information.
type: boolean
default: false
default: false

View File

@ -51,18 +51,24 @@ dependencies {
tasks {
withType<Test>().configureEach {
// TODO run tests both with and without experimental span attributes
jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true")
// required on jdk17
jvmArgs("--add-opens=java.base/java.nio=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database")
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
}
val testExperimental by registering(Test::class) {
jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true")
systemProperty("metaDataConfig", "otel.instrumentation.elasticsearch.experimental-span-attributes=true")
}
check {
dependsOn(testStableSemconv)
dependsOn(testStableSemconv, testExperimental)
}
}

View File

@ -0,0 +1,8 @@
description: >
This instrumentation provides client spans for Elasticsearch transport client requests. Each call
produces a span named after the Elasticsearch action, enriched with transport-specific attributes.
configurations:
- name: otel.instrumentation.elasticsearch.experimental-span-attributes
description: Enable the capture of experimental span attributes.
type: boolean
default: false

View File

@ -69,19 +69,25 @@ tasks {
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
// TODO run tests both with and without experimental span attributes
jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true")
// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database,code")
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
}
val testExperimental by registering(Test::class) {
jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true")
systemProperty("metaDataConfig", "otel.instrumentation.elasticsearch.experimental-span-attributes=true")
}
check {
dependsOn(testStableSemconv)
dependsOn(testStableSemconv, testExperimental)
}
}

View File

@ -13,6 +13,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static java.util.Arrays.asList;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
@ -21,6 +22,7 @@ import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
@ -33,7 +35,7 @@ import spock.util.environment.Jvm;
@SuppressWarnings("deprecation") // using deprecated semconv
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Elasticsearch53SpringRepositoryTest {
class Elasticsearch53SpringRepositoryTest extends ElasticsearchSpringTest {
@RegisterExtension
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@ -48,7 +50,9 @@ class Elasticsearch53SpringRepositoryTest {
private static DocRepository repository() {
// when running on jdk 21 this test occasionally fails with timeout
Assumptions.assumeTrue(
Boolean.getBoolean("testLatestDeps") || !Jvm.getCurrent().isJava21Compatible());
Boolean.getBoolean("testLatestDeps")
|| !Jvm.getCurrent().isJava21Compatible()
|| Boolean.getBoolean("collectMetadata"));
DocRepository result =
testing.runWithSpan(
@ -91,10 +95,16 @@ class Elasticsearch53SpringRepositoryTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "SearchAction"),
equalTo(stringKey("elasticsearch.action"), "SearchAction"),
equalTo(stringKey("elasticsearch.request"), "SearchRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.request.search.types"), "doc"))));
equalTo(
stringKey("elasticsearch.action"), experimental("SearchAction")),
equalTo(
stringKey("elasticsearch.request"), experimental("SearchRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental("test-index")),
equalTo(
stringKey("elasticsearch.request.search.types"),
experimental("doc")))));
}
@Test
@ -116,35 +126,12 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("IndexAction")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "IndexAction"),
equalTo(stringKey("elasticsearch.action"), "IndexAction"),
equalTo(stringKey("elasticsearch.request"), "IndexRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.request.write.type"), "doc"),
equalTo(longKey("elasticsearch.request.write.version"), -3),
equalTo(longKey("elasticsearch.response.status"), 201),
equalTo(longKey("elasticsearch.shard.replication.failed"), 0),
equalTo(longKey("elasticsearch.shard.replication.successful"), 1),
equalTo(longKey("elasticsearch.shard.replication.total"), 2)),
.hasAttributesSatisfyingExactly(indexActionAssertions(201)),
span ->
span.hasName("RefreshAction")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), "RefreshAction"),
equalTo(stringKey("elasticsearch.request"), "RefreshRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(longKey("elasticsearch.shard.broadcast.failed"), 0),
equalTo(longKey("elasticsearch.shard.broadcast.successful"), 5),
equalTo(longKey("elasticsearch.shard.broadcast.total"), 10))));
.hasAttributesSatisfyingExactly(refreshActionAssertions())));
testing.clearData();
assertThat(repository.findById("1").get()).isEqualTo(doc);
@ -161,17 +148,7 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("GetAction")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(stringKey("elasticsearch.action"), "GetAction"),
equalTo(stringKey("elasticsearch.request"), "GetRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.type"), "doc"),
equalTo(stringKey("elasticsearch.id"), "1"),
equalTo(longKey("elasticsearch.version"), 1))));
.hasAttributesSatisfyingExactly(getActionAssertions(1))));
testing.clearData();
doc.setData("other data");
@ -191,35 +168,12 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("IndexAction")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "IndexAction"),
equalTo(stringKey("elasticsearch.action"), "IndexAction"),
equalTo(stringKey("elasticsearch.request"), "IndexRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.request.write.type"), "doc"),
equalTo(longKey("elasticsearch.request.write.version"), -3),
equalTo(longKey("elasticsearch.response.status"), 200),
equalTo(longKey("elasticsearch.shard.replication.failed"), 0),
equalTo(longKey("elasticsearch.shard.replication.successful"), 1),
equalTo(longKey("elasticsearch.shard.replication.total"), 2)),
.hasAttributesSatisfyingExactly(indexActionAssertions(200)),
span ->
span.hasName("RefreshAction")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), "RefreshAction"),
equalTo(stringKey("elasticsearch.request"), "RefreshRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(longKey("elasticsearch.shard.broadcast.failed"), 0),
equalTo(longKey("elasticsearch.shard.broadcast.successful"), 5),
equalTo(longKey("elasticsearch.shard.broadcast.total"), 10))),
.hasAttributesSatisfyingExactly(refreshActionAssertions())),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -231,17 +185,7 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("GetAction")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(stringKey("elasticsearch.action"), "GetAction"),
equalTo(stringKey("elasticsearch.request"), "GetRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.type"), "doc"),
equalTo(stringKey("elasticsearch.id"), "1"),
equalTo(longKey("elasticsearch.version"), 2))));
.hasAttributesSatisfyingExactly(getActionAssertions(2))));
testing.clearData();
repository.deleteById("1");
@ -264,29 +208,29 @@ class Elasticsearch53SpringRepositoryTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "DeleteAction"),
equalTo(stringKey("elasticsearch.action"), "DeleteAction"),
equalTo(stringKey("elasticsearch.request"), "DeleteRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.request.write.type"), "doc"),
equalTo(longKey("elasticsearch.request.write.version"), -3),
equalTo(longKey("elasticsearch.shard.replication.failed"), 0),
equalTo(longKey("elasticsearch.shard.replication.successful"), 1),
equalTo(longKey("elasticsearch.shard.replication.total"), 2)),
equalTo(
stringKey("elasticsearch.action"), experimental("DeleteAction")),
equalTo(
stringKey("elasticsearch.request"), experimental("DeleteRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental("test-index")),
equalTo(
stringKey("elasticsearch.request.write.type"), experimental("doc")),
equalTo(
longKey("elasticsearch.request.write.version"), experimental(-3)),
equalTo(
longKey("elasticsearch.shard.replication.failed"), experimental(0)),
equalTo(
longKey("elasticsearch.shard.replication.successful"),
experimental(1)),
equalTo(
longKey("elasticsearch.shard.replication.total"), experimental(2))),
span ->
span.hasName("RefreshAction")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), "RefreshAction"),
equalTo(stringKey("elasticsearch.request"), "RefreshRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(longKey("elasticsearch.shard.broadcast.failed"), 0),
equalTo(longKey("elasticsearch.shard.broadcast.successful"), 5),
equalTo(longKey("elasticsearch.shard.broadcast.total"), 10))),
.hasAttributesSatisfyingExactly(refreshActionAssertions())),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -303,10 +247,64 @@ class Elasticsearch53SpringRepositoryTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "SearchAction"),
equalTo(stringKey("elasticsearch.action"), "SearchAction"),
equalTo(stringKey("elasticsearch.request"), "SearchRequest"),
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.request.search.types"), "doc"))));
equalTo(
stringKey("elasticsearch.action"), experimental("SearchAction")),
equalTo(
stringKey("elasticsearch.request"), experimental("SearchRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental("test-index")),
equalTo(
stringKey("elasticsearch.request.search.types"),
experimental("doc")))));
}
private static List<AttributeAssertion> indexActionAssertions(long status) {
return new ArrayList<>(
asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "IndexAction"),
equalTo(stringKey("elasticsearch.action"), experimental("IndexAction")),
equalTo(stringKey("elasticsearch.request"), experimental("IndexRequest")),
equalTo(stringKey("elasticsearch.request.indices"), experimental("test-index")),
equalTo(stringKey("elasticsearch.request.write.type"), experimental("doc")),
equalTo(longKey("elasticsearch.request.write.version"), experimental(-3)),
equalTo(longKey("elasticsearch.response.status"), experimental(status)),
equalTo(longKey("elasticsearch.shard.replication.failed"), experimental(0)),
equalTo(longKey("elasticsearch.shard.replication.successful"), experimental(1)),
equalTo(longKey("elasticsearch.shard.replication.total"), experimental(2))));
}
private static List<AttributeAssertion> refreshActionAssertions() {
return new ArrayList<>(
asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), experimental("RefreshAction")),
equalTo(stringKey("elasticsearch.request"), experimental("RefreshRequest")),
equalTo(stringKey("elasticsearch.request.indices"), experimental("test-index")),
equalTo(longKey("elasticsearch.shard.broadcast.failed"), experimental(0)),
equalTo(longKey("elasticsearch.shard.broadcast.successful"), experimental(5)),
equalTo(longKey("elasticsearch.shard.broadcast.total"), experimental(10))));
}
private static List<AttributeAssertion> getActionAssertions(long version) {
return new ArrayList<>(
asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(stringKey("elasticsearch.action"), experimental("GetAction")),
equalTo(stringKey("elasticsearch.request"), experimental("GetRequest")),
equalTo(stringKey("elasticsearch.request.indices"), experimental("test-index")),
equalTo(stringKey("elasticsearch.type"), experimental("doc")),
equalTo(stringKey("elasticsearch.id"), experimental("1")),
equalTo(longKey("elasticsearch.version"), experimental(version))));
}
private static List<AttributeAssertion> assertFunctionName(String methodName) {

View File

@ -12,6 +12,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equal
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.awaitility.Awaitility.await;
@ -28,7 +29,6 @@ import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -69,7 +69,7 @@ import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
import spock.util.environment.Jvm;
@SuppressWarnings("deprecation") // using deprecated semconv
class Elasticsearch53SpringTemplateTest {
class Elasticsearch53SpringTemplateTest extends ElasticsearchSpringTest {
private static final Logger logger =
LoggerFactory.getLogger(Elasticsearch53SpringTemplateTest.class);
@ -157,7 +157,9 @@ class Elasticsearch53SpringTemplateTest {
void prepareTest() {
// when running on jdk 21 this test occasionally fails with timeout
Assumptions.assumeTrue(
Boolean.getBoolean("testLatestDeps") || !Jvm.getCurrent().isJava21Compatible());
Boolean.getBoolean("testLatestDeps")
|| !Jvm.getCurrent().isJava21Compatible()
|| Boolean.getBoolean("collectMetadata"));
}
@Test
@ -166,17 +168,7 @@ class Elasticsearch53SpringTemplateTest {
assertThatThrownBy(() -> template.refresh(indexName))
.isInstanceOf(IndexNotFoundException.class);
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), "RefreshAction"),
equalTo(stringKey("elasticsearch.request"), "RefreshRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName)));
List<AttributeAssertion> assertions = refreshActionAttributes(indexName);
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "org.elasticsearch.index.IndexNotFoundException"));
}
@ -244,9 +236,15 @@ class Elasticsearch53SpringTemplateTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "CreateIndexAction"),
equalTo(stringKey("elasticsearch.action"), "CreateIndexAction"),
equalTo(stringKey("elasticsearch.request"), "CreateIndexRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName))),
equalTo(
stringKey("elasticsearch.action"),
experimental("CreateIndexAction")),
equalTo(
stringKey("elasticsearch.request"),
experimental("CreateIndexRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental(indexName)))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -258,8 +256,12 @@ class Elasticsearch53SpringTemplateTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "ClusterHealthAction"),
equalTo(stringKey("elasticsearch.action"), "ClusterHealthAction"),
equalTo(stringKey("elasticsearch.request"), "ClusterHealthRequest"))),
equalTo(
stringKey("elasticsearch.action"),
experimental("ClusterHealthAction")),
equalTo(
stringKey("elasticsearch.request"),
experimental("ClusterHealthRequest")))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -271,10 +273,16 @@ class Elasticsearch53SpringTemplateTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "SearchAction"),
equalTo(stringKey("elasticsearch.action"), "SearchAction"),
equalTo(stringKey("elasticsearch.request"), "SearchRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName),
equalTo(stringKey("elasticsearch.request.search.types"), indexType))),
equalTo(
stringKey("elasticsearch.action"), experimental("SearchAction")),
equalTo(
stringKey("elasticsearch.request"), experimental("SearchRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental(indexName)),
equalTo(
stringKey("elasticsearch.request.search.types"),
experimental(indexType)))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -286,15 +294,26 @@ class Elasticsearch53SpringTemplateTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "IndexAction"),
equalTo(stringKey("elasticsearch.action"), "IndexAction"),
equalTo(stringKey("elasticsearch.request"), "IndexRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName),
equalTo(stringKey("elasticsearch.request.write.type"), indexType),
equalTo(longKey("elasticsearch.request.write.version"), -3),
equalTo(longKey("elasticsearch.response.status"), 201),
equalTo(longKey("elasticsearch.shard.replication.failed"), 0),
equalTo(longKey("elasticsearch.shard.replication.successful"), 1),
equalTo(longKey("elasticsearch.shard.replication.total"), 2))),
equalTo(stringKey("elasticsearch.action"), experimental("IndexAction")),
equalTo(
stringKey("elasticsearch.request"), experimental("IndexRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental(indexName)),
equalTo(
stringKey("elasticsearch.request.write.type"),
experimental(indexType)),
equalTo(
longKey("elasticsearch.request.write.version"), experimental(-3)),
equalTo(longKey("elasticsearch.response.status"), experimental(201)),
equalTo(
longKey("elasticsearch.shard.replication.failed"), experimental(0)),
equalTo(
longKey("elasticsearch.shard.replication.successful"),
experimental(1)),
equalTo(
longKey("elasticsearch.shard.replication.total"),
experimental(2)))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -302,16 +321,7 @@ class Elasticsearch53SpringTemplateTest {
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), "RefreshAction"),
equalTo(stringKey("elasticsearch.request"), "RefreshRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName),
equalTo(longKey("elasticsearch.shard.broadcast.failed"), 0),
equalTo(longKey("elasticsearch.shard.broadcast.successful"), 5),
equalTo(longKey("elasticsearch.shard.broadcast.total"), 10))),
refreshBroadcastActionAttributes(indexName))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -323,10 +333,38 @@ class Elasticsearch53SpringTemplateTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "SearchAction"),
equalTo(stringKey("elasticsearch.action"), "SearchAction"),
equalTo(stringKey("elasticsearch.request"), "SearchRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName),
equalTo(stringKey("elasticsearch.request.search.types"), indexType))));
equalTo(
stringKey("elasticsearch.action"), experimental("SearchAction")),
equalTo(
stringKey("elasticsearch.request"), experimental("SearchRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental(indexName)),
equalTo(
stringKey("elasticsearch.request.search.types"),
experimental(indexType)))));
}
private static List<AttributeAssertion> refreshActionAttributes(String indexName) {
return new ArrayList<>(
asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), experimental("RefreshAction")),
equalTo(stringKey("elasticsearch.request"), experimental("RefreshRequest")),
equalTo(stringKey("elasticsearch.request.indices"), experimental(indexName))));
}
private static List<AttributeAssertion> refreshBroadcastActionAttributes(String indexName) {
List<AttributeAssertion> assertions = refreshActionAttributes(indexName);
assertions.addAll(
asList(
equalTo(longKey("elasticsearch.shard.broadcast.failed"), experimental(0)),
equalTo(longKey("elasticsearch.shard.broadcast.successful"), experimental(5)),
equalTo(longKey("elasticsearch.shard.broadcast.total"), experimental(10))));
return assertions;
}
@Test
@ -407,8 +445,12 @@ class Elasticsearch53SpringTemplateTest {
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "SearchAction"),
equalTo(stringKey("elasticsearch.action"), "SearchAction"),
equalTo(stringKey("elasticsearch.request"), "SearchRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName))));
equalTo(
stringKey("elasticsearch.action"), experimental("SearchAction")),
equalTo(
stringKey("elasticsearch.request"), experimental("SearchRequest")),
equalTo(
stringKey("elasticsearch.request.indices"),
experimental(indexName)))));
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3.springdata;
abstract class ElasticsearchSpringTest {
protected static final String EXPERIMENTAL_FLAG =
"otel.instrumentation.elasticsearch.experimental-span-attributes";
protected static String experimental(String value) {
if (!Boolean.getBoolean(EXPERIMENTAL_FLAG)) {
return null;
}
return value;
}
protected static Long experimental(long value) {
if (!Boolean.getBoolean(EXPERIMENTAL_FLAG)) {
return null;
}
return value;
}
}

View File

@ -0,0 +1,8 @@
description: >
This instrumentation provides client spans for Elasticsearch transport client requests. Each call
produces a span named after the Elasticsearch action, enriched with transport-specific attributes.
configurations:
- name: otel.instrumentation.elasticsearch.experimental-span-attributes
description: Enable the capture of experimental span attributes.
type: boolean
default: false

View File

@ -97,16 +97,34 @@ testing {
tasks {
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
// TODO run tests both with and without experimental span attributes
jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true")
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database")
val testSuites = testing.suites.withType(JvmTestSuite::class)
val stableSemconvSuites = testSuites.map { suite ->
register<Test>("${suite.name}StableSemconv") {
testClassesDirs = suite.sources.output.classesDirs
classpath = suite.sources.runtimeClasspath
jvmArgs("-Dotel.semconv-stability.opt-in=database")
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
}
}
val experimentalSuites = testSuites.map { suite ->
register<Test>("${suite.name}Experimental") {
testClassesDirs = suite.sources.output.classesDirs
classpath = suite.sources.runtimeClasspath
jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true")
systemProperty("metaDataConfig", "otel.instrumentation.elasticsearch.experimental-span-attributes=true")
}
}
check {
dependsOn(testing.suites)
dependsOn(testStableSemconv)
dependsOn(testing.suites, stableSemconvSuites, experimentalSuites)
}
}

View File

@ -0,0 +1,8 @@
description: >
This instrumentation provides client spans for Elasticsearch transport client requests. Each call
produces a span named after the Elasticsearch action, enriched with transport-specific attributes.
configurations:
- name: otel.instrumentation.elasticsearch.experimental-span-attributes
description: Enable the capture of experimental span attributes.
type: boolean
default: false

View File

@ -44,6 +44,9 @@ abstract class AbstractElasticsearchClientTest {
protected static final AttributeKey<Long> ELASTICSEARCH_VERSION =
AttributeKey.longKey("elasticsearch.version");
protected static final String EXPERIMENTAL_FLAG =
"otel.instrumentation.elasticsearch.experimental-span-attributes";
@RegisterExtension
protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@ -96,6 +99,20 @@ abstract class AbstractElasticsearchClientTest {
return result.get();
}
protected static String experimental(String value) {
if (!Boolean.getBoolean(EXPERIMENTAL_FLAG)) {
return null;
}
return value;
}
protected static Long experimental(long value) {
if (!Boolean.getBoolean(EXPERIMENTAL_FLAG)) {
return null;
}
return value;
}
static class Result<RESPONSE> {
private final CountDownLatch latch = new CountDownLatch(1);
private RESPONSE response;

View File

@ -12,6 +12,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equal
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Named.named;
@ -23,7 +24,6 @@ import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
@ -76,8 +76,8 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "ClusterHealthAction"),
equalTo(ELASTICSEARCH_ACTION, "ClusterHealthAction"),
equalTo(ELASTICSEARCH_REQUEST, "ClusterHealthRequest")),
equalTo(ELASTICSEARCH_ACTION, experimental("ClusterHealthAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("ClusterHealthRequest"))),
span ->
span.hasName("callback")
.hasKind(SpanKind.INTERNAL)
@ -107,14 +107,14 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, "invalid-index")));
equalTo(ELASTICSEARCH_ACTION, experimental("GetAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("GetRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental("invalid-index"))));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "org.elasticsearch.index.IndexNotFoundException"));
@ -152,6 +152,22 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
.actionGet(TIMEOUT);
}
private static List<AttributeAssertion> getActionAttributes(
String indexName, String indexType, String id, long version) {
return new ArrayList<>(
asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, experimental("GetAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("GetRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental(indexName)),
equalTo(ELASTICSEARCH_TYPE, experimental(indexType)),
equalTo(ELASTICSEARCH_ID, experimental(id)),
equalTo(ELASTICSEARCH_VERSION, experimental(version))));
}
@Test
void elasticsearchGet() {
String indexName = "test-index";
@ -195,9 +211,9 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "CreateIndexAction"),
equalTo(ELASTICSEARCH_ACTION, "CreateIndexAction"),
equalTo(ELASTICSEARCH_REQUEST, "CreateIndexRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName))),
equalTo(ELASTICSEARCH_ACTION, experimental("CreateIndexAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("CreateIndexRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental(indexName)))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -209,8 +225,8 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "ClusterHealthAction"),
equalTo(ELASTICSEARCH_ACTION, "ClusterHealthAction"),
equalTo(ELASTICSEARCH_REQUEST, "ClusterHealthRequest"))),
equalTo(ELASTICSEARCH_ACTION, experimental("ClusterHealthAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("ClusterHealthRequest")))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -218,16 +234,7 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName),
equalTo(ELASTICSEARCH_TYPE, indexType),
equalTo(ELASTICSEARCH_ID, id),
equalTo(ELASTICSEARCH_VERSION, -1))),
getActionAttributes(indexName, indexType, id, -1))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -235,19 +242,27 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
addIndexActionAttributes(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "IndexAction"),
equalTo(ELASTICSEARCH_ACTION, "IndexAction"),
equalTo(ELASTICSEARCH_REQUEST, "IndexRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName),
equalTo(stringKey("elasticsearch.request.write.type"), indexType),
equalTo(longKey("elasticsearch.response.status"), 201),
equalTo(longKey("elasticsearch.shard.replication.total"), 2),
equalTo(longKey("elasticsearch.shard.replication.successful"), 1),
equalTo(longKey("elasticsearch.shard.replication.failed"), 0)))),
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "IndexAction"),
equalTo(ELASTICSEARCH_ACTION, experimental("IndexAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("IndexRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental(indexName)),
equalTo(
stringKey("elasticsearch.request.write.type"),
experimental(indexType)),
equalTo(longKey("elasticsearch.response.status"), experimental(201)),
equalTo(
longKey("elasticsearch.shard.replication.total"), experimental(2)),
equalTo(
longKey("elasticsearch.shard.replication.successful"),
experimental(1)),
equalTo(
longKey("elasticsearch.shard.replication.failed"), experimental(0)),
equalTo(
longKey("elasticsearch.request.write.version"),
hasWriteVersion() ? experimental(-3) : null))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -255,27 +270,10 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName),
equalTo(ELASTICSEARCH_TYPE, indexType),
equalTo(ELASTICSEARCH_ID, id),
equalTo(ELASTICSEARCH_VERSION, 1))));
getActionAttributes(indexName, indexType, id, 1))));
}
protected boolean hasWriteVersion() {
return true;
}
private List<AttributeAssertion> addIndexActionAttributes(AttributeAssertion... assertions) {
List<AttributeAssertion> result = new ArrayList<>(Arrays.asList(assertions));
if (hasWriteVersion()) {
result.add(equalTo(longKey("elasticsearch.request.write.version"), -3));
}
return result;
}
}

View File

@ -18,6 +18,7 @@ import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT;
import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Named.named;
@ -29,7 +30,6 @@ import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
@ -91,14 +91,29 @@ public abstract class AbstractElasticsearchTransportClientTest
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "ClusterHealthAction"),
equalTo(ELASTICSEARCH_ACTION, "ClusterHealthAction"),
equalTo(ELASTICSEARCH_REQUEST, "ClusterHealthRequest"))),
equalTo(ELASTICSEARCH_ACTION, experimental("ClusterHealthAction")),
equalTo(
ELASTICSEARCH_REQUEST, experimental("ClusterHealthRequest")))),
span ->
span.hasName("callback")
.hasKind(SpanKind.INTERNAL)
.hasParent(trace.getSpan(0))));
}
private List<AttributeAssertion> addNetworkTypeAttribute(AttributeAssertion... assertions) {
List<AttributeAssertion> result = new ArrayList<>(asList(assertions));
if (hasNetworkType()) {
result.add(
satisfies(
NETWORK_TYPE,
k ->
k.satisfiesAnyOf(
val -> assertThat(val).isEqualTo("ipv4"),
val -> assertThat(val).isEqualTo("ipv6"))));
}
return result;
}
private Stream<Arguments> errorArguments() {
return Stream.of(
Arguments.of(
@ -122,14 +137,14 @@ public abstract class AbstractElasticsearchTransportClientTest
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, "invalid-index")));
equalTo(ELASTICSEARCH_ACTION, experimental("GetAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("GetRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental("invalid-index"))));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "org.elasticsearch.transport.RemoteTransportException"));
@ -216,9 +231,9 @@ public abstract class AbstractElasticsearchTransportClientTest
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "CreateIndexAction"),
equalTo(ELASTICSEARCH_ACTION, "CreateIndexAction"),
equalTo(ELASTICSEARCH_REQUEST, "CreateIndexRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName)))),
equalTo(ELASTICSEARCH_ACTION, experimental("CreateIndexAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("CreateIndexRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental(indexName))))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -230,8 +245,8 @@ public abstract class AbstractElasticsearchTransportClientTest
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), getPutMappingActionName()),
equalTo(ELASTICSEARCH_ACTION, getPutMappingActionName()),
equalTo(ELASTICSEARCH_REQUEST, "PutMappingRequest"))),
equalTo(ELASTICSEARCH_ACTION, experimental(getPutMappingActionName())),
equalTo(ELASTICSEARCH_REQUEST, experimental("PutMappingRequest")))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -239,21 +254,33 @@ public abstract class AbstractElasticsearchTransportClientTest
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
addIndexActionAttributes(
addNetworkTypeAttribute(
equalTo(NETWORK_PEER_ADDRESS, getAddress()),
equalTo(NETWORK_PEER_PORT, getPort()),
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "IndexAction"),
equalTo(ELASTICSEARCH_ACTION, "IndexAction"),
equalTo(ELASTICSEARCH_REQUEST, "IndexRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName),
equalTo(stringKey("elasticsearch.request.write.type"), indexType),
equalTo(longKey("elasticsearch.response.status"), 201),
equalTo(longKey("elasticsearch.shard.replication.total"), 2),
equalTo(longKey("elasticsearch.shard.replication.successful"), 1),
equalTo(longKey("elasticsearch.shard.replication.failed"), 0)))),
equalTo(ELASTICSEARCH_ACTION, experimental("IndexAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("IndexRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental(indexName)),
equalTo(
stringKey("elasticsearch.request.write.type"),
experimental(indexType)),
equalTo(
longKey("elasticsearch.response.status"), experimental(201)),
equalTo(
longKey("elasticsearch.shard.replication.total"),
experimental(2)),
equalTo(
longKey("elasticsearch.shard.replication.successful"),
experimental(1)),
equalTo(
longKey("elasticsearch.shard.replication.failed"),
experimental(0)),
equalTo(
longKey("elasticsearch.request.write.version"),
hasWriteVersion() ? experimental(-3) : null)))),
// moved here by sorting, chronologically happens before PutMappingAction
trace ->
trace.hasSpansSatisfyingExactly(
@ -262,19 +289,7 @@ public abstract class AbstractElasticsearchTransportClientTest
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
addNetworkTypeAttribute(
equalTo(NETWORK_PEER_ADDRESS, getAddress()),
equalTo(NETWORK_PEER_PORT, getPort()),
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName),
equalTo(ELASTICSEARCH_TYPE, indexType),
equalTo(ELASTICSEARCH_ID, id),
equalTo(ELASTICSEARCH_VERSION, -1)))),
getActionAttributes(indexName, indexType, id, -1))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
@ -282,48 +297,32 @@ public abstract class AbstractElasticsearchTransportClientTest
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
addNetworkTypeAttribute(
equalTo(NETWORK_PEER_ADDRESS, getAddress()),
equalTo(NETWORK_PEER_PORT, getPort()),
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, indexName),
equalTo(ELASTICSEARCH_TYPE, indexType),
equalTo(ELASTICSEARCH_ID, id),
equalTo(ELASTICSEARCH_VERSION, 1)))));
getActionAttributes(indexName, indexType, id, 1))));
}
protected boolean hasNetworkType() {
return false;
}
private List<AttributeAssertion> addNetworkTypeAttribute(AttributeAssertion... assertions) {
List<AttributeAssertion> result = new ArrayList<>(Arrays.asList(assertions));
if (hasNetworkType()) {
result.add(
satisfies(
NETWORK_TYPE,
k ->
k.satisfiesAnyOf(
val -> assertThat(val).isEqualTo("ipv4"),
val -> assertThat(val).isEqualTo("ipv6"))));
}
return result;
private List<AttributeAssertion> getActionAttributes(
String indexName, String indexType, String id, long version) {
return new ArrayList<>(
addNetworkTypeAttribute(
equalTo(NETWORK_PEER_ADDRESS, getAddress()),
equalTo(NETWORK_PEER_PORT, getPort()),
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, experimental("GetAction")),
equalTo(ELASTICSEARCH_REQUEST, experimental("GetRequest")),
equalTo(ELASTICSEARCH_REQUEST_INDICES, experimental(indexName)),
equalTo(ELASTICSEARCH_TYPE, experimental(indexType)),
equalTo(ELASTICSEARCH_ID, experimental(id)),
equalTo(ELASTICSEARCH_VERSION, experimental(version))));
}
protected boolean hasWriteVersion() {
return true;
}
private List<AttributeAssertion> addIndexActionAttributes(AttributeAssertion... assertions) {
List<AttributeAssertion> result = new ArrayList<>(addNetworkTypeAttribute(assertions));
if (hasWriteVersion()) {
result.add(equalTo(longKey("elasticsearch.request.write.version"), -3));
}
return result;
protected boolean hasNetworkType() {
return false;
}
}