|
5 | 5 |
|
6 | 6 | package io.opentelemetry.instrumentation.jdbc.testing; |
7 | 7 |
|
| 8 | +import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsLogs; |
| 9 | +import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsSpanEvents; |
8 | 10 | import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; |
9 | 11 | import static io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil.codeFunctionAssertions; |
10 | 12 | import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; |
|
14 | 16 | import static io.opentelemetry.instrumentation.testing.util.TestLatestDeps.testLatestDeps; |
15 | 17 | import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; |
16 | 18 | import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; |
| 19 | +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; |
17 | 20 | import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE; |
18 | 21 | import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_BATCH_SIZE; |
19 | 22 | import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; |
20 | 23 | import static io.opentelemetry.semconv.DbAttributes.DB_STORED_PROCEDURE_NAME; |
21 | 24 | import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; |
| 25 | +import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; |
| 26 | +import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; |
| 27 | +import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; |
22 | 28 | import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; |
23 | 29 | import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; |
24 | 30 | import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; |
|
30 | 36 | import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DbSystemNameIncubatingValues.HSQLDB; |
31 | 37 | import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DbSystemNameIncubatingValues.OTHER_SQL; |
32 | 38 | import static java.util.Arrays.asList; |
| 39 | +import static java.util.stream.Collectors.toList; |
33 | 40 | import static org.assertj.core.api.Assertions.assertThatThrownBy; |
34 | 41 |
|
35 | 42 | import com.google.common.collect.ImmutableMap; |
36 | 43 | import com.google.common.collect.Maps; |
37 | 44 | import com.mchange.v2.c3p0.ComboPooledDataSource; |
38 | 45 | import com.zaxxer.hikari.HikariConfig; |
39 | 46 | import com.zaxxer.hikari.HikariDataSource; |
| 47 | +import io.opentelemetry.api.logs.Severity; |
40 | 48 | import io.opentelemetry.api.trace.SpanKind; |
41 | 49 | import io.opentelemetry.instrumentation.jdbc.TestConnection; |
42 | 50 | import io.opentelemetry.instrumentation.jdbc.TestDriver; |
43 | 51 | import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; |
44 | 52 | import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; |
| 53 | +import io.opentelemetry.sdk.logs.data.LogRecordData; |
45 | 54 | import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; |
46 | 55 | import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; |
47 | 56 | import io.opentelemetry.sdk.testing.assertj.TraceAssert; |
| 57 | +import io.opentelemetry.sdk.trace.data.StatusData; |
48 | 58 | import java.beans.PropertyVetoException; |
49 | 59 | import java.io.Closeable; |
50 | 60 | import java.sql.CallableStatement; |
|
66 | 76 | import org.apache.derby.jdbc.EmbeddedDataSource; |
67 | 77 | import org.apache.derby.jdbc.EmbeddedDriver; |
68 | 78 | import org.assertj.core.api.ThrowingConsumer; |
| 79 | +import org.awaitility.Awaitility; |
69 | 80 | import org.h2.jdbcx.JdbcDataSource; |
70 | 81 | import org.hsqldb.jdbc.JDBCDriver; |
71 | 82 | import org.junit.jupiter.api.BeforeAll; |
@@ -418,6 +429,65 @@ isCallStatement && emitStableDatabaseSemconv() |
418 | 429 | testing(), "io.opentelemetry.jdbc", DB_SYSTEM_NAME, DB_NAMESPACE, DB_QUERY_SUMMARY); |
419 | 430 | } |
420 | 431 |
|
| 432 | + @Test |
| 433 | + void testFailedStatement() throws SQLException { |
| 434 | + Connection connection = wrap(new org.h2.Driver().connect(JDBC_URLS.get("h2"), null)); |
| 435 | + Statement statement = connection.createStatement(); |
| 436 | + cleanup.deferCleanup(statement); |
| 437 | + |
| 438 | + assertThatThrownBy( |
| 439 | + () -> |
| 440 | + testing() |
| 441 | + .runWithSpan( |
| 442 | + "parent", |
| 443 | + () -> statement.executeQuery("SELECT * FROM table_does_not_exist"))) |
| 444 | + .isInstanceOf(SQLException.class); |
| 445 | + |
| 446 | + testing() |
| 447 | + .waitAndAssertTraces( |
| 448 | + trace -> |
| 449 | + trace.hasSpansSatisfyingExactly( |
| 450 | + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), |
| 451 | + span -> |
| 452 | + span.hasKind(SpanKind.CLIENT) |
| 453 | + .hasParent(trace.getSpan(0)) |
| 454 | + .hasStatus(StatusData.error()) |
| 455 | + .satisfies( |
| 456 | + spanData -> { |
| 457 | + if (emitExceptionAsSpanEvents()) { |
| 458 | + assertThat(spanData.getEvents()).hasSize(1); |
| 459 | + assertThat(spanData.getEvents().get(0).getName()) |
| 460 | + .isEqualTo("exception"); |
| 461 | + } else { |
| 462 | + assertThat(spanData.getEvents()).isEmpty(); |
| 463 | + } |
| 464 | + }))); |
| 465 | + |
| 466 | + if (emitExceptionAsLogs()) { |
| 467 | + assertExceptionLog(); |
| 468 | + } |
| 469 | + } |
| 470 | + |
| 471 | + private void assertExceptionLog() { |
| 472 | + Awaitility.await() |
| 473 | + .untilAsserted( |
| 474 | + () -> { |
| 475 | + List<LogRecordData> logs = |
| 476 | + testing().logRecords().stream() |
| 477 | + .filter(log -> "db.client.operation.exception".equals(log.getEventName())) |
| 478 | + .collect(toList()); |
| 479 | + |
| 480 | + assertThat(logs).hasSize(1); |
| 481 | + assertThat(logs.get(0)) |
| 482 | + .hasSeverity(Severity.WARN) |
| 483 | + .hasEventName("db.client.operation.exception") |
| 484 | + .hasAttributesSatisfyingExactly( |
| 485 | + satisfies(EXCEPTION_TYPE, val -> val.isNotNull()), |
| 486 | + satisfies(EXCEPTION_MESSAGE, val -> val.isNotNull()), |
| 487 | + satisfies(EXCEPTION_STACKTRACE, val -> val.isNotNull())); |
| 488 | + }); |
| 489 | + } |
| 490 | + |
421 | 491 | static Stream<Arguments> preparedStatementStream() throws SQLException { |
422 | 492 | return Stream.of( |
423 | 493 | Arguments.of( |
|
0 commit comments