1 package com.zaxxer.hikari.metrics.micrometer;
2
3 import com.zaxxer.hikari.metrics.IMetricsTracker;
4 import com.zaxxer.hikari.metrics.PoolStats;
5 import io.micrometer.core.instrument.Counter;
6 import io.micrometer.core.instrument.Gauge;
7 import io.micrometer.core.instrument.MeterRegistry;
8 import io.micrometer.core.instrument.Timer;
9
10 import java.util.concurrent.TimeUnit;
11
12 /**
13  * {@link IMetricsTracker Metrics tracker} for Micrometer.
14  * HikariCP metrics can be configured in your application by applying a
15  * {@link io.micrometer.core.instrument.config.MeterFilter MeterFilter} to metrics starting with
16  * {@link #HIKARI_METRIC_NAME_PREFIX}. For example, to configure client-side calculated percentiles:
17  *
18  * <blockquote><pre>
19  *     new MeterFilter() {
20  *       &#064;Override
21  *       public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
22  *         if (id.getName().startsWith(MicrometerMetricsTracker.HIKARI_METRIC_NAME_PREFIX)) {
23  *           return DistributionStatisticConfig.builder()
24  *               .percentiles(0.5, 0.95)
25  *               .build()
26  *               .merge(config);
27  *            }
28  *         return config;
29  *         }
30  *      };
31  * </pre></blockquote>
32  */

33 public class MicrometerMetricsTracker implements IMetricsTracker
34 {
35    /** Prefix used for all HikariCP metric names. */
36    public static final String HIKARI_METRIC_NAME_PREFIX = "hikaricp";
37
38    private static final String METRIC_CATEGORY = "pool";
39    private static final String METRIC_NAME_WAIT = HIKARI_METRIC_NAME_PREFIX + ".connections.acquire";
40    private static final String METRIC_NAME_USAGE = HIKARI_METRIC_NAME_PREFIX + ".connections.usage";
41    private static final String METRIC_NAME_CONNECT = HIKARI_METRIC_NAME_PREFIX + ".connections.creation";
42
43    private static final String METRIC_NAME_TIMEOUT_RATE = HIKARI_METRIC_NAME_PREFIX + ".connections.timeout";
44    private static final String METRIC_NAME_TOTAL_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections";
45    private static final String METRIC_NAME_IDLE_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.idle";
46    private static final String METRIC_NAME_ACTIVE_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.active";
47    private static final String METRIC_NAME_PENDING_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.pending";
48    private static final String METRIC_NAME_MAX_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.max";
49    private static final String METRIC_NAME_MIN_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.min";
50
51
52    private final Timer connectionObtainTimer;
53    private final Counter connectionTimeoutCounter;
54    private final Timer connectionUsage;
55    private final Timer connectionCreation;
56    @SuppressWarnings({"FieldCanBeLocal""unused"})
57    private final Gauge totalConnectionGauge;
58    @SuppressWarnings({"FieldCanBeLocal""unused"})
59    private final Gauge idleConnectionGauge;
60    @SuppressWarnings({"FieldCanBeLocal""unused"})
61    private final Gauge activeConnectionGauge;
62    @SuppressWarnings({"FieldCanBeLocal""unused"})
63    private final Gauge pendingConnectionGauge;
64    @SuppressWarnings({"FieldCanBeLocal""unused"})
65    private final Gauge maxConnectionGauge;
66    @SuppressWarnings({"FieldCanBeLocal""unused"})
67    private final Gauge minConnectionGauge;
68    @SuppressWarnings({"FieldCanBeLocal""unused"})
69    private final PoolStats poolStats;
70
71    MicrometerMetricsTracker(final String poolName, final PoolStats poolStats, final MeterRegistry meterRegistry)
72    {
73       this.poolStats = poolStats;
74
75       this.connectionObtainTimer = Timer.builder(METRIC_NAME_WAIT)
76          .description("Connection acquire time")
77          .tags(METRIC_CATEGORY, poolName)
78          .register(meterRegistry);
79
80       this.connectionCreation = Timer.builder(METRIC_NAME_CONNECT)
81          .description("Connection creation time")
82          .tags(METRIC_CATEGORY, poolName)
83          .register(meterRegistry);
84
85       this.connectionUsage = Timer.builder(METRIC_NAME_USAGE)
86          .description("Connection usage time")
87          .tags(METRIC_CATEGORY, poolName)
88          .register(meterRegistry);
89
90       this.connectionTimeoutCounter = Counter.builder(METRIC_NAME_TIMEOUT_RATE)
91          .description("Connection timeout total count")
92          .tags(METRIC_CATEGORY, poolName)
93          .register(meterRegistry);
94
95       this.totalConnectionGauge = Gauge.builder(METRIC_NAME_TOTAL_CONNECTIONS, poolStats, PoolStats::getTotalConnections)
96          .description("Total connections")
97          .tags(METRIC_CATEGORY, poolName)
98          .register(meterRegistry);
99
100       this.idleConnectionGauge = Gauge.builder(METRIC_NAME_IDLE_CONNECTIONS, poolStats, PoolStats::getIdleConnections)
101          .description("Idle connections")
102          .tags(METRIC_CATEGORY, poolName)
103          .register(meterRegistry);
104
105       this.activeConnectionGauge = Gauge.builder(METRIC_NAME_ACTIVE_CONNECTIONS, poolStats, PoolStats::getActiveConnections)
106          .description("Active connections")
107          .tags(METRIC_CATEGORY, poolName)
108          .register(meterRegistry);
109
110       this.pendingConnectionGauge = Gauge.builder(METRIC_NAME_PENDING_CONNECTIONS, poolStats, PoolStats::getPendingThreads)
111          .description("Pending threads")
112          .tags(METRIC_CATEGORY, poolName)
113          .register(meterRegistry);
114
115       this.maxConnectionGauge = Gauge.builder(METRIC_NAME_MAX_CONNECTIONS, poolStats, PoolStats::getMaxConnections)
116          .description("Max connections")
117          .tags(METRIC_CATEGORY, poolName)
118          .register(meterRegistry);
119
120       this.minConnectionGauge = Gauge.builder(METRIC_NAME_MIN_CONNECTIONS, poolStats, PoolStats::getMinConnections)
121          .description("Min connections")
122          .tags(METRIC_CATEGORY, poolName)
123          .register(meterRegistry);
124
125    }
126
127    /** {@inheritDoc} */
128    @Override
129    public void recordConnectionAcquiredNanos(final long elapsedAcquiredNanos)
130    {
131       connectionObtainTimer.record(elapsedAcquiredNanos, TimeUnit.NANOSECONDS);
132    }
133
134    /** {@inheritDoc} */
135    @Override
136    public void recordConnectionUsageMillis(final long elapsedBorrowedMillis)
137    {
138       connectionUsage.record(elapsedBorrowedMillis, TimeUnit.MILLISECONDS);
139    }
140
141    @Override
142    public void recordConnectionTimeout()
143    {
144       connectionTimeoutCounter.increment();
145    }
146
147    @Override
148    public void recordConnectionCreatedMillis(long connectionCreatedMillis)
149    {
150       connectionCreation.record(connectionCreatedMillis, TimeUnit.MILLISECONDS);
151    }
152 }
153