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

io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics Maven / Gradle / Ivy

There is a newer version: 1.13.0
Show newest version
/**
 * Copyright 2017 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.binder.jvm; import io.micrometer.core.instrument.*; import io.micrometer.core.instrument.binder.BaseUnits; import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.internal.TimedExecutor; import io.micrometer.core.instrument.internal.TimedExecutorService; import io.micrometer.core.instrument.internal.TimedScheduledExecutorService; import io.micrometer.core.instrument.util.StringUtils; import io.micrometer.core.lang.NonNullApi; import io.micrometer.core.lang.NonNullFields; import io.micrometer.core.lang.Nullable; import io.micrometer.core.util.internal.logging.InternalLogger; import io.micrometer.core.util.internal.logging.InternalLoggerFactory; import java.lang.reflect.Field; import java.util.concurrent.*; import static java.util.Arrays.asList; /** * Monitors the status of executor service pools. Does not record timings on operations executed in the {@link ExecutorService}, * as this requires the instance to be wrapped. Timings are provided separately by wrapping the executor service * with {@link TimedExecutorService}. *

* Supports {@link ThreadPoolExecutor} and {@link ForkJoinPool} types of {@link ExecutorService}. Some libraries may provide * a wrapper type for {@link ExecutorService}, like {@link TimedExecutorService}. Make sure to pass the underlying, * unwrapped ExecutorService to this MeterBinder, if it is wrapped in another type. * * @author Jon Schneider * @author Clint Checketts * @author Johnny Lim */ @NonNullApi @NonNullFields public class ExecutorServiceMetrics implements MeterBinder { private static boolean allowIllegalReflectiveAccess = true; private static final InternalLogger log = InternalLoggerFactory.getInstance(ExecutorServiceMetrics.class); private static final String DEFAULT_EXECUTOR_METRIC_PREFIX = ""; @Nullable private final ExecutorService executorService; private final Iterable tags; private final String metricPrefix; public ExecutorServiceMetrics(@Nullable ExecutorService executorService, String executorServiceName, Iterable tags) { this(executorService, executorServiceName, DEFAULT_EXECUTOR_METRIC_PREFIX, tags); } /** * Create an {@code ExecutorServiceMetrics} instance. * * @param executorService executor service * @param executorServiceName executor service name which will be used as {@literal name} tag * @param metricPrefix metrics prefix which will be used to prefix metric name * @param tags additional tags * @since 1.5.0 */ public ExecutorServiceMetrics(@Nullable ExecutorService executorService, String executorServiceName, String metricPrefix, Iterable tags) { this.executorService = executorService; this.tags = Tags.concat(tags, "name", executorServiceName); this.metricPrefix = sanitizePrefix(metricPrefix); } /** * Record metrics on the use of an {@link Executor}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorName Will be used to tag metrics with "name". * @param tags Tags to apply to all recorded metrics. * @return The instrumented executor, proxied. */ public static Executor monitor(MeterRegistry registry, Executor executor, String executorName, Iterable tags) { return monitor(registry, executor, executorName, DEFAULT_EXECUTOR_METRIC_PREFIX, tags); } /** * Record metrics on the use of an {@link Executor}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorName Will be used to tag metrics with "name". * @param metricPrefix The prefix to use with meter names. This differentiates executor metrics that may have different tag sets. * @param tags Tags to apply to all recorded metrics. * @return The instrumented executor, proxied. * @since 1.5.0 */ public static Executor monitor(MeterRegistry registry, Executor executor, String executorName, String metricPrefix, Iterable tags) { if (executor instanceof ExecutorService) { return monitor(registry, (ExecutorService) executor, executorName, metricPrefix, tags); } return new TimedExecutor(registry, executor, executorName, sanitizePrefix(metricPrefix), tags); } /** * Record metrics on the use of an {@link Executor}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorName Will be used to tag metrics with "name". * @param tags Tags to apply to all recorded metrics. * @return The instrumented executor, proxied. */ public static Executor monitor(MeterRegistry registry, Executor executor, String executorName, Tag... tags) { return monitor(registry, executor, executorName, DEFAULT_EXECUTOR_METRIC_PREFIX, tags); } /** * Record metrics on the use of an {@link Executor}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorName Will be used to tag metrics with "name". * @param metricPrefix The prefix to use with meter names. This differentiates executor metrics that may have different tag sets. * @param tags Tags to apply to all recorded metrics. * @return The instrumented executor, proxied. * @since 1.5.0 */ public static Executor monitor(MeterRegistry registry, Executor executor, String executorName, String metricPrefix, Tag... tags) { return monitor(registry, executor, executorName, metricPrefix, asList(tags)); } /** * Record metrics on the use of an {@link ExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param tags Tags to apply to all recorded metrics. * @return The instrumented executor, proxied. */ public static ExecutorService monitor(MeterRegistry registry, ExecutorService executor, String executorServiceName, Iterable tags) { return monitor(registry, executor, executorServiceName, DEFAULT_EXECUTOR_METRIC_PREFIX, tags); } /** * Record metrics on the use of an {@link ExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param metricPrefix The prefix to use with meter names. This differentiates executor metrics that may have different tag sets. * @param tags Tags to apply to all recorded metrics. * @return The instrumented executor, proxied. * @since 1.5.0 */ public static ExecutorService monitor(MeterRegistry registry, ExecutorService executor, String executorServiceName, String metricPrefix, Iterable tags) { if (executor instanceof ScheduledExecutorService) { return monitor(registry, (ScheduledExecutorService) executor, executorServiceName, metricPrefix, tags); } new ExecutorServiceMetrics(executor, executorServiceName, metricPrefix, tags).bindTo(registry); return new TimedExecutorService(registry, executor, executorServiceName, sanitizePrefix(metricPrefix), tags); } /** * Record metrics on the use of an {@link ExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param tags Tags to apply to all recorded metrics. * @return The instrumented executor, proxied. */ public static ExecutorService monitor(MeterRegistry registry, ExecutorService executor, String executorServiceName, Tag... tags) { return monitor(registry, executor, executorServiceName, DEFAULT_EXECUTOR_METRIC_PREFIX, tags); } /** * Record metrics on the use of an {@link ExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param metricPrefix The prefix to use with meter names. This differentiates executor metrics that may have different tag sets. * @param tags Tags to apply to all recorded metrics. * @since 1.5.0 * @return The instrumented executor, proxied. */ public static ExecutorService monitor(MeterRegistry registry, ExecutorService executor, String executorServiceName, String metricPrefix, Tag... tags) { return monitor(registry, executor, executorServiceName, metricPrefix, asList(tags)); } /** * Record metrics on the use of a {@link ScheduledExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The scheduled executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param tags Tags to apply to all recorded metrics. * @return The instrumented scheduled executor, proxied. * @since 1.3.0 */ public static ScheduledExecutorService monitor(MeterRegistry registry, ScheduledExecutorService executor, String executorServiceName, Iterable tags) { return monitor(registry, executor, executorServiceName, DEFAULT_EXECUTOR_METRIC_PREFIX, tags); } /** * Record metrics on the use of a {@link ScheduledExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The scheduled executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param metricPrefix The prefix to use with meter names. This differentiates executor metrics that may have different tag sets. * @param tags Tags to apply to all recorded metrics. * @return The instrumented scheduled executor, proxied. * @since 1.5.0 */ public static ScheduledExecutorService monitor(MeterRegistry registry, ScheduledExecutorService executor, String executorServiceName, String metricPrefix, Iterable tags) { new ExecutorServiceMetrics(executor, executorServiceName, metricPrefix, tags).bindTo(registry); return new TimedScheduledExecutorService(registry, executor, executorServiceName, sanitizePrefix(metricPrefix), tags); } /** * Record metrics on the use of a {@link ScheduledExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The scheduled executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param tags Tags to apply to all recorded metrics. * @return The instrumented scheduled executor, proxied. * @since 1.3.0 */ public static ScheduledExecutorService monitor(MeterRegistry registry, ScheduledExecutorService executor, String executorServiceName, Tag... tags) { return monitor(registry, executor, executorServiceName, DEFAULT_EXECUTOR_METRIC_PREFIX, tags); } /** * Record metrics on the use of a {@link ScheduledExecutorService}. * * @param registry The registry to bind metrics to. * @param executor The scheduled executor to instrument. * @param executorServiceName Will be used to tag metrics with "name". * @param metricPrefix The prefix to use with meter names. This differentiates executor metrics that may have different tag sets. * @param tags Tags to apply to all recorded metrics. * @return The instrumented scheduled executor, proxied. * @since 1.5.0 */ public static ScheduledExecutorService monitor(MeterRegistry registry, ScheduledExecutorService executor, String executorServiceName, String metricPrefix, Tag... tags) { return monitor(registry, executor, executorServiceName, metricPrefix, asList(tags)); } private static String sanitizePrefix(String metricPrefix) { if (StringUtils.isBlank(metricPrefix)) return ""; if (!metricPrefix.endsWith(".")) return metricPrefix + "."; return metricPrefix; } @Override public void bindTo(MeterRegistry registry) { if (executorService == null) { return; } String className = executorService.getClass().getName(); if (executorService instanceof ThreadPoolExecutor) { monitor(registry, (ThreadPoolExecutor) executorService); } else if (executorService instanceof ForkJoinPool) { monitor(registry, (ForkJoinPool) executorService); } else if (allowIllegalReflectiveAccess) { if (className.equals("java.util.concurrent.Executors$DelegatedScheduledExecutorService")) { monitor(registry, unwrapThreadPoolExecutor(executorService, executorService.getClass())); } else if (className.equals("java.util.concurrent.Executors$FinalizableDelegatedExecutorService")) { monitor(registry, unwrapThreadPoolExecutor(executorService, executorService.getClass().getSuperclass())); } else { log.warn("Failed to bind as {} is unsupported.", className); } } else { log.warn("Failed to bind as {} is unsupported or reflective access is not allowed.", className); } } /** * Every ScheduledThreadPoolExecutor created by {@link Executors} is wrapped. Also, * {@link Executors#newSingleThreadExecutor()} wrap a regular {@link ThreadPoolExecutor}. */ @Nullable private ThreadPoolExecutor unwrapThreadPoolExecutor(ExecutorService executor, Class wrapper) { try { Field e = wrapper.getDeclaredField("e"); e.setAccessible(true); return (ThreadPoolExecutor) e.get(executor); } catch (NoSuchFieldException | IllegalAccessException | RuntimeException e) { // Cannot use InaccessibleObjectException since it was introduced in Java 9, so catch all RuntimeExceptions instead // Do nothing. We simply can't get to the underlying ThreadPoolExecutor. log.info("Cannot unwrap ThreadPoolExecutor for monitoring from {} due to {}: {}", wrapper.getName(), e.getClass().getName(), e.getMessage()); } return null; } private void monitor(MeterRegistry registry, @Nullable ThreadPoolExecutor tp) { if (tp == null) { return; } FunctionCounter.builder(metricPrefix + "executor.completed", tp, ThreadPoolExecutor::getCompletedTaskCount) .tags(tags) .description("The approximate total number of tasks that have completed execution") .baseUnit(BaseUnits.TASKS) .register(registry); Gauge.builder(metricPrefix + "executor.active", tp, ThreadPoolExecutor::getActiveCount) .tags(tags) .description("The approximate number of threads that are actively executing tasks") .baseUnit(BaseUnits.THREADS) .register(registry); Gauge.builder(metricPrefix + "executor.queued", tp, tpRef -> tpRef.getQueue().size()) .tags(tags) .description("The approximate number of tasks that are queued for execution") .baseUnit(BaseUnits.TASKS) .register(registry); Gauge.builder(metricPrefix + "executor.queue.remaining", tp, tpRef -> tpRef.getQueue().remainingCapacity()) .tags(tags) .description("The number of additional elements that this queue can ideally accept without blocking") .baseUnit(BaseUnits.TASKS) .register(registry); Gauge.builder(metricPrefix + "executor.pool.size", tp, ThreadPoolExecutor::getPoolSize) .tags(tags) .description("The current number of threads in the pool") .baseUnit(BaseUnits.THREADS) .register(registry); Gauge.builder(metricPrefix + "executor.pool.core", tp, ThreadPoolExecutor::getCorePoolSize) .tags(tags) .description("The core number of threads for the pool") .baseUnit(BaseUnits.THREADS) .register(registry); Gauge.builder(metricPrefix + "executor.pool.max", tp, ThreadPoolExecutor::getMaximumPoolSize) .tags(tags) .description("The maximum allowed number of threads in the pool") .baseUnit(BaseUnits.THREADS) .register(registry); } private void monitor(MeterRegistry registry, ForkJoinPool fj) { FunctionCounter.builder(metricPrefix + "executor.steals", fj, ForkJoinPool::getStealCount) .tags(tags) .description("Estimate of the total number of tasks stolen from " + "one thread's work queue by another. The reported value " + "underestimates the actual total number of steals when the pool " + "is not quiescent") .register(registry); Gauge.builder(metricPrefix + "executor.queued", fj, ForkJoinPool::getQueuedTaskCount) .tags(tags) .description("An estimate of the total number of tasks currently held in queues by worker threads") .register(registry); Gauge.builder(metricPrefix + "executor.active", fj, ForkJoinPool::getActiveThreadCount) .tags(tags) .description("An estimate of the number of threads that are currently stealing or executing tasks") .register(registry); Gauge.builder(metricPrefix + "executor.running", fj, ForkJoinPool::getRunningThreadCount) .tags(tags) .description("An estimate of the number of worker threads that are not blocked waiting to join tasks or for other managed synchronization threads") .register(registry); } /** * Disable illegal reflective accesses. * * Java 9+ warns illegal reflective accesses, but some metrics from this binder depend on reflective access to * {@link Executors}'s internal implementation details. This method allows to disable the feature to avoid the * warnings. * @since 1.6.0 */ public static void disableIllegalReflectiveAccess() { allowIllegalReflectiveAccess = false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy