org.openrewrite.internal.LoggingMeterRegistry Maven / Gradle / Ivy
Show all versions of rewrite-core Show documentation
/*
* Copyright 2020 the original author or authors.
*
* Licensed 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
*
* https://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.openrewrite.internal;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.BaseUnits;
import io.micrometer.core.instrument.config.NamingConvention;
import io.micrometer.core.instrument.cumulative.CumulativeCounter;
import io.micrometer.core.instrument.cumulative.CumulativeDistributionSummary;
import io.micrometer.core.instrument.cumulative.CumulativeFunctionTimer;
import io.micrometer.core.instrument.cumulative.CumulativeTimer;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.HistogramSnapshot;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import io.micrometer.core.instrument.internal.DefaultGauge;
import io.micrometer.core.instrument.internal.DefaultLongTaskTimer;
import io.micrometer.core.instrument.util.TimeUtils;
import io.micrometer.core.lang.Nullable;
import org.jspecify.annotations.NullMarked;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;
import java.util.function.ToLongFunction;
import java.util.stream.StreamSupport;
import static io.micrometer.core.instrument.util.DoubleFormat.decimalOrNan;
import static java.util.stream.Collectors.joining;
@NullMarked
public class LoggingMeterRegistry extends MeterRegistry {
private final long startInterval;
private final TimeUnit baseTimeUnit;
private final Consumer loggingSink;
private final Function meterIdPrinter;
public LoggingMeterRegistry(TimeUnit baseTimeUnit, Consumer loggingSink, @Nullable Function meterIdPrinter) {
super(Clock.SYSTEM);
this.baseTimeUnit = baseTimeUnit;
this.startInterval = clock.monotonicTime();
this.loggingSink = loggingSink;
this.meterIdPrinter = meterIdPrinter != null ? meterIdPrinter : defaultMeterIdPrinter();
config().namingConvention(NamingConvention.dot);
}
private Function defaultMeterIdPrinter() {
return meter -> getConventionName(meter.getId()) + getConventionTags(meter.getId()).stream()
.map(t -> t.getKey() + "=" + t.getValue())
.collect(joining(",", "{", "}"));
}
public void print() {
getMeters().stream()
.sorted((m1, m2) -> {
int typeComp = m1.getId().getType().compareTo(m2.getId().getType());
if (typeComp == 0) {
return m1.getId().getName().compareTo(m2.getId().getName());
}
return typeComp;
})
.forEach(m -> {
Printer print = new Printer(m);
m.use(
gauge -> loggingSink.accept(print.id() + "\n value=" + print.value(gauge.value())),
counter -> {
double count = counter.count();
if (count == 0) return;
loggingSink.accept(print.id() +
"\n sum=" + print.value(count) +
"\n rate=" + print.rate(count));
},
timer -> {
HistogramSnapshot snapshot = timer.takeSnapshot();
long count = snapshot.count();
if (count == 0) return;
loggingSink.accept(print.id() +
"\n sum=" + count +
"\n rate=" + print.unitlessRate(count) +
"\n mean=" + print.time(snapshot.mean(getBaseTimeUnit())) +
"\n max=" + print.time(snapshot.max(getBaseTimeUnit())));
},
summary -> {
HistogramSnapshot snapshot = summary.takeSnapshot();
long count = snapshot.count();
if (count == 0) return;
loggingSink.accept(print.id() +
"\n sum=" + count +
"\n rate=" + print.unitlessRate(count) +
"\n mean=" + print.value(snapshot.mean()) +
"\n max=" + print.value(snapshot.max()));
},
longTaskTimer -> {
int activeTasks = longTaskTimer.activeTasks();
if (activeTasks == 0) return;
loggingSink.accept(print.id() +
"\n active=" + print.value(activeTasks) +
"\n duration=" + print.time(longTaskTimer.duration(getBaseTimeUnit())));
},
timeGauge -> {
double value = timeGauge.value(getBaseTimeUnit());
if (value == 0) return;
loggingSink.accept(print.id() + "\n value=" + print.time(value));
},
counter -> {
double count = counter.count();
if (count == 0) return;
loggingSink.accept(print.id() +
"\n sum=" + print.value(count) +
"\n rate=" + print.rate(count));
},
timer -> {
double count = timer.count();
if (count == 0) return;
loggingSink.accept(print.id() +
"\n sum=" + print.value(count) +
"\n rate=" + print.rate(count) +
"\n mean=" + print.time(timer.mean(getBaseTimeUnit())));
},
meter -> loggingSink.accept(writeMeter(meter, print))
);
});
}
String writeMeter(Meter meter, Printer print) {
return StreamSupport.stream(meter.measure().spliterator(), false)
.map(ms -> {
String msLine = ms.getStatistic().getTagValueRepresentation() + "=";
switch (ms.getStatistic()) {
case TOTAL:
case MAX:
case VALUE:
return msLine + print.value(ms.getValue());
case TOTAL_TIME:
case DURATION:
return msLine + print.time(ms.getValue());
case COUNT:
return "\n sum=" + print.value(ms.getValue()) +
"\n rate=" + print.rate(ms.getValue());
default:
return msLine + decimalOrNan(ms.getValue());
}
})
.collect(joining(", ", print.id() + " ", ""));
}
class Printer {
private final Duration interval;
private final Meter meter;
Printer(Meter meter) {
interval = Duration.ofNanos(clock.monotonicTime() - startInterval);
this.meter = meter;
}
String id() {
return meterIdPrinter.apply(meter);
}
String time(double time) {
return TimeUtils.format(Duration.ofNanos((long) TimeUtils.convert(time, getBaseTimeUnit(), TimeUnit.NANOSECONDS)));
}
String rate(double rate) {
return humanReadableBaseUnit(rate / ((double) interval.getNano() / 1e9)) + "/s";
}
String unitlessRate(double rate) {
return decimalOrNan(rate / ((double) interval.getNano() / 1e9)) + "/s";
}
String value(double value) {
return humanReadableBaseUnit(value);
}
// see https://stackoverflow.com/a/3758880/510017
String humanReadableByteCount(double bytes) {
int unit = 1024;
if (bytes < unit || Double.isNaN(bytes)) return decimalOrNan(bytes) + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = "KMGTPE".charAt(exp - 1) + "i";
return decimalOrNan(bytes / Math.pow(unit, exp)) + " " + pre + "B";
}
String humanReadableBaseUnit(double value) {
String baseUnit = meter.getId().getBaseUnit();
if (BaseUnits.BYTES.equals(baseUnit)) {
return humanReadableByteCount(value);
}
return decimalOrNan(value) + (baseUnit != null ? " " + baseUnit : "");
}
}
@Override
protected TimeUnit getBaseTimeUnit() {
return baseTimeUnit;
}
@Override
protected DistributionStatisticConfig defaultHistogramConfig() {
return DistributionStatisticConfig.DEFAULT;
}
@Override
protected Gauge newGauge(Meter.Id id, @Nullable T obj, ToDoubleFunction valueFunction) {
return new DefaultGauge<>(id, obj, valueFunction);
}
@Override
protected Counter newCounter(Meter.Id id) {
return new CumulativeCounter(id);
}
@Override
protected LongTaskTimer newLongTaskTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig) {
return new DefaultLongTaskTimer(id, clock, baseTimeUnit, distributionStatisticConfig, false);
}
@Override
protected Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, PauseDetector pauseDetector) {
return new CumulativeTimer(id, clock, distributionStatisticConfig, pauseDetector, getBaseTimeUnit(), false);
}
@Override
protected DistributionSummary newDistributionSummary(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double scale) {
return new CumulativeDistributionSummary(id, clock, distributionStatisticConfig, scale, false);
}
@Override
protected Meter newMeter(Meter.Id id, Meter.Type type, Iterable measurements) {
throw new UnsupportedOperationException("Does not support custom meter types.");
}
@Override
protected FunctionTimer newFunctionTimer(Meter.Id id, T obj, ToLongFunction countFunction, ToDoubleFunction totalTimeFunction, TimeUnit totalTimeFunctionUnit) {
return new CumulativeFunctionTimer<>(id, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit, baseTimeUnit);
}
@Override
protected FunctionCounter newFunctionCounter(Meter.Id id, T obj, ToDoubleFunction countFunction) {
return null;
}
public static LoggingMeterRegistry.Builder builder() {
return new LoggingMeterRegistry.Builder();
}
public static class Builder {
private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS;
private Consumer loggingSink = System.out::println;
@Nullable
private Function meterIdPrinter;
public LoggingMeterRegistry.Builder baseTimeUnit(TimeUnit baseTimeUnit) {
this.baseTimeUnit = baseTimeUnit;
return this;
}
public LoggingMeterRegistry.Builder loggingSink(Consumer loggingSink) {
this.loggingSink = loggingSink;
return this;
}
public LoggingMeterRegistry.Builder meterIdPrinter(Function meterIdPrinter) {
this.meterIdPrinter = meterIdPrinter;
return this;
}
public LoggingMeterRegistry build() {
return new LoggingMeterRegistry(baseTimeUnit, loggingSink, meterIdPrinter);
}
}
}