Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

MetricsCdiExtension.java:error: Something has gone terribly wrong

オープン
#805 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
25/100
issue の種類
バグ
明瞭さ
説明が足りない
活発さ
停滞
技術スタック
java
領域
tooling

調査の方向性

示されている google-java-format コマンドを helidon/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java に対して実行し、どこでフォーマットに失敗するかを調査します。報告されたエラーを再現し、それを引き起こす formatter の入力または処理段階を特定し、このファイルのフォーマットが内部エラーなしで完了することを確認します。

索引モデルが issue の本文から書いたものです。

説明

java \
  -jar core/target/google-java-format-HEAD-SNAPSHOT-all-deps.jar \
  -i \helidon/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java

n/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java
/Users/fsousa/Projects/code/helidon/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java:error: Something has gone terribly wrong. Please file a bug: https://github.com/google/google-java-format/issues/new

actual
=== Actual: ===
package io.helidon.microprofile.metrics;

import static jakarta.interceptor.Interceptor.Priority.LIBRARY_BEFORE;
import io.helidon.common.Errors;
import io.helidon.common.context.Contexts;
import io.helidon.config.Config;
import io.helidon.config.ConfigSources;
import io.helidon.config.ConfigValue;
import io.helidon.config.mp.MpConfig;
import io.helidon.metrics.api.MetricsSettings;
import io.helidon.metrics.api.RegistryFactory;
import io.helidon.metrics.serviceapi.MetricsSupport;
import io.helidon.microprofile.metrics.MetricAnnotationInfo.RegistrationPrep;
import io.helidon.microprofile.metrics.MetricUtil.LookupResult;
import io.helidon.microprofile.metrics.spi.MetricAnnotationDiscoveryObserver;
import io.helidon.microprofile.metrics.spi.MetricRegistrationObserver;
import io.helidon.microprofile.server.ServerCdiExtension;
import io.helidon.servicecommon.restcdi.HelidonRestCdiExtension;
import io.helidon.webserver.Routing;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Initialized;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.Stereotype;
import jakarta.enterprise.inject.spi.AfterDeploymentValidation;
import jakarta.enterprise.inject.spi.Annotated;
import jakarta.enterprise.inject.spi.AnnotatedCallable;
import jakarta.enterprise.inject.spi.AnnotatedMethod;
import jakarta.enterprise.inject.spi.AnnotatedType;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
import jakarta.enterprise.inject.spi.DeploymentException;
import jakarta.enterprise.inject.spi.ProcessAnnotatedType;
import jakarta.enterprise.inject.spi.ProcessManagedBean;
import jakarta.enterprise.inject.spi.WithAnnotations;
import jakarta.enterprise.inject.spi.configurator.AnnotatedConstructorConfigurator;
import jakarta.enterprise.inject.spi.configurator.AnnotatedMethodConfigurator;
import jakarta.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
import jakarta.enterprise.util.AnnotationLiteral;
import jakarta.inject.Singleton;
import jakarta.interceptor.Interceptor;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HEAD;
import jakarta.ws.rs.OPTIONS;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.Metadata;
import org.eclipse.microprofile.metrics.MetricID;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.MetricType;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.SimpleTimer;
import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.annotation.ConcurrentGauge;
import org.eclipse.microprofile.metrics.annotation.Counted;
import org.eclipse.microprofile.metrics.annotation.Gauge;
import org.eclipse.microprofile.metrics.annotation.Metered;
import org.eclipse.microprofile.metrics.annotation.SimplyTimed;
import org.eclipse.microprofile.metrics.annotation.Timed;

/**
 * MetricsCdiExtension class.
 *
 * <p>Earlier versions of this class detected app-provided producer fields and methods and triggered
 * creation and registration of the corresponding metrics upon such detection. As explained in this
 * <a href="https://github.com/eclipse/microprofile-metrics/issues/456">MP metrics issue</a> and
 * this <a href="https://github.com/eclipse/microprofile-metrics/pull/594">MP metrics PR</a>, this
 * probably was never correct and does not work because {@code @Metric} no longer applies to
 * producers per the MP metrics 3.0 spec. The issue and PR discussion explain how developers who
 * provide their own producers should use CDI qualifiers on the producers (and, therefore, injection
 * points) to avoid ambiguity between their own producers and producers written by vendors
 * implementing MP metrics.
 *
 * <p>For Helidon, this means we no longer need to track producer fields and methods, nor do we need
 * to augment injection points with our own {@code VendorProvided} qualifier to disambiguate,
 * because we now rely on developers who write their own producers to avoid the ambiguity using
 * qualifiers.
 */
