package net.johnewart.gearman.engine.metrics;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.ImmutableList;
import net.johnewart.gearman.common.Job;
import net.johnewart.gearman.common.interfaces.EngineWorker;
import net.johnewart.gearman.constants.JobPriority;
import net.johnewart.gearman.engine.queue.JobQueue;
import org.joda.time.DateTime;
import java.util.concurrent.ConcurrentHashMap;
import static com.codahale.metrics.MetricRegistry.name;
public class MetricsEngine implements QueueMetrics {
private final ConcurrentHashMap<String, CounterGroup> queueCounters = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Counter> workerCounters = new ConcurrentHashMap<>();
private final MetricRegistry registry;
private final Gauge<Long> pendingJobsGauge;
private final Counter queuedJobsCounter;
private final Counter completedJobsCounter;
private final Counter failedJobsCounter;
private final Counter activeJobsCounter;
private final Counter jobExceptionsCounter;
private final Meter jobMeter;
private final Counter activeWorkersCounter;
private final DateTime startTime;
public MetricsEngine(MetricRegistry registry) {
this.startTime = DateTime.now();
this.registry = registry;
pendingJobsGauge = registry.register(name("queues", "pending-jobs"), (Gauge<Long>) MetricsEngine.this::getPendingJobsCount);
queuedJobsCounter = registry.counter(name("queues", "queued-jobs"));
completedJobsCounter = registry.counter(name("queues", "completed-jobs"));
failedJobsCounter = registry.counter(name("queues", "failed-jobs"));
activeJobsCounter = registry.counter(name("queues", "active-jobs"));
jobExceptionsCounter = registry.counter(name("queues", "job-exceptions"));
jobMeter = registry.meter(name("queues", "queued-jobs-meter", "enqueued"));
activeWorkersCounter = registry.counter(name("queues", "active-workers"));
}
@Override
public void handleJobCompleted(Job job) {
completedJobsCounter.inc();
queueCounters.get(job.getFunctionName()).completed.inc();
decrementActive(job);
}
@Override
public void handleJobFailed(Job job) {
failedJobsCounter.inc();
queueCounters.get(job.getFunctionName()).failed.inc();
decrementActive(job);
}
@Override
public void handleJobEnqueued(Job job) {
jobMeter.mark();
queuedJobsCounter.inc();
queueCounters.get(job.getFunctionName()).queued.inc();
}
@Override
public void handleWorkerAddition(EngineWorker worker) {
activeWorkersCounter.inc();
for(String jobQueue : worker.getAbilities()) {
workerCounters.getOrDefault(jobQueue, registry.counter(name("queue", jobQueue, "workers"))).inc();
}
}
@Override
public void handleWorkerRemoval(EngineWorker worker) {
activeWorkersCounter.dec();
for(String jobQueue : worker.getAbilities()) {
if (workerCounters.get(jobQueue) != null)
workerCounters.get(jobQueue).dec();
}
}
@Override
public DateTime getStartTime() {
return startTime;
}
@Override
public long getActiveJobCount() {
return activeJobsCounter.getCount();
}
@Override
public long getActiveJobCount(String queueName) {
return queueCounters.get(queueName).active.getCount();
}
@Override
public long getEnqueuedJobCount() {
return queuedJobsCounter.getCount();
}
@Override
public long getEnqueuedJobCount(String queueName) {
return queueCounters.get(queueName).queued.getCount();
}
@Override
public void handleJobStarted(Job job) {
incrementActive(job);
}
@Override
public void handleJobException(Job job) {
decrementActive(job);
jobExceptionsCounter.inc();
queueCounters.get(job.getFunctionName()).exceptions.inc();
}
@Override
public long getCompletedJobCount() {
return completedJobsCounter.getCount();
}
@Override
public long getCompletedJobCount(String queueName) {
return queueCounters.get(queueName).completed.getCount();
}
@Override
public long getFailedJobCount() {
return failedJobsCounter.getCount();
}
@Override
public long getFailedJobCount(String queueName) {
return queueCounters.get(queueName).failed.getCount();
}
@Override
public long getExceptionCount() {
return jobExceptionsCounter.getCount();
}
@Override
public long getExceptionCount(String queueName) {
return queueCounters.get(queueName).exceptions.getCount();
}
@Override
public long getRunningJobsCount() {
return activeJobsCounter.getCount();
}
@Override
public long getRunningJobsCount(String queueName) {
return queueCounters.get(queueName).active.getCount();
}
@Override
public long getPendingJobsCount() {
return queueCounters
.values()
.stream()
.mapToLong(counterGroup -> counterGroup.total.getValue())
.sum();
}
@Override
public long getPendingJobsCount(String queueName) {
return queueCounters.get(queueName).low.getValue() +
queueCounters.get(queueName).mid.getValue() +
queueCounters.get(queueName).high.getValue();
}
@Override
public long getHighPriorityJobsCount(String queueName) {
return queueCounters.get(queueName).high.getValue();
}
@Override
public long getMidPriorityJobsCount(String queueName) {
return queueCounters.get(queueName).mid.getValue();
}
@Override
public long getLowPriorityJobsCount(String queueName) {
return queueCounters.get(queueName).low.getValue();
}
@Override
public ImmutableList<String> getQueueNames() {
return ImmutableList.copyOf(queueCounters.keySet());
}
@Override
public long getActiveWorkers() {
return activeWorkersCounter.getCount();
}
public long getActiveWorkers(String queueName) {
return workerCounters.getOrDefault(queueName, registry.counter(name("queue", queueName, "workers"))).getCount();
}
@Override
public void registerJobQueue(JobQueue jobQueue)
{
queueCounters.put(jobQueue.getName(), new CounterGroup(jobQueue, registry));
}
private void decrementEnqueued(Job job)
{
queueCounters.get(job.getFunctionName()).queued.dec();
queuedJobsCounter.dec();
}
private void decrementActive(Job job) {
activeJobsCounter.dec();
CounterGroup counter = queueCounters.get(job.getFunctionName());
counter.active.dec();
}
private void incrementActive(Job job) {
activeJobsCounter.inc();
CounterGroup counter = queueCounters.get(job.getFunctionName());
counter.active.inc();
}
class CounterGroup {
public final Counter queued;
public final Counter completed;
public final Counter failed;
public final Counter active;
public final Counter exceptions;
public final Gauge<Long> low;
public final Gauge<Long> mid;
public final Gauge<Long> high;
public final Gauge<Long> total;
private final JobQueue jobQueue;
public CounterGroup(JobQueue jobQueue, MetricRegistry registry) {
this.jobQueue = jobQueue;
final String queueName = jobQueue.getName();
low = () -> this.jobQueue.size(JobPriority.LOW);
mid = () -> this.jobQueue.size(JobPriority.NORMAL);
high = () -> this.jobQueue.size(JobPriority.HIGH);
total = () -> this.jobQueue.size();
queued = registry.counter(name("queue", queueName, "queued"));
completed = registry.counter(name("queue", queueName, "completed"));
failed = registry.counter(name("queue", queueName, "failed"));
active = registry.counter(name("queue", queueName, "active"));
exceptions = registry.counter(name("queue", queueName, "exceptions"));
}
}
}