001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.metrics.micrometer;
018
019import org.apache.juneau.http.response.*;
020import org.apache.juneau.rest.server.*;
021import org.apache.juneau.rest.server.servlet.*;
022
023/**
024 * Mixin flavor of the {@code /metrics} endpoint:  renders a Prometheus scrape from a consumer-provided
025 * Micrometer {@link io.micrometer.core.instrument.MeterRegistry} resolved from the host bean store.
026 *
027 * <p>
028 * Composes into a host resource via {@link Rest#mixins() @Rest(mixins=MetricsMixin.class)} and delegates to
029 * a shared {@link MetricsManager} worker.  When no registry bean is present, or the registry is not a
030 * Prometheus registry, the endpoint degrades to HTTP 501 (Not Implemented) rather than failing &mdash;
031 * consistent with {@code micrometer-core} being {@code provided} and the registry choice being the
032 * consumer's.
033 *
034 * <h5 class='section'>See Also:</h5><ul>
035 *    <li class='jc'>{@link MetricsManager}
036 *    <li class='jc'>{@link MicrometerMetricsRecorder}
037 * </ul>
038 *
039 * @since 10.0.0
040 */
041@Rest
042public class MetricsMixin extends RestMixin {
043
044   private final MetricsManager manager = new MetricsManager();
045
046   /**
047    * [GET /metrics] - Prometheus scrape of the consumer-provided MeterRegistry.
048    *
049    * @param req The HTTP request.
050    * @return The Prometheus exposition text.
051    * @throws NotImplemented If no scrapeable {@link io.micrometer.core.instrument.MeterRegistry} bean is registered.
052    */
053   @RestGet(
054      path="/metrics",
055      summary="Metrics scrape",
056      description="Renders a Prometheus text scrape from a consumer-provided Micrometer MeterRegistry bean."
057   )
058   public String getMetrics(RestRequest req) {
059      var registry = manager.resolveRegistry(req.getContext());
060      var scrape = manager.scrape(registry);
061      if (scrape == null)
062         throw new NotImplemented("No scrapeable MeterRegistry is registered (a PrometheusMeterRegistry bean is required).");
063      return scrape;
064   }
065}