public class MetricsCdiExtension extends HelidonRestCdiExtension<MetricsSupport> {
    private static final Logger LOGGER = Logger.getLogger(MetricsCdiExtension.class.getName());
    static final Set<Class<? extends Annotation>> ALL_METRIC_ANNOTATIONS = Set.of(Counted.class, Metered.class, Timed.class, ConcurrentGauge.class, SimplyTimed.class, Gauge.class);
    private static final Map<Class<? extends Annotation>, AnnotationLiteral<?>> INTERCEPTED_METRIC_ANNOTATIONS = Map.of(Counted.class, InterceptorCounted.binding(), Metered.class, InterceptorMetered.binding(), Timed.class, InterceptorTimed.binding(), ConcurrentGauge.class, InterceptorConcurrentGauge.binding(), SimplyTimed.class, InterceptorSimplyTimed.binding());
    private static final List<Class<? extends Annotation>> JAX_RS_ANNOTATIONS = Arrays.asList(GET.class, PUT.class, POST.class, HEAD.class, OPTIONS.class, DELETE.class, PATCH.class);
    private static final Set<Class<? extends Annotation>> METRIC_ANNOTATIONS_ON_ANY_ELEMENT = new HashSet<>(ALL_METRIC_ANNOTATIONS){
        {
            remove(Gauge.class);
        }
    };
    static final String REST_ENDPOINTS_METRIC_ENABLED_PROPERTY_NAME = "rest-request.enabled";
    private static final boolean REST_ENDPOINTS_METRIC_ENABLED_DEFAULT_VALUE = false;
    static final String SYNTHETIC_SIMPLE_TIMER_METRIC_NAME = "REST.request";
    static final String SYNTHETIC_SIMPLE_TIMER_METRIC_UNMAPPED_EXCEPTION_NAME = SYNTHETIC_SIMPLE_TIMER_METRIC_NAME + ".unmappedException.total";
    static final Metadata SYNTHETIC_SIMPLE_TIMER_METADATA = Metadata.builder().withName(SYNTHETIC_SIMPLE_TIMER_METRIC_NAME).withDisplayName("Total Requests and Response Time").withDescription("The number of invocations and total response time of this RESTful resource method since the start of the server. The metric will not record the elapsed time nor count of a REST request if it resulted in an unmapped exception. Also tracks the highest recorded time duration within the previous completed full minute and lowest recorded time duration within the previous completed full minute.").withType(MetricType.SIMPLE_TIMER).withUnit(MetricUnits.NANOSECONDS).build();
    static final Metadata SYNTHETIC_SIMPLE_TIMER_UNMAPPED_EXCEPTION_METADATA = Metadata.builder().withName(SYNTHETIC_SIMPLE_TIMER_METRIC_UNMAPPED_EXCEPTION_NAME).withDisplayName("Total Unmapped Exceptions count").withDescription("The total number of unmapped exceptions that occur from this RESTful resouce method since the start of the server.").withType(MetricType.COUNTER).withUnit(MetricUnits.NONE).build();
    private boolean restEndpointsMetricsEnabled = REST_ENDPOINTS_METRIC_ENABLED_DEFAULT_VALUE;
    private final Map<MetricID, AnnotatedMethod<?>> annotatedGaugeSites = new HashMap<>();
    private final List<RegistrationPrep> annotatedSites = new ArrayList<>();
    private Errors.Collector errors = Errors.collector();
    private final Map<Class<?>, Set<Method>> methodsWithRestRequestMetrics = new HashMap<>();
    private final Set<Class<?>> restRequestMetricsClassesProcessed = new HashSet<>();
    private final Set<Method> restRequestMetricsToRegister = new HashSet<>();
    private final WorkItemsManager<MetricWorkItem> workItemsManager = WorkItemsManager.create();
    private final List<MetricAnnotationDiscoveryObserver> metricAnnotationDiscoveryObservers = new ArrayList<>();
    private final List<MetricRegistrationObserver> metricRegistrationObservers = new ArrayList<>();
    private final Map<Executable, List<MetricAnnotationDiscovery>> metricAnnotationDiscoveriesByExecutable = new HashMap<>();
    @SuppressWarnings("unchecked")
    private final Map<Class<?>, StereotypeMetricsInfo> stereotypeMetricsInfo = new HashMap<>();

    @SuppressWarnings("unchecked")
    private static <T>T getReference(BeanManager bm, Type type, Bean<?> bean) {
        return (T)bm.getReference(bean, type, bm.createCreationalContext(bean));
    }

    /**
     * Creates a new extension instance.
     */
    public MetricsCdiExtension() {
        super(LOGGER, MetricsSupport::create, "metrics");
    }

    /**
     * Records an observer of metric annotation discoveries.
     *
     * @param metricAnnotationDiscoveryObserver the observer to enroll
     */
    public void enroll(MetricAnnotationDiscoveryObserver metricAnnotationDiscoveryObserver) {
        metricAnnotationDiscoveryObservers.add(metricAnnotationDiscoveryObserver);
    }

    /**
     * Records an observer of metric registrations.
     *
     * @param metricRegistrationObserver the observer to enroll
     */
    public void enroll(MetricRegistrationObserver metricRegistrationObserver) {
        metricRegistrationObservers.add(metricRegistrationObserver);
    }

