|
28 | 28 | import io.micrometer.core.instrument.internal.TimedExecutorService;
|
29 | 29 | import io.micrometer.core.instrument.internal.TimedScheduledExecutorService;
|
30 | 30 |
|
| 31 | +import java.lang.invoke.MethodHandle; |
| 32 | +import java.lang.invoke.MethodHandles; |
31 | 33 | import java.lang.reflect.Field;
|
| 34 | +import java.lang.reflect.Method; |
32 | 35 | import java.util.List;
|
33 | 36 | import java.util.Set;
|
34 | 37 | import java.util.concurrent.*;
|
|
56 | 59 | @NonNullFields
|
57 | 60 | public class ExecutorServiceMetrics implements MeterBinder {
|
58 | 61 |
|
| 62 | + private static final String CLASS_NAME_THREAD_PER_TASK_EXECUTOR = "java.util.concurrent.ThreadPerTaskExecutor"; |
| 63 | + |
| 64 | + @Nullable |
| 65 | + private static final MethodHandle METHOD_HANDLE_THREAD_COUNT_FROM_THREAD_PER_TASK_EXECUTOR = getMethodHandleForThreadCountFromThreadPerTaskExecutor(); |
| 66 | + |
59 | 67 | private static boolean allowIllegalReflectiveAccess = true;
|
60 | 68 |
|
61 | 69 | private static final InternalLogger log = InternalLoggerFactory.getInstance(ExecutorServiceMetrics.class);
|
@@ -315,6 +323,9 @@ else if (className.equals("java.util.concurrent.Executors$FinalizableDelegatedEx
|
315 | 323 | monitor(registry,
|
316 | 324 | unwrapThreadPoolExecutor(executorService, executorService.getClass().getSuperclass()));
|
317 | 325 | }
|
| 326 | + else if (className.equals(CLASS_NAME_THREAD_PER_TASK_EXECUTOR)) { |
| 327 | + monitorThreadPerTaskExecutor(registry, executorService); |
| 328 | + } |
318 | 329 | else {
|
319 | 330 | log.warn("Failed to bind as {} is unsupported.", className);
|
320 | 331 | }
|
@@ -439,6 +450,39 @@ private void monitor(MeterRegistry registry, ForkJoinPool fj) {
|
439 | 450 | registeredMeterIds.addAll(meters.stream().map(Meter::getId).collect(toSet()));
|
440 | 451 | }
|
441 | 452 |
|
| 453 | + private void monitorThreadPerTaskExecutor(MeterRegistry registry, ExecutorService executorService) { |
| 454 | + List<Meter> meters = asList(Gauge |
| 455 | + .builder(metricPrefix + "executor.active", executorService, |
| 456 | + ExecutorServiceMetrics::getThreadCountFromThreadPerTaskExecutor) |
| 457 | + .tags(tags) |
| 458 | + .description("The approximate number of threads that are actively executing tasks") |
| 459 | + .baseUnit(BaseUnits.THREADS) |
| 460 | + .register(registry)); |
| 461 | + registeredMeterIds.addAll(meters.stream().map(Meter::getId).collect(toSet())); |
| 462 | + } |
| 463 | + |
| 464 | + private static long getThreadCountFromThreadPerTaskExecutor(ExecutorService executorService) { |
| 465 | + try { |
| 466 | + return (long) METHOD_HANDLE_THREAD_COUNT_FROM_THREAD_PER_TASK_EXECUTOR.invoke(executorService); |
| 467 | + } |
| 468 | + catch (Throwable e) { |
| 469 | + throw new RuntimeException(e); |
| 470 | + } |
| 471 | + } |
| 472 | + |
| 473 | + @Nullable |
| 474 | + private static MethodHandle getMethodHandleForThreadCountFromThreadPerTaskExecutor() { |
| 475 | + try { |
| 476 | + Class<?> clazz = Class.forName(CLASS_NAME_THREAD_PER_TASK_EXECUTOR); |
| 477 | + Method method = clazz.getMethod("threadCount"); |
| 478 | + method.setAccessible(true); |
| 479 | + return MethodHandles.lookup().unreflect(method); |
| 480 | + } |
| 481 | + catch (Throwable e) { |
| 482 | + return null; |
| 483 | + } |
| 484 | + } |
| 485 | + |
442 | 486 | /**
|
443 | 487 | * Disable illegal reflective accesses.
|
444 | 488 | *
|
|
0 commit comments