Dropwizard is an opinionated Java framework that bundles Jetty, Jersey, Jackson, and Dropwizard Metrics into a fat JAR for building production-ready RESTful services. Unlike Spring Boot, Dropwizard ships with first-class operational tooling out of the box: a /healthcheck endpoint, a metrics registry, and a separate admin port for operational access. The challenge is that none of this helps if no one is continuously checking it. Vigilmon connects to Dropwizard's built-in operational endpoints, turning its health checks and metrics into real-time uptime monitoring with alerts.
What You'll Set Up
- Dropwizard
/healthcheckendpoint monitoring with UNHEALTHY alerts - Jetty thread pool exhaustion detection
- API request throughput and p99 latency tracking
- Database connection pool health monitoring
- JVM heap and GC health monitoring
- Admin interface accessibility checks
- Custom health check failure alerts
- Circuit breaker state monitoring
Prerequisites
- A running Dropwizard application (1.3.x or 2.x+)
- Admin port accessible (default 8081) from your monitoring infrastructure
- Application port accessible (default 8080)
- A free Vigilmon account
Step 1: Monitor the Dropwizard Health Check Endpoint
Dropwizard exposes a /healthcheck endpoint on the admin port (default 8081) that runs all registered health checks and returns HTTP 200 if all pass or HTTP 500 if any fail. This single endpoint aggregates your application's entire health signal:
- Log in to vigilmon.online and click Add Monitor.
- Set Type to
HTTP / HTTPS. - Enter the health check URL:
http://your-app-host:8081/healthcheck - Set Check interval to
1 minute. - Set Expected HTTP status to
200. - Click Save.
Dropwizard returns HTTP 500 with a JSON body listing which checks failed when any health check is UNHEALTHY. Vigilmon will alert on the status code change — no body parsing needed.
If you want to expose the healthcheck endpoint at the application port (8080) for load balancer health checks, add an admin servlet or configure the adminContextPath in your Dropwizard YAML configuration:
server:
adminContextPath: /admin
applicationContextPath: /
Then monitor http://your-app-host:8080/admin/healthcheck from your load balancer.
Step 2: Register Custom Health Checks
Dropwizard's value comes from application-specific health checks that test real dependencies. If you haven't already, register health checks for each critical dependency:
public class DatabaseHealthCheck extends HealthCheck {
private final DataSource dataSource;
public DatabaseHealthCheck(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
protected Result check() throws Exception {
try (Connection conn = dataSource.getConnection()) {
conn.createStatement().execute("SELECT 1");
return Result.healthy();
} catch (Exception e) {
return Result.unhealthy("Database ping failed: " + e.getMessage());
}
}
}
// In your Application.run():
environment.healthChecks().register("database", new DatabaseHealthCheck(dataSource));
Register one health check per critical dependency: database, Redis cache, upstream APIs, message brokers. Each shows up individually in the /healthcheck response, and any single UNHEALTHY check causes the endpoint to return 500 — triggering your Vigilmon alert.
For upstream service health checks, use a lightweight HTTP probe:
public class UpstreamServiceHealthCheck extends HealthCheck {
private final Client httpClient;
private final String serviceUrl;
@Override
protected Result check() throws Exception {
Response response = httpClient.target(serviceUrl + "/health")
.request()
.get();
if (response.getStatus() == 200) {
return Result.healthy();
}
return Result.unhealthy("Upstream returned HTTP " + response.getStatus());
}
}
Step 3: Monitor Jetty Thread Pool Health
Dropwizard's embedded Jetty handles HTTP requests using a bounded thread pool. When the pool is exhausted, incoming requests queue up and eventually get rejected. Monitor thread pool metrics via the Dropwizard metrics endpoint:
#!/bin/bash
# /opt/app/scripts/check-jetty-threads.sh
APP_HOST="localhost"
ADMIN_PORT="8081"
QUEUE_THRESHOLD=10
HEARTBEAT_URL="https://vigilmon.online/heartbeat/YOUR_JETTY_HEARTBEAT_ID"
METRICS=$(curl -sf "http://${APP_HOST}:${ADMIN_PORT}/metrics" \
-H "Accept: application/json")
# Check Jetty queued requests
QUEUED=$(echo "$METRICS" | python3 -c "
import sys, json
m = json.load(sys.stdin)
gauges = m.get('gauges', {})
# Key name varies by Dropwizard version
for key in gauges:
if 'queued-requests' in key or 'queue-size' in key:
print(gauges[key]['value'])
break
else:
print(0)
")
if [ "$QUEUED" -le "$QUEUE_THRESHOLD" ]; then
curl -sf "$HEARTBEAT_URL" > /dev/null
else
echo "Jetty request queue depth: ${QUEUED} (threshold: ${QUEUE_THRESHOLD})" >&2
fi
The Dropwizard /metrics endpoint (admin port 8081) returns a JSON document with all registered metrics. Schedule this check every minute and create a Cron Heartbeat in Vigilmon with a 3-minute expected interval.
Step 4: Track API Request Throughput and Latency
Dropwizard Metrics automatically instruments every Jersey endpoint with a Timer that tracks request count, rate, and percentile latencies (p50, p95, p99). Extract p99 latency from the metrics endpoint:
#!/bin/bash
# /opt/app/scripts/check-latency.sh
APP_HOST="localhost"
ADMIN_PORT="8081"
P99_THRESHOLD_MS=1000 # 1 second SLA
HEARTBEAT_URL="https://vigilmon.online/heartbeat/YOUR_LATENCY_HEARTBEAT_ID"
METRICS=$(curl -sf "http://${APP_HOST}:${ADMIN_PORT}/metrics" \
-H "Accept: application/json")
# Find the worst p99 latency across all Jersey resource timers
WORST_P99=$(echo "$METRICS" | python3 -c "
import sys, json
m = json.load(sys.stdin)
timers = m.get('timers', {})
worst = 0
for key, val in timers.items():
if 'resources' in key or 'request' in key.lower():
p99_ns = val.get('p99', 0)
p99_ms = p99_ns / 1_000_000 # convert nanoseconds to ms
if p99_ms > worst:
worst = p99_ms
print(int(worst))
")
if [ "$WORST_P99" -le "$P99_THRESHOLD_MS" ]; then
curl -sf "$HEARTBEAT_URL" > /dev/null
else
echo "API p99 latency: ${WORST_P99}ms exceeds ${P99_THRESHOLD_MS}ms SLA" >&2
fi
This gives you a per-heartbeat SLA check — if any endpoint's p99 breaches your threshold, the heartbeat stops and Vigilmon alerts. Adjust P99_THRESHOLD_MS to match your application's SLA.
Step 5: Monitor Database Connection Pool
Dropwizard typically uses JDBI or Hibernate with a HikariCP or c3p0 connection pool. Pool exhaustion causes requests to hang waiting for a connection. Monitor pool metrics from the /metrics endpoint:
#!/bin/bash
# /opt/app/scripts/check-db-pool.sh
APP_HOST="localhost"
ADMIN_PORT="8081"
PENDING_THRESHOLD=3 # threads waiting for a connection
HEARTBEAT_URL="https://vigilmon.online/heartbeat/YOUR_DBPOOL_HEARTBEAT_ID"
METRICS=$(curl -sf "http://${APP_HOST}:${ADMIN_PORT}/metrics" \
-H "Accept: application/json")
PENDING=$(echo "$METRICS" | python3 -c "
import sys, json
m = json.load(sys.stdin)
gauges = m.get('gauges', {})
for key in gauges:
if 'pool' in key and 'pending' in key:
print(gauges[key]['value'])
break
else:
print(0)
")
if [ "$PENDING" -le "$PENDING_THRESHOLD" ]; then
curl -sf "$HEARTBEAT_URL" > /dev/null
else
echo "DB connection pool pending: ${PENDING} threads waiting (threshold: ${PENDING_THRESHOLD})" >&2
fi
If you're using HikariCP with Dropwizard, HikariCP metrics are automatically registered in the Dropwizard Metrics registry under com.zaxxer.hikari.* keys.
Step 6: Monitor JVM Heap and GC Health
Dropwizard Metrics includes the JVM metrics module which exposes heap usage and GC pause times. Monitor these from the /metrics endpoint:
#!/bin/bash
# /opt/app/scripts/check-jvm-health.sh
APP_HOST="localhost"
ADMIN_PORT="8081"
HEAP_THRESHOLD_PERCENT=85
GC_PAUSE_THRESHOLD_MS=500
HEARTBEAT_URL="https://vigilmon.online/heartbeat/YOUR_JVM_HEARTBEAT_ID"
METRICS=$(curl -sf "http://${APP_HOST}:${ADMIN_PORT}/metrics" \
-H "Accept: application/json")
JVM_CHECK=$(echo "$METRICS" | python3 -c "
import sys, json
m = json.load(sys.stdin)
gauges = m.get('gauges', {})
heap_used = gauges.get('jvm.memory.heap.used', {}).get('value', 0)
heap_max = gauges.get('jvm.memory.heap.max', {}).get('value', 1)
heap_pct = heap_used * 100 / heap_max if heap_max > 0 else 0
# GC pause from histograms/timers — varies by GC implementation
timers = m.get('timers', {})
worst_gc_ms = 0
for key, val in timers.items():
if 'gc' in key.lower():
p99_ns = val.get('p99', 0)
worst_gc_ms = max(worst_gc_ms, p99_ns / 1_000_000)
print(f'{int(heap_pct)} {int(worst_gc_ms)}')
")
HEAP_PCT=$(echo "$JVM_CHECK" | awk '{print $1}')
GC_MS=$(echo "$JVM_CHECK" | awk '{print $2}')
if [ "$HEAP_PCT" -lt "$HEAP_THRESHOLD_PERCENT" ] && [ "$GC_MS" -lt "$GC_PAUSE_THRESHOLD_MS" ]; then
curl -sf "$HEARTBEAT_URL" > /dev/null
else
echo "JVM: heap ${HEAP_PCT}% (threshold: ${HEAP_THRESHOLD_PERCENT}%), GC p99 ${GC_MS}ms (threshold: ${GC_PAUSE_THRESHOLD_MS}ms)" >&2
fi
Make sure your Dropwizard application registers JVM metrics in its initialization:
// In your Application.run():
environment.metrics().registerAll(new JvmAttributeGaugeSet());
environment.metrics().registerAll(new MemoryUsageGaugeSet());
environment.metrics().registerAll(new GarbageCollectorMetricSet());
environment.metrics().registerAll(new ThreadStatesGaugeSet());
Step 7: Monitor Admin Interface Accessibility
The Dropwizard admin port (8081) is your operational lifeline — thread dumps, metrics, health checks, and task execution all go through it. If the admin port becomes unreachable, you lose visibility into the running application:
- Click Add Monitor → TCP Port.
- Enter your application host and admin port
8081. - Set Check interval to
1 minute. - Click Save.
Also add an HTTP monitor for the admin root:
- Click Add Monitor → HTTP / HTTPS.
- Enter:
http://your-app-host:8081/ - Set Expected HTTP status to
200. - Click Save.
The admin port and application port can fail independently — Jetty can stop serving application traffic while the admin port remains accessible (or vice versa during partial failures). Monitor both.
Step 8: Monitor Circuit Breaker Health
If your Dropwizard application uses Resilience4j or Hystrix to protect upstream dependencies, a circuit breaker opening is a critical signal: it means the dependency has been failing consistently and the circuit has tripped to fail-fast mode:
#!/bin/bash
# /opt/app/scripts/check-circuit-breakers.sh
APP_HOST="localhost"
ADMIN_PORT="8081"
HEARTBEAT_URL="https://vigilmon.online/heartbeat/YOUR_CB_HEARTBEAT_ID"
METRICS=$(curl -sf "http://${APP_HOST}:${ADMIN_PORT}/metrics" \
-H "Accept: application/json")
# Check for any Resilience4j circuit breaker in OPEN state
OPEN_CBS=$(echo "$METRICS" | python3 -c "
import sys, json
m = json.load(sys.stdin)
gauges = m.get('gauges', {})
open_count = 0
for key, val in gauges.items():
if 'circuitbreaker' in key.lower() and 'state' in key.lower():
# Resilience4j states: 0=CLOSED, 1=OPEN, 2=HALF_OPEN
if val.get('value', 0) == 1:
open_count += 1
print(open_count)
")
if [ "$OPEN_CBS" -eq "0" ]; then
curl -sf "$HEARTBEAT_URL" > /dev/null
else
echo "${OPEN_CBS} circuit breaker(s) in OPEN state" >&2
fi
For Resilience4j with Dropwizard Metrics bridge, use the resilience4j-metrics integration to expose circuit breaker metrics automatically.
Step 9: Configure Alerting
With all monitors in place, configure alert routing:
- In Vigilmon, click Alert Contacts → Add Contact.
- Add your engineering team email, Slack
#alertschannel webhook, or PagerDuty integration. - Assign contacts to monitors based on severity.
Recommended configuration:
| Monitor | Alert Sensitivity | Notes |
|---|---|---|
| /healthcheck endpoint | 1 failure | Any UNHEALTHY check = immediate alert |
| Application port TCP | 1 failure | Service is completely down |
| Admin port TCP | 2 failures | Operational visibility lost |
| Jetty thread pool heartbeat | 2 missed pings | Saturation imminent |
| DB connection pool heartbeat | 2 missed pings | Request failures likely |
| JVM heap heartbeat | 2 missed pings | OOM risk |
| API latency SLA heartbeat | 3 missed pings | SLA breach sustained |
| Circuit breaker heartbeat | 2 missed pings | Upstream dependency failing |
Set Notify me when back up to yes for all monitors so you get a recovery notification when the application stabilizes.
Conclusion
Dropwizard gives you the building blocks for production observability — health checks, metrics, and an admin interface — but only if you're actively monitoring them. Vigilmon closes the loop by continuously polling your /healthcheck endpoint, tracking JVM health and Jetty thread pool saturation from the /metrics endpoint, and giving you TCP-level visibility into both your application and admin ports.
Start with the /healthcheck monitor and admin port TCP check (Steps 1 and 7) — these cover the most critical failure modes immediately. Then add JVM heap and database connection pool monitoring (Steps 5–6), and finally layer in latency SLA tracking and circuit breaker monitoring (Steps 4 and 8). Your Dropwizard services will have the same operational visibility as large-scale Java platforms.