    private static <E extends Member & AnnotatedElement>void recordAnnotatedSite(List<RegistrationPrep> sites, E element, Class<?> annotatedClass, LookupResult<? extends Annotation> lookupResult, Executable executable) {
        Annotation annotation = lookupResult.getAnnotation();
        RegistrationPrep registrationPrep = RegistrationPrep.create(annotation, element, annotatedClass, lookupResult.getType(), executable);
        sites.add(registrationPrep);
    }

    private void registerMetricsForAnnotatedSites() {
        MetricRegistry registry = getMetricRegistry();
        for (RegistrationPrep registrationPrep : annotatedSites) {
            metricAnnotationDiscoveriesByExecutable.get(registrationPrep.executable()).forEach((discovery)->{
                if (discovery.isActive()) {
                    org.eclipse.microprofile.metrics.Metric metric = registrationPrep.register(registry);
                    MetricID metricID = new MetricID(registrationPrep.metricName(), registrationPrep.tags());
                    metricRegistrationObservers.forEach((o)->o.onRegistration(discovery, registrationPrep.metadata(), metricID, metric));
                    workItemsManager.put(registrationPrep.executable(), registrationPrep.annotationType(), BasicMetricWorkItem.create(new MetricID(registrationPrep.metricName(), registrationPrep.tags()), metric));
                }
            });
        }
        annotatedSites.clear();
    }

    @Override
    protected void processManagedBean(ProcessManagedBean<?> pmb) {
        AnnotatedType<?> type = pmb.getAnnotatedBeanClass();
        Class<?> clazz = type.getJavaClass();
        if (type.isAnnotationPresent(Interceptor.class)) {
            LOGGER.log(Level.FINE, "Ignoring objects defined on type " + clazz.getName() + " because a CDI portable extension added @Interceptor to it dynamically");
            return;
        }
        Stream.concat(type.getMethods().stream(), type.getConstructors().stream()).filter((annotatedCallable)->!Modifier.isPrivate(annotatedCallable.getJavaMember().getModifiers())).filter((annotatedCallable)->type.equals(annotatedCallable.getDeclaringType())).forEach((annotatedCallable)->METRIC_ANNOTATIONS_ON_ANY_ELEMENT.forEach((annotation)->MetricUtil.lookupAnnotations(type, annotatedCallable, annotation, stereotypeMetricsInfo).forEach((lookupResult)->{
            Executable executable = (Executable)annotatedCallable.getJavaMember();
            recordAnnotatedSite(annotatedSites, executable, clazz, lookupResult, executable);
        })));
    }

    private static Tag[] tags(String[] tagStrings) {
        final List<Tag> result = new ArrayList<>();
        for (int i = 0; i < tagStrings.length; i++) {
            final int eq = tagStrings[i].indexOf("=");
            if (eq > 0) {
                final String tagName = tagStrings[i].substring(0, eq);
                final String tagValue = tagStrings[i].substring(eq + 1);
                result.add(new Tag(tagName, tagValue));
            }
        }
        return result.toArray(new Tag[0]);
    }

    Iterable<MetricWorkItem> workItems(Executable executable, Class<? extends Annotation> annotationType) {
        return workItemsManager.workItems(executable, annotationType);
    }

    <S extends MetricWorkItem>Iterable<S> workItems(Executable executable, Class<? extends Annotation> annotationType, Class<S> sClass) {
        return TypeFilteredIterable.create(workItems(executable, annotationType), sClass);
    }

    /**
     * Returns the real class of this object, skipping proxies.
     *
     * @param object The object.
     * @return Its class.
     */
    static Class<?> getRealClass(Object object) {
        Class<?> result = object.getClass();
        while (result.isSynthetic()) {
            result = result.getSuperclass();
        }
        return result;
    }

    static MetricRegistry getMetricRegistry() {
        return RegistryProducer.getDefaultRegistry();
    }

    static MetricRegistry getRegistryForSyntheticRestRequestMetrics() {
        return RegistryProducer.getBaseRegistry();
    }

    /**
     * Initializes the extension prior to bean discovery.
     *
     * @param discovery bean discovery event
     */
    void before(@Observes
    BeforeBeanDiscovery discovery) {
        LOGGER.log(Level.FINE, ()->"Before bean discovery " + discovery);
        discovery.addAnnotatedType(RegistryProducer.class, RegistryProducer.class.getName());
        discovery.addAnnotatedType(MetricProducer.class, MetricProducer.class.getName());
        discovery.addAnnotatedType(InterceptorCounted.class, InterceptorCounted.class.getName());
        discovery.addAnnotatedType(InterceptorMetered.class, InterceptorMetered.class.getName());
        discovery.addAnnotatedType(InterceptorTimed.class, InterceptorTimed.class.getName());
        discovery.addAnnotatedType(InterceptorConcurrentGauge.class, InterceptorConcurrentGauge.class.getName());
        discovery.addAnnotatedType(InterceptorSimplyTimed.class, InterceptorSimplyTimed.class.getName());
        discovery.addAnnotatedType(InterceptorSyntheticRestRequest.class, InterceptorSyntheticRestRequest.class.getName());
        discovery.addAnnotatedType(SyntheticRestRequest.class, SyntheticRestRequest.class.getName());
        restEndpointsMetricsEnabled = restEndpointsMetricsEnabled();
    }

