| /* |
| * 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.geode.internal.statistics; |
| |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.DoubleSupplier; |
| import java.util.function.IntSupplier; |
| import java.util.function.LongSupplier; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.StatisticDescriptor; |
| import org.apache.geode.Statistics; |
| import org.apache.geode.StatisticsType; |
| import org.apache.geode.internal.util.concurrent.CopyOnWriteHashMap; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| |
| /** |
| * An object that maintains the values of various application-defined statistics. The statistics |
| * themselves are described by an instance of {@link StatisticsType}. |
| * |
| * <p> |
| * For optimal statistic access, each statistic may be referred to by its {@link #nameToId id} in |
| * the statistics object. |
| * |
| * @since GemFire 3.0 |
| */ |
| public abstract class StatisticsImpl implements SuppliableStatistics { |
| |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** The type of this statistics instance */ |
| protected final ValidatingStatisticsType type; |
| |
| /** The display name of this statistics instance */ |
| private final String textId; |
| |
| /** Numeric information display with these statistics */ |
| private final long numericId; |
| |
| /** Non-zero if stats values come from operating system system calls */ |
| private final int osStatFlags; |
| |
| /** Uniquely identifies this instance */ |
| private final long uniqueId; |
| |
| /** The StatisticsFactory that created this instance */ |
| private final StatisticsManager statisticsManager; |
| |
| /** |
| * Suppliers of int sample values to be sampled every sample-interval |
| */ |
| private final CopyOnWriteHashMap<Integer, IntSupplier> intSuppliers = new CopyOnWriteHashMap<>(); |
| /** |
| * Suppliers of long sample values to be sampled every sample-interval |
| */ |
| private final CopyOnWriteHashMap<Integer, LongSupplier> longSuppliers = |
| new CopyOnWriteHashMap<>(); |
| /** |
| * Suppliers of double sample values to be sampled every sample-interval |
| */ |
| private final CopyOnWriteHashMap<Integer, DoubleSupplier> doubleSuppliers = |
| new CopyOnWriteHashMap<>(); |
| |
| /** |
| * Suppliers that have previously failed. Tracked to avoid logging many messages about a failing |
| * supplier |
| */ |
| private final Set<Object> flakySuppliers = new HashSet<>(); |
| |
| private final StatisticsLogger statisticsLogger; |
| |
| /** Are these statistics closed? */ |
| private volatile boolean closed; |
| |
| /** |
| * factory method to create a class that implements Statistics |
| */ |
| static Statistics createAtomicNoOS(StatisticsType type, String textId, long numericId, |
| long uniqueId, StatisticsManager statisticsManager) { |
| return new StripedStatisticsImpl(type, textId, numericId, uniqueId, statisticsManager); |
| } |
| |
| /** |
| * Creates a new statistics instance of the given type and unique id |
| * |
| * @param type A description of the statistics |
| * @param textId Text that helps identifies this instance |
| * @param numericId A number that helps identify this instance |
| * @param uniqueId A number that uniquely identifies this instance |
| * @param osStatFlags Non-zero if stats require system calls to collect them; for internal use |
| * only |
| * @param statisticsManager The StatisticsManager responsible for creating this instance |
| */ |
| StatisticsImpl(StatisticsType type, String textId, long numericId, long uniqueId, |
| int osStatFlags, StatisticsManager statisticsManager) { |
| this(type, textId, numericId, uniqueId, osStatFlags, statisticsManager, |
| (message, textId1, statId, throwable) -> logger.warn(message, textId1, statId, throwable)); |
| } |
| |
| /** |
| * Creates a new statistics instance of the given type and unique id |
| * |
| * @param type A description of the statistics |
| * @param textId Text that helps identifies this instance |
| * @param numericId A number that helps identify this instance |
| * @param uniqueId A number that uniquely identifies this instance |
| * @param osStatFlags Non-zero if stats require system calls to collect them; for internal use |
| * only |
| * @param statisticsManager The StatisticsManager responsible for creating this instance |
| * @param statisticsLogger The StatisticsLogger to log warning about flaky suppliers |
| */ |
| StatisticsImpl(StatisticsType type, String textId, long numericId, long uniqueId, |
| int osStatFlags, StatisticsManager statisticsManager, StatisticsLogger statisticsLogger) { |
| this.type = (ValidatingStatisticsType) type; |
| this.textId = StringUtils.isEmpty(textId) ? statisticsManager.getName() : textId; |
| this.numericId = numericId == 0 ? statisticsManager.getPid() : numericId; |
| this.uniqueId = uniqueId; |
| this.osStatFlags = osStatFlags; |
| this.statisticsManager = statisticsManager; |
| this.statisticsLogger = statisticsLogger; |
| closed = false; |
| } |
| |
| @Override |
| public int nameToId(String name) { |
| return type.nameToId(name); |
| } |
| |
| @Override |
| public StatisticDescriptor nameToDescriptor(String name) { |
| return type.nameToDescriptor(name); |
| } |
| |
| @Override |
| public void close() { |
| if (statisticsManager != null) { |
| statisticsManager.destroyStatistics(this); |
| } |
| closed = true; |
| } |
| |
| @Override |
| public boolean isClosed() { |
| return closed; |
| } |
| |
| @Override |
| public StatisticsType getType() { |
| return type; |
| } |
| |
| @Override |
| public String getTextId() { |
| return textId; |
| } |
| |
| @Override |
| public long getNumericId() { |
| return numericId; |
| } |
| |
| /** |
| * Gets the unique id for this resource |
| */ |
| @Override |
| public long getUniqueId() { |
| return uniqueId; |
| } |
| |
| @Override |
| public void setInt(String name, int value) { |
| setLong(name, value); |
| } |
| |
| @Override |
| public void setInt(StatisticDescriptor descriptor, int value) { |
| setLong(descriptor, value); |
| } |
| |
| @Override |
| public void setInt(int id, int value) { |
| setLong(id, value); |
| } |
| |
| @Override |
| public void setLong(String name, long value) { |
| setLong(nameToDescriptor(name), value); |
| } |
| |
| @Override |
| public void setLong(StatisticDescriptor descriptor, long value) { |
| setLong(getLongId(descriptor), value); |
| } |
| |
| @Override |
| public void setLong(int id, long value) { |
| if (isOpen()) { |
| _setLong(id, value); |
| } |
| } |
| |
| @Override |
| public void setDouble(String name, double value) { |
| setDouble(nameToDescriptor(name), value); |
| } |
| |
| @Override |
| public void setDouble(StatisticDescriptor descriptor, double value) { |
| setDouble(getDoubleId(descriptor), value); |
| } |
| |
| @Override |
| public void setDouble(int id, double value) { |
| if (isOpen()) { |
| _setDouble(id, value); |
| } |
| } |
| |
| @Override |
| public int getInt(String name) { |
| return (int) getLong(name); |
| } |
| |
| @Override |
| public int getInt(StatisticDescriptor descriptor) { |
| return (int) getLong(descriptor); |
| } |
| |
| @Override |
| public int getInt(int id) { |
| return (int) getLong(id); |
| } |
| |
| @Override |
| public long getLong(String name) { |
| return getLong(nameToDescriptor(name)); |
| } |
| |
| @Override |
| public long getLong(StatisticDescriptor descriptor) { |
| return getLong(getLongId(descriptor)); |
| } |
| |
| @Override |
| public long getLong(int id) { |
| if (isOpen()) { |
| if (!type.isValidLongId(id)) { |
| throw new IllegalArgumentException("Id, " + id + ", is not a long statistic."); |
| } |
| return _getLong(id); |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public double getDouble(String name) { |
| return getDouble(nameToDescriptor(name)); |
| } |
| |
| @Override |
| public double getDouble(StatisticDescriptor descriptor) { |
| return getDouble(getDoubleId(descriptor)); |
| } |
| |
| @Override |
| public double getDouble(int id) { |
| if (isOpen()) { |
| return _getDouble(id); |
| } else { |
| return 0.0; |
| } |
| } |
| |
| @Override |
| public Number get(StatisticDescriptor descriptor) { |
| if (isOpen()) { |
| return _get((StatisticDescriptorImpl) descriptor); |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public Number get(String name) { |
| return get(nameToDescriptor(name)); |
| } |
| |
| @Override |
| public long getRawBits(StatisticDescriptor descriptor) { |
| if (isOpen()) { |
| return _getRawBits((StatisticDescriptorImpl) descriptor); |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public long getRawBits(String name) { |
| return getRawBits(nameToDescriptor(name)); |
| } |
| |
| @Override |
| public void incInt(String name, int delta) { |
| incLong(name, delta); |
| } |
| |
| @Override |
| public void incInt(StatisticDescriptor descriptor, int delta) { |
| incLong(descriptor, delta); |
| } |
| |
| @Override |
| public void incInt(int id, int delta) { |
| incLong(id, delta); |
| } |
| |
| @Override |
| public void incLong(String name, long delta) { |
| incLong(nameToDescriptor(name), delta); |
| } |
| |
| @Override |
| public void incLong(StatisticDescriptor descriptor, long delta) { |
| incLong(getLongId(descriptor), delta); |
| } |
| |
| @Override |
| public void incLong(int id, long delta) { |
| if (isOpen()) { |
| _incLong(id, delta); |
| } |
| } |
| |
| @Override |
| public void incDouble(String name, double delta) { |
| incDouble(nameToDescriptor(name), delta); |
| } |
| |
| @Override |
| public void incDouble(StatisticDescriptor descriptor, double delta) { |
| incDouble(getDoubleId(descriptor), delta); |
| } |
| |
| @Override |
| public void incDouble(int id, double delta) { |
| if (isOpen()) { |
| _incDouble(id, delta); |
| } |
| } |
| |
| @Override |
| public IntSupplier setIntSupplier(final int id, final IntSupplier supplier) { |
| // setIntSupplier is deprecated but it is too much of a pain to wrap the IntSupplier |
| // in a LongSupplier. So the implementation continues to store IntSupplier instances |
| // but all the checks and actions are long based instead of int based. |
| if (!type.isValidLongId(id)) { |
| throw new IllegalArgumentException("Id " + id + " is not in range for stat" + type); |
| } |
| return intSuppliers.put(id, supplier); |
| } |
| |
| @Override |
| public IntSupplier setIntSupplier(final String name, final IntSupplier supplier) { |
| return setIntSupplier(nameToId(name), supplier); |
| } |
| |
| @Override |
| public IntSupplier setIntSupplier(final StatisticDescriptor descriptor, |
| final IntSupplier supplier) { |
| return setIntSupplier(getLongId(descriptor), supplier); |
| } |
| |
| @Override |
| public LongSupplier setLongSupplier(final int id, final LongSupplier supplier) { |
| if (!type.isValidLongId(id)) { |
| throw new IllegalArgumentException("Id " + id + " is not in range for stat" + type); |
| } |
| return longSuppliers.put(id, supplier); |
| } |
| |
| @Override |
| public LongSupplier setLongSupplier(final String name, final LongSupplier supplier) { |
| return setLongSupplier(nameToId(name), supplier); |
| } |
| |
| @Override |
| public LongSupplier setLongSupplier(final StatisticDescriptor descriptor, |
| final LongSupplier supplier) { |
| return setLongSupplier(getLongId(descriptor), supplier); |
| } |
| |
| @Override |
| public DoubleSupplier setDoubleSupplier(final int id, final DoubleSupplier supplier) { |
| if (!type.isValidDoubleId(id)) { |
| throw new IllegalArgumentException("Id " + id + " is not in range for stat" + type); |
| } |
| return doubleSuppliers.put(id, supplier); |
| } |
| |
| @Override |
| public DoubleSupplier setDoubleSupplier(final String name, final DoubleSupplier supplier) { |
| return setDoubleSupplier(nameToId(name), supplier); |
| } |
| |
| @Override |
| public DoubleSupplier setDoubleSupplier(final StatisticDescriptor descriptor, |
| final DoubleSupplier supplier) { |
| return setDoubleSupplier(getDoubleId(descriptor), supplier); |
| } |
| |
| @Override |
| public int hashCode() { |
| return (int) uniqueId; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == null) { |
| return false; |
| } |
| if (!(obj instanceof StatisticsImpl)) { |
| return false; |
| } |
| StatisticsImpl other = (StatisticsImpl) obj; |
| return uniqueId == other.getUniqueId(); |
| } |
| |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder(getClass().getName()); |
| sb.append("@").append(System.identityHashCode(this)).append("{"); |
| sb.append("uniqueId=").append(uniqueId); |
| sb.append(", numericId=").append(numericId); |
| sb.append(", textId=").append(textId); |
| sb.append(", type=").append(type.getName()); |
| sb.append(", closed=").append(closed); |
| sb.append("}"); |
| return sb.toString(); |
| } |
| |
| @Override |
| public abstract boolean isAtomic(); |
| |
| /** |
| * Sets the value of a statistic of type {@code long} at the given id, but performs no |
| * type checking. |
| */ |
| protected abstract void _setLong(int id, long value); |
| |
| /** |
| * Sets the value of a statistic of type {@code double} at the given id, but performs no |
| * type checking. |
| */ |
| protected abstract void _setDouble(int id, double value); |
| |
| /** |
| * Returns the value of the statistic of type {@code long} at the given id, but performs |
| * no type checking. |
| */ |
| protected abstract long _getLong(int id); |
| |
| /** |
| * Returns the value of the statistic of type {@code double} at the given id, but |
| * performs no type checking. |
| */ |
| protected abstract double _getDouble(int id); |
| |
| /** |
| * Increments the value of the statistic of type {@code long} at the given id by a given |
| * amount, but performs no type checking. |
| */ |
| protected abstract void _incLong(int id, long delta); |
| |
| /** |
| * Increments the value of the statistic of type {@code double} at the given id by a |
| * given amount, but performs no type checking. |
| */ |
| protected abstract void _incDouble(int id, double delta); |
| |
| /** |
| * For internal use only. Tells the implementation to prepare the data in this instance for |
| * sampling. |
| * |
| * @since GemFire 5.1 |
| */ |
| void prepareForSample() { |
| // nothing needed in this impl. |
| } |
| |
| @Override |
| public int updateSuppliedValues() { |
| int errors = 0; |
| for (Map.Entry<Integer, IntSupplier> entry : intSuppliers.entrySet()) { |
| try { |
| _setLong(entry.getKey(), entry.getValue().getAsInt()); |
| } catch (Throwable t) { |
| logSupplierError(t, entry.getKey(), entry.getValue()); |
| errors++; |
| } |
| } |
| for (Map.Entry<Integer, LongSupplier> entry : longSuppliers.entrySet()) { |
| try { |
| _setLong(entry.getKey(), entry.getValue().getAsLong()); |
| } catch (Throwable t) { |
| logSupplierError(t, entry.getKey(), entry.getValue()); |
| errors++; |
| } |
| } |
| for (Map.Entry<Integer, DoubleSupplier> entry : doubleSuppliers.entrySet()) { |
| try { |
| _setDouble(entry.getKey(), entry.getValue().getAsDouble()); |
| } catch (Throwable t) { |
| logSupplierError(t, entry.getKey(), entry.getValue()); |
| errors++; |
| } |
| } |
| |
| return errors; |
| } |
| |
| /** |
| * @return the number of statistics that are measured using supplier callbacks |
| */ |
| int getSupplierCount() { |
| return intSuppliers.size() + doubleSuppliers.size() + longSuppliers.size(); |
| } |
| |
| boolean usesSystemCalls() { |
| return osStatFlags != 0; |
| } |
| |
| int getOsStatFlags() { |
| return osStatFlags; |
| } |
| |
| private void logSupplierError(final Throwable throwable, int statId, Object supplier) { |
| if (flakySuppliers.add(supplier)) { |
| statisticsLogger.logWarning("Error invoking supplier for stat {}, id {}", getTextId(), statId, |
| throwable); |
| } |
| } |
| |
| private boolean isOpen() { |
| return !closed; |
| } |
| |
| /** |
| * Returns the value of the specified statistic descriptor. |
| */ |
| private Number _get(StatisticDescriptorImpl descriptor) { |
| switch (descriptor.getTypeCode()) { |
| case StatisticDescriptorImpl.LONG: |
| return _getLong(descriptor.getId()); |
| case StatisticDescriptorImpl.DOUBLE: |
| return _getDouble(descriptor.getId()); |
| default: |
| throw new RuntimeException( |
| String.format("unexpected stat descriptor type code: %s", |
| descriptor.getTypeCode())); |
| } |
| } |
| |
| /** |
| * Returns the bits that represent the raw value of the specified statistic descriptor. |
| */ |
| private long _getRawBits(StatisticDescriptorImpl descriptor) { |
| switch (descriptor.getTypeCode()) { |
| case StatisticDescriptorImpl.LONG: |
| return _getLong(descriptor.getId()); |
| case StatisticDescriptorImpl.DOUBLE: |
| return Double.doubleToRawLongBits(_getDouble(descriptor.getId())); |
| default: |
| throw new RuntimeException( |
| String.format("unexpected stat descriptor type code: %s", |
| descriptor.getTypeCode())); |
| } |
| } |
| |
| private static int getLongId(StatisticDescriptor descriptor) { |
| return ((StatisticDescriptorImpl) descriptor).checkLong(); |
| } |
| |
| private static int getDoubleId(StatisticDescriptor descriptor) { |
| return ((StatisticDescriptorImpl) descriptor).checkDouble(); |
| } |
| |
| interface StatisticsLogger { |
| void logWarning(String message, String textId, int statId, Throwable throwable); |
| } |
| } |