Dropwizard stands out as a favored Java framework for developing rest-based applications. Ensuring the observability of an application stands as a critical aspect for maintaining service continuity and stability. This directly impacts the end-user experience and, in many instances, revenue generation. The Metrics library simplifies the process within a Dropwizard application, enabling the collection of various system metrics alongside custom metrics. Additionally, it facilitates the authoring of health checks for the application.
To integrate metrics into a Dropwizard application, adding the following dependency is essential:
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.1.2</version>
</dependency>
Metrics Overview:
Metrics necessitate a metric registry to function effectively, which can accommodate numerous metrics.
private final MetricRegistry metrics = new MetricRegistry();
In a Dropwizard application, access to the metric registry and health check registry is available in the initialize method:
public void initialize(final Bootstrap<AppConfiguration> bootstrap) {
metrics = bootstrap.getMetricRegistry();
healthCheckRegistry = bootstrap.getHealthCheckRegistry();
}
Metrics are broadly categorized into:
- Meters:
Meters gauge rates of events and track mean rates. They also monitor 1, 5, and 15-minute moving averages.private final Meter requests = metrics.meter("requests"); public void handleRequest(Request request, Response response) { requests.mark(); // ... }
- Gauges:
Gauges provide a snapshot of a particular value at a given time. For instance, the number of pending jobs.final Queue<String> queue = new LinkedList<String>(); final Gauge<Integer> pendingJobs = new Gauge<Integer>() { public Integer getValue() { return queue.size(); } };
- Counters:
Counters are a type of gauge that can be incremented or decremented, offering more efficiency than gauges.private final Counter pendingJobs = metrics.counter(name(QueueManager.class, "pending-jobs")); public void addJob(Job job) { pendingJobs.inc(); queue.offer(job); } public Job takeJob() { pendingJobs.dec(); return queue.take(); }
- Histograms:
Histograms track various statistical aspects of a data stream, including minimum, maximum, mean, median, and various percentiles.private final Histogram stockPrice = metrics.histogram(name(StockPriceHandler.class, "stock-price")); public void updateStockPrice(Request request, Response response) { stockPrice.update(request.getStockPrice()); }
- Timers:
Timers measure both the rate at which a piece of code is called and the duration of its execution.
Alternatively, theprivate final Timer responses = metrics.timer(name(RequestHandler.class, "responses")); public String handleRequest(Request request, Response response) { try(final Timer.Context context = responses.time()) { return "OK"; } }
@Timed
annotation can be used:@GET @Path("sayhello") @Timed(name = "my-timed-metric") public Response sayHello(@QueryParam("q") @DefaultValue("%") String queryString) { return Response.ok().entity("Hello " + queryString).build(); }
- Healthchecks:
Health checks determine whether a service is healthy or not.
Registration of the health check in the health check registry is necessary:public class DatabaseHealthCheck extends HealthCheck { public final static String NAME = "database-health"; private final HibernateBundle<ApiAppConfiguration> hibernate; public DatabaseHealthCheck(HibernateBundle<AppConfiguration> hibernate) { this.hibernate = hibernate; } @Override protected Result check() throws Exception { if (hibernate.getSessionFactory().isClosed()) { return Result.unhealthy("Database session factory is closed"); } return Result.healthy(); } }
environment.healthChecks().register(DatabaseHealthCheck.NAME, new DatabaseHealthCheck(hibernate));
These metrics play a crucial role in monitoring and maintaining the health and performance of applications.
Reporters play a crucial role in outputting metrics gathered by Dropwizard. There are several out-of-the-box popular reporters available, including console, CSV, SLF4J, Graphite, and Prometheus.
Here is an example of a console reporter:
ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();
reporter.start(10, TimeUnit.SECONDS);
Additionally, we can create our custom reporter to integrate with our monitoring system:
package example.monitoring;
import com.codahale.metrics.*;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
import java.util.Map;
public class MyCustomReporter extends ScheduledReporter {
public MyCustomReporter(MetricRegistry registry, TimeUnit rateUnit, TimeUnit durationUnit) {
super(registry, "custom-metric-reporter", MetricFilter.ALL, rateUnit, durationUnit);
}
// This method will be called periodically by the ScheduledReporter
@Override
public void report(SortedMap<String, Gauge> gauges,
SortedMap<String, Counter> counters,
SortedMap<String, Histogram> histograms,
SortedMap<String, Meter> meters,
SortedMap<String, Timer> timers) {
// Implement reporting logic here
// Example: Print out all metric names and their values
System.out.println("MyCustomReporter in action !!!");
for (Map.Entry<String, Gauge> entry : gauges.entrySet()) {
System.out.println("Gauge: " + entry.getKey() + ", Value: " + entry.getValue().getValue());
}
for (Map.Entry<String, Counter> entry : counters.entrySet()) {
System.out.println("Counter: " + entry.getKey() + ", Count: " + entry.getValue().getCount());
}
// Similarly, report for histograms, meters, and timers
}
// Factory method to create an instance of CustomMetricReporter
public static MyCustomReporter forRegistry(MetricRegistry registry) {
return new MyCustomReporter(registry, TimeUnit.SECONDS, TimeUnit.SECONDS);
}
}
MyCustomReporter myCustomReporter = MyCustomReporter.forRegistry(metrics);
myCustomReporter.start(10, TimeUnit.SECONDS); // Report every 10 seconds
I hope this information is useful. Please let me know if you have any thoughts or questions about it.
Reference: Dropwizard Metrics Documentation
No comments:
Post a Comment
Thanks.