    @Override
    public void clearAnnotationInfo(@Observes
    AfterDeploymentValidation adv) {
        super.clearAnnotationInfo(adv);
        methodsWithRestRequestMetrics.clear();
    }

    /**
     * Records Java classes with a metrics annotation somewhere.
     *
     * <p>By recording the classes here, we let CDI optimize its invocations of this observer method.
     * Later, when we observe managed beans (which CDI invokes for all managed beans) where we also
     * have to examine each method and constructor, we can quickly eliminate from consideration any
     * classes we have not recorded here.
     *
     * <p>This observer runs after other {@code ProcessAnnotatedType} observers to give other
     * extensions a chance to provide their own interceptors for selected constructors and methods by
     * adding {@link MetricAnnotationDiscoveryObserver} to the configured type, constructor, or
     * method.
     *
     * @param pat ProcessAnnotatedType event
     */
    private void recordMetricAnnotatedClass(@Observes
    @Priority(Interceptor.Priority.APPLICATION + 500 + 10)
    @WithAnnotations({Counted.class, Metered.class, Timed.class, ConcurrentGauge.class, SimplyTimed.class})
    ProcessAnnotatedType<?> pat) {
        if (isConcreteNonInterceptor(pat)) {
            recordAnnotatedType(pat);
            recordStereotypes(pat);
            bindInterceptors(pat);
        }
    }

    private void bindInterceptors(ProcessAnnotatedType<?> pat) {
        AnnotatedTypeConfigurator<?> annotatedTypeConfigurator = pat.configureAnnotatedType();
        bindInterceptorsAndRecordDiscoveries(annotatedTypeConfigurator, pat.configureAnnotatedType().constructors(), AnnotatedConstructorConfigurator::getAnnotated, AnnotatedConstructorConfigurator::add);
        bindInterceptorsAndRecordDiscoveries(annotatedTypeConfigurator, pat.configureAnnotatedType().methods(), AnnotatedMethodConfigurator::getAnnotated, (BiFunction<AnnotatedMethodConfigurator<?>, Annotation, AnnotatedMethodConfigurator<?>>)AnnotatedMethodConfigurator::add);
    }

    private <T, C, A extends AnnotatedCallable<?>>void bindInterceptorsAndRecordDiscoveries(AnnotatedTypeConfigurator<T> annotatedTypeConfigurator, Iterable<? extends C> executableConfigurators, Function<C, A> configuratorAnnotatedGetter, BiFunction<C, Annotation, C> annotationAdder) {
        executableConfigurators.forEach((executableConfigurator)->{
            A annotatedCallable = configuratorAnnotatedGetter.apply(executableConfigurator);
            Executable exec = (annotatedCallable.getJavaMember() instanceof Executable) ? (Executable)annotatedCallable.getJavaMember() : null;
            metricsLookupResults(annotatedTypeConfigurator.getAnnotated(), annotatedCallable).forEach((lookupResult)->{
                MetricAnnotationDiscoveryBase discoveryEvent = MetricAnnotationDiscoveryBase.create(annotatedTypeConfigurator, executableConfigurator, lookupResult.getAnnotation());
                if (exec != null) {
                    metricAnnotationDiscoveriesByExecutable.computeIfAbsent(exec, (o)->new ArrayList<>()).add(discoveryEvent);
                    metricAnnotationDiscoveryObservers.forEach((observer)->observer.onDiscovery(discoveryEvent));
                }
                if (discoveryEvent.shouldUseDefaultInterceptor()) {
                    Class<? extends Annotation> metricAnnotationClass = lookupResult.getAnnotation().annotationType();
                    annotationAdder.apply(executableConfigurator, INTERCEPTED_METRIC_ANNOTATIONS.get(metricAnnotationClass));
                }
            });
        });
    }

    private void recordStereotypes(ProcessAnnotatedType<?> pat) {
        AnnotatedType<?> annotatedType = pat.getAnnotatedType();
        Stream.concat(Stream.of(annotatedType), Stream.concat(pat.getAnnotatedType().getMethods().stream(), Stream.concat(pat.getAnnotatedType().getConstructors().stream(), pat.getAnnotatedType().getFields().stream()))).map(Annotated::getAnnotations).flatMap(Set::stream).distinct().filter(MetricsCdiExtension::isStereotype).forEach(this::recordIfMetricsRelatedStereotype);
    }

    private static boolean isStereotype(Annotation annotation) {
        return annotation.annotationType().isAnnotationPresent(Stereotype.class);
    }

    private void recordIfMetricsRelatedStereotype(Annotation stereotypeAnnotation) {
        Class<? extends Annotation> candidateType = stereotypeAnnotation.annotationType();
        Set<Annotation> metricsRelatedAnnotations = Arrays.stream(candidateType.getAnnotations()).filter((a)->ALL_METRIC_ANNOTATIONS.contains(a.annotationType())).collect(Collectors.toSet());
        if (!metricsRelatedAnnotations.isEmpty()) {
            stereotypeMetricsInfo.put(candidateType, StereotypeMetricsInfo.create(metricsRelatedAnnotations));
        }
    }

