docs
reference
tessl install tessl/maven-io-quarkus--quarkus-rest@3.15.0A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive programming support, security integration, and cloud-native features.
Quarkus REST provides built-in observability features for metrics and tracing, including endpoint scoring, URL template path tracking, and integration with monitoring backends.
The observability handler sets URL template paths on requests for accurate metrics and tracing.
package io.quarkus.resteasy.reactive.server.runtime.observability;
class ObservabilityHandler implements ServerRestHandler {
// Template path management
String getTemplatePath();
void setTemplatePath(String templatePath);
// Sub-resource handling
boolean isSubResource();
void setSubResource(boolean subResource);
// Handler execution
void handle(ResteasyReactiveRequestContext requestContext);
}The handler normalizes slashes in template paths and sets the UrlPathTemplate routing context data for use by metrics and tracing systems.
Usage:
// Automatically applied by Quarkus REST
// For path: /api/users/{id}
// Sets template: /api/users/{id} (not /api/users/123)Handler chain customizer that adds observability handlers to the request processing pipeline.
package io.quarkus.resteasy.reactive.server.runtime.observability;
class ObservabilityCustomizer implements HandlerChainCustomizer {
List<ServerRestHandler> handlers(
Phase phase,
ResourceClass resourceClass,
ServerResourceMethod resourceMethod
);
}The customizer adds ObservabilityHandler during the AFTER_MATCH phase, after the resource method has been matched but before it executes.
URL template paths provide consistent metric and trace names regardless of path parameter values.
Example Paths:
@Path("/api/users")
public class UserResource {
@GET
@Path("/{id}")
public User getUser(@PathParam("id") Long id) {
// Template path: /api/users/{id}
// Not: /api/users/123, /api/users/456, etc.
return userService.findById(id);
}
@GET
@Path("/{userId}/orders/{orderId}")
public Order getUserOrder(
@PathParam("userId") Long userId,
@PathParam("orderId") Long orderId
) {
// Template path: /api/users/{userId}/orders/{orderId}
return orderService.findByUserAndId(userId, orderId);
}
}This ensures metrics are aggregated by endpoint, not split across individual parameter values.
Endpoint scoring tracks method invocation statistics for diagnostics and monitoring.
package io.quarkus.resteasy.reactive.server.runtime;
class EndpointScoresSupplier implements Supplier<ScoreSystem.EndpointScores> {
ScoreSystem.EndpointScores get();
}Endpoint scores are available through the Dev UI JSON-RPC service.
package io.quarkus.resteasy.reactive.server.runtime.devui;
class ResteasyReactiveJsonRPCService {
JsonObject getEndpointScores(); // Non-blocking
JsonArray getExceptionMappers(); // Non-blocking
JsonArray getParamConverterProviders(); // Non-blocking
}Endpoint Scores Format:
{
"GET /api/users/{id}": {
"invocations": 1523,
"avgDuration": 45.3,
"maxDuration": 342.1,
"minDuration": 12.5
},
"POST /api/users": {
"invocations": 234,
"avgDuration": 123.7,
"maxDuration": 567.2,
"minDuration": 45.3
}
}URL template paths integrate with Micrometer metrics for accurate endpoint tracking.
Configuration:
# Enable metrics
quarkus.micrometer.enabled=true
quarkus.micrometer.export.prometheus.enabled=true
# HTTP server metrics automatically include URL templates
quarkus.micrometer.binder.http-server.enabled=trueGenerated Metrics:
# HTTP request duration with template path
http_server_requests_seconds_count{method="GET",uri="/api/users/{id}",status="200"} 1523
http_server_requests_seconds_sum{method="GET",uri="/api/users/{id}",status="200"} 68.9
# Not split across parameter values:
# NOT: uri="/api/users/123", uri="/api/users/456", etc.URL template paths are used for span names in distributed tracing.
Configuration:
# Enable OpenTelemetry tracing
quarkus.otel.traces.enabled=true
quarkus.otel.exporter.otlp.traces.endpoint=http://jaeger:4317Trace Span Names:
Span: GET /api/users/{id}
Duration: 45ms
Attributes:
http.method: GET
http.route: /api/users/{id}
http.status_code: 200Template paths prevent span explosion and enable proper trace aggregation.
Recorder for observability integration, especially for authentication failure handling.
package io.quarkus.resteasy.reactive.server.runtime.observability;
class ObservabilityIntegrationRecorder {
Handler<RoutingContext> preAuthFailureHandler(RuntimeValue<Deployment> deployment);
static void setTemplatePath(RoutingContext context, Deployment deployment);
}The pre-auth failure handler sets template paths even when authentication fails, ensuring auth failures are properly tracked in metrics.
Sub-resource methods are handled specially for accurate path templates.
Usage:
@Path("/api/departments")
public class DepartmentResource {
@Path("/{deptId}/employees")
public EmployeeSubResource getEmployeeSubResource(@PathParam("deptId") Long deptId) {
// Returns sub-resource
return new EmployeeSubResource(deptId);
}
}
public class EmployeeSubResource {
private final Long departmentId;
public EmployeeSubResource(Long departmentId) {
this.departmentId = departmentId;
}
@GET
@Path("/{empId}")
public Employee getEmployee(@PathParam("empId") Long empId) {
// Template path: /api/departments/{deptId}/employees/{empId}
return employeeService.findByDepartmentAndId(departmentId, empId);
}
}The ObservabilityHandler detects sub-resources and constructs the full template path.
Access observability data programmatically for custom monitoring.
Usage:
import io.quarkus.resteasy.reactive.server.runtime.EndpointScoresSupplier;
import jakarta.inject.Inject;
@ApplicationScoped
public class MonitoringService {
@Inject
EndpointScoresSupplier scoresSupplier;
public Map<String, EndpointStats> getEndpointStatistics() {
ScoreSystem.EndpointScores scores = scoresSupplier.get();
// Process and return statistics
return convertToStats(scores);
}
public List<String> getSlowEndpoints(double thresholdMs) {
ScoreSystem.EndpointScores scores = scoresSupplier.get();
// Filter endpoints exceeding threshold
return findSlow(scores, thresholdMs);
}
}Integrate REST endpoint health into Quarkus health checks.
Usage:
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
@Readiness
@ApplicationScoped
public class EndpointHealthCheck implements HealthCheck {
@Inject
EndpointScoresSupplier scoresSupplier;
@Override
public HealthCheckResponse call() {
ScoreSystem.EndpointScores scores = scoresSupplier.get();
// Check if any endpoints are failing
boolean healthy = checkEndpointHealth(scores);
return HealthCheckResponse.named("rest-endpoints")
.status(healthy)
.withData("totalEndpoints", scores.size())
.build();
}
}Add custom metrics to REST endpoints.
Usage:
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
@Path("/api/data")
public class DataResource {
@Inject
MeterRegistry registry;
@GET
@Path("/{id}")
public Response getData(@PathParam("id") Long id, @Context UriInfo uriInfo) {
Timer.Sample sample = Timer.start(registry);
try {
Data data = dataService.findById(id);
sample.stop(Timer.builder("data.fetch")
.tag("method", "GET")
.tag("endpoint", uriInfo.getPath())
.tag("status", "success")
.register(registry));
return Response.ok(data).build();
} catch (Exception e) {
sample.stop(Timer.builder("data.fetch")
.tag("status", "error")
.register(registry));
throw e;
}
}
}Propagate tracing context to downstream services.
Usage:
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
@Path("/api/orchestrator")
public class OrchestratorResource {
@Inject
Tracer tracer;
@Inject
DownstreamService downstreamService;
@GET
@Path("/process")
public Response process() {
// Current span is automatically created by framework
Span currentSpan = Span.current();
currentSpan.setAttribute("custom.attribute", "value");
// Call downstream service (context propagated automatically)
String result = downstreamService.call();
return Response.ok(result).build();
}
@GET
@Path("/custom-span")
public Response processWithCustomSpan() {
Span span = tracer.spanBuilder("custom-operation").startSpan();
try (var scope = span.makeCurrent()) {
// Custom tracing logic
String result = performOperation();
span.setAttribute("result.size", result.length());
return Response.ok(result).build();
} finally {
span.end();
}
}
}Integrate request logging with observability.
Usage:
import org.jboss.logging.Logger;
import io.vertx.ext.web.RoutingContext;
@Path("/api/logged")
public class LoggedResource {
private static final Logger LOG = Logger.getLogger(LoggedResource.class);
@GET
@Path("/{id}")
public Response get(
@PathParam("id") Long id,
@Context RoutingContext context
) {
// Log with template path for correlation
String templatePath = (String) context.data().get("UrlPathTemplate");
LOG.infof("Processing request: %s, id: %d", templatePath, id);
try {
Data data = dataService.findById(id);
LOG.infof("Successfully retrieved data for id: %d", id);
return Response.ok(data).build();
} catch (Exception e) {
LOG.errorf(e, "Error processing request: %s, id: %d", templatePath, id);
throw e;
}
}
}Monitor endpoint performance and identify bottlenecks.
Configuration:
# Enable detailed metrics
quarkus.micrometer.binder.http-server.enabled=true
quarkus.micrometer.binder.http-server.max-uri-tags=100
quarkus.micrometer.binder.http-server.ignore-patterns=/health,/metrics
# Enable percentile histograms for latency analysis
quarkus.micrometer.binder.http-server.enable-percentile-histograms=trueMonitoring Dashboard Example:
Endpoint: GET /api/users/{id}
Requests: 1523
Success Rate: 99.3%
Avg Duration: 45ms
P50: 38ms
P95: 120ms
P99: 250ms
Max: 342msSet up alerts based on endpoint metrics.
Example Alert Rules (Prometheus):
groups:
- name: rest_endpoint_alerts
rules:
- alert: HighErrorRate
expr: |
rate(http_server_requests_seconds_count{status=~"5.."}[5m])
/ rate(http_server_requests_seconds_count[5m]) > 0.05
for: 5m
annotations:
summary: "High error rate on {{ $labels.uri }}"
- alert: SlowEndpoint
expr: |
histogram_quantile(0.95,
rate(http_server_requests_seconds_bucket[5m])
) > 1.0
for: 5m
annotations:
summary: "Slow endpoint: {{ $labels.uri }} (P95 > 1s)"Access observability data through the Quarkus Dev UI.
Dev UI Features:
Access at http://localhost:8080/q/dev during development.
Verify observability features in tests.
Usage:
import io.restassured.RestAssured;
import io.micrometer.core.instrument.MeterRegistry;
import org.junit.jupiter.api.Test;
@QuarkusTest
public class ObservabilityTest {
@Inject
MeterRegistry registry;
@Test
public void testMetricsRecorded() {
// Make request
RestAssured.given()
.when().get("/api/users/123")
.then().statusCode(200);
// Verify metric recorded with template path
Timer timer = registry.find("http.server.requests")
.tag("uri", "/api/users/{id}")
.timer();
assertNotNull(timer);
assertTrue(timer.count() > 0);
}
}