OpenTelemetry SDK Testing utilities providing comprehensive testing support for OpenTelemetry Java SDK applications with AssertJ assertions, in-memory exporters, and JUnit integration.
—
In-memory exporters and readers for collecting telemetry data during tests. These components capture spans, metrics, and logs in memory for easy access and validation in test scenarios.
In-memory span exporter that collects SpanData objects during test execution.
class InMemorySpanExporter implements SpanExporter {
// Factory method
static InMemorySpanExporter create();
// Data access
List<SpanData> getFinishedSpanItems();
void reset();
// SpanExporter interface methods
CompletableResultCode export(Collection<SpanData> spans);
CompletableResultCode flush();
CompletableResultCode shutdown();
}Usage examples:
// Setup in-memory span exporter
InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())
.build();
// Use OpenTelemetry normally in your code
Tracer tracer = tracerProvider.get("test");
Span span = tracer.spanBuilder("test-operation").startSpan();
span.setAttribute("key", "value");
span.end();
// Access collected spans in tests
List<SpanData> spans = spanExporter.getFinishedSpanItems();
assertThat(spans).hasSize(1);
assertThat(spans.get(0)).hasName("test-operation");
// Clean up for next test
spanExporter.reset();In-memory metric exporter that collects MetricData objects with configurable aggregation temporality.
class InMemoryMetricExporter implements MetricExporter {
// Factory methods
static InMemoryMetricExporter create();
static InMemoryMetricExporter create(AggregationTemporality temporality);
// Data access
List<MetricData> getFinishedMetricItems();
void reset();
// MetricExporter interface methods
AggregationTemporality getAggregationTemporality(InstrumentType instrumentType);
CompletableResultCode export(Collection<MetricData> metrics);
CompletableResultCode flush();
CompletableResultCode shutdown();
}Usage examples:
// Setup in-memory metric exporter with default temporality
InMemoryMetricExporter metricExporter = InMemoryMetricExporter.create();
SdkMeterProvider meterProvider = SdkMeterProvider.builder()
.registerMetricReader(
PeriodicMetricReader.builder(metricExporter)
.setInterval(Duration.ofSeconds(1))
.build())
.build();
// Use OpenTelemetry normally
Meter meter = meterProvider.get("test");
LongCounter counter = meter.counterBuilder("test-counter").build();
counter.add(10, Attributes.of(AttributeKey.stringKey("key"), "value"));
// Trigger collection and access metrics
List<MetricData> metrics = metricExporter.getFinishedMetricItems();
assertThat(metrics).hasSize(1);
// Setup with specific temporality
InMemoryMetricExporter deltaExporter = InMemoryMetricExporter.create(AggregationTemporality.DELTA);In-memory log record exporter that collects LogRecordData objects during test execution.
class InMemoryLogRecordExporter implements LogRecordExporter {
// Factory method
static InMemoryLogRecordExporter create();
// Data access
List<LogRecordData> getFinishedLogRecordItems();
void reset();
// LogRecordExporter interface methods
CompletableResultCode export(Collection<LogRecordData> logs);
CompletableResultCode flush();
CompletableResultCode shutdown();
}Usage examples:
// Setup in-memory log exporter
InMemoryLogRecordExporter logExporter = InMemoryLogRecordExporter.create();
SdkLoggerProvider loggerProvider = SdkLoggerProvider.builder()
.addLogRecordProcessor(BatchLogRecordProcessor.builder(logExporter).build())
.build();
// Use OpenTelemetry logging
Logger logger = loggerProvider.get("test");
logger.logRecordBuilder()
.setBody("Test log message")
.setSeverity(Severity.INFO)
.setAttribute(AttributeKey.stringKey("key"), "value")
.emit();
// Access collected logs
List<LogRecordData> logs = logExporter.getFinishedLogRecordItems();
assertThat(logs).hasSize(1);
assertThat(logs.get(0)).hasBody("Test log message");In-memory metric reader that provides more control over metric collection with configurable aggregation and temporality selection.
class InMemoryMetricReader implements MetricReader {
// Factory methods
static InMemoryMetricReaderBuilder builder();
static InMemoryMetricReader create();
static InMemoryMetricReader create(
AggregationTemporalitySelector temporalitySelector,
DefaultAggregationSelector aggregationSelector);
static InMemoryMetricReader createDelta();
// Data collection
Collection<MetricData> collectAllMetrics();
// MetricReader interface methods
void register(CollectionRegistration registration);
AggregationTemporality getAggregationTemporality(InstrumentType instrumentType);
Aggregation getDefaultAggregation(InstrumentType instrumentType);
CompletableResultCode forceFlush();
CompletableResultCode shutdown();
MemoryMode getMemoryMode();
}Usage examples:
// Create with default configuration
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProvider meterProvider = SdkMeterProvider.builder()
.registerMetricReader(reader)
.build();
// Create delta reader for delta temporality
InMemoryMetricReader deltaReader = InMemoryMetricReader.createDelta();
// Create with custom configuration
InMemoryMetricReader customReader = InMemoryMetricReader.create(
instrumentType -> AggregationTemporality.CUMULATIVE,
InstrumentType.HISTOGRAM.equals(instrumentType) ?
Aggregation.explicitBucketHistogram() : Aggregation.defaultAggregation()
);
// Collect metrics on demand
Collection<MetricData> currentMetrics = reader.collectAllMetrics();
assertThat(currentMetrics).hasSize(expectedMetricCount);Builder for creating customized InMemoryMetricReader instances with specific configuration.
class InMemoryMetricReaderBuilder {
// Configuration methods
InMemoryMetricReaderBuilder setAggregationTemporalitySelector(AggregationTemporalitySelector selector);
InMemoryMetricReaderBuilder setDefaultAggregationSelector(DefaultAggregationSelector selector);
InMemoryMetricReaderBuilder setMemoryMode(MemoryMode memoryMode);
// Build method
InMemoryMetricReader build();
}Usage examples:
// Build custom metric reader
InMemoryMetricReader reader = InMemoryMetricReader.builder()
.setAggregationTemporalitySelector(instrumentType -> {
if (instrumentType == InstrumentType.COUNTER) {
return AggregationTemporality.DELTA;
}
return AggregationTemporality.CUMULATIVE;
})
.setDefaultAggregationSelector(instrumentType -> {
if (instrumentType == InstrumentType.HISTOGRAM) {
return Aggregation.explicitBucketHistogram(Arrays.asList(1.0, 5.0, 10.0, 50.0));
}
return Aggregation.defaultAggregation();
})
.setMemoryMode(MemoryMode.REUSABLE_DATA)
.build();// OpenTelemetry core exporter interfaces
interface SpanExporter {
CompletableResultCode export(Collection<SpanData> spans);
CompletableResultCode flush();
CompletableResultCode shutdown();
}
interface MetricExporter {
AggregationTemporality getAggregationTemporality(InstrumentType instrumentType);
CompletableResultCode export(Collection<MetricData> metrics);
CompletableResultCode flush();
CompletableResultCode shutdown();
}
interface LogRecordExporter {
CompletableResultCode export(Collection<LogRecordData> logs);
CompletableResultCode flush();
CompletableResultCode shutdown();
}
interface MetricReader {
void register(CollectionRegistration registration);
AggregationTemporality getAggregationTemporality(InstrumentType instrumentType);
Aggregation getDefaultAggregation(InstrumentType instrumentType);
CompletableResultCode forceFlush();
CompletableResultCode shutdown();
MemoryMode getMemoryMode();
}
// Configuration types
enum AggregationTemporality { CUMULATIVE, DELTA }
enum InstrumentType { COUNTER, UP_DOWN_COUNTER, HISTOGRAM, GAUGE, OBSERVABLE_COUNTER, OBSERVABLE_UP_DOWN_COUNTER, OBSERVABLE_GAUGE }
enum MemoryMode { REUSABLE_DATA, IMMUTABLE_DATA }
// Functional interfaces for configuration
interface AggregationTemporalitySelector {
AggregationTemporality getAggregationTemporality(InstrumentType instrumentType);
}
interface DefaultAggregationSelector {
Aggregation getDefaultAggregation(InstrumentType instrumentType);
}
// Result handling
class CompletableResultCode {
static CompletableResultCode ofSuccess();
static CompletableResultCode ofFailure();
CompletableResultCode join(long timeout, TimeUnit unit);
boolean isSuccess();
}Example showing how to set up all exporters together for comprehensive testing:
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class IntegrationTest {
private InMemorySpanExporter spanExporter;
private InMemoryMetricReader metricReader;
private InMemoryLogRecordExporter logExporter;
private OpenTelemetry openTelemetry;
@BeforeAll
void setUp() {
spanExporter = InMemorySpanExporter.create();
metricReader = InMemoryMetricReader.create();
logExporter = InMemoryLogRecordExporter.create();
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder()
.setTracerProvider(
SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())
.build())
.setMeterProvider(
SdkMeterProvider.builder()
.registerMetricReader(metricReader)
.build())
.setLoggerProvider(
SdkLoggerProvider.builder()
.addLogRecordProcessor(BatchLogRecordProcessor.builder(logExporter).build())
.build())
.build();
openTelemetry = sdk;
}
@BeforeEach
void clearData() {
spanExporter.reset();
logExporter.reset();
// Note: metric reader doesn't have reset, use new collection
}
@Test
void testFullTelemetry() {
// Use openTelemetry instance in your code
// Then validate collected data
assertThat(spanExporter.getFinishedSpanItems()).hasSize(expectedSpans);
assertThat(metricReader.collectAllMetrics()).hasSize(expectedMetrics);
assertThat(logExporter.getFinishedLogRecordItems()).hasSize(expectedLogs);
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-opentelemetry--opentelemetry-sdk-testing