class QueryServlet extends HttpServlet { {@literal @}Stat("search-hits") private final AtomicInteger hits = new AtomicInteger(0); {@literal @}Inject QueryServlet(....) { } {@literal @}Override void doGet( HttpServletRequest req, HttpServletResponse resp) { .... String searchTerm = req.getParameter("q"); SearchResult result = searchService.searchFor(searchTerm); if (result.hasHits()) { hits.incrementAndGet(); } ... } }
This class registers a stat called {@code search-hits}. To configure the server to publish this stat, install a {@link StatModule}, such as:
public class YourServerModule extends AbstractModule { {@literal @}Override protected void configure() { install(new StatsModule("/stats"); ... } }
Then, to query the server for its stats, hit the url that was registered with the module (which was {@code /stats}, in the example above).
@Stat
annotation. Members of a class annotated by @Stat
are registered automatically when an instance of the class is created by Guice. The value of the member is read when a snapshot of the stats is requested, most likely by the {@link StatsServlet} upon a request to {@code /stats}. At times it is convenient to "manually" register a stat. To do this, inject an instance of {@link StatRegistrar} and use it to register a stat.For example:
class RegistersLocalVariableAsStat { private final StatRegistrar statRegistrar; {@literal @}Inject RegistersLocalVariableAsStat( StatRegistrar statRegistrar) { this.statRegistrar = statRegistrar; } void initialize() { long start = System.currentTimeMillis(); doInitialization(); statRegistrar.registerSingleStat( "init-time-in-ms", "Initialization time of a class", System.currentTimeMillis() - start); } }
There are other convenience methods on {@link StatRegistrar} to facilitateregistering annotated static members on classes and registering all annotated members on instances as well. List
, then a stat publisher could inadvertently (or purposely) mutate it. It is the role of a {@link StatExposer} to guard against such leaks: Anexposer is given the raw value of a stat, and should return a safe view of it. This view is then passed to the {@link StatsPublishers publishers}.
By default, a {@link StatExposers.InferenceExposer} is used to guard statsregistered via {@link Stat @Stat
}. This implementation should handle the majority of common use cases. If, however, you want to use a different {@link StatExposer} for your stat, then you may do so byspecifying its class within the {@link Stat @Stat
} annotation.For example:
class ServiceStat implements Cloneable { int calls; AtomicLong<Long> latencyInMs; {@literal @}Override protected Object clone() { return new ServiceStat(calls, latencyInMs); } } class ServiceStatExposer implements StatExposer<ServiceStat> { {@literal @}Override Object expose(ServiceStat serviceStat) { return serviceStat.clone(); } } class Service { {@literal @}Stat(value = "service-stat", exposer = ServiceStatExposer.class) private final ServiceStat serviceStat; ... }
public class CustomPublisherModule extends AbstractModule { {@literal @}Override protected void configure() { MapBinder<String, StatsPublisher> mapBinder = MapBinder.newMapBinder(binder(), String.class, StatsPublisher.class); mapBinder.addBinding("custom").to(CustomStatsPublisher.class); } }
You can then retrieve stats from your custom publisher by hitting {@code /stats?format=custom}.
@author dhanji@gmail.com (Dhanji R. Prasanna)
@author ffaber@gmail.com (Fred Faber)
|
|
|
|