|
| 1 | +# Telemetry Testing |
| 2 | + |
| 3 | +This document explains the telemetry testing approach used in the reference application to validate OpenTelemetry data export. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The `TelemetryTest` class demonstrates how to test that your application correctly generates and exports OpenTelemetry telemetry data. This approach is based on the [telemetry-testing example](../telemetry-testing) in the repository. |
| 8 | + |
| 9 | +## How it Works |
| 10 | + |
| 11 | +### MockServer Setup |
| 12 | +- Uses [MockServer](https://www.mock-server.com/) to simulate an OTLP collector |
| 13 | +- Listens on port 4318 (default OTLP HTTP endpoint) |
| 14 | +- Captures all OTLP requests sent by the OpenTelemetry Java Agent |
| 15 | + |
| 16 | +### Test Configuration |
| 17 | +The test suite is configured to: |
| 18 | +- Run with OpenTelemetry Java Agent attached (`-javaagent`) |
| 19 | +- Export telemetry using `http/protobuf` protocol |
| 20 | +- Set faster metric export interval (5 seconds vs default 60 seconds) |
| 21 | +- Suppress MockServer logging to avoid circular telemetry |
| 22 | + |
| 23 | +### Protobuf Parsing |
| 24 | +Uses OpenTelemetry protocol buffers to parse captured requests: |
| 25 | +- `ExportTraceServiceRequest` for traces/spans |
| 26 | +- `ExportMetricsServiceRequest` for metrics |
| 27 | +- Direct access to span attributes, events, and metric values |
| 28 | + |
| 29 | +## What Gets Tested |
| 30 | + |
| 31 | +### Traces |
| 32 | +- **HTTP spans**: Automatic instrumentation spans (e.g., `GET /rolldice`) |
| 33 | +- **Custom spans**: Manual spans created in application code (e.g., `roll-dice`, `fibonacci-calculation`) |
| 34 | +- **Span hierarchy**: Parent-child relationships between spans |
| 35 | +- **Attributes**: Custom attributes like `dice.player`, `fibonacci.n` |
| 36 | +- **Events**: Custom events like `dice-rolled`, `fibonacci-calculated` |
| 37 | +- **Error handling**: Exception recording and error status |
| 38 | + |
| 39 | +### Metrics |
| 40 | +- **Custom counters**: `dice_rolls_total`, `fibonacci_calculations_total` |
| 41 | +- **Custom timers**: `dice_roll_duration_seconds`, `fibonacci_duration_seconds` |
| 42 | +- **Micrometer integration**: Metrics created via Micrometer and exported via OpenTelemetry |
| 43 | + |
| 44 | +### Baggage |
| 45 | +- **Cross-cutting data**: Player names, request types |
| 46 | +- **Propagation**: Baggage values accessible across span boundaries |
| 47 | + |
| 48 | +## Test Scenarios |
| 49 | + |
| 50 | +### Basic Functionality |
| 51 | +```java |
| 52 | +@Test |
| 53 | +public void testDiceRollTelemetry() { |
| 54 | + // Call endpoint |
| 55 | + template.getForEntity("/rolldice", String.class); |
| 56 | + |
| 57 | + // Verify spans are created |
| 58 | + await().untilAsserted(() -> { |
| 59 | + var spans = extractSpansFromRequests(requests); |
| 60 | + assertThat(spans) |
| 61 | + .extracting(Span::getName) |
| 62 | + .contains("GET /rolldice", "roll-dice", "roll-single-die"); |
| 63 | + }); |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +### Complex Scenarios |
| 68 | +- **Multiple operations**: Testing endpoints that create multiple spans |
| 69 | +- **Parameterized requests**: Verifying span attributes contain request parameters |
| 70 | +- **Error conditions**: Testing that exceptions are properly recorded |
| 71 | +- **Performance scenarios**: Large computations that generate multiple events |
| 72 | + |
| 73 | +## Implementation Details |
| 74 | + |
| 75 | +### Java Agent Configuration |
| 76 | +```kotlin |
| 77 | +jvmArgs = listOf( |
| 78 | + "-javaagent:${agentJarPath}", |
| 79 | + "-Dotel.metric.export.interval=5000", |
| 80 | + "-Dotel.exporter.otlp.protocol=http/protobuf", |
| 81 | + "-Dmockserver.logLevel=off" |
| 82 | +) |
| 83 | +``` |
| 84 | + |
| 85 | +### Request Parsing |
| 86 | +```java |
| 87 | +private List<Span> extractSpansFromRequests(HttpRequest[] requests) { |
| 88 | + return Arrays.stream(requests) |
| 89 | + .map(HttpRequest::getBody) |
| 90 | + .flatMap(body -> getExportTraceServiceRequest(body).stream()) |
| 91 | + .flatMap(r -> r.getResourceSpansList().stream()) |
| 92 | + .flatMap(r -> r.getScopeSpansList().stream()) |
| 93 | + .flatMap(r -> r.getSpansList().stream()) |
| 94 | + .collect(Collectors.toList()); |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +## Benefits |
| 99 | + |
| 100 | +### Comprehensive Validation |
| 101 | +- **End-to-end verification**: Tests actual OTLP export, not just in-memory data |
| 102 | +- **Protocol-level testing**: Uses the same protobuf format as real collectors |
| 103 | +- **Realistic conditions**: Tests with the actual Java Agent configuration |
| 104 | + |
| 105 | +### Development Confidence |
| 106 | +- **Regression testing**: Catch telemetry regressions before production |
| 107 | +- **Documentation**: Tests serve as living examples of expected telemetry |
| 108 | +- **Debugging**: Easy to inspect actual telemetry data during development |
| 109 | + |
| 110 | +## Running the Tests |
| 111 | + |
| 112 | +```bash |
| 113 | +# Run all tests (including telemetry tests) |
| 114 | +../gradlew test |
| 115 | + |
| 116 | +# Run only telemetry tests |
| 117 | +../gradlew test --tests "TelemetryTest" |
| 118 | + |
| 119 | +# Run specific test method |
| 120 | +../gradlew test --tests "TelemetryTest.testDiceRollTelemetry" |
| 121 | +``` |
| 122 | + |
| 123 | +## Troubleshooting |
| 124 | + |
| 125 | +### Common Issues |
| 126 | + |
| 127 | +1. **Port conflicts**: Ensure port 4318 is available |
| 128 | +2. **Timing issues**: Use appropriate `await()` timeouts for telemetry export |
| 129 | +3. **Agent not loaded**: Verify Java Agent is properly attached in test configuration |
| 130 | +4. **Network issues**: MockServer requires localhost connectivity |
| 131 | + |
| 132 | +### Debugging Tips |
| 133 | + |
| 134 | +- Enable debug logging: `-Dotel.javaagent.debug=true` |
| 135 | +- Inspect raw requests: Log `request.getBodyAsString()` before parsing |
| 136 | +- Check span timing: Some spans may export in separate batches |
| 137 | +- Verify test isolation: Each test should reset MockServer expectations |
| 138 | + |
| 139 | +## Further Reading |
| 140 | + |
| 141 | +- [OpenTelemetry Java Agent Configuration](https://opentelemetry.io/docs/languages/java/configuration/) |
| 142 | +- [MockServer Documentation](https://www.mock-server.com/) |
| 143 | +- [OpenTelemetry Protocol Specification](https://opentelemetry.io/docs/specs/otlp/) |
| 144 | +- [Telemetry Testing Example](../telemetry-testing/README.md) |
0 commit comments