Skip to content

Commit

Permalink
Replace the id label with a name label for Netty allocator
Browse files Browse the repository at this point in the history
The Netty allocator metrics exposed an `id` label using the allocator hashcode. However, this does not make a lot of sense when you need to aggregate metrics from multiple metrics.
Thus, this commit replaces the `id` with a `name` label with stable names for the different allocators.

Unfortunately, the initial code was in micrometer. This commit forks the 2 classes required to replace `id` with `name` and adds stable names to the different allocators.

Fix quarkusio#45847
  • Loading branch information
cescoffier committed Jan 27, 2025
1 parent ddd9c85 commit 2ee2894
Show file tree
Hide file tree
Showing 6 changed files with 374 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.binder.netty4.NettyAllocatorMetrics;
import io.micrometer.core.instrument.binder.netty4.NettyEventExecutorMetrics;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.EventLoopGroup;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.SingleThreadEventExecutor;
import io.quarkus.micrometer.runtime.binder.netty.NettyAllocatorMetrics;
import io.quarkus.micrometer.runtime.binder.netty.NettyMetricsProvider;
import io.quarkus.micrometer.runtime.binder.netty.VertxNettyAllocatorMetricsProvider;
import io.quarkus.micrometer.test.HelloResource;
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.impl.VertxByteBufAllocator;
import io.vertx.core.impl.VertxInternal;

