@Override public void metricRemoval(KafkaMetric metric) { _interestedMetrics.remove(metric.metricName()); }
/** * Convert a KafkaMetric to a CruiseControlMetric */ public static CruiseControlMetric toCruiseControlMetric(KafkaMetric kafkaMetric, long now, int brokerId) { org.apache.kafka.common.MetricName metricName = kafkaMetric.metricName(); CruiseControlMetric ccm = toCruiseControlMetric(now, brokerId, metricName.name(), metricName.tags(), kafkaMetric.value()); if (ccm == null) { throw new IllegalArgumentException(String.format("Cannot convert KafkaMetric %s to a Cruise Control metric for " + "broker %d at time %d", kafkaMetric.metricName(), brokerId, now)); } return ccm; }
public double value() { return (Double) metric.metricValue(); }
public void checkQuotas(long timeMs) { for (KafkaMetric metric : this.metrics.values()) { MetricConfig config = metric.config(); if (config != null) { Quota quota = config.quota(); if (quota != null) { double value = metric.measurableValue(timeMs); if (!quota.acceptable(value)) { throw new QuotaViolationException(metric.metricName(), value, quota.bound()); } } } } }
/** * Register a compound statistic with this sensor which yields multiple measurable quantities (like a histogram) * @param stat The stat to register * @param config The configuration for this stat. If null then the stat will use the default configuration for this * sensor. * @return true if stat is added to sensor, false if sensor is expired */ public synchronized boolean add(CompoundStat stat, MetricConfig config) { if (hasExpired()) return false; this.stats.add(Utils.notNull(stat)); Object lock = metricLock(); for (NamedMeasurable m : stat.stats()) { final KafkaMetric metric = new KafkaMetric(lock, m.name(), m.stat(), config == null ? this.config : config, time); if (!metrics.containsKey(metric.metricName())) { registry.registerMetric(metric); metrics.put(metric.metricName(), metric); } } return true; }
@Override public double measure(MetricConfig config, long now) { double recordsConsumedRate = metrics.metrics().get(metrics.metricName("records-consumed-rate", METRIC_GROUP_NAME, tags)).value(); double recordsLostRate = metrics.metrics().get(metrics.metricName("records-lost-rate", METRIC_GROUP_NAME, tags)).value(); double recordsDelayedRate = metrics.metrics().get(metrics.metricName("records-delayed-rate", METRIC_GROUP_NAME, tags)).value(); if (new Double(recordsLostRate).isNaN()) recordsLostRate = 0; if (new Double(recordsDelayedRate).isNaN()) recordsDelayedRate = 0; double consumeAvailability = recordsConsumedRate + recordsLostRate > 0 ? (recordsConsumedRate - recordsDelayedRate) / (recordsConsumedRate + recordsLostRate) : 0; return consumeAvailability; } }
/** * Check if we have violated our quota for any metric that has a configured quota * @param timeMs */ private void checkQuotas(long timeMs) { for (int i = 0; i < this.metrics.size(); i++) { KafkaMetric metric = this.metrics.get(i); MetricConfig config = metric.config(); if (config != null) { Quota quota = config.quota(); if (quota != null) { if (!quota.acceptable(metric.value(timeMs))) throw new QuotaViolationException(metric.metricName() + " is in violation of its quota of " + quota.bound()); } } } }
sum += 100; time.sleep(cfg.timeWindowMs()); assertEquals(sum, (Double) totalMetric.metricValue(), EPS); assertEquals("Rate(0...2) = 2.666", sum / elapsedSecs, (Double) rateMetric.metricValue(), EPS); assertEquals("Count rate(0...2) = 0.02666", count / elapsedSecs, (Double) countRateMetric.metricValue(), EPS); assertEquals("Elapsed Time = 75 seconds", elapsedSecs, ((Rate) rateMetric.measurable()).windowSize(cfg, time.milliseconds()) / 1000, EPS); assertEquals(sum, (Double) totalMetric.metricValue(), EPS); assertEquals(count, (Double) countTotalMetric.metricValue(), EPS); assertEquals(0, (Double) rateMetric.metricValue(), EPS); assertEquals(0, (Double) countRateMetric.metricValue(), EPS); assertEquals(sum, (Double) totalMetric.metricValue(), EPS); assertEquals(count, (Double) countTotalMetric.metricValue(), EPS);
/** * Add a metric to monitor an object that implements MetricValueProvider. This metric won't be associated with any * sensor. This is a way to expose existing values as metrics. User is expected to add any additional * synchronization to update and access metric values, if required. * * @param metricName The name of the metric * @param metricValueProvider The metric value provider associated with this metric */ public void addMetric(MetricName metricName, MetricConfig config, MetricValueProvider<?> metricValueProvider) { KafkaMetric m = new KafkaMetric(new Object(), Utils.notNull(metricName), Utils.notNull(metricValueProvider), config == null ? this.config : config, time); registerMetric(m); }
public boolean isRate() { return metric.measurable() instanceof Rate; }
@Override public double measure(MetricConfig config, long now) { double availabilitySum = 0.0; int partitionNum = _partitionNum.get(); for (int partition = 0; partition < partitionNum; partition++) { double recordsProduced = metrics.metrics().get(metrics.metricName("records-produced-rate-partition-" + partition, METRIC_GROUP_NAME, tags)).value(); double produceError = metrics.metrics().get(metrics.metricName("produce-error-rate-partition-" + partition, METRIC_GROUP_NAME, tags)).value(); // If there is no error, error rate sensor may expire and the value may be NaN. Treat NaN as 0 for error rate. if (Double.isNaN(produceError) || Double.isInfinite(produceError)) { produceError = 0; } // If there is either succeeded or failed produce to a partition, consider its availability as 0. if (recordsProduced + produceError > 0) { availabilitySum += recordsProduced / (recordsProduced + produceError); } else if (!_treatZeroThroughputAsUnavailable) { // If user configures treatZeroThroughputAsUnavailable to be false, a partition's availability // is 1.0 as long as there is no exception thrown from producer. // This allows kafka admin to exactly monitor the availability experienced by Kafka users which // will block and retry for a certain amount of time based on its configuration (e.g. retries, retry.backoff.ms). // Note that if it takes a long time for messages to be retries and sent, the latency in the ConsumeService // will increase and it will reduce ConsumeAvailability if the latency exceeds consume.latency.sla.ms availabilitySum += 1.0; } } // Assign equal weight to per-partition availability when calculating overall availability return availabilitySum / partitionNum; } }
/** * Register a metric with this sensor * * @param metricName The name of the metric * @param stat The statistic to keep * @param config A special configuration for this metric. If null use the sensor default configuration. * @return true if metric is added to sensor, false if sensor is expired */ public synchronized boolean add(final MetricName metricName, final MeasurableStat stat, final MetricConfig config) { if (hasExpired()) { return false; } else if (metrics.containsKey(metricName)) { return true; } else { final KafkaMetric metric = new KafkaMetric( metricLock(), Utils.notNull(metricName), Utils.notNull(stat), config == null ? this.config : config, time ); registry.registerMetric(metric); metrics.put(metric.metricName(), metric); stats.add(stat); return true; } }
/** * Register a metric with this sensor * @param metricName The name of the metric * @param stat The statistic to keep * @param config A special configuration for this metric. If null use the sensor default configuration. */ public synchronized void add(MetricName metricName, MeasurableStat stat, MetricConfig config) { KafkaMetric metric = new KafkaMetric(new Object(), Utils.notNull(metricName), Utils.notNull(stat), config == null ? this.config : config, time); this.registry.registerMetric(metric); this.metrics.add(metric); this.stats.add(stat); }
@Test public void testIdempotentAdd() { final Metrics metrics = new Metrics(); final Sensor sensor = metrics.sensor("sensor"); assertTrue(sensor.add(metrics.metricName("test-metric", "test-group"), new Avg())); // adding the same metric to the same sensor is a no-op assertTrue(sensor.add(metrics.metricName("test-metric", "test-group"), new Avg())); // but adding the same metric to a DIFFERENT sensor is an error final Sensor anotherSensor = metrics.sensor("another-sensor"); try { anotherSensor.add(metrics.metricName("test-metric", "test-group"), new Avg()); fail("should have thrown"); } catch (final IllegalArgumentException ignored) { // pass } // note that adding a different metric with the same name is also a no-op assertTrue(sensor.add(metrics.metricName("test-metric", "test-group"), new Sum())); // so after all this, we still just have the original metric registered assertEquals(1, sensor.metrics().size()); assertEquals(org.apache.kafka.common.metrics.stats.Avg.class, sensor.metrics().get(0).measurable().getClass()); }
synchronized void registerMetric(KafkaMetric metric) { MetricName metricName = metric.metricName(); if (this.metrics.containsKey(metricName)) throw new IllegalArgumentException("A metric named '" + metricName + "' already exists, can't register another one."); this.metrics.put(metricName, metric); for (MetricsReporter reporter : reporters) { try { reporter.metricChange(metric); } catch (Exception e) { log.error("Error when registering metric on " + reporter.getClass().getName(), e); } } log.trace("Registered metric named {}", metricName); }
/** * Convert a KafkaMetric to a CruiseControlMetric */ public static CruiseControlMetric toCruiseControlMetric(KafkaMetric kafkaMetric, long now, int brokerId) { org.apache.kafka.common.MetricName metricName = kafkaMetric.metricName(); CruiseControlMetric ccm = toCruiseControlMetric(now, brokerId, metricName.name(), metricName.tags(), kafkaMetric.value()); if (ccm == null) { throw new IllegalArgumentException(String.format("Cannot convert KafkaMetric %s to a Cruise Control metric for " + "broker %d at time %d", kafkaMetric.metricName(), brokerId, now)); } return ccm; }
@Override public Double getValue() { return kafkaMetric.value(); } };
@Override public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException { if (this.metrics.containsKey(name)) return this.metrics.get(name).metricValue(); else throw new AttributeNotFoundException("Could not find attribute " + name); }
/** * Add a metric to monitor an object that implements measurable. This metric won't be associated with any sensor. * This is a way to expose existing values as metrics. * @param metricName The name of the metric * @param config The configuration to use when measuring this measurable * @param measurable The measurable that will be measured by this metric */ public synchronized void addMetric(MetricName metricName, MetricConfig config, Measurable measurable) { KafkaMetric m = new KafkaMetric(new Object(), Utils.notNull(metricName), Utils.notNull(measurable), config == null ? this.config : config, time); registerMetric(m); }
private void addMetricIfInterested(KafkaMetric metric) { LOG.trace("Checking Kafka metric {}", metric.metricName()); if (MetricsUtils.isInterested(metric.metricName())) { LOG.debug("Added new metric {} to Cruise Control metrics reporter.", metric.metricName()); _interestedMetrics.put(metric.metricName(), metric); } }