Java HTTP Integration
Telemetry does not publish a native Java SDK. Use the HTTP API through one application-owned java.net.http.HttpClient when a Java or JVM service needs structured event ingestion or SQL queries.
This boundary keeps connection reuse, timeouts, retries, serialization, and failure policy under the control of the service.
Create one shared client
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
HttpClient telemetryHttp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build();
Create the client once during application startup. Use a server-side, write-scoped API key for ingestion and a separate read-scoped key for reports or query automation.
Send one structured event
Use the JSON library already approved by the application. This example assumes eventJson was serialized from an allowlisted object rather than assembled from a request body:
String eventJson = objectMapper.writeValueAsString(Map.of(
"table", "api_request_completed",
"data", Map.of(
"event_id", eventId,
"route_template", "/api/projects/:id",
"method", "POST",
"status_code", 201,
"status", "success",
"latency_ms", latencyMs,
"request_id", requestId,
"release", release
)
));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.telemetry.sh/log"))
.timeout(Duration.ofSeconds(3))
.header("Authorization", telemetryApiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(eventJson))
.build();
HttpResponse<String> response = telemetryHttp.send(
request,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() < 200 || response.statusCode() >= 300) {
recordDeliveryFailure(response.statusCode(), eventId);
}
Use route templates and controlled error categories. Do not serialize headers, cookies, credentials, request bodies, SQL parameters, stack traces, prompts, generated content, or unrestricted exception messages.
Run SQL
POST a JSON body containing query to https://api.telemetry.sh/query with a read-scoped key:
String queryJson = objectMapper.writeValueAsString(Map.of(
"query", """
SELECT route_template, COUNT(*) AS requests
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '24 hours'
GROUP BY route_template
ORDER BY requests DESC
"""
));
HttpRequest queryRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.telemetry.sh/query"))
.timeout(Duration.ofSeconds(15))
.header("Authorization", telemetryReadApiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(queryJson))
.build();
Check the HTTP status and the response status, data, and key_order fields. Treat an empty result as a valid state rather than a parsing failure.
Failure and retry policy
Retry only bounded transient failures such as connection errors, 429, 502, 503, and 504. Use exponential backoff with jitter, cap elapsed time, and preserve the same event_id across attempts. Do not retry unchanged 400, 401, or 403 responses.
For ordinary product analytics, a telemetry failure should not replace a completed customer response. If a billing or approved audit event must be durable, write an outbox record in the same transaction as the business state and deliver it from a worker.
Verify the integration
Send controlled success, failure, retry, and timeout fixtures, then inspect recent rows:
SELECT timestamp_utc, event_id, route_template, status, latency_ms, error_type
FROM api_request_completed
ORDER BY timestamp_utc DESC
LIMIT 20;
Confirm the event grain, table name, field types, units, UTC time, and privacy boundary. Test a Telemetry timeout and process shutdown before putting delivery on a high-traffic path.
Continue with the Spring Boot integration, Log API, rate limits and errors, and event delivery and idempotency.