diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java index f349e18d2f4..e7805eaa322 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java @@ -19,6 +19,7 @@ public interface MetricsConstants { String PROTOCOL_PROMETHEUS = "prometheus"; + String PROTOCOL_DEFAULT = "default"; String TAG_IP = "ip"; diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java index a7eede5af99..06579caa9bd 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java @@ -56,6 +56,7 @@ import org.apache.dubbo.metrics.config.event.ConfigCenterEvent; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; +import org.apache.dubbo.metrics.report.DefaultMetricsReporterFactory; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.metrics.report.MetricsReporterFactory; import org.apache.dubbo.metrics.service.MetricsServiceExporter; @@ -92,11 +93,13 @@ import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_EXECUTE_DESTROY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_INIT_CONFIG_CENTER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_START_MODEL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_REFRESH_INSTANCE_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_REGISTER_INSTANCE_ERROR; +import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_DEFAULT; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; @@ -369,25 +372,45 @@ private void initMetricsService() { } private void initMetricsReporter() { + if (!isSupportMetrics()) { + return; + } DefaultMetricsCollector collector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); Optional configOptional = configManager.getMetrics(); - - // TODO compatible with old usage of metrics, remove protocol check after new metrics is ready for use. - if (!isSupportPrometheus()) { - return; - } + //If no specific metrics type is configured and there is no Prometheus dependency in the dependencies. MetricsConfig metricsConfig = configOptional.orElse(new MetricsConfig(applicationModel)); if (StringUtils.isBlank(metricsConfig.getProtocol())) { - metricsConfig.setProtocol(PROTOCOL_PROMETHEUS); + metricsConfig.setProtocol(isSupportPrometheus() ? PROTOCOL_PROMETHEUS : PROTOCOL_DEFAULT); } collector.setCollectEnabled(true); collector.collectApplication(); collector.setThreadpoolCollectEnabled(Optional.ofNullable(metricsConfig.getEnableThreadpool()).orElse(true)); MetricsReporterFactory metricsReporterFactory = getExtensionLoader(MetricsReporterFactory.class).getAdaptiveExtension(); - MetricsReporter metricsReporter = metricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); + MetricsReporter metricsReporter = null; + try { + metricsReporter = metricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); + } catch (IllegalStateException e) { + if (e.getMessage().startsWith("No such extension org.apache.dubbo.metrics.report.MetricsReporterFactory")) { + logger.warn(COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", e.getMessage()); + return; + } else { + throw e; + } + } metricsReporter.init(); applicationModel.getBeanFactory().registerBean(metricsReporter); + //If the protocol is not the default protocol, the default protocol is also initialized. + if (!PROTOCOL_DEFAULT.equals(metricsConfig.getProtocol())) { + DefaultMetricsReporterFactory defaultMetricsReporterFactory = new DefaultMetricsReporterFactory(applicationModel); + MetricsReporter defaultMetricsReporter = defaultMetricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); + defaultMetricsReporter.init(); + applicationModel.getBeanFactory().registerBean(defaultMetricsReporter); + } + } + + public boolean isSupportMetrics() { + return isClassPresent("io.micrometer.core.instrument.MeterRegistry"); } public static boolean isSupportPrometheus() { diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsReporter.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsReporter.java index c24d7b52a73..14bdc70217a 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsReporter.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsReporter.java @@ -31,4 +31,8 @@ public interface MetricsReporter { void refreshData(); String getResponse(); + + default String getResponseWithName(String metricsName) { + return null; + } } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporter.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporter.java new file mode 100644 index 00000000000..88e16ec56c1 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporter.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.report; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import java.util.concurrent.TimeUnit; + +public class DefaultMetricsReporter extends AbstractMetricsReporter { + + SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); + + protected DefaultMetricsReporter(URL url, ApplicationModel applicationModel) { + super(url, applicationModel); + } + + @Override + public String getResponse() { + return null; + } + + @Override + public String getResponseWithName(String metricsName) { + Map> metricsTags = new HashMap<>(); + Map metricsValue = new HashMap<>(); + StringBuilder sb = new StringBuilder(); + meterRegistry.getMeters().stream().filter(meter -> { + if (meter == null || meter.getId() == null || meter.getId().getName() == null) { + return false; + } + if (metricsName != null) { + return meter.getId().getName().contains(metricsName); + } + return true; + }).forEach(meter -> { + Object value = null; + if (meter instanceof Counter) { + Counter counter = (Counter) meter; + value = counter.count(); + } + if (meter instanceof Gauge) { + Gauge gauge = (Gauge) meter; + value = gauge.value(); + } + if (meter instanceof Timer) { + Timer timer = (Timer) meter; + value = timer.totalTime(TimeUnit.MILLISECONDS); + } + metricsTags.put(meter.getId().getName(), meter.getId().getTags()); + metricsValue.put(meter.getId().getName(), value); + }); + metricsValue.forEach((key, value) -> { + sb.append(key).append("{"); + List tags = metricsTags.get(key); + if (tags != null && tags.size() > 0) { + tags.forEach(tag -> { + sb.append(tag.getKey()).append("=").append(tag.getValue()).append(","); + }); + } + sb.append("} ").append(value).append(System.lineSeparator()); + }); + return sb.toString(); + } + + @Override + protected void doInit() { + addMeterRegistry(meterRegistry); + } + + @Override + protected void doDestroy() { + + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporterFactory.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporterFactory.java new file mode 100644 index 00000000000..504d964db68 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporterFactory.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.report; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.model.ApplicationModel; + +public class DefaultMetricsReporterFactory extends AbstractMetricsReporterFactory { + private final ApplicationModel applicationModel; + + public DefaultMetricsReporterFactory(ApplicationModel applicationModel) { + super(applicationModel); + this.applicationModel = applicationModel; + } + + @Override + public MetricsReporter createMetricsReporter(URL url) { + return new DefaultMetricsReporter(url, applicationModel); + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector b/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector index 74d78cc1f79..d174a4291f3 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector +++ b/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector @@ -1 +1,3 @@ default-collector=org.apache.dubbo.metrics.collector.DefaultMetricsCollector +aggregateMetricsCollector=org.apache.dubbo.metrics.collector.AggregateMetricsCollector +histogramMetricsCollector=org.apache.dubbo.metrics.collector.HistogramMetricsCollector diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory b/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory new file mode 100644 index 00000000000..51051c13816 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory @@ -0,0 +1 @@ +default=org.apache.dubbo.metrics.report.DefaultMetricsReporterFactory diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java index b41d68c998d..baf50dbd6e2 100644 --- a/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java +++ b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java @@ -109,7 +109,7 @@ public boolean logResult() { private String getResponseByApplication(ApplicationModel applicationModel) { String response = "MetricsReporter not init"; - MetricsReporter metricsReporter = applicationModel.getBeanFactory().getBean(MetricsReporter.class); + MetricsReporter metricsReporter = applicationModel.getBeanFactory().getBean(PrometheusMetricsReporter.class); if (metricsReporter != null) { long begin = System.currentTimeMillis(); if (logger.isDebugEnabled()) { diff --git a/dubbo-plugin/dubbo-qos/pom.xml b/dubbo-plugin/dubbo-qos/pom.xml index 3a84b3a798e..6463faf5c25 100644 --- a/dubbo-plugin/dubbo-qos/pom.xml +++ b/dubbo-plugin/dubbo-qos/pom.xml @@ -75,5 +75,10 @@ dubbo-qos-api ${project.version} + + org.apache.dubbo + dubbo-metrics-default + ${project.version} + diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DefaultMetricsReporterCmd.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DefaultMetricsReporterCmd.java new file mode 100644 index 00000000000..de8ec4b9048 --- /dev/null +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DefaultMetricsReporterCmd.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.qos.command.impl; + +import org.apache.dubbo.common.utils.JsonUtils; +import org.apache.dubbo.metrics.report.DefaultMetricsReporter; +import org.apache.dubbo.metrics.report.MetricsReporter; +import org.apache.dubbo.qos.api.BaseCommand; +import org.apache.dubbo.qos.api.Cmd; +import org.apache.dubbo.qos.api.CommandContext; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Cmd(name = "metrics_default", summary = "display metrics information") +public class DefaultMetricsReporterCmd implements BaseCommand { + + public FrameworkModel frameworkModel; + + public DefaultMetricsReporterCmd(FrameworkModel frameworkModel) { + this.frameworkModel = frameworkModel; + } + + @Override + public String execute(CommandContext commandContext, String[] args) { + List models = frameworkModel.getApplicationModels(); + String result = "There is no application with data"; + if (notSpecifyApplication(args)) { + result = useFirst(models, result, null); + } else if (args.length == 1) { + result = specifyApplication(args[0], models, null); + } else if (args.length == 2) { + result = specifyApplication(args[0], models, args[1]); + } + return result; + } + + private boolean notSpecifyApplication(String[] args) { + return args == null || args.length == 0; + } + + private String useFirst(List models, String result, String metricsName) { + for (ApplicationModel model : models) { + String current = getResponseByApplication(model, metricsName); + if (current != null && getLineNumber(current) > 0) { + result = current; + break; + } + } + return result; + } + + private String specifyApplication(String appName, List models, String metricsName) { + if ("application_all".equals(appName)) { + return allApplication(models); + } else { + return specifySingleApplication(appName, models, metricsName); + } + } + + private String specifySingleApplication(String appName, List models, String metricsName) { + Optional modelOptional = models.stream() + .filter(applicationModel -> appName.equals(applicationModel.getApplicationName())).findFirst(); + if (modelOptional.isPresent()) { + return getResponseByApplication(modelOptional.get(), metricsName); + } else { + return "Not exist application: " + appName; + } + } + + private String allApplication(List models) { + Map appResultMap = new HashMap<>(); + for (ApplicationModel model : models) { + appResultMap.put(model.getApplicationName(), getResponseByApplication(model, null)); + } + return JsonUtils.toJson(appResultMap); + } + + private String getResponseByApplication(ApplicationModel applicationModel, String metricsName) { + String response = "DefaultMetricsReporter not init"; + MetricsReporter metricsReporter = applicationModel.getBeanFactory().getBean(DefaultMetricsReporter.class); + if (metricsReporter != null) { + metricsReporter.refreshData(); + response = metricsReporter.getResponseWithName(metricsName); + } + return response; + } + + + private static long getLineNumber(String content) { + LineNumberReader lnr = new LineNumberReader(new CharArrayReader(content.toCharArray())); + try { + lnr.skip(Long.MAX_VALUE); + lnr.close(); + } catch (IOException ignore) { + } + return lnr.getLineNumber(); + } +} diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand index d575733b465..fb1e1171fac 100644 --- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand +++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand @@ -37,3 +37,4 @@ serializeWarnedClasses=org.apache.dubbo.qos.command.impl.SerializeWarnedClasses getConfig=org.apache.dubbo.qos.command.impl.GetConfig getAddress=org.apache.dubbo.qos.command.impl.GetAddress gracefulShutdown=org.apache.dubbo.qos.command.impl.GracefulShutdown +metrics_default=org.apache.dubbo.qos.command.impl.DefaultMetricsReporterCmd diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java index 0370e519eff..d5b82d11345 100644 --- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java +++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java @@ -19,6 +19,7 @@ import org.apache.dubbo.qos.command.GreetingCommand; import org.apache.dubbo.qos.command.impl.ChangeTelnet; import org.apache.dubbo.qos.command.impl.CountTelnet; +import org.apache.dubbo.qos.command.impl.DefaultMetricsReporterCmd; import org.apache.dubbo.qos.command.impl.DisableDetailProfiler; import org.apache.dubbo.qos.command.impl.DisableRouterSnapshot; import org.apache.dubbo.qos.command.impl.DisableSimpleProfiler; @@ -125,6 +126,7 @@ void testGetAllCommandClass() { expectedClasses.add(GetConfig.class); expectedClasses.add(GetAddress.class); expectedClasses.add(GracefulShutdown.class); + expectedClasses.add(DefaultMetricsReporterCmd.class); assertThat(classes, containsInAnyOrder(expectedClasses.toArray(new Class[0]))); }