    /**
     * Collects all {@code LookupResult} objects for metrics annotations on a given annotated
     * executable.
     *
     * @param annotatedType the annotated type containing the constructor or method
     * @param annotatedMember the constructor or method
     * @return {@code LookupResult} instances that apply to the executable
     */
    private Iterable<LookupResult<?>> metricsLookupResults(AnnotatedType<?> annotatedType, AnnotatedCallable<?> annotatedMember) {
        List<LookupResult<?>> result = new ArrayList<>();
        INTERCEPTED_METRIC_ANNOTATIONS.keySet().forEach((metricAnnotationClass)->{
            result.addAll(MetricUtil.lookupAnnotations(annotatedType, annotatedMember, metricAnnotationClass, stereotypeMetricsInfo));
        });
        return result;
    }

    /**
     * Checks to make sure the annotated type is not abstract and is not an interceptor.
     *
     * @param pat {@code ProcessAnnotatedType} event
     * @return true if the annotated type should be kept for potential processing later; false
     *     otherwise
     */
    private boolean checkCandidateMetricClass(ProcessAnnotatedType<?> pat) {
        AnnotatedType<?> annotatedType = pat.getAnnotatedType();
        Class<?> clazz = annotatedType.getJavaClass();
        if (annotatedType.isAnnotationPresent(Interceptor.class) || Modifier.isAbstract(clazz.getModifiers())) {
            LOGGER.log(Level.FINER, ()->"Ignoring " + clazz.getName() + " with annotations " + annotatedType.getAnnotations() + " for later processing: " + (Modifier.isAbstract(clazz.getModifiers()) ? "abstract " : "") + (annotatedType.isAnnotationPresent(Interceptor.class) ? "interceptor " : ""));
            return false;
        }
        LOGGER.log(Level.FINE, ()->"Accepting annotated type " + clazz.getName() + " for later bean processing");
        return true;
    }

    /**
     * Adds a {@code SyntheticRestRequest} annotation to each JAX-RS endpoint method.
     *
     * @param pat the {@code ProcessAnnotatedType} for the type containing the JAX-RS annotated
     *     methods
     */
    private void recordSimplyTimedForRestResources(@Observes
    @WithAnnotations({GET.class, PUT.class, POST.class, HEAD.class, OPTIONS.class, DELETE.class, PATCH.class})
    ProcessAnnotatedType<?> pat) {
        if (!checkCandidateMetricClass(pat) || !restEndpointsMetricsEnabled) {
            return;
        }
        LOGGER.log(Level.FINE, ()->"Processing @SyntheticRestRequest annotation for " + pat.getAnnotatedType().getJavaClass().getName());
        AnnotatedTypeConfigurator<?> configurator = pat.configureAnnotatedType();
        Class<?> clazz = configurator.getAnnotated().getJavaClass();
        Set<Method> methodsToRecord = new HashSet<>();
        configurator.filterMethods((method)->!Modifier.isPrivate(method.getJavaMember().getModifiers())).forEach((annotatedMethodConfigurator)->JAX_RS_ANNOTATIONS.forEach((jaxRsAnnotation)->{
            AnnotatedMethod<?> annotatedMethod = annotatedMethodConfigurator.getAnnotated();
            if (annotatedMethod.isAnnotationPresent(jaxRsAnnotation)) {
                Method m = annotatedMethod.getJavaMember();
                if (clazz.equals(m.getDeclaringClass())) {
                    LOGGER.log(Level.FINE, ()->String.format("Adding @SyntheticRestRequest to %s", m.toString()));
                    annotatedMethodConfigurator.add(SyntheticRestRequest.Literal.getInstance());
                    methodsToRecord.add(m);
                }
            }
        }));
        if (!methodsToRecord.isEmpty()) {
            methodsWithRestRequestMetrics.put(clazz, methodsToRecord);
        }
    }

    /**
     * Creates or looks up the {@code SimpleTimer} instance for measuring REST requests on any JAX-RS
     * method.
     *
     * @param method the {@code Method} for which the SimpleTimer instance is needed
     * @return the located or created {@code SimpleTimer}
     */
    static SimpleTimer restEndpointSimpleTimer(Method method) {
        LOGGER.log(Level.FINE, ()->String.format("Registering synthetic SimpleTimer for %s#%s", method.getDeclaringClass().getName(), method.getName()));
        return getRegistryForSyntheticRestRequestMetrics().simpleTimer(SYNTHETIC_SIMPLE_TIMER_METADATA, syntheticRestRequestMetricTags(method));
    }

    /**
     * Creates or looks up the {@code Counter} instance for measuring REST requests on any JAX-RS
     * method.
     *
     * @param method the {@code Method} for which the Counter instance is needed
     * @return the located or created {@code Counter}
     */
    static Counter restEndpointCounter(Method method) {
        LOGGER.log(Level.FINE, ()->String.format("Registering synthetic Counter for %s#%s", method.getDeclaringClass().getName(), method.getName()));
        return getRegistryForSyntheticRestRequestMetrics().counter(SYNTHETIC_SIMPLE_TIMER_UNMAPPED_EXCEPTION_METADATA, syntheticRestRequestMetricTags(method));
    }

    private void registerAndSaveRestRequestMetrics(Method method) {
        workItemsManager.put(method, SyntheticRestRequest.class, SyntheticRestRequestWorkItem.create(restEndpointSimpleTimerMetricID(method), restEndpointSimpleTimer(method), restEndpointCounterMetricID(method), restEndpointCounter(method)));
    }

    /**
     * Creates the {@link MetricID} for the synthetic {@link SimplyTimed} metric we add to each JAX-RS
     * method.
     *
     * @param method Java method of interest
     * @return {@code MetricID} for the simpletimer for this Java method
     */
    static MetricID restEndpointSimpleTimerMetricID(Method method) {
        return new MetricID(SYNTHETIC_SIMPLE_TIMER_METRIC_NAME, syntheticRestRequestMetricTags(method));
    }

    /**
     * Creates the {@link MetricID} for the synthetic {@link Counter} metric we add to each JAX-RS
     * method.
     *
     * @param method Java method of interest
     * @return {@code MetricID} for the counter for this Java method
     */
    static MetricID restEndpointCounterMetricID(Method method) {
        return new MetricID(SYNTHETIC_SIMPLE_TIMER_METRIC_UNMAPPED_EXCEPTION_NAME, syntheticRestRequestMetricTags(method));
    }

    /**
     * Returns the {@code Tag} array for a synthetic {@code SimplyTimed} annotation.
     *
     * @param method the Java method of interest
     * @return the {@code Tag}s indicating the class and method
     */
    static Tag[] syntheticRestRequestMetricTags(Method method) {
        return new Tag[]{new Tag("class", method.getDeclaringClass().getName()), new Tag("method", methodTagValueForSyntheticRestRequestMetric(method))};
    }

    private static String methodTagValueForSyntheticRestRequestMetric(Method method) {
        StringBuilder methodTagValue = new StringBuilder(method.getName());
        for (Parameter p : method.getParameters()) {
            methodTagValue.append("_").append(prettyParamType(p));
        }
        return methodTagValue.toString();
    }

    private static String prettyParamType(Parameter parameter) {
        return parameter.getType().isArray() || parameter.isVarArgs() ? parameter.getType().getComponentType().getName() + "[]" : parameter.getType().getName();
    }

    private void collectRestRequestMetrics(@Observes
    ProcessManagedBean<?> pmb) {
        AnnotatedType<?> type = pmb.getAnnotatedBeanClass();
        Class<?> clazz = type.getJavaClass();
        if (!methodsWithRestRequestMetrics.containsKey(clazz)) {
            return;
        }
        LOGGER.log(Level.FINE, ()->"Processing synthetic SimplyTimed annotations for " + clazz.getName());
        restRequestMetricsClassesProcessed.add(clazz);
        restRequestMetricsToRegister.addAll(methodsWithRestRequestMetrics.get(clazz));
    }

    private void registerRestRequestMetrics() {
        restRequestMetricsToRegister.forEach(this::registerAndSaveRestRequestMetrics);
        if (LOGGER.isLoggable(Level.FINE)) {
            Set<Class<?>> syntheticSimpleTimerAnnotatedClassesIgnored = new HashSet<>(methodsWithRestRequestMetrics.keySet());
            syntheticSimpleTimerAnnotatedClassesIgnored.removeAll(restRequestMetricsClassesProcessed);
            if (!syntheticSimpleTimerAnnotatedClassesIgnored.isEmpty()) {
                LOGGER.log(Level.FINE, ()->"Classes with synthetic SimplyTimer annotations added that were not processed, probably because they were vetoed:" + syntheticSimpleTimerAnnotatedClassesIgnored.toString());
            }
        }
        restRequestMetricsClassesProcessed.clear();
        restRequestMetricsToRegister.clear();
    }

    boolean restEndpointsMetricsEnabled() {
        try {
            return chooseRestEndpointsSetting(((Config)(ConfigProvider.getConfig())).get("metrics"));
        } catch (Throwable t) {
            LOGGER.log(Level.WARNING, "Error looking up config setting for enabling REST endpoints SimpleTimer metrics; reporting \'false\'", t);
            return false;
        }
    }

