All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.core.instrument.logging.LoggingMeterRegistry Maven / Gradle / Ivy

There is a newer version: 1.13.0
Show newest version
/**
 * Copyright 2018 VMware, Inc.
 * 

* 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 io.micrometer.core.instrument.logging; import io.micrometer.core.annotation.Incubating; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.binder.BaseUnits; import io.micrometer.core.instrument.config.NamingConvention; 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.step.StepDistributionSummary; import io.micrometer.core.instrument.step.StepMeterRegistry; import io.micrometer.core.instrument.step.StepTimer; import io.micrometer.core.instrument.util.NamedThreadFactory; import io.micrometer.core.instrument.util.TimeUtils; import io.micrometer.core.lang.Nullable; import io.micrometer.core.util.internal.logging.InternalLogger; import io.micrometer.core.util.internal.logging.InternalLoggerFactory; import java.time.Duration; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.StreamSupport; import static io.micrometer.core.instrument.util.DoubleFormat.decimalOrNan; import static java.util.stream.Collectors.joining; /** * Logging {@link io.micrometer.core.instrument.MeterRegistry}. * * @author Jon Schneider * @since 1.1.0 */ @Incubating(since = "1.1.0") public class LoggingMeterRegistry extends StepMeterRegistry { private static final InternalLogger log = InternalLoggerFactory.getInstance(LoggingMeterRegistry.class); private final LoggingRegistryConfig config; private final Consumer loggingSink; private final Function meterIdPrinter; public LoggingMeterRegistry() { this(LoggingRegistryConfig.DEFAULT, Clock.SYSTEM); } public LoggingMeterRegistry(LoggingRegistryConfig config, Clock clock) { this(config, clock, new NamedThreadFactory("logging-metrics-publisher"), log::info, null); } private LoggingMeterRegistry(LoggingRegistryConfig config, Clock clock, ThreadFactory threadFactory, Consumer loggingSink, @Nullable Function meterIdPrinter) { super(config, clock); this.config = config; this.loggingSink = loggingSink; this.meterIdPrinter = meterIdPrinter != null ? meterIdPrinter : defaultMeterIdPrinter(); config().namingConvention(NamingConvention.dot); start(threadFactory); } private Function defaultMeterIdPrinter() { return (meter) -> getConventionName(meter.getId()) + getConventionTags(meter.getId()).stream() .map(t -> t.getKey() + "=" + t.getValue()) .collect(joining(",", "{", "}")); } @Override protected void publish() { if (config.enabled()) { 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() + " value=" + print.value(gauge.value())), counter -> { double count = counter.count(); if (!config.logInactive() && count == 0) return; loggingSink.accept(print.id() + " throughput=" + print.rate(count)); }, timer -> { HistogramSnapshot snapshot = timer.takeSnapshot(); long count = snapshot.count(); if (!config.logInactive() && count == 0) return; loggingSink.accept(print.id() + " throughput=" + print.unitlessRate(count) + " mean=" + print.time(snapshot.mean(getBaseTimeUnit())) + " max=" + print.time(snapshot.max(getBaseTimeUnit()))); }, summary -> { HistogramSnapshot snapshot = summary.takeSnapshot(); long count = snapshot.count(); if (!config.logInactive() && count == 0) return; loggingSink.accept(print.id() + " throughput=" + print.unitlessRate(count) + " mean=" + print.value(snapshot.mean()) + " max=" + print.value(snapshot.max())); }, longTaskTimer -> { int activeTasks = longTaskTimer.activeTasks(); if (!config.logInactive() && activeTasks == 0) return; loggingSink.accept(print.id() + " active=" + print.value(activeTasks) + " duration=" + print.time(longTaskTimer.duration(getBaseTimeUnit()))); }, timeGauge -> { double value = timeGauge.value(getBaseTimeUnit()); if (!config.logInactive() && value == 0) return; loggingSink.accept(print.id() + " value=" + print.time(value)); }, counter -> { double count = counter.count(); if (!config.logInactive() && count == 0) return; loggingSink.accept(print.id() + " throughput=" + print.rate(count)); }, timer -> { double count = timer.count(); if (!config.logInactive() && count == 0) return; loggingSink.accept(print.id() + " throughput=" + print.rate(count) + " 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 "throughput=" + print.rate(ms.getValue()); default: return msLine + decimalOrNan(ms.getValue()); } }) .collect(joining(", ", print.id() + " ", "")); } @Override protected Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, PauseDetector pauseDetector) { return new StepTimer(id, clock, distributionStatisticConfig, pauseDetector, getBaseTimeUnit(), this.config.step().toMillis(), false); } @Override protected DistributionSummary newDistributionSummary(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double scale) { return new StepDistributionSummary(id, clock, distributionStatisticConfig, scale, config.step().toMillis(), false); } class Printer { private final Meter meter; Printer(Meter meter) { 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) config.step().getSeconds()) + "/s"; } String unitlessRate(double rate) { return decimalOrNan(rate / (double) config.step().getSeconds()) + "/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 TimeUnit.MILLISECONDS; } public static Builder builder(LoggingRegistryConfig config) { return new Builder(config); } public static class Builder { private final LoggingRegistryConfig config; private Clock clock = Clock.SYSTEM; private ThreadFactory threadFactory = new NamedThreadFactory("logging-metrics-publisher"); private Consumer loggingSink = log::info; @Nullable private Function meterIdPrinter; Builder(LoggingRegistryConfig config) { this.config = config; } public Builder clock(Clock clock) { this.clock = clock; return this; } public Builder threadFactory(ThreadFactory threadFactory) { this.threadFactory = threadFactory; return this; } public Builder loggingSink(Consumer loggingSink) { this.loggingSink = loggingSink; return this; } /** * Configure printer for meter IDs. * * @param meterIdPrinter printer to use for meter IDs * @return this builder instance * @since 1.2.0 */ public Builder meterIdPrinter(Function meterIdPrinter) { this.meterIdPrinter = meterIdPrinter; return this; } public LoggingMeterRegistry build() { return new LoggingMeterRegistry(config, clock, threadFactory, loggingSink, meterIdPrinter); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy