diff --git a/pom.xml b/pom.xml index 1a25a5cb..39da354a 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,13 @@ + + io.micrometer + micrometer-bom + ${micrometer.version} + pom + import + org.junit junit-bom @@ -137,11 +144,6 @@ classgraph ${classgraph.version} - - io.micrometer - micrometer-core - ${micrometer.version} - org.apache.commons commons-compress diff --git a/src/main/java/land/oras/auth/HttpClient.java b/src/main/java/land/oras/auth/HttpClient.java index 0352349b..a18952ba 100644 --- a/src/main/java/land/oras/auth/HttpClient.java +++ b/src/main/java/land/oras/auth/HttpClient.java @@ -20,7 +20,6 @@ package land.oras.auth; -import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import java.io.FileNotFoundException; @@ -68,11 +67,6 @@ public final class HttpClient { */ private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class); - /** - * Metric name for token refresh counter - */ - public static final String TOKEN_REFRESH_METRIC = "oras.auth.token.refresh"; - /** * The pattern for the WWW-Authenticate header value */ @@ -103,12 +97,6 @@ public final class HttpClient { * The meter registry for metrics */ private MeterRegistry meterRegistry; - - /** - * Counter for token refreshes - */ - private Counter tokenRefreshCounter; - /** * Hidden constructor */ @@ -157,9 +145,6 @@ private void setTlsVerify(boolean skipTlsVerify) { */ public HttpClient build() { this.client = this.builder.build(); - this.tokenRefreshCounter = Counter.builder(TOKEN_REFRESH_METRIC) - .description("Number of token refreshes performed against the registry") - .register(meterRegistry); return this; } @@ -463,7 +448,9 @@ public TokenResponse refreshToken( TokenResponse token = JsonUtils.fromJson(responseWrapper.response(), TokenResponse.class) .forService(service); TokenCache.put(newScopes, token); - tokenRefreshCounter.increment(); + meterRegistry + .counter(Const.METRIC_TOKEN_REFRESH, Const.METRIC_TAG_SERVICE, service, Const.METRIC_TAG_REALM, realm) + .increment(); return token; } diff --git a/src/main/java/land/oras/utils/Const.java b/src/main/java/land/oras/utils/Const.java index d93223a9..b03708c4 100644 --- a/src/main/java/land/oras/utils/Const.java +++ b/src/main/java/land/oras/utils/Const.java @@ -440,4 +440,19 @@ public static String currentTimestamp() { * OCI Chunk Minimum Length header */ public static final String OCI_CHUNK_MIN_LENGTH_HEADER = "OCI-Chunk-Min-Length"; + + /** + * Metric name for token refresh counter + */ + public static final String METRIC_TOKEN_REFRESH = "land_oras_auth_token_refresh_total"; + + /** + * Metric name for token refresh duration + */ + public static final String METRIC_TAG_SERVICE = "service"; + + /** + * Metric name for token refresh duration + */ + public static final String METRIC_TAG_REALM = "realm"; } diff --git a/src/test/java/land/oras/DockerIoITCase.java b/src/test/java/land/oras/DockerIoITCase.java index 1e26f9ca..76507b99 100644 --- a/src/test/java/land/oras/DockerIoITCase.java +++ b/src/test/java/land/oras/DockerIoITCase.java @@ -22,7 +22,11 @@ import static org.junit.jupiter.api.Assertions.*; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import java.nio.file.Path; +import land.oras.utils.Const; import land.oras.utils.ZotUnsecureContainer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -106,8 +110,12 @@ void shouldPullOneBlob() { void shouldCopyTagToInternalRegistry() { // Source registry - Registry sourceRegistry = - Registry.Builder.builder().withParallelism(3).defaults().build(); + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + Registry sourceRegistry = Registry.Builder.builder() + .withMeterRegistry(meterRegistry) + .withParallelism(3) + .defaults() + .build(); // Copy to this internal registry Registry targetRegistry = Registry.Builder.builder() @@ -122,6 +130,12 @@ void shouldCopyTagToInternalRegistry() { CopyUtils.copy(sourceRegistry, containerSource, targetRegistry, containerTarget, CopyUtils.CopyOptions.deep()); assertTrue(targetRegistry.exists(containerTarget)); + + assertEquals( + 1.0, + meterRegistry.find(Const.METRIC_TOKEN_REFRESH).counters().stream() + .mapToDouble(Counter::count) + .sum()); } @Test diff --git a/src/test/java/land/oras/RegistryWireMockTest.java b/src/test/java/land/oras/RegistryWireMockTest.java index 36446a33..c2572103 100644 --- a/src/test/java/land/oras/RegistryWireMockTest.java +++ b/src/test/java/land/oras/RegistryWireMockTest.java @@ -31,6 +31,8 @@ import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import com.github.tomakehurst.wiremock.stubbing.Scenario; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -584,8 +586,7 @@ void shouldRefreshExpiredToken(WireMockRuntimeInfo wmRuntimeInfo) { WireMock.ok().withBody("blob-data").withHeader(Const.DOCKER_CONTENT_DIGEST_HEADER, digest))); // Insecure registry with a custom meter registry to track metrics - io.micrometer.core.instrument.simple.SimpleMeterRegistry meterRegistry = - new io.micrometer.core.instrument.simple.SimpleMeterRegistry(); + SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); Registry registry = Registry.Builder.builder() .withAuthProvider(new BearerTokenProvider()) // Already bearer token .withInsecure(true) @@ -600,8 +601,21 @@ void shouldRefreshExpiredToken(WireMockRuntimeInfo wmRuntimeInfo) { // Verify that exactly one token refresh was performed assertEquals( 1.0, - meterRegistry.counter(HttpClient.TOKEN_REFRESH_METRIC).count(), + meterRegistry + .counter( + Const.METRIC_TOKEN_REFRESH, + Const.METRIC_TAG_SERVICE, + "localhost", + Const.METRIC_TAG_REALM, + "http://localhost:%d/token".formatted(wmRuntimeInfo.getHttpPort())) + .count(), "Token refresh counter should be 1 after one token refresh"); + assertEquals( + 1.0, + meterRegistry.find(Const.METRIC_TOKEN_REFRESH).counters().stream() + .mapToDouble(Counter::count) + .sum()); + TestUtils.dumpMetrics(meterRegistry); } @Test diff --git a/src/test/java/land/oras/TestUtils.java b/src/test/java/land/oras/TestUtils.java index 9ed005dc..8f16e0f7 100644 --- a/src/test/java/land/oras/TestUtils.java +++ b/src/test/java/land/oras/TestUtils.java @@ -20,9 +20,13 @@ package land.oras; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; /** @@ -30,6 +34,24 @@ */ public final class TestUtils { + /** + * Logger + */ + private static final Logger LOG = LoggerFactory.getLogger(TestUtils.class); + + /** + * Dump current metrics to console for debug purpose + * @param meterRegistry the meter registry to dump + */ + public static void dumpMetrics(MeterRegistry meterRegistry) { + meterRegistry.getMeters().forEach(meter -> { + Meter.Id id = meter.getId(); + LOG.info("{} {}", id.getName(), id.getTags()); + + meter.measure().forEach(ms -> LOG.info(" {}={}", ms.getStatistic(), ms.getValue())); + }); + } + /** * Create a registries.conf file in the given home directory with the given content. * @param homeDir the home directory where the .config/containers/registries.conf file will be created