    @Override
    public Routing.Builder registerService(@Observes
    @Priority(LIBRARY_BEFORE + 10)
    @Initialized(ApplicationScoped.class)
    Object adv, BeanManager bm, ServerCdiExtension server) {
        Errors problems = errors.collect();
        errors = null;
        if (problems.hasFatal()) {
            throw new DeploymentException("Metrics module found issues with deployment: " + problems.toString());
        }
        Routing.Builder defaultRouting = super.registerService(adv, bm, server);
        MetricsSupport metricsSupport = serviceSupport();
        RegistryProducer.clearApplicationRegistry();
        registerMetricsForAnnotatedSites();
        registerAnnotatedGauges(bm);
        registerRestRequestMetrics();
        Set<String> vendorMetricsAdded = new HashSet<>();
        vendorMetricsAdded.add("@default");
        Config config = MpConfig.toHelidonConfig(ConfigProvider.getConfig()).get(MetricsSettings.Builder.METRICS_CONFIG_KEY);
        config.get("vendor-metrics-routings").asList(String.class).orElseGet(List::of).forEach((routeName)->{
            if (!vendorMetricsAdded.contains(routeName)) {
                metricsSupport.configureVendorMetrics(routeName, server.serverNamedRoutingBuilder(routeName));
                vendorMetricsAdded.add(routeName);
            }
        });
        Contexts.globalContext().register(RegistryFactory.getInstance());
        return defaultRouting;
    }

    @Override
    protected Config seComponentConfig() {
        Config mpConfig = MpConfig.toHelidonConfig(ConfigProvider.getConfig());
        Map<String, String> mpConfigSettings = new HashMap<>();
        Stream.of("tags", "appName").forEach((key)->{
            mpConfig.get("mp.metrics." + key).asString().ifPresent((value)->mpConfigSettings.put(key, value));
        });
        Config metricsConfig = mpConfig.get("metrics").detach();
        Config.Builder builder = Config.builder();
        if (!mpConfigSettings.isEmpty()) {
            builder.addSource(ConfigSources.create(mpConfigSettings));
        }
        if (metricsConfig.exists()) {
            builder.addSource(ConfigSources.create(metricsConfig));
        }
        return builder.build();
    }

    private static boolean chooseRestEndpointsSetting(Config metricsConfig) {
        ConfigValue<Boolean> explicitRestEndpointsSetting = metricsConfig.get(REST_ENDPOINTS_METRIC_ENABLED_PROPERTY_NAME).asBoolean();
        boolean result = explicitRestEndpointsSetting.orElse(REST_ENDPOINTS_METRIC_ENABLED_DEFAULT_VALUE);
        if (explicitRestEndpointsSetting.isPresent()) {
            LOGGER.log(Level.FINE, ()->String.format("Support for MP REST.request metric and annotation handling explicitly set to %b in configuration", explicitRestEndpointsSetting.get()));
        } else {
            LOGGER.log(Level.FINE, ()->String.format("Support for MP REST.request metric and annotation handling defaulted to %b", REST_ENDPOINTS_METRIC_ENABLED_DEFAULT_VALUE));
        }
        return result;
    }

    private void recordAnnotatedGaugeSite(@Observes
    ProcessManagedBean<?> pmb) {
        AnnotatedType<?> type = pmb.getAnnotatedBeanClass();
        Class<?> clazz = type.getJavaClass();
        LOGGER.log(Level.FINE, ()->"recordAnnotatedGaugeSite for class " + clazz);
        LOGGER.log(Level.FINE, ()->"Processing annotations for " + clazz.getName());
        if (Modifier.isAbstract(clazz.getModifiers())) {
            return;
        }
        for (AnnotatedMethod<?> method : type.getMethods()) {
            Method javaMethod = method.getJavaMember();
            if (!javaMethod.getDeclaringClass().equals(clazz) || Modifier.isPrivate(javaMethod.getModifiers())) {
                continue;
            }
            MetricUtil.metricsAnnotationsOnElement(method, Gauge.class, stereotypeMetricsInfo).forEach((gaugeAnnotation)->{
                Class<? extends Annotation> scopeAnnotation = pmb.getBean().getScope();
                if (scopeAnnotation == RequestScoped.class) {
                    errors.fatal(clazz, "Cannot configure @Gauge on a request scoped bean");
                    return;
                }
                if (scopeAnnotation != ApplicationScoped.class && type.getAnnotation(Singleton.class) == null) {
                    if (ConfigProvider.getConfig().getOptionalValue("metrics.warn-dependent", Boolean.class).orElse(true)) {
                        LOGGER.warning(String.format("                                     @Gauge is configured\"\n                      + \" on a bean %s that is neither ApplicationScoped nor \"\n                      + \"                                     Singleton. This is most\"\n                      + \" likely a bug. You may set \'metrics.warn-dependent\' \"\n                      + \"                                     configuration option to\"\n                      + \" \'false\' to remove this warning.", clazz.getName()));
                    }
                }
                String explicitGaugeName = gaugeAnnotation.name();
                String gaugeNameSuffix = (explicitGaugeName.length() > 0 ? explicitGaugeName : javaMethod.getName());
                String gaugeName = (gaugeAnnotation.absolute() ? gaugeNameSuffix : String.format("%s.%s", clazz.getName(), gaugeNameSuffix));
                annotatedGaugeSites.put(new MetricID(gaugeName, tags(gaugeAnnotation.tags())), method);
                LOGGER.log(Level.FINE, ()->String.format("Recorded annotated gauge with name %s", gaugeName));
            });
        }
    }

    private void registerAnnotatedGauges(BeanManager bm) {
        LOGGER.log(Level.FINE, ()->"registerGauges");
        MetricRegistry registry = getMetricRegistry();
        List<Exception> gaugeProblems = new ArrayList<>();
        annotatedGaugeSites.entrySet().forEach((gaugeSite)->{
            LOGGER.log(Level.FINE, ()->"gaugeSite " + gaugeSite.toString());
            MetricID gaugeID = gaugeSite.getKey();
            AnnotatedMethod<?> site = gaugeSite.getValue();
            DelegatingGauge<? extends Number> dg;
            try {
                dg = buildDelegatingGauge(gaugeID.getName(), site, bm);
                Gauge gaugeAnnotation = siteAnnotation(site, Gauge.class);
                if (gaugeAnnotation == null) {
                    gaugeProblems.add(new IllegalArgumentException(String.format("Unable to find expected @Gauge annotation at previously-identified site %s; ignoring site", site.getJavaMember())));
                } else {
                    Metadata md = Metadata.builder().withName(gaugeID.getName()).withDisplayName(gaugeAnnotation.displayName()).withDescription(gaugeAnnotation.description()).withType(MetricType.GAUGE).withUnit(gaugeAnnotation.unit()).build();
                    LOGGER.log(Level.FINE, ()->String.format("Registering gauge with metadata %s", md.toString()));
                    registry.register(md, dg, gaugeID.getTagsAsList().toArray(new Tag[0]));
                }
            } catch (Throwable t) {
                gaugeProblems.add(new IllegalArgumentException(String.format("Error processing @Gauge annotation on %s#%s: %s", site.getJavaMember().getDeclaringClass().getName(), site.getJavaMember().getName(), t.getMessage()), t));
            }
        });
        if (!gaugeProblems.isEmpty()) {
            throw new RuntimeException("Could not process one or more @Gauge annotations" + gaugeProblems);
        }
        annotatedGaugeSites.clear();
    }

    private <T extends Annotation>T siteAnnotation(Annotated site, Class<T> annotationType) {
        Annotation result = site.getAnnotation(annotationType);
        if (result != null) {
            return annotationType.cast(result);
        }
        for (Annotation a : site.getAnnotations()) {
            if (isStereotype(a)) {
                StereotypeMetricsInfo info = stereotypeMetricsInfo.get(a.annotationType());
                if (info != null) {
                    for (Annotation annotationOnStereotype : info.metricsAnnotations()) {
                        if (annotationType.isInstance(annotationOnStereotype)) {
                            return annotationType.cast(annotationOnStereotype);
                        }
                    }
                }
            }
        }
        return null;
    }

    private DelegatingGauge<? extends Number> buildDelegatingGauge(String gaugeName, AnnotatedMethod<?> site, BeanManager bm) {
        Bean<?> bean = bm.getBeans(site.getJavaMember().getDeclaringClass()).stream().findFirst().orElseThrow(()->new IllegalArgumentException("Cannot find bean for annotated gauge " + gaugeName));
        Class<?> returnType = site.getJavaMember().getReturnType();
        Class<? extends Number> narrowedReturnType = typeToNumber(returnType);
        return DelegatingGauge.newInstance(site.getJavaMember(), getReference(bm, bean.getBeanClass(), bean), narrowedReturnType);
    }

    @SuppressWarnings("unchecked")
    private static Class<? extends Number> typeToNumber(Class<?> clazz) {
        Class<? extends Number> narrowedReturnType;
        if (byte.class.isAssignableFrom(clazz)) {
            narrowedReturnType = Byte.class;
        } else if (short.class.isAssignableFrom(clazz)) {
            narrowedReturnType = Short.class;
        } else if (int.class.isAssignableFrom(clazz)) {
            narrowedReturnType = Integer.class;
        } else if (long.class.isAssignableFrom(clazz)) {
            narrowedReturnType = Long.class;
        } else if (float.class.isAssignableFrom(clazz)) {
            narrowedReturnType = Float.class;
        } else if (double.class.isAssignableFrom(clazz)) {
            narrowedReturnType = Double.class;
        } else if (Number.class.isAssignableFrom(clazz)) {
            narrowedReturnType = (Class<? extends Number>)clazz;
        } else {
            throw new IllegalArgumentException("Annotated gauge type must extend or be assignment-compatible with Number but is " + clazz.getName());
        }
        return narrowedReturnType;
    }

    class StereotypeMetricsInfo {
        private final Set<Annotation> metricsAnnotations;

        static StereotypeMetricsInfo create(Set<Annotation> metricsAnnotations) {
            return new StereotypeMetricsInfo(metricsAnnotations);
        }
    }
}
主要言語
Java
スター
6.2k
フォーク
936
平均マージ
6分
マージ済み PR(30日)
3

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

google/google-java-format のほかの issue

google/google-java-format の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。