diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java index 408805c5ebb2..7b6d8b4c3eeb 100644 --- a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java +++ b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java @@ -19,6 +19,7 @@ package org.apache.pinot.broker.broker.helix; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; @@ -309,12 +310,13 @@ public void start() _brokerConf.getProperty(Broker.CONFIG_OF_ENABLE_TABLE_LEVEL_METRICS, Broker.DEFAULT_ENABLE_TABLE_LEVEL_METRICS), _brokerConf.getProperty(Broker.CONFIG_OF_ALLOWED_TABLES_FOR_EMITTING_METRICS, Collections.emptyList())); _brokerMetrics.initializeGlobalMeters(); - _brokerMetrics.setValueOfGlobalGauge(BrokerGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, 1); + _brokerMetrics.setValueOfGlobalGauge(BrokerGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, + 1, ImmutableMap.of()); _brokerMetrics.setValueOfGlobalGauge(BrokerGauge.ZK_JUTE_MAX_BUFFER, Integer.getInteger(ZkSystemPropertyKeys.JUTE_MAXBUFFER, 0xfffff)); _brokerMetrics.setValueOfGlobalGauge(BrokerGauge.ADAPTIVE_SERVER_SELECTOR_TYPE, _brokerConf.getProperty(Broker.AdaptiveServerSelector.CONFIG_OF_TYPE, - Broker.AdaptiveServerSelector.DEFAULT_TYPE), 1); + Broker.AdaptiveServerSelector.DEFAULT_TYPE), 1, ImmutableMap.of()); BrokerMetrics.register(_brokerMetrics); // Set up request handling classes _serverRoutingStatsManager = new ServerRoutingStatsManager(_brokerConf, _brokerMetrics); diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java index e2e765241278..8ee33c32777f 100644 --- a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java +++ b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.net.URI; import java.util.ArrayList; @@ -65,6 +66,7 @@ import org.apache.pinot.common.metrics.BrokerMetrics; import org.apache.pinot.common.metrics.BrokerQueryPhase; import org.apache.pinot.common.metrics.BrokerTimer; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.request.BrokerRequest; import org.apache.pinot.common.request.DataSource; import org.apache.pinot.common.request.Expression; @@ -862,8 +864,13 @@ protected BrokerResponse doHandleRequest(long requestId, String query, SqlNodeAn } for (int pool : brokerResponse.getPools()) { + String poolTag = BrokerMetrics.getTagForPreferredPool(sqlNodeAndOptions.getOptions()); + String poolId = String.valueOf(pool); _brokerMetrics.addMeteredValue(BrokerMeter.POOL_QUERIES, 1, - BrokerMetrics.getTagForPreferredPool(sqlNodeAndOptions.getOptions()), String.valueOf(pool)); + ImmutableList.of(poolTag, poolId), + ImmutableMap.of(MetricAttributeConstants.POOL_TAG, poolTag, + MetricAttributeConstants.POOL_ID, poolId) + ); } brokerResponse.setRLSFiltersApplied(rlsFiltersApplied.get()); diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/BalancedInstanceSelector.java b/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/BalancedInstanceSelector.java index 4afd3a26dcd0..f945de76e0f4 100644 --- a/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/BalancedInstanceSelector.java +++ b/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/BalancedInstanceSelector.java @@ -18,6 +18,8 @@ */ package org.apache.pinot.broker.routing.instanceselector; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import java.time.Clock; import java.util.HashMap; import java.util.List; @@ -30,6 +32,7 @@ import org.apache.pinot.broker.routing.adaptiveserverselector.ServerSelectionContext; import org.apache.pinot.common.metrics.BrokerMeter; import org.apache.pinot.common.metrics.BrokerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.utils.HashUtil; /// Instance selector to balance the number of segments served by each selected server instance. @@ -96,8 +99,13 @@ Pair, Map> select(List segments, int } for (Map.Entry entry : poolToSegmentCount.entrySet()) { - _brokerMetrics.addMeteredValue(BrokerMeter.POOL_SEG_QUERIES, entry.getValue(), - BrokerMetrics.getTagForPreferredPool(queryOptions), String.valueOf(entry.getKey())); + String poolTag = BrokerMetrics.getTagForPreferredPool(queryOptions); + String poolID = String.valueOf(entry.getKey()); + _brokerMetrics.addMeteredValue( + BrokerMeter.POOL_SEG_QUERIES, entry.getValue(), + ImmutableList.of(poolTag, poolID), + ImmutableMap.of(MetricAttributeConstants.POOL_TAG, poolTag, MetricAttributeConstants.POOL_ID, poolID) + ); } return Pair.of(segmentToSelectedInstanceMap, optionalSegmentToInstanceMap); } diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/ReplicaGroupInstanceSelector.java b/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/ReplicaGroupInstanceSelector.java index 88793eac7cbc..80c2111b81bf 100644 --- a/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/ReplicaGroupInstanceSelector.java +++ b/pinot-broker/src/main/java/org/apache/pinot/broker/routing/instanceselector/ReplicaGroupInstanceSelector.java @@ -18,6 +18,8 @@ */ package org.apache.pinot.broker.routing.instanceselector; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import java.time.Clock; import java.util.ArrayList; import java.util.Comparator; @@ -33,6 +35,7 @@ import org.apache.pinot.broker.routing.adaptiveserverselector.ServerSelectionContext; import org.apache.pinot.common.metrics.BrokerMeter; import org.apache.pinot.common.metrics.BrokerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.utils.HashUtil; import org.apache.pinot.common.utils.config.QueryOptionsUtils; @@ -146,8 +149,11 @@ private Pair, Map> selectServers(List entry : poolToSegmentCount.entrySet()) { + String poolTag = BrokerMetrics.getTagForPreferredPool(ctx.getQueryOptions()); + String poolId = String.valueOf(entry.getKey()); _brokerMetrics.addMeteredValue(BrokerMeter.POOL_SEG_QUERIES, entry.getValue(), - BrokerMetrics.getTagForPreferredPool(ctx.getQueryOptions()), String.valueOf(entry.getKey())); + ImmutableList.of(poolTag, poolId), + ImmutableMap.of(MetricAttributeConstants.POOL_TAG, poolTag, MetricAttributeConstants.POOL_ID, poolId)); } return Pair.of(segmentToSelectedInstanceMap, optionalSegmentToInstanceMap); } diff --git a/pinot-common/src/main/java/org/apache/pinot/common/metrics/AbstractMetrics.java b/pinot-common/src/main/java/org/apache/pinot/common/metrics/AbstractMetrics.java index 4c879e127868..2ecaee6c5219 100644 --- a/pinot-common/src/main/java/org/apache/pinot/common/metrics/AbstractMetrics.java +++ b/pinot-common/src/main/java/org/apache/pinot/common/metrics/AbstractMetrics.java @@ -19,9 +19,12 @@ package org.apache.pinot.common.metrics; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; @@ -141,7 +144,12 @@ public void removePhaseTiming(String tableName, QP phase) { public void addPhaseTiming(String tableName, QP phase, long duration, TimeUnit timeUnit) { String fullTimerName = _metricPrefix + getTableName(tableName) + "." + phase.getQueryPhaseName(); - addValueToTimer(fullTimerName, duration, timeUnit); + addValueToTimer(fullTimerName, + phase.getQueryPhaseName(), + duration, + timeUnit, + ImmutableMap.of(MetricAttributeConstants.TABLE_NAME, tableName) + ); } public void addPhaseTiming(String tableName, QP phase, long nanos) { @@ -158,21 +166,34 @@ public void addPhaseTiming(String tableName, QP phase, long nanos) { */ public void addTimedTableValue(final String tableName, T timer, final long duration, final TimeUnit timeUnit) { final String fullTimerName = _metricPrefix + getTableName(tableName) + "." + timer.getTimerName(); - addValueToTimer(fullTimerName, duration, timeUnit); + addValueToTimer(fullTimerName, + timer.getTimerName(), + duration, + timeUnit, + ImmutableMap.of(MetricAttributeConstants.TABLE_NAME, tableName) + ); } /** * Logs the timing for a table with an additional key * @param tableName The table associated with this timer - * @param key The additional key associated with this timer + * @param taskType The additional taskType associated with this timer * @param timer The name of timer * @param duration The log time duration time value * @param timeUnit The log time duration time unit */ - public void addTimedTableValue(final String tableName, final String key, final T timer, final long duration, + public void addTimedTableTaskValue(final String tableName, final String taskType, final T timer, final long duration, final TimeUnit timeUnit) { - final String fullTimerName = _metricPrefix + getTableName(tableName) + "." + key + "." + timer.getTimerName(); - addValueToTimer(fullTimerName, duration, timeUnit); + final String fullTimerName = _metricPrefix + getTableName(tableName) + "." + taskType + "." + timer.getTimerName(); + addValueToTimer(fullTimerName, + timer.getTimerName(), + duration, + timeUnit, + ImmutableMap.of( + MetricAttributeConstants.TABLE_NAME, tableName, + MetricAttributeConstants.TASK_TYPE, taskType + ) + ); } /** @@ -183,7 +204,12 @@ public void addTimedTableValue(final String tableName, final String key, final T */ public void addTimedValue(T timer, final long duration, final TimeUnit timeUnit) { final String fullTimerName = _metricPrefix + timer.getTimerName(); - addValueToTimer(fullTimerName, duration, timeUnit); + addValueToTimer(fullTimerName, + timer.getTimerName(), + duration, + timeUnit, + ImmutableMap.of() + ); } /** @@ -192,21 +218,27 @@ public void addTimedValue(T timer, final long duration, final TimeUnit timeUnit) * @param timer The name of timer * @param duration The log time duration time value * @param timeUnit The log time duration time unit + * @param attributes Additional attributes to be added to the metric */ - public void addTimedValue(final String key, final T timer, final long duration, final TimeUnit timeUnit) { + public void addTimedValue(final String key, final T timer, final long duration, final TimeUnit timeUnit, + Map attributes) { final String fullTimerName = _metricPrefix + key + "." + timer.getTimerName(); - addValueToTimer(fullTimerName, duration, timeUnit); + addValueToTimer(fullTimerName, timer.getTimerName(), duration, timeUnit, attributes); } /** * Logs the timing for a metric * * @param fullTimerName The full name of timer + * @param simplifiedTimerName The simplified name of timer * @param duration The log time duration time value * @param timeUnit The log time duration time unit + * @param attributes Additional attributes to be added to the metric */ - private void addValueToTimer(String fullTimerName, final long duration, final TimeUnit timeUnit) { - final PinotMetricName metricName = PinotMetricUtils.makePinotMetricName(_clazz, fullTimerName); + private void addValueToTimer(String fullTimerName, String simplifiedTimerName, final long duration, + final TimeUnit timeUnit, Map attributes) { + final PinotMetricName metricName = PinotMetricUtils.makePinotMetricName(_clazz, fullTimerName, + simplifiedTimerName, new HashMap<>(attributes)); PinotTimer timer = PinotMetricUtils.makePinotTimer(_metricsRegistry, metricName, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); if (timer != null) { @@ -215,8 +247,8 @@ private void addValueToTimer(String fullTimerName, final long duration, final Ti } public void removeTimer(final String fullTimerName) { - PinotMetricUtils - .removeMetric(_metricsRegistry, PinotMetricUtils.makePinotMetricName(_clazz, fullTimerName)); + PinotMetricUtils.removeMetric(_metricsRegistry, + PinotMetricUtils.makePinotMetricName(_clazz, fullTimerName, fullTimerName, ImmutableMap.of())); } public void removeTableTimer(final String tableName, final T timer) { @@ -249,7 +281,9 @@ public PinotMeter addMeteredGlobalValue(final M meter, final long unitCount, Pin final String fullMeterName; String meterName = meter.getMeterName(); fullMeterName = _metricPrefix + meterName; - final PinotMetricName metricName = PinotMetricUtils.makePinotMetricName(_clazz, fullMeterName); + + final PinotMetricName metricName = PinotMetricUtils.makePinotMetricName(_clazz, fullMeterName, + meterName, ImmutableMap.of()); final PinotMeter newMeter = PinotMetricUtils.makePinotMeter(_metricsRegistry, metricName, meter.getUnit(), TimeUnit.SECONDS); @@ -258,15 +292,8 @@ public PinotMeter addMeteredGlobalValue(final M meter, final long unitCount, Pin } } - /** - * Logs a value to a meter with a key. - * - * @param key The key associated with this meter - * @param meter The meter to use - * @param unitCount The number of units to add to the meter - */ - public void addMeteredValue(final String key, final M meter, final long unitCount) { - addMeteredValue(key, meter, unitCount, null); + public void addMeteredValue(final String key, final M meter, final long unitCount, Map attributes) { + addMeteredValue(key, meter, unitCount, null, attributes); } /** @@ -277,10 +304,11 @@ public void addMeteredValue(final String key, final M meter, final long unitCoun * @param unitCount The number of units to add to the meter * @param reusedMeter The meter to reuse */ - public PinotMeter addMeteredValue(final String key, final M meter, final long unitCount, PinotMeter reusedMeter) { + public PinotMeter addMeteredValue(final String key, final M meter, final long unitCount, + PinotMeter reusedMeter, Map attributes) { String meterName = meter.getMeterName(); final String fullMeterName = _metricPrefix + key + "." + meterName; - return addValueToMeter(fullMeterName, meter.getUnit(), unitCount, reusedMeter); + return addValueToMeter(fullMeterName, meterName, meter.getUnit(), unitCount, reusedMeter, attributes); } /** @@ -303,7 +331,8 @@ public void addMeteredTableValue(final String tableName, final M meter, final lo */ public PinotMeter addMeteredTableValue(final String tableName, final M meter, final long unitCount, PinotMeter reusedMeter) { - return addValueToMeter(getTableFullMeterName(tableName, meter), meter.getUnit(), unitCount, reusedMeter); + return addValueToMeter(getTableFullMeterName(tableName, meter), meter.getMeterName(), + meter.getUnit(), unitCount, reusedMeter, ImmutableMap.of()); } /** @@ -312,9 +341,11 @@ public PinotMeter addMeteredTableValue(final String tableName, final M meter, fi * @param key The additional key associated with this meter * @param meter The meter to use * @param unitCount The number of units to add to the meter + * @param attributes The attributes to attach to the meter */ - public void addMeteredTableValue(final String tableName, final String key, final M meter, final long unitCount) { - addMeteredTableValue(tableName, key, meter, unitCount, null); + public void addMeteredTableValue(final String tableName, final String key, final M meter, final long unitCount, + Map attributes) { + addMeteredTableValue(tableName, key, meter, unitCount, null, attributes); } /** @@ -324,27 +355,39 @@ public void addMeteredTableValue(final String tableName, final String key, final * @param meter The meter to use * @param unitCount The number of units to add to the meter * @param reusedMeter The meter to reuse + * @param attributes The attributes to attach to the meter */ public PinotMeter addMeteredTableValue(final String tableName, final String key, final M meter, final long unitCount, - PinotMeter reusedMeter) { + PinotMeter reusedMeter, Map attributes) { String meterName = meter.getMeterName(); final String fullMeterName = _metricPrefix + getTableName(tableName) + "." + key + "." + meterName; - return addValueToMeter(fullMeterName, meter.getUnit(), unitCount, reusedMeter); + Map fullAttributes = new HashMap<>(attributes); + fullAttributes.put(MetricAttributeConstants.TABLE_NAME, tableName); + + return addValueToMeter(fullMeterName, meterName, meter.getUnit(), unitCount, reusedMeter, fullAttributes); } - public PinotMeter addMeteredValue(final M meter, final long unitCount, final String... tags) { + public PinotMeter addMeteredValue(final M meter, final long unitCount, final List tags, + Map attributes) { String meterName = meter.getMeterName(); final String fullMeterName = _metricPrefix + meterName + "." + String.join(".", tags); - return addValueToMeter(fullMeterName, meter.getUnit(), unitCount, null); + return addValueToMeter(fullMeterName, meterName, meter.getUnit(), unitCount, null, attributes); } - private PinotMeter addValueToMeter(final String fullMeterName, final String unit, final long unitCount, - PinotMeter reusedMeter) { + private PinotMeter addValueToMeter( + final String fullMeterName, + final String simplifiedMeterName, + final String unit, final long unitCount, + PinotMeter reusedMeter, + Map attributes + ) { if (reusedMeter != null) { reusedMeter.mark(unitCount); return reusedMeter; } else { - final PinotMetricName metricName = PinotMetricUtils.makePinotMetricName(_clazz, fullMeterName); + final PinotMetricName metricName = PinotMetricUtils.makePinotMetricName(_clazz, fullMeterName, + simplifiedMeterName, attributes); + final PinotMeter newMeter = PinotMetricUtils.makePinotMeter(_metricsRegistry, metricName, unit, TimeUnit.SECONDS); newMeter.mark(unitCount); @@ -354,14 +397,15 @@ private PinotMeter addValueToMeter(final String fullMeterName, final String unit public PinotMeter getMeteredTableValue(final String tableName, final M meter) { final PinotMetricName metricName = PinotMetricUtils.makePinotMetricName(_clazz, - getTableFullMeterName(tableName, meter)); + getTableFullMeterName(tableName, meter), meter.getMeterName(), ImmutableMap.of()); return PinotMetricUtils.makePinotMeter(_metricsRegistry, metricName, meter.getUnit(), TimeUnit.SECONDS); } public PinotMeter getMeteredValue(final M meter) { final PinotMetricName metricName = - PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + meter.getMeterName()); + PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + meter.getMeterName(), + meter.getMeterName(), ImmutableMap.of()); return PinotMetricUtils.makePinotMeter(_metricsRegistry, metricName, meter.getUnit(), TimeUnit.SECONDS); } @@ -383,19 +427,14 @@ private String getTableFullMeterName(final String tableName, final M meter) { @Deprecated public void addValueToTableGauge(final String tableName, final G gauge, final long unitCount) { final String fullGaugeName = composeTableGaugeName(tableName, gauge); - + Map attributes = ImmutableMap.of(MetricAttributeConstants.TABLE_NAME, tableName); AtomicLong gaugeValue = _gaugeValues.get(fullGaugeName); if (gaugeValue == null) { synchronized (_gaugeValues) { if (!_gaugeValues.containsKey(fullGaugeName)) { _gaugeValues.put(fullGaugeName, new AtomicLong(unitCount)); - addCallbackGauge(fullGaugeName, new Callable() { - @Override - public Long call() - throws Exception { - return _gaugeValues.get(fullGaugeName).get(); - } - }); + addCallbackGauge(fullGaugeName, gauge.getGaugeName(), attributes, + () -> _gaugeValues.get(fullGaugeName).get()); } else { _gaugeValues.get(fullGaugeName).addAndGet(unitCount); } @@ -414,7 +453,8 @@ public Long call() */ public void setValueOfTableGauge(final String tableName, final G gauge, final long value) { final String fullGaugeName = composeTableGaugeName(tableName, gauge); - setValueOfGauge(value, fullGaugeName); + Map attributes = ImmutableMap.of(MetricAttributeConstants.TABLE_NAME, tableName); + setValueOfGauge(value, fullGaugeName, gauge.getGaugeName(), attributes); } /** @@ -427,7 +467,11 @@ public void setValueOfTableGauge(final String tableName, final G gauge, final lo */ public void setValueOfPartitionGauge(final String tableName, final int partitionId, final G gauge, final long value) { final String fullGaugeName = composeTableGaugeName(tableName, String.valueOf(partitionId), gauge); - setValueOfGauge(value, fullGaugeName); + Map attributes = ImmutableMap.of( + MetricAttributeConstants.TABLE_NAME, tableName, + MetricAttributeConstants.PARTITION_ID, String.valueOf(partitionId) + ); + setValueOfGauge(value, fullGaugeName, gauge.getGaugeName(), attributes); } /** @@ -436,13 +480,14 @@ public void setValueOfPartitionGauge(final String tableName, final int partition * @param suffix The suffix to attach to the gauge name * @param gauge The gauge to use * @param value The value to set the gauge to + * @param attributes Additional attributes to be added to the metric */ - public void setValueOfGlobalGauge(final G gauge, final String suffix, final long value) { + public void setValueOfGlobalGauge(final G gauge, final String suffix, final long value, + Map attributes) { final String fullGaugeName; String gaugeName = gauge.getGaugeName(); fullGaugeName = gaugeName + "." + suffix; - - setValueOfGauge(value, fullGaugeName); + setValueOfGauge(value, fullGaugeName, gaugeName, attributes); } /** @@ -453,19 +498,21 @@ public void setValueOfGlobalGauge(final G gauge, final String suffix, final long */ public void setValueOfGlobalGauge(final G gauge, final long value) { final String gaugeName = gauge.getGaugeName(); - - setValueOfGauge(value, gaugeName); + // global gauge has no extra attributes + setValueOfGauge(value, gaugeName, gaugeName, ImmutableMap.of()); } - protected void setValueOfGauge(long value, String gaugeName) { - AtomicLong gaugeValue = _gaugeValues.get(gaugeName); + protected void setValueOfGauge(long value, String fullGaugeName, String simplifiedGaugeName, + Map attributes) { + AtomicLong gaugeValue = _gaugeValues.get(fullGaugeName); if (gaugeValue == null) { synchronized (_gaugeValues) { - if (!_gaugeValues.containsKey(gaugeName)) { - _gaugeValues.put(gaugeName, new AtomicLong(value)); - setOrUpdateGauge(gaugeName, () -> _gaugeValues.get(gaugeName).get()); + if (!_gaugeValues.containsKey(fullGaugeName)) { + _gaugeValues.put(fullGaugeName, new AtomicLong(value)); + setOrUpdateGauge(fullGaugeName, simplifiedGaugeName, attributes, + () -> _gaugeValues.get(fullGaugeName).get()); } else { - _gaugeValues.get(gaugeName).set(value); + _gaugeValues.get(fullGaugeName).set(value); } } } else { @@ -491,7 +538,7 @@ public void addValueToGlobalGauge(final G gauge, final long unitCount) { synchronized (_gaugeValues) { if (!_gaugeValues.containsKey(gaugeName)) { _gaugeValues.put(gaugeName, new AtomicLong(unitCount)); - setOrUpdateGauge(gaugeName, () -> _gaugeValues.get(gaugeName).get()); + setOrUpdateGauge(gaugeName, gaugeName, ImmutableMap.of(), () -> _gaugeValues.get(gaugeName).get()); } else { _gaugeValues.get(gaugeName).addAndGet(unitCount); } @@ -548,7 +595,8 @@ public void initializeGlobalMeters() { @Deprecated public void addCallbackTableGaugeIfNeeded(final String tableName, final G gauge, final Callable valueCallback) { final String fullGaugeName = composeTableGaugeName(tableName, gauge); - addCallbackGaugeIfNeeded(fullGaugeName, valueCallback); + Map attributes = ImmutableMap.of(MetricAttributeConstants.TABLE_NAME, tableName); + addCallbackGaugeIfNeeded(fullGaugeName, gauge.getGaugeName(), attributes, valueCallback); } /** @@ -561,8 +609,12 @@ public void addCallbackTableGaugeIfNeeded(final String tableName, final G gauge, */ public void setOrUpdatePartitionGauge(final String tableName, final int partitionId, final G gauge, final Supplier valueSupplier) { + Map attributes = ImmutableMap.of( + MetricAttributeConstants.TABLE_NAME, tableName, + MetricAttributeConstants.PARTITION_ID, String.valueOf(partitionId) + ); final String fullGaugeName = composeTableGaugeName(tableName, String.valueOf(partitionId), gauge); - setOrUpdateGauge(fullGaugeName, valueSupplier); + setOrUpdateGauge(fullGaugeName, gauge.getGaugeName(), attributes, valueSupplier::get); } /** @@ -577,12 +629,15 @@ public void setOrUpdatePartitionGauge(final String tableName, final int partitio * @param valueCallback The callback function used to retrieve the value of the gauge */ @Deprecated - public void addCallbackGaugeIfNeeded(final String metricName, final Callable valueCallback) { + public void addCallbackGaugeIfNeeded(final String metricName, + final String simplifiedMetricName, + final Map attributes, + final Callable valueCallback) { if (!_gaugeValues.containsKey(metricName)) { synchronized (_gaugeValues) { if (!_gaugeValues.containsKey(metricName)) { _gaugeValues.put(metricName, new AtomicLong(0L)); - addCallbackGauge(metricName, valueCallback); + addCallbackGauge(metricName, simplifiedMetricName, attributes, valueCallback); } } } @@ -599,11 +654,34 @@ public void addCallbackGaugeIfNeeded(final String metricName, final Callable valueCallback) { + addCallbackGauge(metricName, metricName, ImmutableMap.of(), valueCallback); + } + /** + * @deprecated please use setOrUpdateGauge(final String metricName, final Supplier valueSupplier) instead. + * + * Adds a new gauge whose values are retrieved from a callback function. + * Once added, the callback function cannot be updated. + * + * It's actually same as addCallbackGaugeIfNeeded(final String metricName, final Callable valueCallback) method + * + * @param metricName The name of the metric + * @param simplifiedMetricName The simplified name of the metric + * @param attributes Additional attributes to be added to the metric + * @param valueCallback The callback function used to retrieve the value of the gauge + */ @Deprecated - public void addCallbackGauge(final String metricName, final Callable valueCallback) { + public void addCallbackGauge(final String metricName, + String simplifiedMetricName, + Map attributes, + final Callable valueCallback) { + PinotMetricName pinotMetricName = PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName, + simplifiedMetricName, attributes); + PinotMetricUtils - .makeGauge(_metricsRegistry, PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName), - PinotMetricUtils.makePinotGauge(avoid -> { + .makeGauge(_metricsRegistry, pinotMetricName, + PinotMetricUtils.makePinotGauge(pinotMetricName, avoid -> { try { return valueCallback.call(); } catch (Exception e) { @@ -621,11 +699,12 @@ public void addCallbackGauge(final String metricName, final Callable value * @param tableName The table name * @param key The key associated with this gauge * @param gauge The gauge to use + * @param attributes Additional attributes to be added to the metric * @param value The value of the gauge */ - public void setOrUpdateTableGauge(final String tableName, final String key, final G gauge, final long value) { - String fullGaugeName = composeTableGaugeName(tableName, key, gauge); - setOrUpdateGauge(fullGaugeName, value); + public void setOrUpdateTableGauge(final String tableName, final String key, final G gauge, + final Map attributes, final long value) { + setOrUpdateTableGauge(tableName, key, gauge, attributes, () -> value); } /** @@ -635,12 +714,17 @@ public void setOrUpdateTableGauge(final String tableName, final String key, fina * @param tableName The table name * @param key The key associated with this gauge * @param gauge The gauge to use + * @param attributes Additional attributes to be added to the metric * @param valueSupplier The supplier function used to retrieve the value of the gauge */ public void setOrUpdateTableGauge(final String tableName, final String key, final G gauge, - final Supplier valueSupplier) { + Map attributes, final Supplier valueSupplier) { String fullGaugeName = composeTableGaugeName(tableName, key, gauge); - setOrUpdateGauge(fullGaugeName, valueSupplier); + + Map fullAttributes = new HashMap<>(attributes); + fullAttributes.put(MetricAttributeConstants.TABLE_NAME, tableName); + + setOrUpdateGauge(fullGaugeName, gauge.getGaugeName(), fullAttributes, valueSupplier::get); } /** @@ -653,7 +737,8 @@ public void setOrUpdateTableGauge(final String tableName, final String key, fina */ public void setOrUpdateTableGauge(final String tableName, final G gauge, final long value) { String fullGaugeName = composeTableGaugeName(tableName, gauge); - setOrUpdateGauge(fullGaugeName, value); + Map attributes = ImmutableMap.of(MetricAttributeConstants.TABLE_NAME, tableName); + setOrUpdateGauge(fullGaugeName, gauge.getGaugeName(), attributes, () -> value); } /** @@ -667,21 +752,8 @@ public void setOrUpdateTableGauge(final String tableName, final G gauge, final l public void setOrUpdateTableGauge(final String tableName, final G gauge, final Supplier valueSupplier) { String fullGaugeName = composeTableGaugeName(tableName, gauge); - setOrUpdateGauge(fullGaugeName, valueSupplier); - } - - /** - * Sets or updates a gauge to the given value. - * The value can be updated by calling this method again. - * - * @param metricName The name of the metric - * @param value The value of the gauge - */ - public void setOrUpdateGauge(final String metricName, long value) { - PinotGauge pinotGauge = PinotMetricUtils.makeGauge(_metricsRegistry, - PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName), - PinotMetricUtils.makePinotGauge(avoid -> value)); - pinotGauge.setValue(value); + setOrUpdateGauge(fullGaugeName, gauge.getGaugeName(), + ImmutableMap.of(MetricAttributeConstants.TABLE_NAME, tableName), valueSupplier::get); } /** @@ -691,39 +763,53 @@ public void setOrUpdateGauge(final String metricName, long value) { * @param metricName The name of the metric * @param valueSupplier The supplier function used to retrieve the value of the gauge */ - public void setOrUpdateGauge(final String metricName, final Supplier valueSupplier) { - PinotGauge pinotGauge = PinotMetricUtils.makeGauge(_metricsRegistry, - PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName), - PinotMetricUtils.makePinotGauge(avoid -> valueSupplier.get())); + public void setOrUpdateGlobalGauge(final String metricName, final Supplier valueSupplier) { + PinotMetricName pinotMetricName = PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName, + metricName, ImmutableMap.of()); + PinotGauge pinotGauge = PinotMetricUtils.makeGauge(_metricsRegistry, pinotMetricName, + PinotMetricUtils.makePinotGauge(pinotMetricName, avoid -> valueSupplier.get())); pinotGauge.setValueSupplier(valueSupplier); } /** - * Like {@link #setOrUpdateGauge(String, Supplier)} + * Like {@link #setOrUpdateGlobalGauge(String, Supplier)} */ - public void setOrUpdateGauge(final String metricName, final LongSupplier valueSupplier) { - PinotGauge pinotGauge = PinotMetricUtils.makeGauge(_metricsRegistry, - PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName), - PinotMetricUtils.makePinotGauge(avoid -> valueSupplier.getAsLong())); - pinotGauge.setValueSupplier((Supplier) () -> (Long) valueSupplier.getAsLong()); + public void setOrUpdateGlobalGauge(final String metricName, final LongSupplier valueSupplier) { + PinotMetricName pinotMetricName = PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName, + metricName, ImmutableMap.of()); + PinotGauge pinotGauge = PinotMetricUtils.makeGauge(_metricsRegistry, pinotMetricName, + PinotMetricUtils.makePinotGauge(pinotMetricName, avoid -> valueSupplier.getAsLong())); + pinotGauge.setValueSupplier(valueSupplier::getAsLong); + } + + public void setOrUpdateGauge(final String metricName, final String simplifiedMetricName, + Map attributes, final LongSupplier valueSupplier) { + PinotMetricName pinotMetricName = PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName, + simplifiedMetricName, new HashMap<>(attributes)); + + PinotGauge pinotGauge = PinotMetricUtils.makeGauge(_metricsRegistry, pinotMetricName, + PinotMetricUtils.makePinotGauge(pinotMetricName, avoid -> valueSupplier.getAsLong())); + + pinotGauge.setValueSupplier(valueSupplier::getAsLong); } /** - * Like {@link #setOrUpdateGauge(String, Supplier)} but using a global gauge + * Like {@link #setOrUpdateGlobalGauge(String, Supplier)} but providing a Gauge not String * @throws IllegalArgumentException if the gauge is not global */ public void setOrUpdateGlobalGauge(final G gauge, final Supplier valueSupplier) { Preconditions.checkArgument(gauge.isGlobal(), "Only global gauges should be sent to this method"); - setOrUpdateGauge(gauge.getGaugeName(), valueSupplier); + setOrUpdateGauge(gauge.getGaugeName(), gauge.getGaugeName(), ImmutableMap.of(), valueSupplier::get); } /** - * Like {@link #setOrUpdateGauge(String, LongSupplier)} but using a global gauge + * Like {@link #setOrUpdateGauge(String, String, Map, LongSupplier)} but providing a global gauge so no simplified + * metric name or attributes are needed. * @throws IllegalArgumentException if the gauge is not global */ public void setOrUpdateGlobalGauge(final G gauge, final LongSupplier valueSupplier) { Preconditions.checkArgument(gauge.isGlobal(), "Only global gauges should be sent to this method"); - setOrUpdateGauge(gauge.getGaugeName(), valueSupplier); + setOrUpdateGauge(gauge.getGaugeName(), gauge.getGaugeName(), ImmutableMap.of(), valueSupplier); } /** @@ -801,7 +887,8 @@ public void removeGauge(final String gaugeName) { public void removeTableMeter(final String tableName, final M meter) { PinotMetricUtils.removeMetric(_metricsRegistry, - PinotMetricUtils.makePinotMetricName(_clazz, getTableFullMeterName(tableName, meter))); + PinotMetricUtils.makePinotMetricName(_clazz, getTableFullMeterName(tableName, meter), + meter.getMeterName(), ImmutableMap.of())); } /** @@ -810,7 +897,8 @@ public void removeTableMeter(final String tableName, final M meter) { */ private void removeGaugeFromMetricRegistry(String metricName) { PinotMetricUtils - .removeMetric(_metricsRegistry, PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName)); + .removeMetric(_metricsRegistry, PinotMetricUtils.makePinotMetricName(_clazz, _metricPrefix + metricName, + metricName, ImmutableMap.of())); } protected abstract QP[] getQueryPhases(); diff --git a/pinot-common/src/main/java/org/apache/pinot/common/metrics/MetricAttributeConstants.java b/pinot-common/src/main/java/org/apache/pinot/common/metrics/MetricAttributeConstants.java new file mode 100644 index 000000000000..1d194d0109ec --- /dev/null +++ b/pinot-common/src/main/java/org/apache/pinot/common/metrics/MetricAttributeConstants.java @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.common.metrics; + +public class MetricAttributeConstants { + + private MetricAttributeConstants() { + } + + public static final String TABLE_NAME = "TableName"; + public static final String TASK_TYPE = "TaskType"; + public static final String TASK_NAME = "TaskName"; + public static final String MERGE_LEVEL = "MergeLevel"; + public static final String SEGMENT_NAME = "SegmentName"; + public static final String RESOURCE_NAME = "ResourceName"; + public static final String TOPIC_NAME = "TopicName"; + public static final String PARTITION_ID = "PartitionId"; + public static final String COLUMN_NAME = "ColumnName"; + public static final String POOL_TAG = "PoolTag"; + public static final String POOL_ID = "PoolId"; + public static final String PINOT_METRIC_NAME = "PinotMetricName"; + public static final String WORKLOAD_NAME = "WorkloadName"; +} diff --git a/pinot-common/src/main/java/org/apache/pinot/common/metrics/ValidationMetrics.java b/pinot-common/src/main/java/org/apache/pinot/common/metrics/ValidationMetrics.java index 055d50ea68d6..3d68d9d7296c 100644 --- a/pinot-common/src/main/java/org/apache/pinot/common/metrics/ValidationMetrics.java +++ b/pinot-common/src/main/java/org/apache/pinot/common/metrics/ValidationMetrics.java @@ -19,11 +19,13 @@ package org.apache.pinot.common.metrics; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; import org.apache.pinot.spi.metrics.PinotGauge; import org.apache.pinot.spi.metrics.PinotMetricName; import org.apache.pinot.spi.metrics.PinotMetricUtils; @@ -57,7 +59,9 @@ public Long value() { @Override public Object getGauge() { - return PinotMetricUtils.makePinotGauge(avoid -> value()).getGauge(); + return PinotMetricUtils.makePinotGauge( + new PinotMetricsFactory.SimpleMetricName(_key), + avoid -> value()).getGauge(); } @Override @@ -92,10 +96,11 @@ public Double value() { public Object getMetric() { return getGauge(); } - @Override public Object getGauge() { - return PinotMetricUtils.makePinotGauge(avoid -> value()).getGauge(); + return PinotMetricUtils.makePinotGauge( + new PinotMetricsFactory.SimpleMetricName(_key), + avoid -> value()).getGauge(); } } @@ -245,7 +250,7 @@ public static String makeGaugeName(final String resource, final String gaugeName } private PinotMetricName makeMetricName(final String gaugeName) { - return PinotMetricUtils.makePinotMetricName(ValidationMetrics.class, gaugeName); + return PinotMetricUtils.makePinotMetricName(ValidationMetrics.class, gaugeName, gaugeName, ImmutableMap.of()); } private void makeGauge(final String resource, final ValidationMetricName validationMetricName, diff --git a/pinot-common/src/main/java/org/apache/pinot/common/utils/helix/IdealStateGroupCommit.java b/pinot-common/src/main/java/org/apache/pinot/common/utils/helix/IdealStateGroupCommit.java index 0b55fd9fafba..1f30efb9d181 100644 --- a/pinot-common/src/main/java/org/apache/pinot/common/utils/helix/IdealStateGroupCommit.java +++ b/pinot-common/src/main/java/org/apache/pinot/common/utils/helix/IdealStateGroupCommit.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.common.utils.helix; +import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; @@ -37,6 +38,7 @@ import org.apache.pinot.common.metrics.ControllerMeter; import org.apache.pinot.common.metrics.ControllerMetrics; import org.apache.pinot.common.metrics.ControllerTimer; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.spi.utils.retry.RetryPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -300,15 +302,20 @@ private boolean shouldCompress(IdealState is) { } }); if (controllerMetrics != null) { - controllerMetrics.addMeteredValue(resourceName, ControllerMeter.IDEAL_STATE_UPDATE_RETRY, retries); + Map attributes = ImmutableMap.of(MetricAttributeConstants.RESOURCE_NAME, resourceName); + controllerMetrics.addMeteredValue(resourceName, ControllerMeter.IDEAL_STATE_UPDATE_RETRY, retries, attributes); controllerMetrics.addTimedValue(resourceName, ControllerTimer.IDEAL_STATE_UPDATE_TIME_MS, - System.currentTimeMillis() - startTimeMs, TimeUnit.MILLISECONDS); - controllerMetrics.addMeteredValue(resourceName, ControllerMeter.IDEAL_STATE_UPDATE_SUCCESS, 1L); + System.currentTimeMillis() - startTimeMs, TimeUnit.MILLISECONDS, attributes); + controllerMetrics.addMeteredValue(resourceName, ControllerMeter.IDEAL_STATE_UPDATE_SUCCESS, + 1L, attributes); } return idealStateWrapper._idealState; } catch (Throwable e) { if (controllerMetrics != null) { - controllerMetrics.addMeteredValue(resourceName, ControllerMeter.IDEAL_STATE_UPDATE_FAILURE, 1L); + controllerMetrics.addMeteredValue( + resourceName, ControllerMeter.IDEAL_STATE_UPDATE_FAILURE, 1L, + ImmutableMap.of(MetricAttributeConstants.RESOURCE_NAME, resourceName) + ); } throw new RuntimeException("Caught exception while updating ideal state for resource: " + resourceName, e); } diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/AbstractMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/AbstractMetricsTest.java index c6bd8d8c30b6..cee3dd663bd1 100644 --- a/pinot-common/src/test/java/org/apache/pinot/common/metrics/AbstractMetricsTest.java +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/AbstractMetricsTest.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.common.metrics; +import com.google.common.collect.ImmutableMap; import com.yammer.metrics.core.MetricName; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -49,11 +50,11 @@ public void testAddOrUpdateGauge() { ControllerMetrics controllerMetrics = new ControllerMetrics(new YammerMetricsRegistry()); String metricName = "test"; // add gauge - controllerMetrics.setOrUpdateGauge(metricName, () -> 1L); + controllerMetrics.setOrUpdateGlobalGauge(metricName, () -> 1L); Assert.assertEquals(MetricValueUtils.getGaugeValue(controllerMetrics, metricName), 1); // update gauge - controllerMetrics.setOrUpdateGauge(metricName, () -> 2L); + controllerMetrics.setOrUpdateGlobalGauge(metricName, () -> 2L); Assert.assertEquals(MetricValueUtils.getGaugeValue(controllerMetrics, metricName), 2); // remove gauge @@ -72,7 +73,7 @@ public void testMultipleUpdatesToGauge() throws InterruptedException { // update and remove gauge simultaneously IntStream.range(0, 1000).forEach(i -> { - controllerMetrics.setOrUpdateGauge(metricName, () -> (long) i); + controllerMetrics.setOrUpdateGlobalGauge(metricName, () -> (long) i); }); // Verify final value @@ -107,8 +108,8 @@ public void testMultipleGauges() { String metricName2 = "testMultiple2"; // Add multiple gauges - controllerMetrics.setOrUpdateGauge(metricName1, () -> 1L); - controllerMetrics.setOrUpdateGauge(metricName2, () -> 2L); + controllerMetrics.setOrUpdateGlobalGauge(metricName1, () -> 1L); + controllerMetrics.setOrUpdateGlobalGauge(metricName2, () -> 2L); // Verify values Assert.assertEquals(MetricValueUtils.getGaugeValue(controllerMetrics, metricName1), 1); @@ -183,7 +184,7 @@ public void testTimerMetrics() { testMetrics.addTimedTableValue(tableName, timer, 6, TimeUnit.SECONDS); final MetricName t1Metric = inspector.lastMetric(); Assert.assertEquals(inspector.getTimer(t1Metric).sum(), 6000); - testMetrics.addTimedTableValue(tableName, keyName, timer, 500, TimeUnit.MILLISECONDS); + testMetrics.addTimedTableTaskValue(tableName, keyName, timer, 500, TimeUnit.MILLISECONDS); final MetricName t2Metric = inspector.lastMetric(); Assert.assertEquals(inspector.getTimer(t2Metric).sum(), 500); @@ -191,7 +192,7 @@ public void testTimerMetrics() { testMetrics.addTimedValue(timer, 40, TimeUnit.MILLISECONDS); final MetricName t3Metric = inspector.lastMetric(); Assert.assertEquals(inspector.getTimer(t3Metric).sum(), 40); - testMetrics.addTimedValue(keyName, timer, 3, TimeUnit.MILLISECONDS); + testMetrics.addTimedValue(keyName, timer, 3, TimeUnit.MILLISECONDS, ImmutableMap.of()); final MetricName t4Metric = inspector.lastMetric(); Assert.assertEquals(inspector.getTimer(t4Metric).sum(), 3); @@ -239,13 +240,13 @@ public void testMeteredMetrics() { expectMeteredCount.accept(9); // Test meter with key APIs - testMetrics.addMeteredValue(keyName, meter, 9); + testMetrics.addMeteredValue(keyName, meter, 9, ImmutableMap.of()); expectNewMetric.run(); expectMeteredCount.accept(9); - PinotMeter reusedMeter = testMetrics.addMeteredValue(keyName, meter2, 13, null); + PinotMeter reusedMeter = testMetrics.addMeteredValue(keyName, meter2, 13, null, ImmutableMap.of()); expectNewMetric.run(); expectMeteredCount.accept(13); - testMetrics.addMeteredValue(keyName, meter2, 6, reusedMeter); + testMetrics.addMeteredValue(keyName, meter2, 6, reusedMeter, ImmutableMap.of()); expectMeteredCount.accept(19); // Test table-level meter APIs @@ -256,13 +257,13 @@ public void testMeteredMetrics() { expectMeteredCount.accept(18); // Test table-level meter with additional key APIs - testMetrics.addMeteredTableValue(tableName, keyName, meter, 21); + testMetrics.addMeteredTableValue(tableName, keyName, meter, 21, ImmutableMap.of()); expectNewMetric.run(); expectMeteredCount.accept(21); - reusedMeter = testMetrics.addMeteredTableValue(tableName, keyName, meter2, 23, null); + reusedMeter = testMetrics.addMeteredTableValue(tableName, keyName, meter2, 23, null, ImmutableMap.of()); expectNewMetric.run(); expectMeteredCount.accept(23); - testMetrics.addMeteredTableValue(tableName, keyName, meter2, 5, reusedMeter); + testMetrics.addMeteredTableValue(tableName, keyName, meter2, 5, reusedMeter, ImmutableMap.of()); expectMeteredCount.accept(28); // Test removal APIs @@ -349,7 +350,8 @@ public void testSetValueOfGaugeAsyncAddRemove() throws ExecutionException, Inter ControllerMetrics controllerMetrics = buildTestMetrics(); testAsyncAddRemove( - () -> controllerMetrics.setValueOfGauge(1L, ControllerGauge.VERSION.getGaugeName()), + () -> controllerMetrics.setValueOfGauge(1L, ControllerGauge.VERSION.getGaugeName(), + ControllerGauge.VERSION.getGaugeName(), ImmutableMap.of()), () -> controllerMetrics.removeGauge(ControllerGauge.VERSION.getGaugeName()) ); @@ -392,7 +394,7 @@ public void testSetOrUpdateGlobalGauges() { controllerMetrics.setOrUpdateGlobalGauge(ControllerGauge.VERSION, (Supplier) () -> 2L); Assert.assertEquals(MetricValueUtils.getGaugeValue(controllerMetrics, ControllerGauge.VERSION.getGaugeName()), 2); - controllerMetrics.setValueOfGlobalGauge(ControllerGauge.OFFLINE_TABLE_COUNT, "suffix", 3L); + controllerMetrics.setValueOfGlobalGauge(ControllerGauge.OFFLINE_TABLE_COUNT, "suffix", 3L, ImmutableMap.of()); Assert.assertEquals(MetricValueUtils.getGaugeValue(controllerMetrics, ControllerGauge.OFFLINE_TABLE_COUNT.getGaugeName() + ".suffix"), 3); @@ -413,11 +415,11 @@ public void testSetOrUpdateTableGauges() { String table = "test_table"; String key = "key"; - controllerMetrics.setOrUpdateTableGauge(table, key, ControllerGauge.VERSION, () -> 1L); + controllerMetrics.setOrUpdateTableGauge(table, key, ControllerGauge.VERSION, ImmutableMap.of(), () -> 1L); Assert.assertEquals(MetricValueUtils.getGaugeValue(controllerMetrics, ControllerGauge.VERSION.getGaugeName() + "." + table + "." + key), 1); - controllerMetrics.setOrUpdateTableGauge(table, key, ControllerGauge.VERSION, 2L); + controllerMetrics.setOrUpdateTableGauge(table, key, ControllerGauge.VERSION, ImmutableMap.of(), 2L); Assert.assertEquals(MetricValueUtils.getGaugeValue(controllerMetrics, ControllerGauge.VERSION.getGaugeName() + "." + table + "." + key), 2); diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/PinotMetricUtilsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/PinotMetricUtilsTest.java index b8429e88acbf..0f0eaaab9b3c 100644 --- a/pinot-common/src/test/java/org/apache/pinot/common/metrics/PinotMetricUtilsTest.java +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/PinotMetricUtilsTest.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.common.metrics; +import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -77,7 +78,8 @@ public void testPinotMetricsRegistration() { PinotConfiguration configuration = new PinotConfiguration(properties); PinotMetricUtils.init(configuration.subset("pinot.broker.metrics")); PinotMetricsRegistry registry = PinotMetricUtils.getPinotMetricsRegistry(); - PinotMetricUtils.makePinotTimer(registry, PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, "dummy"), + PinotMetricUtils.makePinotTimer(registry, PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, + "dummy", "dummy", ImmutableMap.of()), TimeUnit.MILLISECONDS, TimeUnit.MILLISECONDS); // Check that the two listeners fired @@ -89,7 +91,8 @@ public void testPinotMetricsRegistration() { public void testMetricValue() { PinotMetricsRegistry registry = PinotMetricUtils.getPinotMetricsRegistry(); PinotMeter pinotMeter = PinotMetricUtils - .makePinotMeter(registry, PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, "testMeter"), + .makePinotMeter(registry, PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, + "testMeter", "testMeter", ImmutableMap.of()), "dummyEventType", TimeUnit.MILLISECONDS); pinotMeter.mark(); Assert.assertEquals(pinotMeter.count(), 1L); @@ -101,9 +104,11 @@ public void testMetricValue() { @Test public void testPinotMetricName() { PinotMetricName testMetricName1 = - PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, "testMetricName"); + PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, "testMetricName", + "testMetricName", ImmutableMap.of()); PinotMetricName testMetricName2 = - PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, "testMetricName"); + PinotMetricUtils.makePinotMetricName(PinotMetricUtilsTest.class, "testMetricName", + "testMetricName", ImmutableMap.of()); Assert.assertNotNull(testMetricName1); Assert.assertNotNull(testMetricName2); Assert.assertEquals(testMetricName1, testMetricName2); diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ControllerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ControllerPrometheusMetricsTest.java index 45583184d413..7a01ee74ac74 100644 --- a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ControllerPrometheusMetricsTest.java +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ControllerPrometheusMetricsTest.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.common.metrics.prometheus; +import com.google.common.collect.ImmutableMap; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; @@ -142,10 +143,12 @@ public void meterTest(ControllerMeter meter) { @Test(dataProvider = "controllerGauges") public void gaugeTest(ControllerGauge controllerGauge) { if (controllerGauge.isGlobal()) { - _controllerMetrics.setValueOfGlobalGauge(controllerGauge, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, 1L); + _controllerMetrics.setValueOfGlobalGauge(controllerGauge, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, 1L, + ImmutableMap.of()); //some global gauges also accept the taskType (which should not be). todo: this should be fixed if (GLOBAL_GAUGES_ACCEPTING_TASKTYPE.contains(controllerGauge)) { - _controllerMetrics.setValueOfGlobalGauge(controllerGauge, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, 1L); + _controllerMetrics.setValueOfGlobalGauge(controllerGauge, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, 1L, + ImmutableMap.of()); String strippedMetricName = getStrippedMetricName(controllerGauge); assertGaugeExportedCorrectly(strippedMetricName, List.of(LABEL_KEY_TASK_TYPE, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC), EXPORTED_METRIC_PREFIX); @@ -162,7 +165,7 @@ public void gaugeTest(ControllerGauge controllerGauge) { EXPORTED_METRIC_PREFIX); } else if (GAUGES_ACCEPTING_TASKTYPE.contains(controllerGauge)) { _controllerMetrics.setOrUpdateTableGauge(TABLE_NAME_WITH_TYPE, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, - controllerGauge, () -> 50L); + controllerGauge, ImmutableMap.of(), () -> 50L); assertGaugeExportedCorrectly(controllerGauge.getGaugeName(), ExportedLabels.TABLENAME_TABLETYPE_CONTROLLER_TASKTYPE, EXPORTED_METRIC_PREFIX); } else if (GAUGES_ACCEPTING_RAW_TABLENAME.contains(controllerGauge)) { diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/MinionPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/MinionPrometheusMetricsTest.java index 1dd982d6273f..21d53d2fb254 100644 --- a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/MinionPrometheusMetricsTest.java +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/MinionPrometheusMetricsTest.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.common.metrics.prometheus; +import com.google.common.collect.ImmutableMap; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.pinot.common.metrics.MinionGauge; @@ -47,10 +48,11 @@ public void timerTest(MinionTimer timer) { _minionMetrics.addTimedValue(timer, 30L, TimeUnit.MILLISECONDS); assertTimerExportedCorrectly(timer.getTimerName(), EXPORTED_METRIC_PREFIX); } else { - _minionMetrics.addTimedValue(ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, timer, 30L, TimeUnit.MILLISECONDS); + _minionMetrics.addTimedValue(ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, timer, 30L, + TimeUnit.MILLISECONDS, ImmutableMap.of()); assertTimerExportedCorrectly(timer.getTimerName(), List.of(ExportedLabelKeys.ID, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT), EXPORTED_METRIC_PREFIX); - _minionMetrics.addTimedTableValue(TABLE_NAME_WITH_TYPE, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, timer, + _minionMetrics.addTimedTableTaskValue(TABLE_NAME_WITH_TYPE, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, timer, 30L, TimeUnit.MILLISECONDS); assertTimerExportedCorrectly(timer.getTimerName(), ExportedLabels.TABLENAME_TABLETYPE_MINION_TASKTYPE, EXPORTED_METRIC_PREFIX); @@ -77,7 +79,7 @@ private void validateMetersWithLabels(MinionMeter meter) { assertMeterExportedCorrectly(meter.getMeterName(), List.of(ExportedLabelKeys.ID, ExportedLabelValues.TABLENAME), EXPORTED_METRIC_PREFIX); - _minionMetrics.addMeteredValue(ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, meter, 1L); + _minionMetrics.addMeteredValue(ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, meter, 1L, ImmutableMap.of()); assertMeterExportedCorrectly(meter.getMeterName(), List.of(ExportedLabelKeys.ID, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT), EXPORTED_METRIC_PREFIX); } else if (meter == MinionMeter.SEGMENT_UPLOAD_FAIL_COUNT || meter == MinionMeter.SEGMENT_DOWNLOAD_FAIL_COUNT) { @@ -88,7 +90,7 @@ private void validateMetersWithLabels(MinionMeter meter) { //all remaining meters are also being used as global meters, check their usage _minionMetrics.addMeteredGlobalValue(meter, 1L); _minionMetrics.addMeteredTableValue(TABLE_NAME_WITH_TYPE, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, meter, - 1L); + 1L, ImmutableMap.of()); assertMeterExportedCorrectly(meter.getMeterName(), EXPORTED_METRIC_PREFIX); assertMeterExportedCorrectly(meter.getMeterName(), ExportedLabels.TABLENAME_TABLETYPE_MINION_TASKTYPE, EXPORTED_METRIC_PREFIX); diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/PinotPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/PinotPrometheusMetricsTest.java index 5173bc00df77..1ee1f83cb9c8 100644 --- a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/PinotPrometheusMetricsTest.java +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/PinotPrometheusMetricsTest.java @@ -97,7 +97,7 @@ public void setupTest() { _pinotMetricsFactory.getClass().getCanonicalName()); PinotMetricUtils.init(pinotConfiguration); - _pinotMetricsFactory.makePinotJmxReporter(_pinotMetricsFactory.getPinotMetricsRegistry()).start(); + _pinotMetricsFactory.makePinotMetricReporter(_pinotMetricsFactory.getPinotMetricsRegistry()).start(); _httpClient = new HttpClient(); _httpServer = startExporter(); } diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/BaseControllerStarter.java b/pinot-controller/src/main/java/org/apache/pinot/controller/BaseControllerStarter.java index d3bc2b78fe99..bdfef711a664 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/BaseControllerStarter.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/BaseControllerStarter.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.primitives.Longs; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.File; @@ -767,7 +768,8 @@ private void initControllerMetrics() { _metricsRegistry = PinotMetricUtils.getPinotMetricsRegistry(_config.subset(METRICS_REGISTRY_NAME)); _controllerMetrics = new ControllerMetrics(_config.getMetricsPrefix(), _metricsRegistry); _controllerMetrics.initializeGlobalMeters(); - _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, 1); + _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, + 1, ImmutableMap.of()); // log zookeeper's JUTE_MAX_BUFFER value, default is 0xfffff bytes (just under 1MB) _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.ZK_JUTE_MAX_BUFFER, Integer.getInteger(ZkSystemPropertyKeys.JUTE_MAXBUFFER, 0xfffff)); diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java b/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java index 74abcbf813d9..7e4146458f5a 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java @@ -161,7 +161,7 @@ private void registerHttpThreadUtilizationGauge(ControllerMetrics metrics) { mc.addProbes(probe); } - metrics.setOrUpdateGauge(ControllerGauge.HTTP_THREAD_UTILIZATION.getGaugeName(), () -> { + metrics.setOrUpdateGlobalGauge(ControllerGauge.HTTP_THREAD_UTILIZATION, () -> { int max = poolCfg.getMaxPoolSize(); if (max <= 0) { return 0L; diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/SegmentStatusChecker.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/SegmentStatusChecker.java index 9a09761b8153..b614c845f7b8 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/SegmentStatusChecker.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/SegmentStatusChecker.java @@ -142,11 +142,11 @@ protected void postprocess(Context context) { context._tierBackendTableCountMap.forEach((tier, count) -> { String gaugeName = _controllerMetrics.composePluginGaugeName(tier, ControllerGauge.TIER_BACKEND_TABLE_COUNT); _tierBackendGauges.add(gaugeName); - _controllerMetrics.setOrUpdateGauge(gaugeName, count); + _controllerMetrics.setOrUpdateGlobalGauge(gaugeName, () -> count); }); // metric for total number of tables having tier backend configured - _controllerMetrics.setOrUpdateGauge(ControllerGauge.TIER_BACKEND_TABLE_COUNT.getGaugeName(), - context._tierBackendConfiguredTableCount); + _controllerMetrics.setOrUpdateGlobalGauge(ControllerGauge.TIER_BACKEND_TABLE_COUNT.getGaugeName(), + () -> context._tierBackendConfiguredTableCount); //emit a 0 for tables that are not paused/disabled. This makes alert expressions simpler as we don't have to deal // with missing metrics diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/PinotTaskManager.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/PinotTaskManager.java index 06e1692f592f..659792a69e56 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/PinotTaskManager.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/PinotTaskManager.java @@ -19,6 +19,7 @@ package org.apache.pinot.controller.helix.core.minion; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.io.PrintWriter; import java.io.StringWriter; @@ -46,6 +47,7 @@ import org.apache.pinot.common.metrics.ControllerGauge; import org.apache.pinot.common.metrics.ControllerMeter; import org.apache.pinot.common.metrics.ControllerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.minion.TaskGeneratorMostRecentRunInfo; import org.apache.pinot.common.minion.TaskManagerStatusCache; import org.apache.pinot.controller.ControllerConf; @@ -795,9 +797,12 @@ protected TaskSchedulingInfo scheduleTask(PinotTaskGenerator taskGenerator, List // TODO: find a better way to report task generation information _controllerMetrics.setOrUpdateTableGauge(tableName, taskType, ControllerGauge.TIME_MS_SINCE_LAST_SUCCESSFUL_MINION_TASK_GENERATION, + ImmutableMap.of(MetricAttributeConstants.TASK_TYPE, taskType), () -> System.currentTimeMillis() - successRunTimestamp); _controllerMetrics.setOrUpdateTableGauge(tableName, taskType, - ControllerGauge.LAST_MINION_TASK_GENERATION_ENCOUNTERS_ERROR, 0L); + ControllerGauge.LAST_MINION_TASK_GENERATION_ENCOUNTERS_ERROR, + ImmutableMap.of(MetricAttributeConstants.TASK_TYPE, taskType), + 0L); } catch (Exception e) { StringWriter errors = new StringWriter(); try (PrintWriter pw = new PrintWriter(errors)) { @@ -812,7 +817,9 @@ protected TaskSchedulingInfo scheduleTask(PinotTaskGenerator taskGenerator, List // before the first task schedule, the follow gauge metric will be empty // TODO: find a better way to report task generation information _controllerMetrics.setOrUpdateTableGauge(tableName, taskType, - ControllerGauge.LAST_MINION_TASK_GENERATION_ENCOUNTERS_ERROR, 1L); + ControllerGauge.LAST_MINION_TASK_GENERATION_ENCOUNTERS_ERROR, + ImmutableMap.of(MetricAttributeConstants.TASK_TYPE, taskType), + 1L); LOGGER.error("Failed to generate tasks for task type {} for table {}", taskType, tableName, e); } } diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/TaskMetricsEmitter.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/TaskMetricsEmitter.java index 9eb69210539a..acb8f89acddd 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/TaskMetricsEmitter.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/minion/TaskMetricsEmitter.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.controller.helix.core.minion; +import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -26,6 +27,7 @@ import java.util.Set; import org.apache.pinot.common.metrics.ControllerGauge; import org.apache.pinot.common.metrics.ControllerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.controller.ControllerConf; import org.apache.pinot.controller.LeadControllerManager; import org.apache.pinot.controller.helix.core.PinotHelixResourceManager; @@ -80,6 +82,7 @@ protected final void runTask(Properties periodicTaskProperties) { taskTypeLastUpdateTime.forEach((taskType, lastUpdateTimeMs) -> _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, ControllerGauge.TIME_MS_SINCE_LAST_MINION_TASK_METADATA_UPDATE, + ImmutableMap.of(MetricAttributeConstants.TASK_TYPE, taskType), () -> System.currentTimeMillis() - lastUpdateTimeMs))); // The call to get task types can take time if there are a lot of tasks. @@ -106,52 +109,56 @@ protected final void runTask(Properties periodicTaskProperties) { }); } // Emit metrics for taskType. + Map attributes = ImmutableMap.of(MetricAttributeConstants.TASK_TYPE, taskType); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_TASKS_IN_PROGRESS, taskType, - numRunningTasks); + numRunningTasks, attributes); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_SUBTASKS_RUNNING, taskType, - taskTypeAccumulatedCount.getRunning()); + taskTypeAccumulatedCount.getRunning(), attributes); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_SUBTASKS_WAITING, taskType, - taskTypeAccumulatedCount.getWaiting()); + taskTypeAccumulatedCount.getWaiting(), attributes); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_SUBTASKS_ERROR, taskType, - taskTypeAccumulatedCount.getError()); + taskTypeAccumulatedCount.getError(), attributes); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_SUBTASKS_UNKNOWN, taskType, - taskTypeAccumulatedCount.getUnknown()); + taskTypeAccumulatedCount.getUnknown(), attributes); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_SUBTASKS_DROPPED, taskType, - taskTypeAccumulatedCount.getDropped()); + taskTypeAccumulatedCount.getDropped(), attributes); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_SUBTASKS_TIMED_OUT, taskType, - taskTypeAccumulatedCount.getTimedOut()); + taskTypeAccumulatedCount.getTimedOut(), attributes); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.NUM_MINION_SUBTASKS_ABORTED, taskType, - taskTypeAccumulatedCount.getAborted()); + taskTypeAccumulatedCount.getAborted(), attributes); int total = taskTypeAccumulatedCount.getTotal(); int percent = total != 0 ? (taskTypeAccumulatedCount.getWaiting() + taskTypeAccumulatedCount.getRunning()) * 100 / total : 0; - _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.PERCENT_MINION_SUBTASKS_IN_QUEUE, taskType, percent); + _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.PERCENT_MINION_SUBTASKS_IN_QUEUE, + taskType, percent, attributes); percent = total != 0 ? taskTypeAccumulatedCount.getError() * 100 / total : 0; - _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.PERCENT_MINION_SUBTASKS_IN_ERROR, taskType, percent); + _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.PERCENT_MINION_SUBTASKS_IN_ERROR, + taskType, percent, attributes); // Emit metrics for table taskType tableAccumulatedCount.forEach((tableNameWithType, taskCount) -> { _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.NUM_MINION_SUBTASKS_RUNNING, () -> (long) taskCount.getRunning()); + ControllerGauge.NUM_MINION_SUBTASKS_RUNNING, attributes, + () -> (long) taskCount.getRunning()); _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.NUM_MINION_SUBTASKS_WAITING, taskCount.getWaiting()); + ControllerGauge.NUM_MINION_SUBTASKS_WAITING, attributes, taskCount.getWaiting()); _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.NUM_MINION_SUBTASKS_ERROR, taskCount.getError()); + ControllerGauge.NUM_MINION_SUBTASKS_ERROR, attributes, taskCount.getError()); _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.NUM_MINION_SUBTASKS_UNKNOWN, taskCount.getUnknown()); + ControllerGauge.NUM_MINION_SUBTASKS_UNKNOWN, attributes, taskCount.getUnknown()); _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.NUM_MINION_SUBTASKS_DROPPED, taskCount.getDropped()); + ControllerGauge.NUM_MINION_SUBTASKS_DROPPED, attributes, taskCount.getDropped()); _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.NUM_MINION_SUBTASKS_TIMED_OUT, taskCount.getTimedOut()); + ControllerGauge.NUM_MINION_SUBTASKS_TIMED_OUT, attributes, taskCount.getTimedOut()); _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.NUM_MINION_SUBTASKS_ABORTED, taskCount.getAborted()); + ControllerGauge.NUM_MINION_SUBTASKS_ABORTED, attributes, taskCount.getAborted()); int tableTotal = taskCount.getTotal(); int tablePercent = tableTotal != 0 ? (taskCount.getWaiting() + taskCount.getRunning()) * 100 / tableTotal : 0; _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.PERCENT_MINION_SUBTASKS_IN_QUEUE, tablePercent); + ControllerGauge.PERCENT_MINION_SUBTASKS_IN_QUEUE, attributes, tablePercent); tablePercent = tableTotal != 0 ? taskCount.getError() * 100 / tableTotal : 0; _controllerMetrics.setOrUpdateTableGauge(tableNameWithType, taskType, - ControllerGauge.PERCENT_MINION_SUBTASKS_IN_ERROR, tablePercent); + ControllerGauge.PERCENT_MINION_SUBTASKS_IN_ERROR, attributes, tablePercent); }); if (_preReportedTables.containsKey(taskType)) { diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTask.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTask.java index d3584df0682e..3a862c24df22 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTask.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTask.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.controller.helix.core.periodictask; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.HashSet; @@ -29,6 +30,7 @@ import org.apache.pinot.common.metrics.ControllerGauge; import org.apache.pinot.common.metrics.ControllerMeter; import org.apache.pinot.common.metrics.ControllerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.controller.LeadControllerManager; import org.apache.pinot.controller.helix.core.PinotHelixResourceManager; import org.apache.pinot.core.periodictask.BasePeriodicTask; @@ -123,7 +125,9 @@ protected void processTables(List tableNamesWithType, Properties periodi } postprocess(context); _controllerMetrics - .setValueOfGlobalGauge(ControllerGauge.PERIODIC_TASK_NUM_TABLES_PROCESSED, _taskName, numTablesProcessed); + .setValueOfGlobalGauge(ControllerGauge.PERIODIC_TASK_NUM_TABLES_PROCESSED, _taskName, numTablesProcessed, + ImmutableMap.of(MetricAttributeConstants.TASK_NAME, _taskName) + ); LOGGER.info("Finish processing {}/{} tables in task: {}", numTablesProcessed, numTables, _taskName); } diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/PinotLLCRealtimeSegmentManager.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/PinotLLCRealtimeSegmentManager.java index 034f88b761a3..247f0732b27e 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/PinotLLCRealtimeSegmentManager.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/PinotLLCRealtimeSegmentManager.java @@ -1965,8 +1965,8 @@ public void uploadToDeepStoreIfMissing(TableConfig tableConfig, List queueSize); continue; } @@ -2009,8 +2009,8 @@ public void uploadToDeepStoreIfMissing(TableConfig tableConfig, List queueSize); } }; diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/segment/SegmentSizeBasedFlushThresholdUpdater.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/segment/SegmentSizeBasedFlushThresholdUpdater.java index 4f76a3382f3e..a395f1dfa297 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/segment/SegmentSizeBasedFlushThresholdUpdater.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/realtime/segment/SegmentSizeBasedFlushThresholdUpdater.java @@ -18,9 +18,11 @@ */ package org.apache.pinot.controller.helix.core.realtime.segment; +import com.google.common.collect.ImmutableMap; import org.apache.pinot.common.metadata.segment.SegmentZKMetadata; import org.apache.pinot.common.metrics.ControllerGauge; import org.apache.pinot.common.metrics.ControllerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.spi.stream.StreamConfig; @@ -51,7 +53,9 @@ public void onSegmentCommit(StreamConfig streamConfig, CommittingSegmentDescript long segmentSize = committingSegmentDescriptor.getSegmentSizeBytes(); _controllerMetrics.setOrUpdateTableGauge(_realtimeTableName, ControllerGauge.COMMITTING_SEGMENT_SIZE, segmentSize); _controllerMetrics.setOrUpdateTableGauge(_realtimeTableName, _topicName, - ControllerGauge.COMMITTING_SEGMENT_SIZE_WITH_TOPIC, segmentSize); + ControllerGauge.COMMITTING_SEGMENT_SIZE_WITH_TOPIC, + ImmutableMap.of(MetricAttributeConstants.TOPIC_NAME, _topicName), + segmentSize); _flushThresholdComputer.onSegmentCommit(committingSegmentDescriptor, committingSegmentZKMetadata); } @@ -64,6 +68,8 @@ public void updateFlushThreshold(StreamConfig streamConfig, SegmentZKMetadata ne _controllerMetrics.setOrUpdateTableGauge(_realtimeTableName, ControllerGauge.NUM_ROWS_THRESHOLD, threshold); _controllerMetrics.setOrUpdateTableGauge(_realtimeTableName, _topicName, - ControllerGauge.NUM_ROWS_THRESHOLD_WITH_TOPIC, threshold); + ControllerGauge.NUM_ROWS_THRESHOLD_WITH_TOPIC, + ImmutableMap.of(MetricAttributeConstants.TOPIC_NAME, _topicName), + threshold); } } diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/rebalance/RebalanceChecker.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/rebalance/RebalanceChecker.java index 5c73cce4c5b9..ada37c5d38ca 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/rebalance/RebalanceChecker.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/rebalance/RebalanceChecker.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -34,6 +35,7 @@ import org.apache.pinot.common.metrics.ControllerGauge; import org.apache.pinot.common.metrics.ControllerMeter; import org.apache.pinot.common.metrics.ControllerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.controller.ControllerConf; import org.apache.pinot.controller.LeadControllerManager; import org.apache.pinot.controller.helix.core.PinotHelixResourceManager; @@ -71,7 +73,7 @@ protected void processTables(List tableNamesWithType, Properties periodi LOGGER.info("Processing {} tables in task: {}", numTables, _taskName); int numTablesProcessed = retryRebalanceTables(new HashSet<>(tableNamesWithType)); _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.PERIODIC_TASK_NUM_TABLES_PROCESSED, _taskName, - numTablesProcessed); + numTablesProcessed, ImmutableMap.of(MetricAttributeConstants.TASK_NAME, _taskName)); LOGGER.info("Finish processing {}/{} tables in task: {}", numTablesProcessed, numTables, _taskName); } diff --git a/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTaskTest.java b/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTaskTest.java index f4e0eb46b14f..6d4e27bedeed 100644 --- a/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTaskTest.java +++ b/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/periodictask/ControllerPeriodicTaskTest.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.controller.helix.core.periodictask; +import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -96,7 +97,8 @@ private void resetState() { _stopTaskCalled.set(false); _processTablesCalled.set(false); _tablesProcessed.set(0); - _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.PERIODIC_TASK_NUM_TABLES_PROCESSED, TASK_NAME, 0); + _controllerMetrics.setValueOfGlobalGauge(ControllerGauge.PERIODIC_TASK_NUM_TABLES_PROCESSED, TASK_NAME, 0, + ImmutableMap.of()); } @Test diff --git a/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/realtime/segment/FlushThresholdUpdaterTest.java b/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/realtime/segment/FlushThresholdUpdaterTest.java index 7a9f01b26719..9f567d8195a4 100644 --- a/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/realtime/segment/FlushThresholdUpdaterTest.java +++ b/pinot-controller/src/test/java/org/apache/pinot/controller/helix/core/realtime/segment/FlushThresholdUpdaterTest.java @@ -111,6 +111,7 @@ private StreamConfig mockStreamConfig(int flushThresholdRows, int flushThreshold private StreamConfig mockAutotuneStreamConfig(long flushSegmentDesiredSizeBytes, long flushThresholdTimeMillis, int flushAutotuneInitialRows) { StreamConfig streamConfig = mock(StreamConfig.class); + when(streamConfig.getTopicName()).thenReturn("testTopic"); when(streamConfig.getTableNameWithType()).thenReturn(REALTIME_TABLE_NAME); when(streamConfig.getFlushThresholdRows()).thenReturn(0); when(streamConfig.getFlushThresholdSegmentSizeBytes()).thenReturn(flushSegmentDesiredSizeBytes); diff --git a/pinot-core/src/main/java/org/apache/pinot/core/query/scheduler/WorkloadScheduler.java b/pinot-core/src/main/java/org/apache/pinot/core/query/scheduler/WorkloadScheduler.java index e90f624e22e9..5827ced0be49 100644 --- a/pinot-core/src/main/java/org/apache/pinot/core/query/scheduler/WorkloadScheduler.java +++ b/pinot-core/src/main/java/org/apache/pinot/core/query/scheduler/WorkloadScheduler.java @@ -18,10 +18,12 @@ */ package org.apache.pinot.core.query.scheduler; +import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFutureTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.LongAccumulator; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.metrics.ServerMeter; import org.apache.pinot.common.metrics.ServerMetrics; import org.apache.pinot.common.metrics.ServerQueryPhase; @@ -91,7 +93,8 @@ public ListenableFuture submit(ServerQueryRequest queryRequest) { String tableName = TableNameBuilder.extractRawTableName(queryRequest.getTableNameWithType()); LOGGER.warn("Workload budget exceeded for workload: {} query: {} table: {}", workloadName, queryRequest.getRequestId(), tableName); - _serverMetrics.addMeteredValue(workloadName, ServerMeter.WORKLOAD_BUDGET_EXCEEDED, 1L); + _serverMetrics.addMeteredValue(workloadName, ServerMeter.WORKLOAD_BUDGET_EXCEEDED, 1L, + ImmutableMap.of(MetricAttributeConstants.WORKLOAD_NAME, workloadName)); _serverMetrics.addMeteredTableValue(tableName, ServerMeter.WORKLOAD_BUDGET_EXCEEDED, 1L); _serverMetrics.addMeteredGlobalValue(ServerMeter.WORKLOAD_BUDGET_EXCEEDED, 1L); return outOfCapacity(queryRequest); diff --git a/pinot-core/src/test/java/org/apache/pinot/core/data/manager/realtime/ConsumerCoordinatorTest.java b/pinot-core/src/test/java/org/apache/pinot/core/data/manager/realtime/ConsumerCoordinatorTest.java index 3a9722817255..d27a68558220 100644 --- a/pinot-core/src/test/java/org/apache/pinot/core/data/manager/realtime/ConsumerCoordinatorTest.java +++ b/pinot-core/src/test/java/org/apache/pinot/core/data/manager/realtime/ConsumerCoordinatorTest.java @@ -46,6 +46,7 @@ public FakeRealtimeTableDataManager(Semaphore segmentBuildSemaphore, boolean useIdealStateToCalculatePreviousSegment) { super(segmentBuildSemaphore); super._recentlyDeletedSegments = CacheBuilder.newBuilder().build(); + super._tableNameWithType = "testTable"; StreamIngestionConfig streamIngestionConfig = new StreamIngestionConfig(List.of(new HashMap<>())); streamIngestionConfig.setEnforceConsumptionInOrder(true); if (useIdealStateToCalculatePreviousSegment) { @@ -147,7 +148,6 @@ public void testFirstConsumer() FakeConsumerCoordinator consumerCoordinator = new FakeConsumerCoordinator(true, realtimeTableDataManager); realtimeTableDataManager.setConsumerCoordinator(consumerCoordinator); ReentrantLock lock = (ReentrantLock) consumerCoordinator.getLock(); - RealtimeSegmentDataManager mockedRealtimeSegmentDataManager = getMockedRealtimeSegmentDataManager(); Map serverSegmentStatusMap = new HashMap<>() {{ put("server_1", "ONLINE"); put("server_3", "ONLINE"); diff --git a/pinot-minion/src/main/java/org/apache/pinot/minion/BaseMinionStarter.java b/pinot-minion/src/main/java/org/apache/pinot/minion/BaseMinionStarter.java index a5cffb1a607d..f703263c464b 100644 --- a/pinot-minion/src/main/java/org/apache/pinot/minion/BaseMinionStarter.java +++ b/pinot-minion/src/main/java/org/apache/pinot/minion/BaseMinionStarter.java @@ -19,6 +19,7 @@ package org.apache.pinot.minion; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.File; import java.io.IOException; @@ -231,7 +232,8 @@ public void start() MinionMetrics minionMetrics = new MinionMetrics(_config.getMetricsPrefix(), metricsRegistry); minionMetrics.initializeGlobalMeters(); - minionMetrics.setValueOfGlobalGauge(MinionGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, 1); + minionMetrics.setValueOfGlobalGauge(MinionGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, + 1, ImmutableMap.of()); minionMetrics.setValueOfGlobalGauge(MinionGauge.ZK_JUTE_MAX_BUFFER, Integer.getInteger(ZkSystemPropertyKeys.JUTE_MAXBUFFER, 0xfffff)); MinionMetrics.register(minionMetrics); @@ -297,7 +299,7 @@ public void start() new TaskFactoryRegistry(_taskExecutorFactoryRegistry, _eventObserverFactoryRegistry).getTaskFactoryRegistry())); _helixManager.connect(); updateInstanceConfigIfNeeded(); - minionMetrics.setOrUpdateGauge(CommonConstants.Helix.INSTANCE_CONNECTED_METRIC_NAME, + minionMetrics.setOrUpdateGlobalGauge(CommonConstants.Helix.INSTANCE_CONNECTED_METRIC_NAME, () -> _helixManager.isConnected() ? 1L : 0L); minionContext.setHelixPropertyStore(_helixManager.getHelixPropertyStore()); minionContext.setHelixManager(_helixManager); diff --git a/pinot-minion/src/main/java/org/apache/pinot/minion/taskfactory/TaskFactoryRegistry.java b/pinot-minion/src/main/java/org/apache/pinot/minion/taskfactory/TaskFactoryRegistry.java index 209c33347d4d..37e2b6a725f4 100644 --- a/pinot-minion/src/main/java/org/apache/pinot/minion/taskfactory/TaskFactoryRegistry.java +++ b/pinot-minion/src/main/java/org/apache/pinot/minion/taskfactory/TaskFactoryRegistry.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.minion.taskfactory; +import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -31,6 +32,7 @@ import org.apache.helix.task.TaskFactory; import org.apache.helix.task.TaskResult; import org.apache.pinot.common.auth.AuthProviderUtils; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.metrics.MinionGauge; import org.apache.pinot.common.metrics.MinionMeter; import org.apache.pinot.common.metrics.MinionMetrics; @@ -79,6 +81,7 @@ public TaskFactoryRegistry(TaskExecutorFactoryRegistry taskExecutorFactoryRegist for (String taskType : taskExecutorFactoryRegistry.getAllTaskTypes()) { PinotTaskExecutorFactory taskExecutorFactory = taskExecutorFactoryRegistry.getTaskExecutorFactory(taskType); MinionEventObserverFactory eventObserverFactory = eventObserverFactoryRegistry.getEventObserverFactory(taskType); + Map attributes = ImmutableMap.of(MetricAttributeConstants.TASK_TYPE, taskType); LOGGER.info("Registering {} with task executor factory: {}, event observer factory: {}", taskType, taskExecutorFactory.getClass().getSimpleName(), eventObserverFactory.getClass().getSimpleName()); @@ -99,7 +102,7 @@ public TaskResult run() { long jobDequeueTimeMs = System.currentTimeMillis(); _minionMetrics .addTimedValue(taskType, MinionTimer.TASK_QUEUEING, jobDequeueTimeMs - jobInQueueTimeMs, - TimeUnit.MILLISECONDS); + TimeUnit.MILLISECONDS, attributes); String tableName = null; try { // Set taskId in MDC so that one may config logger to route task logs to separate file. @@ -107,28 +110,31 @@ public TaskResult run() { PinotTaskConfig pinotTaskConfig = PinotTaskConfig.fromHelixTaskConfig(_taskConfig); tableName = pinotTaskConfig.getTableName(); _minionMetrics.addValueToGlobalGauge(MinionGauge.NUMBER_OF_TASKS, 1L); - _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS, 1L); + _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS, 1L, attributes); if (tableName != null) { _minionMetrics - .addTimedTableValue(tableName, taskType, MinionTimer.TASK_QUEUEING, + .addTimedTableTaskValue(tableName, taskType, MinionTimer.TASK_QUEUEING, jobDequeueTimeMs - jobInQueueTimeMs, TimeUnit.MILLISECONDS); _minionMetrics.addValueToTableGauge(tableName, MinionGauge.NUMBER_OF_TASKS, 1L); - _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS, 1L); + _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS, 1L, + attributes); } MinionEventObservers.getInstance().addMinionEventObserver(_taskConfig.getId(), _eventObserver); return runInternal(pinotTaskConfig); } finally { MinionEventObservers.getInstance().removeMinionEventObserver(_taskConfig.getId()); _minionMetrics.addValueToGlobalGauge(MinionGauge.NUMBER_OF_TASKS, -1L); - _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS, -1L); + _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS, -1L, attributes); long executionTimeMs = System.currentTimeMillis() - jobDequeueTimeMs; _minionMetrics - .addTimedValue(taskType, MinionTimer.TASK_EXECUTION, executionTimeMs, TimeUnit.MILLISECONDS); + .addTimedValue(taskType, MinionTimer.TASK_EXECUTION, executionTimeMs, TimeUnit.MILLISECONDS, + attributes); if (tableName != null) { _minionMetrics.addValueToTableGauge(tableName, MinionGauge.NUMBER_OF_TASKS, -1L); - _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS, -1L); + _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS, -1L, + attributes); _minionMetrics - .addTimedTableValue(tableName, taskType, MinionTimer.TASK_EXECUTION, + .addTimedTableTaskValue(tableName, taskType, MinionTimer.TASK_EXECUTION, executionTimeMs, TimeUnit.MILLISECONDS); } LOGGER.info("Task: {} completed in: {}ms", _taskConfig.getId(), executionTimeMs); @@ -154,45 +160,46 @@ private TaskResult runInternal(PinotTaskConfig pinotTaskConfig) { try { Object executionResult = _taskExecutor.executeTask(pinotTaskConfig); _eventObserver.notifyTaskSuccess(pinotTaskConfig, executionResult); - _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_COMPLETED, 1L); + _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_COMPLETED, 1L, attributes); if (tableName != null) { - _minionMetrics.addMeteredTableValue(tableName, taskType, - MinionMeter.NUMBER_TASKS_COMPLETED, 1L); + _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS_COMPLETED, + 1L, attributes); } LOGGER.info("Task: {} succeeded", _taskConfig.getId()); return new TaskResult(TaskResult.Status.COMPLETED, "Succeeded"); } catch (TaskCancelledException e) { _eventObserver.notifyTaskCancelled(pinotTaskConfig); - _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_CANCELLED, 1L); + _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_CANCELLED, 1L, attributes); if (tableName != null) { - _minionMetrics.addMeteredTableValue(tableName, taskType, - MinionMeter.NUMBER_TASKS_CANCELLED, 1L); + _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS_CANCELLED, + 1L, attributes); } LOGGER.info("Task: {} got cancelled", _taskConfig.getId(), e); return new TaskResult(TaskResult.Status.CANCELED, extractAndTrimRootCauseMessage(e)); } catch (FatalException e) { _eventObserver.notifyTaskError(pinotTaskConfig, e); - _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_FATAL_FAILED, 1L); + _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_FATAL_FAILED, + 1L, attributes); if (tableName != null) { - _minionMetrics.addMeteredTableValue(tableName, taskType, - MinionMeter.NUMBER_TASKS_FATAL_FAILED, 1L); + _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS_FATAL_FAILED, + 1L, attributes); } LOGGER.error("Caught fatal exception while executing task: {}", _taskConfig.getId(), e); return new TaskResult(TaskResult.Status.FATAL_FAILED, extractAndTrimRootCauseMessage(e)); } catch (Exception e) { _eventObserver.notifyTaskError(pinotTaskConfig, e); - _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_FAILED, 1L); + _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_FAILED, 1L, attributes); if (tableName != null) { - _minionMetrics.addMeteredTableValue(tableName, taskType, - MinionMeter.NUMBER_TASKS_FAILED, 1L); + _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS_FAILED, + 1L, attributes); } LOGGER.error("Caught exception while executing task: {}", _taskConfig.getId(), e); return new TaskResult(TaskResult.Status.FAILED, extractAndTrimRootCauseMessage(e)); } finally { - _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_EXECUTED, 1L); + _minionMetrics.addMeteredValue(taskType, MinionMeter.NUMBER_TASKS_EXECUTED, 1L, attributes); if (tableName != null) { - _minionMetrics.addMeteredTableValue(tableName, taskType, - MinionMeter.NUMBER_TASKS_EXECUTED, 1L); + _minionMetrics.addMeteredTableValue(tableName, taskType, MinionMeter.NUMBER_TASKS_EXECUTED, + 1L, attributes); } } } diff --git a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotJmxReporter.java b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotJmxReporter.java index a7b6f032a169..119227a68136 100644 --- a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotJmxReporter.java +++ b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotJmxReporter.java @@ -22,6 +22,11 @@ import org.apache.pinot.spi.metrics.PinotJmxReporter; +/** + * CompoundPinotJmxReporter is a composite reporter that aggregates multiple PinotJmxReporters. + * @deprecated Use {@link org.apache.pinot.plugin.metrics.compound.CompoundPinotMetricReporter} instead. + */ +@Deprecated public class CompoundPinotJmxReporter implements PinotJmxReporter { private final List _reporters; diff --git a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricName.java b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricName.java index 5fa4fecdffe3..85ac101ce449 100644 --- a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricName.java +++ b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricName.java @@ -19,27 +19,47 @@ package org.apache.pinot.plugin.metrics.compound; import java.util.List; +import java.util.Map; import java.util.Objects; import org.apache.pinot.spi.metrics.PinotMetricName; public class CompoundPinotMetricName implements PinotMetricName { - private final String _toString; - private final List _names; + private final String _fullMetricName; + private final String _simplifiedMetricName; + private final Map _attributes; + private final List _subMetricNames; - public CompoundPinotMetricName(String toString, List names) { - _toString = toString; - _names = names; + public CompoundPinotMetricName(String fullMetricName, String simplifiedMetricName, Map attributes, + List subMetricNames) { + _fullMetricName = fullMetricName; + _simplifiedMetricName = simplifiedMetricName; + _attributes = attributes; + _subMetricNames = subMetricNames; } @Override public String toString() { - return _toString; + return _fullMetricName; } @Override - public List getMetricName() { - return _names; + public String getSimplifiedMetricName() { + return _simplifiedMetricName; + } + + @Override + public Map getAttributes() { + return _attributes; + } + + @Override + public String getMetricName() { + return _fullMetricName; + } + + public PinotMetricName getSubMetricName(int index) { + return _subMetricNames.get(index); } @Override @@ -51,11 +71,11 @@ public boolean equals(Object o) { return false; } CompoundPinotMetricName that = (CompoundPinotMetricName) o; - return Objects.equals(_names, that._names); + return Objects.equals(_subMetricNames, that._subMetricNames); } @Override public int hashCode() { - return Objects.hash(_names); + return Objects.hash(_subMetricNames); } } diff --git a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricRegistry.java b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricRegistry.java index debcb173e2ab..db88bddd4dc4 100644 --- a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricRegistry.java +++ b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricRegistry.java @@ -51,7 +51,7 @@ public void removeMetric(PinotMetricName name) { CompoundPinotMetricName castedName = (CompoundPinotMetricName) name; _allMetrics.remove(name); for (int i = 0; i < _registries.size(); i++) { - _registries.get(i).removeMetric(castedName.getMetricName().get(i)); + _registries.get(i).removeMetric(castedName.getSubMetricName(i)); } } @@ -59,7 +59,7 @@ private List map(PinotMetricName name, BiFunction result = new ArrayList<>(_registries.size()); for (int i = 0; i < _registries.size(); i++) { - PinotMetricName metricName = castedName.getMetricName().get(i); + PinotMetricName metricName = castedName.getSubMetricName(i); T mappedElement = mapFun.apply(_registries.get(i), metricName); result.add(mappedElement); } @@ -70,25 +70,25 @@ private List map(PinotMetricName name, BiFunction PinotGauge newGauge(PinotMetricName name, PinotGauge gauge) { if (gauge == null) { return (PinotGauge) _allMetrics.computeIfAbsent(name, - key -> new CompoundPinotGauge<>(map(key, (reg, subName) -> reg.newGauge(subName, null)))); - } else { - CompoundPinotGauge compoundGauge = - (CompoundPinotGauge) PinotMetricUtils.makePinotGauge(avoid -> gauge.value()); - - Function> creator = key -> { - CompoundPinotMetricName compoundName = (CompoundPinotMetricName) key; - ArrayList> gauges = new ArrayList<>(_registries.size()); - for (int i = 0; i < _registries.size(); i++) { - PinotGauge subGauge = compoundGauge.getMeter(i); - PinotMetricName subName = compoundName.getMetricName().get(i); - PinotGauge created = (PinotGauge) _registries.get(i).newGauge(subName, subGauge); - gauges.add(created); - } - return new CompoundPinotGauge<>(gauges); - }; - - return (PinotGauge) _allMetrics.computeIfAbsent(name, creator); + key -> new CompoundPinotGauge<>(map(key, (reg, subName) -> reg.newGauge(subName, gauge)))); } + + CompoundPinotGauge compoundGauge = (CompoundPinotGauge) PinotMetricUtils + .makePinotGauge(name, avoid -> gauge.value()); + + Function> creator = key -> { + CompoundPinotMetricName compoundName = (CompoundPinotMetricName) key; + ArrayList> gauges = new ArrayList<>(_registries.size()); + for (int i = 0; i < _registries.size(); i++) { + PinotGauge subGauge = compoundGauge.getMeter(i); + PinotMetricName subName = compoundName.getSubMetricName(i); + PinotGauge created = (PinotGauge) _registries.get(i).newGauge(subName, subGauge); + gauges.add(created); + } + return new CompoundPinotGauge<>(gauges); + }; + + return (PinotGauge) _allMetrics.computeIfAbsent(name, creator); } @Override @@ -122,7 +122,9 @@ public Map allMetrics() { @Override public void addListener(PinotMetricsRegistryListener listener) { - throw new UnsupportedOperationException("Not implemented yet"); + for (int i = 0; i < _registries.size(); i++) { + _registries.get(i).addListener(listener); + } } @Override diff --git a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricReporter.java b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricReporter.java new file mode 100644 index 000000000000..74c7126a9fc7 --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricReporter.java @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.compound; + +import java.util.List; +import org.apache.pinot.spi.metrics.PinotMetricReporter; + + +/** + * CompoundPinotMetricReporter delegates metric reporting to each of the reporters in the list. + */ +public class CompoundPinotMetricReporter implements PinotMetricReporter { + private final List _reporters; + + public CompoundPinotMetricReporter(List reporters) { + _reporters = reporters; + } + + @Override + public void start() { + for (PinotMetricReporter reporter : _reporters) { + reporter.start(); + } + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricsFactory.java b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricsFactory.java index fe1b19e62b29..104c6a20bd71 100644 --- a/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricsFactory.java +++ b/pinot-plugins/pinot-metrics/pinot-compound-metrics/src/main/java/org/apache/pinot/plugin/metrics/compound/CompoundPinotMetricsFactory.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.function.Function; @@ -35,6 +36,7 @@ import org.apache.pinot.spi.metrics.PinotGauge; import org.apache.pinot.spi.metrics.PinotJmxReporter; import org.apache.pinot.spi.metrics.PinotMetricName; +import org.apache.pinot.spi.metrics.PinotMetricReporter; import org.apache.pinot.spi.metrics.PinotMetricUtils; import org.apache.pinot.spi.metrics.PinotMetricsRegistry; import org.apache.pinot.spi.plugin.PluginManager; @@ -47,11 +49,14 @@ * When it is created, a bunch of factories are used as sub-factories. Whenever a metric is registered in this factory, * it actually registers the metric in all the sub-factories. * - * Probably the main reason to use this metrics is to compare the differences between one metric registry and another. - * For example, Yammer and Dropwizard provide their own timer, but each one provides their own metrics on their timers. - * Most metrics are the same (p50, p90, p95, etc) but some other may be different. + * The main use cases for this metrics registry including: + * - to progressively migrate from one metrics plugin to another. In production there must be a lot of dashboards + * and alerts set up based on old metrics, it needs dual reporting for some time to allow progressive migration. + * - to compare the differences between different metrics registries. for example, Yammer and Dropwizard provide their + * own timer, but each one provides their own metrics on their timers. Most metrics are the same (p50, p90, p95, etc) + * but some other may be different. * - * Alternative it could be used in production, but it is important to make sure that the JMX MBeans produced by each + * If both Yammer and Dropwizard are used, it is important to make sure that the JMX MBeans produced by each * sub-registry are different. Otherwise the reported value is undetermined. * * In order to use this factory, you have to set the following properties in Pinot configuration: @@ -114,7 +119,8 @@ public void init(PinotConfiguration metricsConfiguration) { Algorithm algorithm = Algorithm.valueOf(algorithmName.toUpperCase(Locale.US)); _factories = algorithm.streamInstances(metricsConfiguration) .filter(factory -> allIgnored.stream().noneMatch(ignored -> ignored.isAssignableFrom(factory.getClass()))) - .filter(factory -> CompoundPinotMetricsFactory.class.isAssignableFrom(factory.getClass())) + // remove the compound factory itself from the list to avoid infinite recursion + .filter(factory -> !CompoundPinotMetricsFactory.class.isAssignableFrom(factory.getClass())) .collect(Collectors.toList()); if (_factories.isEmpty()) { @@ -135,23 +141,44 @@ public PinotMetricsRegistry getPinotMetricsRegistry() { return _registry; } + @Deprecated @Override public PinotMetricName makePinotMetricName(Class klass, String name) { + throw new UnsupportedOperationException("Please use makePinotMetricName(Class, String, String, Map) instead"); + } + + @Override + public PinotMetricName makePinotMetricName(Class klass, String fullName, String simplifiedName, + Map attributes) { List names = _factories.stream() - .map(factory -> factory.makePinotMetricName(klass, name)) + .map(factory -> factory.makePinotMetricName(klass, fullName, simplifiedName, attributes)) .collect(Collectors.toList()); - return new CompoundPinotMetricName(name, names); + return new CompoundPinotMetricName(fullName, simplifiedName, attributes, names); } @Override + @Deprecated public PinotGauge makePinotGauge(Function condition) { + throw new UnsupportedOperationException("Please use makePinotGauge(PinotMetricName, Function) instead"); + } + + @Deprecated + @Override + public PinotGauge makePinotGauge(String metricName, Function condition) { + throw new UnsupportedOperationException("Please use makePinotGauge(PinotMetricName, Function) instead"); + } + + public PinotGauge makePinotGauge(PinotMetricName pinotMetricName, Function condition) { List> gauges = _factories.stream() - .map(factory -> factory.makePinotGauge(condition)) + .map(factory -> factory.makePinotGauge(pinotMetricName, condition)) .collect(Collectors.toList()); return new CompoundPinotGauge(gauges); } - + /** + * @deprecated Use {@link #makePinotMetricReporter(PinotMetricsRegistry)} instead. + */ @Override + @Deprecated public PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry) { CompoundPinotMetricRegistry registry = (CompoundPinotMetricRegistry) metricsRegistry; List subRegistries = registry.getRegistries(); @@ -164,12 +191,30 @@ public PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistr PinotMetricsFactory subFactory = _factories.get(i); PinotMetricsRegistry subRegistry = subRegistries.get(i); - subJmx.add(subFactory.makePinotJmxReporter(subRegistry)); + subJmx.add(subFactory.makePinotMetricReporter(subRegistry)); } return new CompoundPinotJmxReporter(subJmx); } + public PinotMetricReporter makePinotMetricReporter(PinotMetricsRegistry metricsRegistry) { + CompoundPinotMetricRegistry registry = (CompoundPinotMetricRegistry) metricsRegistry; + List subRegistries = registry.getRegistries(); + Preconditions.checkState(subRegistries.size() == _factories.size(), + "Number of registries ({}) should be the same than the number of factories ({})", + subRegistries.size(), _factories.size()); + + ArrayList subReporter = new ArrayList<>(_factories.size()); + for (int i = 0; i < _factories.size(); i++) { + PinotMetricsFactory subFactory = _factories.get(i); + PinotMetricsRegistry subRegistry = subRegistries.get(i); + + subReporter.add(subFactory.makePinotMetricReporter(subRegistry)); + } + + return new CompoundPinotMetricReporter(subReporter); + } + @Override public String getMetricsFactoryName() { return "Compound"; diff --git a/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardJmxReporter.java b/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardJmxReporter.java index 70c1a2b773ca..8be022a3117f 100644 --- a/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardJmxReporter.java +++ b/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardJmxReporter.java @@ -20,11 +20,13 @@ import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.jmx.JmxReporter; -import org.apache.pinot.spi.metrics.PinotJmxReporter; +import org.apache.pinot.spi.metrics.PinotMetricReporter; import org.apache.pinot.spi.metrics.PinotMetricsRegistry; - -public class DropwizardJmxReporter implements PinotJmxReporter { +/** + * DropwizardJmxReporter is a metric reporter that exposes metrics to JMX using Dropwizard's JmxReporter. + */ +public class DropwizardJmxReporter implements PinotMetricReporter { private final JmxReporter _jmxReporter; public DropwizardJmxReporter(PinotMetricsRegistry metricsRegistry, String domainName) { diff --git a/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricName.java b/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricName.java index ba51ffa9bbd5..8395044b7500 100644 --- a/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricName.java +++ b/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricName.java @@ -18,6 +18,7 @@ */ package org.apache.pinot.plugin.metrics.dropwizard; +import java.util.Map; import java.util.Objects; import org.apache.pinot.spi.metrics.PinotMetricName; @@ -65,4 +66,14 @@ public int hashCode() { public String toString() { return _metricName; } + + @Override + public String getSimplifiedMetricName() { + return _metricName; + } + + @Override + public Map getAttributes() { + return Map.of(); + } } diff --git a/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricsFactory.java b/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricsFactory.java index 0f80b9aa02e5..0f71aba1ecd6 100644 --- a/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricsFactory.java +++ b/pinot-plugins/pinot-metrics/pinot-dropwizard/src/main/java/org/apache/pinot/plugin/metrics/dropwizard/DropwizardMetricsFactory.java @@ -19,6 +19,7 @@ package org.apache.pinot.plugin.metrics.dropwizard; import com.google.auto.service.AutoService; +import java.util.Map; import java.util.function.Function; import org.apache.pinot.spi.annotations.metrics.MetricsFactory; import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; @@ -26,6 +27,7 @@ import org.apache.pinot.spi.metrics.PinotGauge; import org.apache.pinot.spi.metrics.PinotJmxReporter; import org.apache.pinot.spi.metrics.PinotMetricName; +import org.apache.pinot.spi.metrics.PinotMetricReporter; import org.apache.pinot.spi.metrics.PinotMetricsRegistry; @@ -52,20 +54,46 @@ public PinotMetricsRegistry getPinotMetricsRegistry() { } @Override + @Deprecated public PinotMetricName makePinotMetricName(Class klass, String name) { - return new DropwizardMetricName(klass, name); + throw new UnsupportedOperationException("Please use makePinotMetricName(Class, String, String, Map) instead"); } @Override + public PinotMetricName makePinotMetricName(Class klass, String fullName, String simplifiedName, + Map attributes) { + return new DropwizardMetricName(klass, fullName); + } + + @Override + @Deprecated public PinotGauge makePinotGauge(Function condition) { return new DropwizardGauge(condition); } + @Override + @Deprecated + public PinotGauge makePinotGauge(String metricName, Function condition) { + throw new UnsupportedOperationException("Please use makePinotGauge(PinotMetricName, Function) instead"); + } + + @Override + public PinotGauge makePinotGauge(PinotMetricName pinotMetricName, Function condition) { + return new DropwizardGauge(condition); + } + + /** + * @deprecated Use {@link #makePinotMetricReporter(PinotMetricsRegistry)} instead. + */ @Override public PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry) { return new DropwizardJmxReporter(metricsRegistry, _domainName); } + public PinotMetricReporter makePinotMetricReporter(PinotMetricsRegistry metricsRegistry) { + return new DropwizardJmxReporter(metricsRegistry, _domainName); + } + @Override public String getMetricsFactoryName() { return "Dropwizard"; diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/pom.xml b/pinot-plugins/pinot-metrics/pinot-open-telemetry/pom.xml new file mode 100644 index 000000000000..ffc768fe121d --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/pom.xml @@ -0,0 +1,78 @@ + + + + 4.0.0 + + pinot-metrics + org.apache.pinot + 1.5.0-SNAPSHOT + + + pinot-open-telemetry + Pinot OpenTelemetry Metrics + https://pinot.apache.org/ + + ${basedir}/../../.. + package + + + + + com.google.auto.service + auto-service-annotations + + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-exporter-common + + + io.opentelemetry + opentelemetry-exporter-logging + + + io.opentelemetry + opentelemetry-exporter-otlp + + + org.apache.pinot + pinot-common + + + + + + pinot-fastdev + + none + + + + + \ No newline at end of file diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryCounter.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryCounter.java new file mode 100644 index 000000000000..a7f3b0b1fcdc --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryCounter.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.api.metrics.LongCounter; +import org.apache.pinot.spi.metrics.PinotCounter; + + +/** + * OpenTelemetryCounter is the implementation of {@link PinotCounter} for OpenTelemetry. + * Actually Pinot-Core does NOT use {@link PinotCounter} anywhere, so this is just a dummy implementation + */ +public class OpenTelemetryCounter implements PinotCounter { + private final LongCounter _counter; + + public OpenTelemetryCounter(LongCounter counter) { + _counter = counter; + } + + @Override + public Object getCounter() { + return _counter; + } + + @Override + public Object getMetric() { + return _counter; + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryDoubleGauge.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryDoubleGauge.java new file mode 100644 index 000000000000..41669e86d099 --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryDoubleGauge.java @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleGauge; +import java.util.function.Function; +import java.util.function.Supplier; +import org.apache.pinot.spi.metrics.PinotGauge; + + +/** + * OpenTelemetryDoubleGauge is the implementation of {@link PinotGauge} for OpenTelemetry. + * Most of metric libraries including OpenTelemetry do not support retrieve the current value of a gauge, so it allows + * setting a value supplier to get the current value when calling {@link #value()}. + */ +public class OpenTelemetryDoubleGauge implements PinotGauge { + private final DoubleGauge _doubleGauge; + private final Attributes _attributes; + private Supplier _valueSupplier; + + public OpenTelemetryDoubleGauge(DoubleGauge doubleGauge, Attributes attributes, Function valueSupplier) { + _doubleGauge = doubleGauge; + _attributes = attributes; + setValueSupplier(() -> valueSupplier.apply(null)); + } + + @Override + public Object getGauge() { + return _doubleGauge; + } + + @Override + public Object getMetric() { + return _doubleGauge; + } + + @Override + public T value() { + return _valueSupplier.get(); + } + + @Override + public void setValue(T value) { + _doubleGauge.set((Double) value, _attributes); + _valueSupplier = () -> value; + } + + @Override + public void setValueSupplier(Supplier valueSupplier) { + _valueSupplier = valueSupplier; + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryHistogram.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryHistogram.java new file mode 100644 index 000000000000..e21d6f193511 --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryHistogram.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import org.apache.pinot.spi.metrics.PinotHistogram; + +/** + * OpenTelemetryHistogram is the implementation of {@link PinotHistogram} for OpenTelemetry. + * Actually Pinot-Core does NOT use {@link PinotHistogram} anywhere, so this is just a dummy implementation + */ +public class OpenTelemetryHistogram implements PinotHistogram { + private final DoubleHistogram _histogram; + private final Attributes _attributes; + + public OpenTelemetryHistogram(DoubleHistogram histogram, Attributes attributes) { + _histogram = histogram; + _attributes = attributes; + } + + @Override + public Object getHistogram() { + return _histogram; + } + + @Override + public Object getMetric() { + return _histogram; + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryHttpReporter.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryHttpReporter.java new file mode 100644 index 000000000000..2ff14148ce4d --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryHttpReporter.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; +import org.apache.pinot.spi.metrics.PinotMetricReporter; + + +/** + * OpenTelemetryHttpReporter exports metrics to an OpenTelemetry collector on HTTP endpoint. + */ +public class OpenTelemetryHttpReporter implements PinotMetricReporter { + public static final OtlpHttpMetricExporter DEFAULT_HTTP_METRIC_EXPORTER = OtlpHttpMetricExporter + .builder() + //.setEndpoint("http://[::1]:22784/v1/metrics") // default OpenTelemetry collector endpoint + .setEndpoint("http://127.0.0.1:4318/v1/metrics") // default OpenTelemetry collector endpoint + .build(); + // by default export metrics every second, as we want to report QPS and OpenTelemetryMeter implementation use + // ObservableLongCounter which fetch the current value the at reporting time by calling a callback function. If we + // set an export interval longer than 1 second, the QPS metric will not be accurate (during an export interval, the + // QPS will be same). + public static final int DEFAULT_EXPORT_INTERVAL_SECONDS = 1; + + public OpenTelemetryHttpReporter() { + } + + @Override + public void start() { + // TODO: make the collector endpoint and export interval configurable + OpenTelemetryMetricsRegistry.init(DEFAULT_HTTP_METRIC_EXPORTER, DEFAULT_EXPORT_INTERVAL_SECONDS); + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryLongGauge.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryLongGauge.java new file mode 100644 index 000000000000..4d4f07ab4a0a --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryLongGauge.java @@ -0,0 +1,68 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongGauge; +import java.util.function.Function; +import java.util.function.Supplier; +import org.apache.pinot.spi.metrics.PinotGauge; + +/** + * OpenTelemetryLongGauge is the implementation of {@link PinotGauge} for OpenTelemetry. + * Most of metric libraries including OpenTelemetry do not support retrieve the current value of a gauge, so it allows + * setting a value supplier to get the current value when calling {@link #value()}. + */ +public class OpenTelemetryLongGauge implements PinotGauge { + private final LongGauge _longGauge; + private final Attributes _attributes; + private Supplier _valueSupplier; + + public OpenTelemetryLongGauge(LongGauge longGauge, Attributes attributes, Function valueSupplier) { + _longGauge = longGauge; + _attributes = attributes; + setValueSupplier(() -> valueSupplier.apply(null)); + } + + @Override + public Object getGauge() { + return _longGauge; + } + + @Override + public Object getMetric() { + return _longGauge; + } + + @Override + public T value() { + return _valueSupplier.get(); + } + + @Override + public void setValue(T value) { + _longGauge.set((Long) value, _attributes); + _valueSupplier = () -> value; + } + + @Override + public void setValueSupplier(Supplier valueSupplier) { + _valueSupplier = valueSupplier; + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMeter.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMeter.java new file mode 100644 index 000000000000..2ce25f5de8f9 --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMeter.java @@ -0,0 +1,115 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.pinot.spi.metrics.PinotMeter; + +/** + * OpenTelemetryMeter is the implementation of {@link PinotMeter} for OpenTelemetry, which measures the rate of events + * over time (e.g., QPS). The {@link PinotMeter} interface provides methods to mark the occurrence of events and + * retrieve various rate metrics (e.g. one-, five-, and fifteen-minute EWMA(exponentially-weighted moving average) + * throughput. Those EWMA throughput is not directly supported by OpenTelemetry's standard metric instruments. + * You cannot configure an EWMA instrument in your application code. Instead, you must build the EWMA calculation on + * the backend, using OpenTelemetry metric primitives like Counters, and then send the data to a compatible telemetry + * backend for calculation and visualization. + */ +public class OpenTelemetryMeter implements PinotMeter { + private final String _eventType; + private final TimeUnit _rateUnit; + private final LongUpDownCounter _counter; + private final Attributes _attributes; + private final AtomicLong _value = new AtomicLong(0); + + public OpenTelemetryMeter(LongUpDownCounter counter, Attributes attributes, String eventType, TimeUnit rateUnit) { + _counter = counter; + _attributes = attributes; + _eventType = eventType; + _rateUnit = rateUnit; + } + + @Override + public void mark() { + mark(1L); + } + + @Override + public void mark(long unitCount) { + _counter.add(unitCount - _value.get(), _attributes); + _value.set(unitCount); + } + + @Override + public long count() { + // Not applicable. OTel does not support retrieve count directly from the counter, but this method is only called + // in tests, so we are good here. + return 0L; + } + + @Override + public Object getMetric() { + return _counter; + } + + @Override + public Object getMetered() { + return _counter; + } + + @Override + public TimeUnit rateUnit() { + return _rateUnit; + } + + @Override + public String eventType() { + return _eventType; + } + + @Override + public double fifteenMinuteRate() { + // Not applicable. OTel does not support retrieve rate directly from the counter. This method is in the interface, + // but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("fifteenMinuteRate is not supported in OpenTelemetryMeter"); + } + + @Override + public double fiveMinuteRate() { + // Not applicable. OTel does not support retrieve rate directly from the counter. This method is in the interface, + // but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("fiveMinuteRate is not supported in OpenTelemetryMeter"); + } + + @Override + public double meanRate() { + // Not applicable. OTel does not support retrieve rate directly from the counter. This method is in the interface, + // but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("meanRate is not supported in OpenTelemetryMeter"); + } + + @Override + public double oneMinuteRate() { + // Not applicable. OTel does not support retrieve rate directly from the counter. This method is in the interface, + // but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("oneMinuteRate is not supported in OpenTelemetryMeter"); + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricName.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricName.java new file mode 100644 index 000000000000..72e59945b846 --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricName.java @@ -0,0 +1,90 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import java.util.HashMap; +import java.util.Map; +import org.apache.pinot.common.metrics.MetricAttributeConstants; +import org.apache.pinot.spi.metrics.PinotMetricName; + + +/** + * OpenTelemetryMetricName is the implementation of PinotMetricName for OpenTelemetry. + * It all to set a simplified metricName and add attributes. + */ +public class OpenTelemetryMetricName implements PinotMetricName { + private final String _fullMetricName; + private String _simplifiedMetricName; + private Map _attributes; + + public OpenTelemetryMetricName(String fullMetricName, String simplifiedMetricName, Map attributes) { + _fullMetricName = fullMetricName; + _simplifiedMetricName = simplifiedMetricName; + _attributes = new HashMap<>(attributes); + _attributes.put(MetricAttributeConstants.PINOT_METRIC_NAME, fullMetricName); + } + + public OpenTelemetryMetricName(PinotMetricName pinotMetricName) { + this(pinotMetricName.getMetricName().toString(), pinotMetricName.getSimplifiedMetricName(), + pinotMetricName.getAttributes()); + } + + @Override + public String getMetricName() { + return _fullMetricName; + } + + /** + * Overrides equals method by calling the equals from the actual metric name. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + OpenTelemetryMetricName that = (OpenTelemetryMetricName) obj; + return _fullMetricName.equals(that._fullMetricName); + } + + /** + * Overrides hashCode method by calling the hashCode method from the actual metric name. + */ + @Override + public int hashCode() { + return _fullMetricName.hashCode(); + } + + @Override + public String toString() { + return _fullMetricName; + } + + @Override + public String getSimplifiedMetricName() { + return _simplifiedMetricName; + } + + @Override + public Map getAttributes() { + return _attributes; + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsFactory.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsFactory.java new file mode 100644 index 000000000000..a7c26ae53ef3 --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsFactory.java @@ -0,0 +1,110 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import com.google.auto.service.AutoService; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleGauge; +import io.opentelemetry.api.metrics.LongGauge; +import java.util.Map; +import java.util.function.Function; +import org.apache.pinot.spi.annotations.metrics.MetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; +import org.apache.pinot.spi.env.PinotConfiguration; +import org.apache.pinot.spi.metrics.PinotGauge; +import org.apache.pinot.spi.metrics.PinotJmxReporter; +import org.apache.pinot.spi.metrics.PinotMetricName; +import org.apache.pinot.spi.metrics.PinotMetricReporter; +import org.apache.pinot.spi.metrics.PinotMetricsRegistry; + + +@AutoService(PinotMetricsFactory.class) +@MetricsFactory +public class OpenTelemetryMetricsFactory implements PinotMetricsFactory { + private final PinotMetricsRegistry _pinotMetricsRegistry = new OpenTelemetryMetricsRegistry(); + + @Override + public void init(PinotConfiguration metricsConfiguration) { + } + + @Override + public PinotMetricsRegistry getPinotMetricsRegistry() { + return _pinotMetricsRegistry; + } + + @Override + @Deprecated + public PinotMetricName makePinotMetricName(Class klass, String name) { + throw new UnsupportedOperationException("Please use makePinotMetricName(Class, String, String, Map) instead"); + } + + @Override + public PinotMetricName makePinotMetricName(Class klass, String fullName, String simplifiedName, + Map attributes) { + return new OpenTelemetryMetricName(fullName, simplifiedName, attributes); + } + + @Override + @Deprecated + public PinotGauge makePinotGauge(Function condition) { + throw new UnsupportedOperationException("Please use makePinotGauge(PinotMetricName, Function) instead"); + } + + @Override + @Deprecated + public PinotGauge makePinotGauge(String metricName, Function condition) { + throw new UnsupportedOperationException("Please use makePinotGauge(PinotMetricName, Function) instead"); + } + + @Override + public PinotGauge makePinotGauge(PinotMetricName pinotMetricName, Function condition) { + T value = condition.apply(null); + + String simplifiedMetricName = pinotMetricName.getSimplifiedMetricName(); + Attributes attributes = OpenTelemetryUtil.toOpenTelemetryAttributes(pinotMetricName.getAttributes()); + + if (value instanceof Integer || value instanceof Long) { + LongGauge longGauge = OpenTelemetryMetricsRegistry.SharedOtelMetricRegistry + .getOrCreateLongGauge(simplifiedMetricName); + return new OpenTelemetryLongGauge<>(longGauge, attributes, condition); + } + if (value instanceof Double) { + DoubleGauge doubleGauge = OpenTelemetryMetricsRegistry.SharedOtelMetricRegistry + .getOrCreateDoubleGauge(simplifiedMetricName); + return new OpenTelemetryDoubleGauge<>(doubleGauge, attributes, condition); + } + throw new IllegalArgumentException("Unsupported supplier type: " + condition.getClass().getName()); + } + + @Override + @Deprecated + public PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry) { + throw new UnsupportedOperationException("OpenTelemetry does not support JMX reporter"); + } + + @Override + public PinotMetricReporter makePinotMetricReporter(PinotMetricsRegistry metricsRegistry) { + return new OpenTelemetryHttpReporter(); + } + + @Override + public String getMetricsFactoryName() { + return "OpenTelemetry"; + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistry.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistry.java new file mode 100644 index 000000000000..6be2759b544e --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistry.java @@ -0,0 +1,337 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleGauge; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongGauge; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import java.time.Duration; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import org.apache.pinot.spi.metrics.PinotCounter; +import org.apache.pinot.spi.metrics.PinotGauge; +import org.apache.pinot.spi.metrics.PinotHistogram; +import org.apache.pinot.spi.metrics.PinotMeter; +import org.apache.pinot.spi.metrics.PinotMetric; +import org.apache.pinot.spi.metrics.PinotMetricName; +import org.apache.pinot.spi.metrics.PinotMetricsRegistry; +import org.apache.pinot.spi.metrics.PinotMetricsRegistryListener; +import org.apache.pinot.spi.metrics.PinotTimer; + +/** + * OpenTelemetryMetricsRegistry is the implementation of {@link PinotMetricsRegistry} for OpenTelemetry. + */ +public class OpenTelemetryMetricsRegistry implements PinotMetricsRegistry { + public static final String OTEL_METRICS_SCOPE = "Pinot"; + + private static final Map PINOT_COUNTER_MAP = new ConcurrentHashMap<>(); + private static final Map PINOT_METER_MAP = new ConcurrentHashMap<>(); + private static final Map PINOT_LONG_GAUGE_MAP = + new ConcurrentHashMap<>(); + private static final Map PINOT_DOUBLE_GAUGE_MAP = + new ConcurrentHashMap<>(); + private static final Map PINOT_HISTOGRAM_MAP = + new ConcurrentHashMap<>(); + private static final Map PINOT_TIMER_MAP = new ConcurrentHashMap<>(); + private static final List LISTENERS = new LinkedList<>(); + + // _otelMeterProvider is the metric provider for OpenTelemetry metrics. + // The naming is a little confusing here, PinotMeter is actually a Counter in OpenTelemetry. While in OpenTelemetry, + // the terminology of Meter is used to represent a broader category of metrics, including counters, gauges, and + // histograms. + public static Meter _otelMeterProvider; + + public OpenTelemetryMetricsRegistry() { + init(OpenTelemetryHttpReporter.DEFAULT_HTTP_METRIC_EXPORTER, + OpenTelemetryHttpReporter.DEFAULT_EXPORT_INTERVAL_SECONDS); + } + + public static void init(OtlpHttpMetricExporter otlpHttpMetricExporter, int exportIntervalInSeconds) { + // Configures the OpenTelemetry SDK Meter Provider with the given OTLP HTTP Metric Exporter and set export interval. + SdkMeterProvider sdkMeterProvider = SdkMeterProvider + .builder() + .registerMetricReader(PeriodicMetricReader.builder(otlpHttpMetricExporter) + .setInterval(Duration.ofSeconds(exportIntervalInSeconds)) + .build()) + .build(); + // Initialize the static _otelMeterProvider + _otelMeterProvider = sdkMeterProvider.get(OTEL_METRICS_SCOPE); + } + + @Override + public void removeMetric(PinotMetricName pinotMetricname) { + PINOT_COUNTER_MAP.remove(pinotMetricname); + PINOT_METER_MAP.remove(pinotMetricname); + PINOT_LONG_GAUGE_MAP.remove(pinotMetricname); + PINOT_DOUBLE_GAUGE_MAP.remove(pinotMetricname); + PINOT_HISTOGRAM_MAP.remove(pinotMetricname); + PINOT_TIMER_MAP.remove(pinotMetricname); + + + if (!LISTENERS.isEmpty()) { + OpenTelemetryMetricName metricName = new OpenTelemetryMetricName(pinotMetricname); + for (OpenTelemetryMetricsRegistryListener listener : LISTENERS) { + listener.onMetricRemoved(metricName); + } + } + } + + @Override + public PinotCounter newCounter(PinotMetricName pinotMetricname) { + OpenTelemetryMetricName metricName = new OpenTelemetryMetricName(pinotMetricname); + String simpleMetricName = metricName.getSimplifiedMetricName(); + + return PINOT_COUNTER_MAP.computeIfAbsent(metricName, n -> { + OpenTelemetryCounter otelCounter = new OpenTelemetryCounter( + SharedOtelMetricRegistry.getOrCreateLongCounter(simpleMetricName)); + if (!LISTENERS.isEmpty()) { + for (OpenTelemetryMetricsRegistryListener listener : LISTENERS) { + listener.onMetricAdded(metricName, otelCounter); + } + } + return otelCounter; + }); + } + + @Override + public PinotGauge newGauge(PinotMetricName pinotMetricname, PinotGauge gauge) { + OpenTelemetryMetricName metricName = new OpenTelemetryMetricName(pinotMetricname); + String simpleMetricName = metricName.getSimplifiedMetricName(); + Attributes attributes = OpenTelemetryUtil.toOpenTelemetryAttributes(metricName.getAttributes()); + T value = gauge.value(); + + if (value instanceof Integer || value instanceof Long) { + return PINOT_LONG_GAUGE_MAP.computeIfAbsent(metricName, n -> { + OpenTelemetryLongGauge otelLongGauge = new OpenTelemetryLongGauge<>( + SharedOtelMetricRegistry.getOrCreateLongGauge(simpleMetricName), attributes, v -> (long) value + ); + if (!LISTENERS.isEmpty()) { + for (OpenTelemetryMetricsRegistryListener listener : LISTENERS) { + listener.onMetricAdded(metricName, otelLongGauge); + } + } + return otelLongGauge; + }); + } + + if (value instanceof Double) { + return PINOT_DOUBLE_GAUGE_MAP.computeIfAbsent(metricName, n -> { + OpenTelemetryDoubleGauge otelDoubleGauge = new OpenTelemetryDoubleGauge<>( + SharedOtelMetricRegistry.getOrCreateDoubleGauge(simpleMetricName), attributes, v -> (double) value + ); + if (!LISTENERS.isEmpty()) { + for (OpenTelemetryMetricsRegistryListener listener : LISTENERS) { + listener.onMetricAdded(metricName, otelDoubleGauge); + } + } + return otelDoubleGauge; + }); + } + // this should never happen as Pinot core only creates Gauges with Long or Double values + throw new IllegalArgumentException(String.format("Value type %s is not supported for gauge", + value.getClass().getSimpleName())); + } + + @Override + public PinotMeter newMeter(PinotMetricName pinotMetricname, String eventType, TimeUnit unit) { + OpenTelemetryMetricName metricName = new OpenTelemetryMetricName(pinotMetricname); + String simpleMetricName = metricName.getSimplifiedMetricName(); + Attributes attributes = OpenTelemetryUtil.toOpenTelemetryAttributes(metricName.getAttributes()); + + return PINOT_METER_MAP.computeIfAbsent(pinotMetricname, n -> { + OpenTelemetryMeter otelMeter = new OpenTelemetryMeter( + SharedOtelMetricRegistry.getOrCreateLongUpDownCounter(simpleMetricName, unit), attributes, eventType, unit); + if (!LISTENERS.isEmpty()) { + for (OpenTelemetryMetricsRegistryListener listener : LISTENERS) { + listener.onMetricAdded(metricName, otelMeter); + } + } + return otelMeter; + }); + } + + + /** + * Creates a new timer. A timer's duration is the total amount of time it is set to run, while its rate is the + * frequency at which it performs its task or counts its ticks. + */ + @Override + public PinotTimer newTimer(PinotMetricName pinotMetricname, TimeUnit durationUnit, TimeUnit rateUnit) { + OpenTelemetryMetricName metricName = new OpenTelemetryMetricName(pinotMetricname); + String simpleMetricName = metricName.getSimplifiedMetricName(); + Attributes attributes = OpenTelemetryUtil.toOpenTelemetryAttributes(metricName.getAttributes()); + + return PINOT_TIMER_MAP.computeIfAbsent(metricName, n -> { + OpenTelemetryTimer otelTimer = new OpenTelemetryTimer( + SharedOtelMetricRegistry.getOrCreateDoubleHistogram(simpleMetricName), attributes, rateUnit); + if (!LISTENERS.isEmpty()) { + for (OpenTelemetryMetricsRegistryListener listener : LISTENERS) { + listener.onMetricAdded(metricName, otelTimer); + } + } + return otelTimer; + }); + } + + @Override + public PinotHistogram newHistogram(PinotMetricName pinotMetricname, boolean biased) { + OpenTelemetryMetricName metricName = new OpenTelemetryMetricName(pinotMetricname); + String simpleMetricName = metricName.getSimplifiedMetricName(); + Attributes attributes = OpenTelemetryUtil.toOpenTelemetryAttributes(metricName.getAttributes()); + + return PINOT_HISTOGRAM_MAP.computeIfAbsent(metricName, n -> { + OpenTelemetryHistogram otelHistogram = new OpenTelemetryHistogram( + SharedOtelMetricRegistry.getOrCreateDoubleHistogram(simpleMetricName), attributes); + if (!LISTENERS.isEmpty()) { + for (OpenTelemetryMetricsRegistryListener listener : LISTENERS) { + listener.onMetricAdded(metricName, otelHistogram); + } + } + return otelHistogram; + }); + } + + @Override + public Map allMetrics() { + Map allMetrics = new HashMap<>(); + allMetrics.putAll(PINOT_COUNTER_MAP); + allMetrics.putAll(PINOT_METER_MAP); + allMetrics.putAll(PINOT_LONG_GAUGE_MAP); + allMetrics.putAll(PINOT_DOUBLE_GAUGE_MAP); + allMetrics.putAll(PINOT_HISTOGRAM_MAP); + allMetrics.putAll(PINOT_TIMER_MAP); + return allMetrics; + } + + @Override + public void addListener(PinotMetricsRegistryListener listener) { + if (listener != null) { + LISTENERS.add((OpenTelemetryMetricsRegistryListener) listener.getMetricsRegistryListener()); + } + } + + @Override + public Object getMetricsRegistry() { + // This method is here because some metric libraries (e.g. Dropwizard and Yammer) have built-in metrics registry + // and its reporter needs to access the metrics registry instance to get all the metrics. However, OpenTelemetry + // does not have a concept of metrics registry but a "MetricsProvider" (more precisely, a MeterProvider in + // OpenTelemetry terminology) instead. In addition, OpenTelemetry HTTP reporter/exporter accesses the global static + // MetricsProvider, so we can return anything here. + return ImmutableMap.of( + "counters", PINOT_COUNTER_MAP, + "meters", PINOT_METER_MAP, + "longGauges", PINOT_LONG_GAUGE_MAP, + "doubleGauges", PINOT_DOUBLE_GAUGE_MAP, + "histograms", PINOT_HISTOGRAM_MAP, + "timers", PINOT_TIMER_MAP + ); + } + + @Override + public void shutdown() { + PINOT_COUNTER_MAP.clear(); + PINOT_METER_MAP.clear(); + PINOT_LONG_GAUGE_MAP.clear(); + PINOT_DOUBLE_GAUGE_MAP.clear(); + PINOT_HISTOGRAM_MAP.clear(); + PINOT_TIMER_MAP.clear(); + + SharedOtelMetricRegistry.clear(); + + LISTENERS.clear(); + } + + + /** + * It's OpenTelemetry's rule that you cannot create two different metric instruments with the same name. However, + * Two different Pinot metrics may have the same simplified metric name but different attributes. So we need to + * use a shared registry to ensure that we only create one metric instrument for each simplified metric name. + */ + static class SharedOtelMetricRegistry { + private static final Map LONG_COUNTER_MAP = new ConcurrentHashMap<>(); + private static final Map LONG_UP_DOWN_COUNTER_MAP = new ConcurrentHashMap<>(); + private static final Map LONG_GAUGE_MAP = new ConcurrentHashMap<>(); + private static final Map DOUBLE_GAUGE_MAP = new ConcurrentHashMap<>(); + private static final Map DOUBLE_HISTOGRAM_MAP = new ConcurrentHashMap<>(); + + public static LongCounter getOrCreateLongCounter(String simpleMetricName) { + return LONG_COUNTER_MAP.computeIfAbsent(simpleMetricName, + avoid -> _otelMeterProvider + .counterBuilder(simpleMetricName) + .build() + ); + } + + // There is no such thing that two PinotTimer with the same name but different time units. + // So it's safe to just use the simplified metric name as the key here. + public static LongUpDownCounter getOrCreateLongUpDownCounter(String simpleMetricName, TimeUnit unit) { + return LONG_UP_DOWN_COUNTER_MAP.computeIfAbsent(simpleMetricName, + avoid -> _otelMeterProvider + .upDownCounterBuilder(simpleMetricName) + .setUnit(unit.name()) + .build() + ); + } + + public static LongGauge getOrCreateLongGauge(String simpleMetricName) { + return LONG_GAUGE_MAP.computeIfAbsent(simpleMetricName, + avoid -> _otelMeterProvider + .gaugeBuilder(simpleMetricName) + .ofLongs() + .build() + ); + } + + public static DoubleGauge getOrCreateDoubleGauge(String simpleMetricName) { + return DOUBLE_GAUGE_MAP.computeIfAbsent(simpleMetricName, + avoid -> _otelMeterProvider + .gaugeBuilder(simpleMetricName) + .build() + ); + } + + public static DoubleHistogram getOrCreateDoubleHistogram(String simpleMetricName) { + return DOUBLE_HISTOGRAM_MAP.computeIfAbsent(simpleMetricName, + avoid -> _otelMeterProvider + .histogramBuilder(simpleMetricName) + .build() + ); + } + + public static void clear() { + LONG_COUNTER_MAP.clear(); + LONG_UP_DOWN_COUNTER_MAP.clear(); + LONG_GAUGE_MAP.clear(); + DOUBLE_GAUGE_MAP.clear(); + DOUBLE_HISTOGRAM_MAP.clear(); + } + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistryListener.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistryListener.java new file mode 100644 index 000000000000..5ea0100ee493 --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistryListener.java @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import org.apache.pinot.spi.metrics.PinotMetric; + +/** + * OpenTelemetryMetricsRegistryListener is the listener interface for listening to the metric addition and removal. + * It is used to allow Pinot Core to register callbacks when a metric is added or removed. + */ +public interface OpenTelemetryMetricsRegistryListener { + + void onMetricAdded(OpenTelemetryMetricName name, PinotMetric metric); + + void onMetricRemoved(OpenTelemetryMetricName name); +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryTimer.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryTimer.java new file mode 100644 index 000000000000..a0f1470578fb --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryTimer.java @@ -0,0 +1,105 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import java.util.concurrent.TimeUnit; +import org.apache.pinot.spi.metrics.PinotTimer; + +/** + * OpenTelemetryTimer is the implementation of {@link PinotTimer} for OpenTelemetry. + * OpenTelemetry does not have a direct Timer metric, so we use a Histogram to record the durations. + */ +public class OpenTelemetryTimer implements PinotTimer { + private final DoubleHistogram _histogram; + private final Attributes _attributes; + private final TimeUnit _rateUnit; + + public OpenTelemetryTimer(DoubleHistogram histogram, Attributes attributes, TimeUnit rateUnit) { + _histogram = histogram; + _attributes = attributes; + _rateUnit = rateUnit; + } + + @Override + public void update(long duration, TimeUnit unit) { + _histogram.record(_rateUnit.convert(duration, unit), _attributes); + } + + @Override + public Object getTimer() { + return _histogram; + } + + @Override + public Object getMetered() { + return _histogram; + } + + @Override + public TimeUnit rateUnit() { + return _rateUnit; + } + + @Override + public String eventType() { + return _histogram.getClass().getName(); + } + + @Override + public long count() { + // Not applicable. OTel does not support retrieve count directly from the histogram. This method is in the + // interface, but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("count() is not supported in OpenTelemetryTimer"); + } + + @Override + public double fifteenMinuteRate() { + // Not applicable. OTel does not support retrieve rate directly from the histogram. This method is in the + // interface, but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("fifteenMinuteRate() is not supported in OpenTelemetryTimer"); + } + + @Override + public double fiveMinuteRate() { + // Not applicable. OTel does not support retrieve rate directly from the histogram. This method is in the + // interface, but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("fiveMinuteRate() is not supported in OpenTelemetryTimer"); + } + + @Override + public double meanRate() { + // Not applicable. OTel does not support retrieve rate directly from the histogram. This method is in the + // interface, but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("meanRate() is not supported in OpenTelemetryTimer"); + } + + @Override + public double oneMinuteRate() { + // Not applicable. OTel does not support retrieve rate directly from the histogram. This method is in the + // interface, but it's actually only called in tests, so we are good here. + throw new UnsupportedOperationException("oneMinuteRate() is not supported in OpenTelemetryTimer"); + } + + @Override + public Object getMetric() { + return _histogram; + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryUtil.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryUtil.java new file mode 100644 index 000000000000..c8fedd0f758a --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/main/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryUtil.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import java.util.Map; + + +/** + * OpenTelemetryUtil provides parsing method to parse pinot metric name (which has dimensions/attributes being + * concatenated in the metric name) into the Otel metric name and dimensions/attributes. + */ +public class OpenTelemetryUtil { + + private OpenTelemetryUtil() { + // Utility class, no instantiation + } + + public static Attributes toOpenTelemetryAttributes(Map attributes) { + AttributesBuilder attributesBuilder = Attributes.builder(); + if (attributes != null) { + for (Map.Entry entry : attributes.entrySet()) { + // OpenTelemetry does not allow null attribute values, in case of null value, replace it with "-". This should + // be rare and only happen when the attribute value is not set properly, but we should still handle it + // gracefully here, otherwise metric emission will fail. + attributesBuilder.put(entry.getKey(), entry.getValue() == null ? "-" : entry.getValue()); + } + } + return attributesBuilder.build(); + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/test/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistryGaugeTest.java b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/test/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistryGaugeTest.java new file mode 100644 index 000000000000..cc2556347b2d --- /dev/null +++ b/pinot-plugins/pinot-metrics/pinot-open-telemetry/src/test/java/org/apache/pinot/plugin/metrics/opentelemetry/OpenTelemetryMetricsRegistryGaugeTest.java @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.plugin.metrics.opentelemetry; + +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; +import org.apache.pinot.spi.metrics.PinotGauge; +import org.testng.Assert; +import org.testng.annotations.Test; + + +public class OpenTelemetryMetricsRegistryGaugeTest { + + @Test + public void testNewGaugeGenerics() { + OpenTelemetryMetricsFactory factory = new OpenTelemetryMetricsFactory(); + OpenTelemetryMetricsRegistry registry = new OpenTelemetryMetricsRegistry(); + PinotMetricsFactory.SimpleMetricName longGaugeName = new PinotMetricsFactory.SimpleMetricName("testLongGauge"); + PinotGauge pinotLongGauge = registry.newGauge(longGaugeName, + factory.makePinotGauge(longGaugeName, (v) -> 1L)); + PinotMetricsFactory.SimpleMetricName doubleGaugeName = new PinotMetricsFactory.SimpleMetricName("testDoubleGauge"); + PinotGauge pinotDoubleGauge = registry.newGauge(doubleGaugeName, + factory.makePinotGauge(doubleGaugeName, (v) -> 1.0)); + Assert.assertEquals(pinotLongGauge.value(), Long.valueOf(1L)); + Assert.assertEquals(pinotDoubleGauge.value(), Double.valueOf(1.0)); + } +} diff --git a/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerJmxReporter.java b/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerJmxReporter.java index a7f055d9989f..aadf4ff1c6fd 100644 --- a/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerJmxReporter.java +++ b/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerJmxReporter.java @@ -20,11 +20,13 @@ import com.yammer.metrics.core.MetricsRegistry; import com.yammer.metrics.reporting.JmxReporter; -import org.apache.pinot.spi.metrics.PinotJmxReporter; +import org.apache.pinot.spi.metrics.PinotMetricReporter; import org.apache.pinot.spi.metrics.PinotMetricsRegistry; - -public class YammerJmxReporter implements PinotJmxReporter { +/** + * YammerJmxReporter is a metric reporter that exposes metrics to JMX using Yammer's JmxReporter. + */ +public class YammerJmxReporter implements PinotMetricReporter { private final JmxReporter _jmxReporter; public YammerJmxReporter(PinotMetricsRegistry metricsRegistry) { diff --git a/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricName.java b/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricName.java index 0eab2370f218..473c2c08c764 100644 --- a/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricName.java +++ b/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricName.java @@ -19,6 +19,7 @@ package org.apache.pinot.plugin.metrics.yammer; import com.yammer.metrics.core.MetricName; +import java.util.Map; import org.apache.pinot.spi.metrics.PinotMetricName; @@ -65,4 +66,14 @@ public int hashCode() { public String toString() { return _metricName.toString(); } + + @Override + public String getSimplifiedMetricName() { + return _metricName.getName(); + } + + @Override + public Map getAttributes() { + return Map.of(); + } } diff --git a/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricsFactory.java b/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricsFactory.java index f2f0e0788f4c..d411fdedfc0d 100644 --- a/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricsFactory.java +++ b/pinot-plugins/pinot-metrics/pinot-yammer/src/main/java/org/apache/pinot/plugin/metrics/yammer/YammerMetricsFactory.java @@ -19,6 +19,7 @@ package org.apache.pinot.plugin.metrics.yammer; import com.google.auto.service.AutoService; +import java.util.Map; import java.util.function.Function; import org.apache.pinot.spi.annotations.metrics.MetricsFactory; import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; @@ -26,6 +27,7 @@ import org.apache.pinot.spi.metrics.PinotGauge; import org.apache.pinot.spi.metrics.PinotJmxReporter; import org.apache.pinot.spi.metrics.PinotMetricName; +import org.apache.pinot.spi.metrics.PinotMetricReporter; import org.apache.pinot.spi.metrics.PinotMetricsRegistry; @@ -47,20 +49,43 @@ public PinotMetricsRegistry getPinotMetricsRegistry() { } @Override + @Deprecated public PinotMetricName makePinotMetricName(Class klass, String name) { - return new YammerMetricName(klass, name); + throw new UnsupportedOperationException("Deprecated, use makePinotMetricName(Class, String, String, Map) instead"); } @Override + public PinotMetricName makePinotMetricName(Class klass, String fullName, String simplifiedName, + Map attributes) { + return new YammerMetricName(klass, fullName); + } + + @Override + @Deprecated public PinotGauge makePinotGauge(Function condition) { return new YammerGauge(condition); } + @Override + @Deprecated + public PinotGauge makePinotGauge(String metricName, Function condition) { + throw new UnsupportedOperationException("Deprecated, use makePinotGauge(PinotMetricName, Function) instead"); + } + + public PinotGauge makePinotGauge(PinotMetricName pinotMetricName, Function condition) { + return new YammerGauge(condition); + } + @Override public PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry) { return new YammerJmxReporter(metricsRegistry); } + @Override + public PinotMetricReporter makePinotMetricReporter(PinotMetricsRegistry metricsRegistry) { + return new YammerJmxReporter(metricsRegistry); + } + @Override public String getMetricsFactoryName() { return "Yammer"; diff --git a/pinot-plugins/pinot-metrics/pom.xml b/pinot-plugins/pinot-metrics/pom.xml index c51714e5d576..ce0346e65878 100644 --- a/pinot-plugins/pinot-metrics/pom.xml +++ b/pinot-plugins/pinot-metrics/pom.xml @@ -37,6 +37,7 @@ pinot-dropwizard pinot-yammer + pinot-open-telemetry pinot-compound-metrics diff --git a/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/BaseTaskExecutor.java b/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/BaseTaskExecutor.java index c61a5b58d94c..1e24d33a7f8f 100644 --- a/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/BaseTaskExecutor.java +++ b/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/BaseTaskExecutor.java @@ -19,6 +19,7 @@ package org.apache.pinot.plugin.minion.tasks; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import java.io.File; import java.net.URI; import java.util.Collections; @@ -27,6 +28,7 @@ import org.apache.pinot.common.metadata.ZKMetadataProvider; import org.apache.pinot.common.metadata.segment.SegmentZKMetadata; import org.apache.pinot.common.metadata.segment.SegmentZKMetadataCustomMapModifier; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.metrics.MinionMeter; import org.apache.pinot.common.metrics.MinionMetrics; import org.apache.pinot.common.utils.TarCompressionUtils; @@ -108,7 +110,8 @@ protected void reportTaskProcessingMetrics(String tableNameWithType, String task private void addTaskMeterMetrics(MinionMeter meter, long unitCount, String tableName, String taskType) { _minionMetrics.addMeteredGlobalValue(meter, unitCount); _minionMetrics.addMeteredTableValue(tableName, meter, unitCount); - _minionMetrics.addMeteredTableValue(tableName, taskType, meter, unitCount); + _minionMetrics.addMeteredTableValue(tableName, taskType, meter, unitCount, + ImmutableMap.of(MetricAttributeConstants.TASK_TYPE, taskType)); } protected File downloadSegmentToLocalAndUntar(String tableNameWithType, String segmentName, String deepstoreURL, diff --git a/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/mergerollup/MergeRollupTaskGenerator.java b/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/mergerollup/MergeRollupTaskGenerator.java index c4f9bd2175e4..f6830a6f264d 100644 --- a/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/mergerollup/MergeRollupTaskGenerator.java +++ b/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/mergerollup/MergeRollupTaskGenerator.java @@ -41,6 +41,7 @@ import org.apache.pinot.common.metadata.segment.SegmentPartitionMetadata; import org.apache.pinot.common.metadata.segment.SegmentZKMetadata; import org.apache.pinot.common.metrics.ControllerMetrics; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.minion.MergeRollupTaskMetadata; import org.apache.pinot.common.utils.LLCSegmentName; import org.apache.pinot.controller.helix.core.minion.generator.BaseTaskGenerator; @@ -828,7 +829,10 @@ private void createOrUpdateDelayMetrics(String tableNameWithType, String mergeLe ? _tableLowestLevelMaxValidBucketEndTimeMs.get(tableNameWithType) : watermarkForTable.get(lowerMergeLevel), bufferTimeMs, bucketTimeMs)); - controllerMetrics.addCallbackGaugeIfNeeded(getMetricNameForTaskDelay(tableNameWithType, mergeLevel), + controllerMetrics.addCallbackGaugeIfNeeded( + getMetricNameForTaskDelay(tableNameWithType, mergeLevel), + MERGE_ROLLUP_TASK_DELAY_IN_NUM_BUCKETS, + getMetricsAttributesForTaskDelay(tableNameWithType, mergeLevel), (() -> getMergeRollupTaskDelayInNumTimeBuckets(watermarkForTable.getOrDefault(k, -1L), lowerMergeLevel == null ? _tableLowestLevelMaxValidBucketEndTimeMs.get(tableNameWithType) : watermarkForTable.get(lowerMergeLevel), bufferTimeMs, bucketTimeMs))); @@ -922,7 +926,10 @@ private void createOrUpdateNumBucketsToProcessMetrics(String tableNameWithType, "Creating the gauge metric for tracking the merge/roll-up number buckets to process for table: {} " + "and mergeLevel: {}.(bufferTimeMs={}, bucketTimeMs={}, numTimeBucketsToProcess={})", tableNameWithType, mergeLevel, bufferTimeMs, bucketTimeMs, finalCount); - controllerMetrics.setOrUpdateGauge(getMetricNameForNumBucketsToProcess(tableNameWithType, mergeLevel), + controllerMetrics.setOrUpdateGauge( + getMetricNameForNumBucketsToProcess(tableNameWithType, mergeLevel), + MERGE_ROLLUP_TASK_NUM_BUCKETS_TO_PROCESS, + getMetricsAttributesForNumBucketsToProcess(tableNameWithType, mergeLevel), () -> _tableNumberBucketsToProcess.get(tableNameWithType).getOrDefault(mergeLevel, finalCount)); } return finalCount; @@ -1050,7 +1057,21 @@ private String getMetricNameForTaskDelay(String tableNameWithType, String mergeL return MERGE_ROLLUP_TASK_DELAY_IN_NUM_BUCKETS + "." + tableNameWithType + "." + mergeLevel; } + private Map getMetricsAttributesForTaskDelay(String tableNameWithType, String mergeLevel) { + Map attributes = new HashMap<>(); + attributes.put(MetricAttributeConstants.TABLE_NAME, tableNameWithType); + attributes.put(MetricAttributeConstants.MERGE_LEVEL, mergeLevel); + return attributes; + } + private String getMetricNameForNumBucketsToProcess(String tableNameWithType, String mergeLevel) { return MERGE_ROLLUP_TASK_NUM_BUCKETS_TO_PROCESS + "." + tableNameWithType + "." + mergeLevel; } + + private Map getMetricsAttributesForNumBucketsToProcess(String tableNameWithType, String mergeLevel) { + Map attributes = new HashMap<>(); + attributes.put(MetricAttributeConstants.TABLE_NAME, tableNameWithType); + attributes.put(MetricAttributeConstants.MERGE_LEVEL, mergeLevel); + return attributes; + } } diff --git a/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/purge/PurgeTaskExecutor.java b/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/purge/PurgeTaskExecutor.java index 2877f1b3ccd2..c462d3595ee1 100644 --- a/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/purge/PurgeTaskExecutor.java +++ b/pinot-plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/src/main/java/org/apache/pinot/plugin/minion/tasks/purge/PurgeTaskExecutor.java @@ -66,7 +66,7 @@ protected SegmentConversionResult convert(PinotTaskConfig pinotTaskConfig, File long purgeTaskStartTimeNs = ThreadResourceUsageProvider.getCurrentThreadCpuTime(); File purgedSegmentFile = segmentPurger.purgeSegment(); long purgeTaskEndTimeNs = ThreadResourceUsageProvider.getCurrentThreadCpuTime(); - _minionMetrics.addTimedTableValue(tableNameWithType, taskType, MinionTimer.TASK_THREAD_CPU_TIME_NS, + _minionMetrics.addTimedTableTaskValue(tableNameWithType, taskType, MinionTimer.TASK_THREAD_CPU_TIME_NS, purgeTaskEndTimeNs - purgeTaskStartTimeNs, TimeUnit.NANOSECONDS); if (purgedSegmentFile == null) { purgedSegmentFile = indexDir; diff --git a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/realtime/impl/json/MutableJsonIndexImpl.java b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/realtime/impl/json/MutableJsonIndexImpl.java index fe6ade159c39..6349eaa39b7d 100644 --- a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/realtime/impl/json/MutableJsonIndexImpl.java +++ b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/realtime/impl/json/MutableJsonIndexImpl.java @@ -20,6 +20,7 @@ import com.google.common.base.Preconditions; import com.google.common.base.Utf8; +import com.google.common.collect.ImmutableMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -35,6 +36,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.Nullable; import org.apache.commons.lang3.tuple.Pair; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.metrics.ServerMeter; import org.apache.pinot.common.metrics.ServerMetrics; import org.apache.pinot.common.request.context.ExpressionContext; @@ -765,7 +767,7 @@ public void close() { try { String tableName = SegmentUtils.getTableNameFromSegmentName(_segmentName); _serverMetrics.addMeteredTableValue(tableName, _columnName, ServerMeter.MUTABLE_JSON_INDEX_MEMORY_USAGE, - _bytesSize); + _bytesSize, ImmutableMap.of(MetricAttributeConstants.COLUMN_NAME, _columnName)); } catch (Exception e) { LOGGER.warn( "Caught exception while updating mutable json index memory usage for segment: {}, column: {}, value: {}", diff --git a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java index 5373db37fa00..57dd18b30007 100644 --- a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java +++ b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.AtomicDouble; import java.io.File; import java.io.IOException; @@ -38,6 +39,7 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; import org.apache.helix.HelixManager; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.metrics.ServerGauge; import org.apache.pinot.common.metrics.ServerMeter; import org.apache.pinot.common.metrics.ServerMetrics; @@ -1001,11 +1003,15 @@ private void updateSnapshotMetrics(int numImmutableSegments, long numPrimaryKeys int numMissedSegments = numTrackedSegments - numImmutableSegments - numConsumingSegments - numUnchangedSegments; if (numMissedSegments > 0) { _serverMetrics.addMeteredTableValue(_tableNameWithType, String.valueOf(_partitionId), - ServerMeter.UPSERT_MISSED_VALID_DOC_ID_SNAPSHOT_COUNT, numMissedSegments); + ServerMeter.UPSERT_MISSED_VALID_DOC_ID_SNAPSHOT_COUNT, numMissedSegments, + ImmutableMap.of(MetricAttributeConstants.PARTITION_ID, String.valueOf(_partitionId)) + ); _logger.warn("Missed taking snapshot for {} immutable segments", numMissedSegments); if (_deleteRecordColumn != null) { _serverMetrics.addMeteredTableValue(_tableNameWithType, String.valueOf(_partitionId), - ServerMeter.UPSERT_MISSED_QUERYABLE_DOC_ID_SNAPSHOT_COUNT, numMissedSegments); + ServerMeter.UPSERT_MISSED_QUERYABLE_DOC_ID_SNAPSHOT_COUNT, numMissedSegments, + ImmutableMap.of(MetricAttributeConstants.PARTITION_ID, String.valueOf(_partitionId)) + ); } } } diff --git a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/JsonIndexTest.java b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/JsonIndexTest.java index b576a4439426..3750a034e47b 100644 --- a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/JsonIndexTest.java +++ b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/JsonIndexTest.java @@ -19,6 +19,7 @@ package org.apache.pinot.segment.local.segment.index; import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import java.io.File; import java.io.IOException; @@ -29,6 +30,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; +import org.apache.pinot.common.metrics.MetricAttributeConstants; import org.apache.pinot.common.metrics.ServerMeter; import org.apache.pinot.common.metrics.ServerMetrics; import org.apache.pinot.segment.local.PinotBuffersAfterMethodCheckRule; @@ -1018,9 +1020,12 @@ public void testMutableJsonIndexSizeLimit() { fail(); } - verify(_serverMetrics, times(1)).addMeteredTableValue(eq("table1"), eq("col"), + verify(_serverMetrics, times(1)).addMeteredTableValue( + eq("table1"), + eq("col"), eq(ServerMeter.MUTABLE_JSON_INDEX_MEMORY_USAGE), - eq(25L)); // bytes(.key) + bytes(.key\u0000value) + bytes(.key\u0000value2) + eq(25L), // bytes(.key) + bytes(.key\u0000value) + bytes(.key\u0000value2) + eq(ImmutableMap.of(MetricAttributeConstants.COLUMN_NAME, "col"))); jsonIndexConfig.setMaxBytesSize(5L); try (MutableJsonIndexImpl mutableIndex = new MutableJsonIndexImpl(jsonIndexConfig, "table2__0__1", "col")) { @@ -1030,9 +1035,12 @@ public void testMutableJsonIndexSizeLimit() { } catch (IOException e) { fail(); } - verify(_serverMetrics, times(1)).addMeteredTableValue(eq("table2"), eq("col"), + verify(_serverMetrics, times(1)).addMeteredTableValue( + eq("table2"), + eq("col"), eq(ServerMeter.MUTABLE_JSON_INDEX_MEMORY_USAGE), - eq(35L)); // bytes(.anotherKey) + bytes(.anotherKey\u0000anotherValue) + eq(35L), // bytes(.anotherKey) + bytes(.anotherKey\u0000anotherValue) + eq(ImmutableMap.of(MetricAttributeConstants.COLUMN_NAME, "col"))); } public static class ConfTest extends AbstractSerdeIndexContract { diff --git a/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java index 516fb720fe1d..788fa10c53d2 100644 --- a/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java +++ b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java @@ -19,6 +19,7 @@ package org.apache.pinot.server.starter; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import io.netty.channel.ChannelHandler; import java.util.HashSet; import java.util.Set; @@ -98,7 +99,8 @@ public ServerInstance(ServerConf serverConf, HelixManager helixManager, AccessCo new ServerMetrics(serverConf.getMetricsPrefix(), metricsRegistry, serverConf.emitTableLevelMetrics(), serverConf.getAllowedTablesForEmittingMetrics()); _serverMetrics.initializeGlobalMeters(); - _serverMetrics.setValueOfGlobalGauge(ServerGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, 1); + _serverMetrics.setValueOfGlobalGauge(ServerGauge.VERSION, PinotVersion.VERSION_METRIC_NAME, 1, + ImmutableMap.of()); ServerMetrics.register(_serverMetrics); if (segmentOperationsThrottler != null) { // Initialize the metrics for the throttler so it picks up the newly registered ServerMetrics object diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/annotations/metrics/PinotMetricsFactory.java b/pinot-spi/src/main/java/org/apache/pinot/spi/annotations/metrics/PinotMetricsFactory.java index ec1dc9d70693..057a1b09021c 100644 --- a/pinot-spi/src/main/java/org/apache/pinot/spi/annotations/metrics/PinotMetricsFactory.java +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/annotations/metrics/PinotMetricsFactory.java @@ -18,12 +18,15 @@ */ package org.apache.pinot.spi.annotations.metrics; +import com.google.common.collect.ImmutableMap; +import java.util.Map; import java.util.function.Function; import org.apache.pinot.spi.env.PinotConfiguration; import org.apache.pinot.spi.metrics.NoopPinotMetricsRegistry; import org.apache.pinot.spi.metrics.PinotGauge; import org.apache.pinot.spi.metrics.PinotJmxReporter; import org.apache.pinot.spi.metrics.PinotMetricName; +import org.apache.pinot.spi.metrics.PinotMetricReporter; import org.apache.pinot.spi.metrics.PinotMetricsRegistry; @@ -44,19 +47,36 @@ public interface PinotMetricsFactory { /** * Makes a {@link PinotMetricName} given the class and the metric name. + * @deprecated use {@link #makePinotMetricName(Class, String, String, Map)} instead. */ + @Deprecated PinotMetricName makePinotMetricName(Class klass, String name); + PinotMetricName makePinotMetricName(Class klass, String fullName, String simplifiedName, + Map attributes); + /** * Makes a {@link PinotGauge} given a function. + * @deprecated use {@link #makePinotGauge(PinotMetricName, Function)} instead. */ - PinotGauge makePinotGauge(Function condition); + @Deprecated + PinotGauge makePinotGauge(Function valueSupplier); + + @Deprecated + PinotGauge makePinotGauge(String metricName, Function valueSupplier); + + PinotGauge makePinotGauge(PinotMetricName pinotMetricName, Function valueSupplier); /** * Makes a {@link PinotJmxReporter} given a {@link PinotMetricsRegistry}. + * + * @deprecated Use {@link #makePinotMetricReporter(PinotMetricsRegistry)} instead. */ + @Deprecated PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry); + PinotMetricReporter makePinotMetricReporter(PinotMetricsRegistry metricsRegistry); + /** * Returns the name of metrics factory. */ @@ -74,8 +94,15 @@ public PinotMetricsRegistry getPinotMetricsRegistry() { } @Override + @Deprecated public PinotMetricName makePinotMetricName(Class klass, String name) { - return () -> "noopMetricName"; + throw new UnsupportedOperationException("Please use makePinotMetricName(Class, String, String, Map) instead"); + } + + @Override + public PinotMetricName makePinotMetricName(Class klass, String fullName, String simplifiedName, + Map attributes) { + return new SimpleMetricName("noopMetricName"); } @Override @@ -83,15 +110,58 @@ public PinotGauge makePinotGauge(Function condition) { return _registry.newGauge(); } + @Deprecated + @Override + public PinotGauge makePinotGauge(String metricName, Function condition) { + return _registry.newGauge(); + } + + @Override + public PinotGauge makePinotGauge(PinotMetricName pinotMetricName, Function condition) { + return _registry.newGauge(); + } + + /** + * @deprecated Use {@link #makePinotMetricReporter(PinotMetricsRegistry)} instead. + */ @Override + @Deprecated public PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry) { return () -> { }; } + public PinotMetricReporter makePinotMetricReporter(PinotMetricsRegistry metricsRegistry) { + return () -> { + }; + } + @Override public String getMetricsFactoryName() { return "noop"; } } + + /** + * Simple implementation of {@link PinotMetricName} with full metric name is the same as simplified metric name and + * has no attributes. + */ + class SimpleMetricName implements PinotMetricName { + private final String _metricName; + public SimpleMetricName(String metricName) { + _metricName = metricName; + } + @Override + public Object getMetricName() { + return _metricName; + } + @Override + public String getSimplifiedMetricName() { + return _metricName; + } + @Override + public Map getAttributes() { + return ImmutableMap.of(); + } + } } diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/JmxReporterMetricsRegistryRegistrationListener.java b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/JmxReporterMetricsRegistryRegistrationListener.java index 738640e28e4e..1312d1ab7302 100644 --- a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/JmxReporterMetricsRegistryRegistrationListener.java +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/JmxReporterMetricsRegistryRegistrationListener.java @@ -24,8 +24,9 @@ /** * Adapter that causes metrics from a metric registry to be published to JMX. - * + * @deprecated use {@link MetricReporterRegistryRegistrationListener} instead. */ +@Deprecated public class JmxReporterMetricsRegistryRegistrationListener implements MetricsRegistryRegistrationListener { private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporterMetricsRegistryRegistrationListener.class); diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/MetricReporterRegistryRegistrationListener.java b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/MetricReporterRegistryRegistrationListener.java new file mode 100644 index 000000000000..be04dfb8740d --- /dev/null +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/MetricReporterRegistryRegistrationListener.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.spi.metrics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Adapter that causes metrics from a metric registry to be published. + */ +public class MetricReporterRegistryRegistrationListener implements MetricsRegistryRegistrationListener { + private static final Logger LOGGER = LoggerFactory.getLogger(MetricReporterRegistryRegistrationListener.class); + + @Override + public void onMetricsRegistryRegistered(PinotMetricsRegistry metricsRegistry) { + LOGGER.info("Registering MetricReporterRegistryRegistrationListener"); + PinotMetricUtils.makePinotMetricReporter(metricsRegistry).start(); + LOGGER.info("Number of metrics in metricsRegistry: {}", metricsRegistry.allMetrics().size()); + } +} diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotJmxReporter.java b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotJmxReporter.java index d112ec81c76b..d1e52ca59f4e 100644 --- a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotJmxReporter.java +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotJmxReporter.java @@ -20,7 +20,9 @@ /** * A reporter which exposes application metric as JMX MBeans in Pinot. + * @deprecated use {@link PinotMetricReporter} instead. */ +@Deprecated public interface PinotJmxReporter { void start(); diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricName.java b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricName.java index d54786004a6b..27309a211bd3 100644 --- a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricName.java +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricName.java @@ -18,6 +18,8 @@ */ package org.apache.pinot.spi.metrics; +import java.util.Map; + /** * Metric Name in Pinot. */ @@ -46,4 +48,8 @@ public interface PinotMetricName { * This could be used to print out the actual metrics name instead of the memory address under this wrapper. */ String toString(); + + String getSimplifiedMetricName(); + + Map getAttributes(); } diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricReporter.java b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricReporter.java new file mode 100644 index 000000000000..328367e63b03 --- /dev/null +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricReporter.java @@ -0,0 +1,22 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.spi.metrics; + +public interface PinotMetricReporter extends PinotJmxReporter { +} diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricUtils.java b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricUtils.java index 90fd53527ed4..a2b8cf5883a3 100644 --- a/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricUtils.java +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/metrics/PinotMetricUtils.java @@ -116,7 +116,7 @@ public static Set> getPinotMetricsFactoryClasses() { private static void initializeMetrics(PinotConfiguration configuration) { synchronized (PinotMetricUtils.class) { List listenerClassNames = configuration.getProperty("metricsRegistryRegistrationListeners", - Arrays.asList(JmxReporterMetricsRegistryRegistrationListener.class.getName())); + Arrays.asList(MetricReporterRegistryRegistrationListener.class.getName())); // Build each listener using their default constructor and add them for (String listenerClassName : listenerClassNames) { @@ -215,12 +215,34 @@ public static synchronized PinotMetricsRegistry getPinotMetricsRegistry(PinotCon return _pinotMetricsFactory.getPinotMetricsRegistry(); } + @Deprecated public static PinotMetricName makePinotMetricName(Class klass, String name) { - return _pinotMetricsFactory.makePinotMetricName(klass, name); + throw new UnsupportedOperationException("Please use makePinotMetricName(Class, String, String, Map) instead"); } - public static PinotGauge makePinotGauge(Function condition) { - return _pinotMetricsFactory.makePinotGauge(condition); + public static PinotMetricName makePinotMetricName(Class klass, String fulName, + String simplifiedName, Map attributes) { + return _pinotMetricsFactory.makePinotMetricName(klass, fulName, simplifiedName, attributes); + } + + /** + * Creates a PinotGauge with the given condition. + * @param valueSupplier The valueSupplier to evaluate the value for the gauge + * @return A PinotGauge instance + * @deprecated Use {@link #makePinotGauge(PinotMetricName, Function)} instead, which allows specifying a metric name. + */ + @Deprecated + public static PinotGauge makePinotGauge(Function valueSupplier) { + throw new UnsupportedOperationException("Please use makePinotGauge(PinotMetricName, Function) instead"); + } + + @Deprecated + public static PinotGauge makePinotGauge(String metricName, Function valueSupplier) { + throw new UnsupportedOperationException("Please use makePinotGauge(PinotMetricName, Function) instead"); + } + + public static PinotGauge makePinotGauge(PinotMetricName pinotMetricName, Function valueSupplier) { + return _pinotMetricsFactory.makePinotGauge(pinotMetricName, valueSupplier); } public static PinotGauge makeGauge(PinotMetricsRegistry registry, PinotMetricName name, PinotGauge gauge) { @@ -241,7 +263,15 @@ public static void removeMetric(PinotMetricsRegistry registry, PinotMetricName n registry.removeMetric(name); } + /** + * @deprecated use {@link #makePinotMetricReporter(PinotMetricsRegistry)} instead. + */ + @Deprecated public static PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry) { return _pinotMetricsFactory.makePinotJmxReporter(metricsRegistry); } + + public static PinotMetricReporter makePinotMetricReporter(PinotMetricsRegistry metricsRegistry) { + return _pinotMetricsFactory.makePinotMetricReporter(metricsRegistry); + } } diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java index 9b5302cea6d2..7bc5d492c79a 100644 --- a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java +++ b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java @@ -49,9 +49,10 @@ private CommonConstants() { public static final String CONFIG_OF_BROKER_EVENT_LISTENER_CLASS_NAME = "factory.className"; public static final String CONFIG_OF_REQUEST_CONTEXT_TRACKED_HEADER_KEYS = "request.context.tracked.header.keys"; public static final String DEFAULT_METRICS_FACTORY_CLASS_NAME = - //"org.apache.pinot.plugin.metrics.compound.CompoundPinotMetricsFactory"; + // "org.apache.pinot.plugin.metrics.compound.CompoundPinotMetricsFactory"; + // "org.apache.pinot.plugin.metrics.opentelemetry.OpenTelemetryMetricsFactory"; "org.apache.pinot.plugin.metrics.yammer.YammerMetricsFactory"; - //"org.apache.pinot.plugin.metrics.dropwizard.DropwizardMetricsFactory"; + // "org.apache.pinot.plugin.metrics.dropwizard.DropwizardMetricsFactory"; public static final String DEFAULT_BROKER_EVENT_LISTENER_CLASS_NAME = "org.apache.pinot.spi.eventlistener.query.NoOpBrokerQueryEventListener"; diff --git a/pinot-tools/pom.xml b/pinot-tools/pom.xml index fdccae671561..1df5a433db3f 100644 --- a/pinot-tools/pom.xml +++ b/pinot-tools/pom.xml @@ -119,11 +119,14 @@ org.apache.pinot pinot-yammer - org.apache.pinot pinot-dropwizard + + org.apache.pinot + pinot-open-telemetry + org.apache.pinot pinot-compound-metrics diff --git a/pom.xml b/pom.xml index 0c68f501560b..eb00d4be56f8 100644 --- a/pom.xml +++ b/pom.xml @@ -162,6 +162,7 @@ 0.10.2 0.26.0 2.2.0 + 1.45.0 4.2.36 1.1.10.8 @@ -792,6 +793,11 @@ pinot-dropwizard ${project.version} + + org.apache.pinot + pinot-open-telemetry + ${project.version} + org.apache.pinot pinot-compound-metrics @@ -1502,6 +1508,14 @@ + + io.opentelemetry + opentelemetry-bom + ${opentelemetry.version} + pom + import + + com.yammer.metrics metrics-core