/** * Returns a newly-created immutable {@link Map} which contains all values of {@link Meter}s in the * specified {@link MeterRegistry}. The format of the key string is: * <ul> * <li>{@code <name>#<statistic>{tagName=tagValue,...}}</li> * <li>e.g. {@code "armeria.server.activeRequests#value{method=greet}"}</li> * <li>e.g. {@code "someSubsystem.someValue#sumOfSquares"} (no tags)</li> * </ul> * Note: It is not recommended to use this method for the purposes other than testing. */ public static Map<String, Double> measureAll(MeterRegistry registry) { requireNonNull(registry, "registry"); final ImmutableMap.Builder<String, Double> builder = ImmutableMap.builder(); registry.forEachMeter(meter -> Streams.stream(meter.measure()).forEach(measurement -> { final String fullName = measurementName(meter.getId(), measurement); final double value = measurement.getValue(); builder.put(fullName, value); })); return builder.build(); }
private void assertMetricsContainsTag(String tagKey, String tagValue) { List<Meter.Id> meterIds = null; try { meterIds = this.meterRegistry.getMeters().stream() .map(Meter::getId) .collect(Collectors.toList()); Collection<Timer> timers = this.meterRegistry.get(REQUEST_METRICS_NAME).timers(); System.err.println("Looking for gateway.requests: tag: " + tagKey + ", value: "+ tagValue); timers.forEach(timer -> System.err.println(timer.getId()+timer.getClass().getSimpleName())); long count = getCount(tagKey, tagValue); assertThat(count).isEqualTo(1); } catch (MeterNotFoundException e) { System.err.println("\n\n\nError finding gatway.requests meter: tag: " + tagKey + ", value: "+ tagValue); System.err.println("\n\n\nMeter ids prior to search: "+meterIds + "\n\n\n and after:"); this.meterRegistry.forEachMeter(meter -> System.err.println(meter.getId() + meter.getClass().getSimpleName())); // try again? long count = getCount(tagKey, tagValue); if (count != 1) { throw e; } } }
@Test public void decorateTwiceWithSameSchedulerInstance() { Scheduler instance = Schedulers.newElastic("TWICE", 1); ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); Schedulers.decorateExecutorService(instance, service); Schedulers.decorateExecutorService(instance, service); assertThat(simpleMeterRegistry.getMeters() .stream() .map(m -> m.getId().getTag("name")) .distinct()) .containsOnly( "elastic(\"TWICE\")-0", "elastic(\"TWICE\")-1" ); }
@Test public void metricsActivatedHasDistinctNameTags() { Schedulers.newParallel("A", 3); Schedulers.newParallel("B", 2); assertThat(simpleMeterRegistry.getMeters() .stream() .map(m -> m.getId().getTag("name")) .distinct()) .containsOnly( "parallel(3,\"A\")-0", "parallel(3,\"A\")-1", "parallel(3,\"A\")-2", "parallel(2,\"B\")-0", "parallel(2,\"B\")-1" ); }
@Test public void disablingMetricsRemovesSchedulerMeters() { Schedulers.newParallel("A", 1); Schedulers.newParallel("A", 1); Schedulers.newParallel("A", 1); Metrics.globalRegistry.counter("foo", "tagged", "bar"); Schedulers.disableMetrics(); assertThat(simpleMeterRegistry.getMeters() .stream() .map(m -> m.getId().getName()) .distinct()) .containsExactly("foo"); } }
@Test public void metricsActivatedHandleNamingClash() { Schedulers.newParallel("A", 1); Schedulers.newParallel("A", 1); Schedulers.newParallel("A", 1); assertThat(simpleMeterRegistry.getMeters() .stream() .map(m -> m.getId().getTag("name")) .distinct()) .containsOnly( "parallel(1,\"A\")-0", "parallel(1,\"A\")#1-0", "parallel(1,\"A\")#2-0" ); }
@Test public void flowDurationTagsConsistency() { Mono<Integer> source1 = Mono.just(1) .name("normal") .hide(); Mono<Object> source2 = Mono.error(new IllegalStateException("dummy")) .name("error") .hide(); new MonoMetrics<>(source1, registry) .block(); new MonoMetrics<>(source2, registry) .onErrorReturn(0) .block(); Set<Set<String>> uniqueTagKeySets = registry .find(METER_FLOW_DURATION) .meters() .stream() .map(it -> it.getId().getTags()) .map(it -> it.stream().map(Tag::getKey).collect(Collectors.toSet())) .collect(Collectors.toSet()); assertThat(uniqueTagKeySets).hasSize(1); }
@Test public void flowDurationTagsConsistency() { Flux<Integer> source1 = Flux.just(1) .name("normal") .hide(); Flux<Object> source2 = Flux.error(new IllegalStateException("dummy")) .name("error") .hide(); new FluxMetrics<>(source1, registry) .blockLast(); new FluxMetrics<>(source2, registry) .onErrorReturn(0) .blockLast(); Set<Set<String>> uniqueTagKeySets = registry .find(METER_FLOW_DURATION) .meters() .stream() .map(it -> it.getId().getTags()) .map(it -> it.stream().map(Tag::getKey).collect(Collectors.toSet())) .collect(Collectors.toSet()); assertThat(uniqueTagKeySets).hasSize(1); } }
@Test public void metricsActivatedHasDistinctSchedulerIdTags() { Schedulers.newParallel("A", 4); Schedulers.newParallel("A", 4); Schedulers.newParallel("A", 3); Schedulers.newSingle("B"); Schedulers.newElastic("C").createWorker(); assertThat(simpleMeterRegistry.getMeters() .stream() .map(m -> m.getId().getTag(SchedulerMetricDecorator.TAG_SCHEDULER_ID)) .distinct()) .containsOnly( "parallel(4,\"A\")", "parallel(4,\"A\")#1", "parallel(3,\"A\")", "single(\"B\")", "elastic(\"C\")" ); }
private String getName(Meter meter) { return meter.getId().getName(); }
private void mergeAvailableTags(Map<String, Set<String>> availableTags, Meter meter) { meter.getId().getTags().forEach((tag) -> { Set<String> value = Collections.singleton(tag.getValue()); availableTags.merge(tag.getKey(), value, this::merge); }); }
@ReadOperation public MetricResponse metric(@Selector String requiredMetricName, @Nullable List<String> tag) { List<Tag> tags = parseTags(tag); Collection<Meter> meters = findFirstMatchingMeters(this.registry, requiredMetricName, tags); if (meters.isEmpty()) { return null; } Map<Statistic, Double> samples = getSamples(meters); Map<String, Set<String>> availableTags = getAvailableTags(meters); tags.forEach((t) -> availableTags.remove(t.getKey())); Meter.Id meterId = meters.iterator().next().getId(); return new MetricResponse(requiredMetricName, meterId.getDescription(), meterId.getBaseUnit(), asList(samples, Sample::new), asList(availableTags, AvailableTag::new)); }
@Test @DirtiesContext public void testAutoDiscovery() { log.info("--- Starting tests with full auto discovery ---"); assertEquals(METHOD_COUNT * 2, this.meterRegistry.getMeters().stream() .filter(Counter.class::isInstance) .filter(m -> m.getId().getName().startsWith("grpc.")) // Only count grpc metrics .count()); assertEquals(METHOD_COUNT, this.meterRegistry.getMeters().stream() .filter(Timer.class::isInstance) .filter(m -> m.getId().getName().startsWith("grpc.")) // Only count grpc metrics .count()); log.info("--- Test completed ---"); }
@Test @DirtiesContext public void testAutoDiscovery() { log.info("--- Starting tests with auto discovery ---"); for (final Meter meter : meterRegistry.getMeters()) { log.debug("Found meter: {}", meter.getId()); } assertEquals(METHOD_COUNT * 2, this.meterRegistry.getMeters().stream().filter(Counter.class::isInstance).count()); assertEquals(METHOD_COUNT, this.meterRegistry.getMeters().stream().filter(Timer.class::isInstance).count()); log.info("--- Test completed ---"); }
/** * Logs a sorted and readable list of meters using the debug level. Useful for debugging. * * @param meters The meters to be logged. */ public static void logMeters(final Collection<? extends Meter> meters) { if (!log.isDebugEnabled()) { return; } // The original collection is usually unmodifiable final List<Meter> sortedMeters = new ArrayList<>(meters); Collections.sort(sortedMeters, METER_COMPARATOR); log.debug("Found meters:"); for (final Meter meter : sortedMeters) { final Id id = meter.getId(); final String type = id.getType().name(); final String name = id.getName(); final Map<String, String> tagMap = new LinkedHashMap<>(); // Tags are already sorted for (final Tag tag : id.getTags()) { tagMap.put(tag.getKey(), tag.getValue()); } log.debug("- {} {} {}", type, name, tagMap); } }
@Override public final void serialize(T meter, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); json.writeObjectField("id", meter.getId()); serializeStatistics(meter, json, provider); json.writeEndObject(); }
private Meter convert(io.micrometer.core.instrument.Meter meter) { Id id = convert(meter.getId()); switch (meter.getId().getType()) { case COUNTER: return counter(id); case TIMER: return timer(id); case DISTRIBUTION_SUMMARY: return distributionSummary(id); case GAUGE: return gauge(id); default: return null; } }
private Stream<String> writeMeter(Meter m, Map<String, DatadogMetricMetadata> metadata) { long wallTime = clock.wallTime(); return stream(m.measure().spliterator(), false) .map(ms -> { Meter.Id id = m.getId().withTag(ms.getStatistic()); addToMetadataList(metadata, id, null, ms.getStatistic(), null); return writeMetric(id, null, wallTime, ms.getValue()); }); }
private Stream<String> writeCustomMetric(Meter meter) { long wallTime = config().clock().wallTime(); List<Tag> tags = getConventionTags(meter.getId()); return StreamSupport.stream(meter.measure().spliterator(), false) .map(ms -> new KairosMetricBuilder() .field("name", ms.getStatistic().getTagValueRepresentation()) .datapoints(wallTime, ms.getValue()) .tags(tags) .build()); }
log.debug("Found meter: {}", meter.getId());