//Dstl (c) Crown Copyright 2017 package uk.gov.dstl.baleen.uima; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.gov.dstl.baleen.core.metrics.Metrics; import uk.gov.dstl.baleen.core.metrics.MetricsFactory; import uk.gov.dstl.baleen.uima.utils.UimaUtils; import com.codahale.metrics.Counter; import com.codahale.metrics.Meter; import com.codahale.metrics.Timer; /** * Provides metrics and logging for Baleen UIMA components, such as * {@link uk.gov.dstl.baleen.uima.BaleenAnnotator}. * * */ public class UimaMonitor { private final Logger logger; private final Metrics metrics; private final Map<String, Timer.Context> timers = new HashMap<>(); private final Map<String, Long> entityAddedCounts = new HashMap<>(); private final Map<String, Long> entityRemovedCounts = new HashMap<>(); private final String pipelineName; /** * Constructor to create a UimaMonitor. The class and pipeline name should * be specified so that we can differentiate between them in the logging and * metrics * * @param clazz * The class that is creating the UimaMonitor instance * @param pipelineName * The name of the pipeline */ public UimaMonitor(String pipelineName, Class<?> clazz) { this.pipelineName = pipelineName; logger = LoggerFactory.getLogger(UimaUtils.makePipelineSpecificName(pipelineName, clazz)); metrics = MetricsFactory.getMetrics(pipelineName, clazz); } /** * Get the name of the pipeline to which this belongs. * * @return pipeline name */ public String getPipelineName() { return pipelineName; } /** * Start a timer at the start of the function, and log that the function is * about to begin * * @param functionName * The name of the function */ public void startFunction(String functionName) { logger.debug("Starting function {}", functionName); timers.put(functionName, metrics.getTimer(functionName).time()); } /** * Finish the timer at the end of the function, and log that the function * has finished * * @param functionName * The name of the function */ public void finishFunction(String functionName) { stopTimer(functionName); logger.debug("Finishing function {}", functionName); } /** * Start a timer at the start of the function, and log that the function is * about to begin. Logs to TRACE level rather than DEBUG, as some functions * are called so often we don't want to fill up the logs! * * @param functionName * The name of the function */ public void startFunctionTrace(String functionName) { logger.trace("Starting function {}", functionName); timers.put(functionName, metrics.getTimer(functionName).time()); } /** * Finish the timer at the end of the function, and log that the function * has finished Logs to TRACE level rather than DEBUG, as some functions are * called so often we don't want to fill up the logs! * * @param functionName * The name of the function */ public void finishFunctionTrace(String functionName) { stopTimer(functionName); logger.trace("Finishing function {}", functionName); } /** * Register that an entity of a specific type has been added * * @param type * The type of entity that has been added */ public void entityAdded(String type) { Long count = entityAddedCounts.get(type); if (count == null) { count = 0L; } count++; entityAddedCounts.put(type, count); logger.trace("Entity of type {} added", type); } /** * Register that an entity of a specific type has been removed * * @param type * The type of entity that has been removed */ public void entityRemoved(String type) { Long count = entityRemovedCounts.get(type); if (count == null) { count = 0L; } count++; entityRemovedCounts.put(type, count); logger.trace("Entity of type {} removed", type); } /** * Persist entity counts into the Metrics object, as we only want to do this * once per process() function and not every time we add an entity. */ public void persistCounts() { for (String type : entityAddedCounts.keySet()) { metrics.getCounter(type + "-added").inc(entityAddedCounts.get(type)); } entityAddedCounts.clear(); for (String type : entityRemovedCounts.keySet()) { metrics.getCounter(type + "-removed").inc(entityRemovedCounts.get(type)); } entityRemovedCounts.clear(); } /** * Log a trace level message. * * @param message * the formatted message * @param args * parameters to substitute */ public void trace(String message, Object... args) { logger.trace(message, args); } /** * Log a debug level message. * * @param message * the formatted message * @param args * parameters to substitute */ public void debug(String message, Object... args) { logger.debug(message, args); } /** * Log an info level message. * * @param message * the formatted message * @param args * parameters to substitute */ public void info(String message, Object... args) { logger.info(message, args); } /** * Log a warning level message. * * @param message * the formatted message * @param args * parameters to substitute */ public void warn(String message, Object... args) { logger.warn(message, args); } /** * Log a warning level message, with an exception. * * @param message * the formatted message * @param args * parameters to substitute */ public void warn(String message, Throwable e) { logger.error(message, e); } /** * Log an error level message. * * @param message * the formatted message * @param args * parameters to substitute */ public void error(String message, Object... args) { logger.error(message, args); } /** * Log a error level message, with an exception. * * @param message * the formatted message * @param args * parameters to substitute */ public void error(String message, Throwable e) { logger.error(message, e); } /** * Get the counter metric with the supplied name, within the scope of this * monitor. * * @param name * the name of the counter * @return the counter */ public Counter counter(String name) { return metrics.getCounter(name); } /** * Get the timer metric with the supplied name, within the scope of this * monitor. * * @param name * the name of the timer * @return the timer */ public Timer timer(String name) { return metrics.getTimer(name); } /** * Get the histogram metric with the supplied name, within the scope of this * monitor. * * @param name * the name of the histogram * @return the histogram */ public Counter histogram(String name) { return metrics.getCounter(name); } /** * Get the meter metric with the supplied name, within the scope of this * monitor. * * @param name * the name of the meter * @return the meter */ public Meter meter(String name) { return metrics.getMeter(name); } private void stopTimer(String name) { if (timers.containsKey(name)) { timers.remove(name).stop(); } } }