public class NettyMetricsTest {
Expand Down Expand Up @@ -72,45 +71,32 @@ static void removeRegistry() {
Vertx vertx;

private static final Set<Tag> NAM_PBBA_TAGS = Tags.of(
"id", String.valueOf(PooledByteBufAllocator.DEFAULT.hashCode()),
"name", NettyMetricsProvider.NETTY_DEFAULT_POOLED_ALLOCATOR_NAME,
"allocator.type", "PooledByteBufAllocator")
.stream()
.collect(Collectors.toSet());

private static final Set<Tag> NAM_UNPBBA_TAGS = Tags.of(
"id", String.valueOf(UnpooledByteBufAllocator.DEFAULT.hashCode()),
"name", NettyMetricsProvider.NETTY_DEFAULT_UNPOOLED_ALLOCATOR_NAME,
"allocator.type", "UnpooledByteBufAllocator")
.stream()
.collect(Collectors.toSet());

private static final Set<Tag> VX_NAM_PBBA_TAGS = Tags.of(
"id", String.valueOf(VertxByteBufAllocator.POOLED_ALLOCATOR.hashCode()),
"name", VertxNettyAllocatorMetricsProvider.VERTX_POOLED_ALLOCATOR_NAME,
"allocator.type", "PooledByteBufAllocator")
.stream()
.collect(Collectors.toSet());

private static final Set<Tag> VX_NAM_UNPBBA_TAGS = Tags.of(
"id", String.valueOf(VertxByteBufAllocator.UNPOOLED_ALLOCATOR.hashCode()),
"name", VertxNettyAllocatorMetricsProvider.VERTX_UNPOOLED_ALLOCATOR_NAME,
"allocator.type", "UnpooledByteBufAllocator")
.stream()
.collect(Collectors.toSet());

private static final Tag HEAP_MEMORY = Tag.of(AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "heap");
private static final Tag DIRECT_MEMORY = Tag.of(AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "direct");

enum AllocatorKeyNames implements KeyName {
ID {
public String asString() {
return "id";
}
},
ALLOCATOR_TYPE {
public String asString() {
return "allocator.type";
}
};
}

enum AllocatorMemoryKeyNames implements KeyName {
MEMORY_TYPE {
public String asString() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package io.quarkus.micrometer.runtime.binder.netty;

import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.netty.buffer.ByteBufAllocatorMetric;
import io.netty.buffer.ByteBufAllocatorMetricProvider;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocatorMetric;

/**
* {@link MeterBinder} for Netty memory allocators.
* <p>
* This class is based on the MicroMeter NettyAllocatorMetrics class, but remove the "id" from the tags are it's
* computed from the `hashCode` which does not allow aggregation across processed.
* Instead, it gets a {@code name} label indicating an unique name for the allocator.
*/
public class NettyAllocatorMetrics implements MeterBinder {

private final ByteBufAllocatorMetricProvider allocator;
private final String name;

/**
* Create a binder instance for the given allocator.
*
* @param name the unique name for the allocator
* @param allocator the {@code ByteBuf} allocator to instrument
*/
public NettyAllocatorMetrics(String name, ByteBufAllocatorMetricProvider allocator) {
this.name = name;
this.allocator = allocator;
}

@Override
public void bindTo(MeterRegistry registry) {
ByteBufAllocatorMetric allocatorMetric = this.allocator.metric();
Tags tags = Tags.of(
NettyMeters.AllocatorKeyNames.NAME.asString(), this.name,
NettyMeters.AllocatorKeyNames.ALLOCATOR_TYPE.asString(), this.allocator.getClass().getSimpleName());

Gauge
.builder(NettyMeters.ALLOCATOR_MEMORY_USED.getName(), allocatorMetric,
ByteBufAllocatorMetric::usedHeapMemory)
.tags(tags.and(NettyMeters.AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "heap"))
.register(registry);

Gauge
.builder(NettyMeters.ALLOCATOR_MEMORY_USED.getName(), allocatorMetric,
ByteBufAllocatorMetric::usedDirectMemory)
.tags(tags.and(NettyMeters.AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "direct"))
.register(registry);

if (this.allocator instanceof PooledByteBufAllocator pooledByteBufAllocator) {
PooledByteBufAllocatorMetric pooledAllocatorMetric = pooledByteBufAllocator.metric();

Gauge
.builder(NettyMeters.ALLOCATOR_MEMORY_PINNED.getName(), pooledByteBufAllocator,
PooledByteBufAllocator::pinnedHeapMemory)
.tags(tags.and(NettyMeters.AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "heap"))
.register(registry);

Gauge
.builder(NettyMeters.ALLOCATOR_MEMORY_PINNED.getName(), pooledByteBufAllocator,
PooledByteBufAllocator::pinnedDirectMemory)
.tags(tags.and(NettyMeters.AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "direct"))
.register(registry);

Gauge
.builder(NettyMeters.ALLOCATOR_POOLED_ARENAS.getName(), pooledAllocatorMetric,
PooledByteBufAllocatorMetric::numHeapArenas)
.tags(tags.and(NettyMeters.AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "heap"))
.register(registry);
Gauge
.builder(NettyMeters.ALLOCATOR_POOLED_ARENAS.getName(), pooledAllocatorMetric,
PooledByteBufAllocatorMetric::numDirectArenas)
.tags(tags.and(NettyMeters.AllocatorMemoryKeyNames.MEMORY_TYPE.asString(), "direct"))
.register(registry);

Gauge
.builder(NettyMeters.ALLOCATOR_POOLED_CACHE_SIZE.getName(), pooledAllocatorMetric,
PooledByteBufAllocatorMetric::normalCacheSize)
.tags(tags.and(NettyMeters.AllocatorPooledCacheKeyNames.CACHE_TYPE.asString(), "normal"))
.register(registry);
Gauge
.builder(NettyMeters.ALLOCATOR_POOLED_CACHE_SIZE.getName(), pooledAllocatorMetric,
PooledByteBufAllocatorMetric::smallCacheSize)
.tags(tags.and(NettyMeters.AllocatorPooledCacheKeyNames.CACHE_TYPE.asString(), "small"))
.register(registry);

Gauge
.builder(NettyMeters.ALLOCATOR_POOLED_THREADLOCAL_CACHES.getName(), pooledAllocatorMetric,
PooledByteBufAllocatorMetric::numThreadLocalCaches)
.tags(tags)
.register(registry);

Gauge
.builder(NettyMeters.ALLOCATOR_POOLED_CHUNK_SIZE.getName(), pooledAllocatorMetric,
PooledByteBufAllocatorMetric::chunkSize)
.tags(tags)
.register(registry);
}
}

}
Loading

0 comments on commit 2ee2894

Please sign in to comment.