Metrics.java
/*
* Copyright (c) 2001-2024, Jean Tessier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Jean Tessier nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jeantessier.metrics;
import java.util.*;
import java.util.stream.*;
import org.apache.logging.log4j.*;
public class Metrics {
private static final Measurement NULL_MEASUREMENT = new NullMeasurement();
private final Metrics parent;
private final String name;
private final Map<String, Measurement> measurements = new TreeMap<>();
private final Map<String, Metrics> submetrics = new TreeMap<>();
public Metrics(String name) {
this(null, name);
}
/**
* @param parent The context for this metrics (e.g., methods's class, class'
* package). You may pass <code>null</code> to create
* top-level metrics.
* @param name The name of the element being measured
* (e.g., class name, method name).
*/
public Metrics(Metrics parent, String name) {
this.parent = parent;
this.name = name;
if (parent == null) {
LogManager.getLogger(getClass()).debug("Created top-level metrics \"{}\"", name);
} else {
LogManager.getLogger(getClass()).debug("Created metrics \"{}\" under \"{}\"", name, parent.getName());
}
}
public Metrics getParent() {
return parent;
}
/**
* @return The name of the element being measured
* (e.g., class name, method name).
*/
public String getName() {
return name;
}
Metrics track(Measurement measurement) {
return track(measurement.getShortName(), measurement);
}
Metrics track(String name, Measurement measurement) {
measurements.put(name, measurement);
return this;
}
public Metrics addToMeasurement(BasicMeasurements name) {
return addToMeasurement(name.getAbbreviation());
}
public Metrics addToMeasurement(String name) {
return addToMeasurement(name, 1);
}
public Metrics addToMeasurement(BasicMeasurements name, int delta) {
return addToMeasurement(name.getAbbreviation(), delta);
}
public Metrics addToMeasurement(String name, int delta) {
getMeasurement(name).add(delta);
return this;
}
public Metrics addToMeasurement(BasicMeasurements name, long delta) {
return addToMeasurement(name.getAbbreviation(), delta);
}
public Metrics addToMeasurement(String name, long delta) {
getMeasurement(name).add(delta);
return this;
}
public Metrics addToMeasurement(BasicMeasurements name, float delta) {
return addToMeasurement(name.getAbbreviation(), delta);
}
public Metrics addToMeasurement(String name, float delta) {
getMeasurement(name).add(delta);
return this;
}
public Metrics addToMeasurement(BasicMeasurements name, double delta) {
return addToMeasurement(name.getAbbreviation(), delta);
}
public Metrics addToMeasurement(String name, double delta) {
getMeasurement(name).add(delta);
return this;
}
public Metrics addToMeasurement(BasicMeasurements name, Object delta) {
return addToMeasurement(name.getAbbreviation(), delta);
}
public Metrics addToMeasurement(String name, Object delta) {
getMeasurement(name).add(delta);
return this;
}
public Measurement getMeasurement(BasicMeasurements name) {
return getMeasurement(name.getAbbreviation());
}
public Measurement getMeasurement(String name) {
return measurements.getOrDefault(name, NULL_MEASUREMENT);
}
public boolean hasMeasurement(String name) {
return measurements.containsKey(name);
}
public Collection<String> getMeasurementNames() {
return Collections.unmodifiableCollection(measurements.keySet());
}
public Metrics addSubMetrics(Metrics metrics) {
return submetrics.put(metrics.getName(), metrics);
}
public Collection<Metrics> getSubMetrics() {
return Collections.unmodifiableCollection(submetrics.values());
}
public boolean isEmpty() {
return
measurements.values().stream()
.filter(measurement -> measurement.getDescriptor().isVisible())
.allMatch(Measurement::isEmpty) &&
submetrics.values().stream()
.allMatch(Metrics::isEmpty);
}
public boolean isInRange() {
return measurements.values().stream()
.filter(measurement -> measurement.getDescriptor().isVisible())
.allMatch(Measurement::isInRange);
}
public String toString() {
StringBuilder result = new StringBuilder();
result.append(getClass().getName()).append(" ").append(getName()).append(" with [");
result.append(measurements.entrySet().stream()
.map(entry -> "\"" + entry.getKey() + "\"(" + entry.getValue().getClass().getName() + ")")
.collect(Collectors.joining(", "))
);
result.append("]");
return result.toString();
}
}