diff --git a/LICENSES/vendor/io.micrometer/micrometer-registry-prometheus/LICENSE b/LICENSES/vendor/io.micrometer/micrometer-registry-prometheus/LICENSE new file mode 100644 index 0000000000..9b259bdfcf --- /dev/null +++ b/LICENSES/vendor/io.micrometer/micrometer-registry-prometheus/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed 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 + + https://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. diff --git a/NOTICE b/NOTICE index 81c381c2e9..2623ad4220 100644 --- a/NOTICE +++ b/NOTICE @@ -102,6 +102,7 @@ api (io.envoyproxy.controlplane:api) - https://github.com/envoyproxy/java-contro grpc-netty (io.grpc:grpc-netty) https://github.com/grpc/grpc-java grpc-stub (io.grpc:grpc-stub) https://github.com/grpc/grpc-java grpc-protobuf (io.grpc:grpc-protobuf) https://github.com/grpc/grpc-java +micrometer-registry-prometheus (io.micrometer:micrometer-registry-prometheus) https://docs.micrometer.io/micrometer/reference/implementations/prometheus.html - The works of other projects included in this project. diff --git a/sermant-agentcore/sermant-agentcore-config/config/config.properties b/sermant-agentcore/sermant-agentcore-config/config/config.properties index 260f42c7f2..b6e079a72e 100644 --- a/sermant-agentcore/sermant-agentcore-config/config/config.properties +++ b/sermant-agentcore/sermant-agentcore-config/config/config.properties @@ -38,6 +38,8 @@ agent.service.httpserver.enable=false agent.service.xds.service.enable=false # dynamic mount service switch agent.service.hot.plugging.service.enable=false +# Metric service switch +agent.service.metric.enable=false #============================= Event configuration =============================# # Event switch event.enable=false @@ -114,4 +116,9 @@ service.meta.project=default # Environment for host service instance, used in service governance scenarios such as service registration and label routing service.meta.environment= # Availability Zone for host service instance, used in service governance scenarios such as service registration and label routing -service.meta.zone= \ No newline at end of file +service.meta.zone= +#=============================metric configuration===============================# +# The metric type, currently supports prometheus. +metric.type=prometheus +# The maximum number of metrics. +metric.maxTimeSeries=1000 diff --git a/sermant-agentcore/sermant-agentcore-config/config/test/config.properties b/sermant-agentcore/sermant-agentcore-config/config/test/config.properties index 0e66f48135..8c9ed5c728 100644 --- a/sermant-agentcore/sermant-agentcore-config/config/test/config.properties +++ b/sermant-agentcore/sermant-agentcore-config/config/test/config.properties @@ -36,6 +36,8 @@ agent.service.dynamic.config.enable=true agent.service.httpserver.enable=false # xDS service switch agent.service.xds.service.enable=false +# Metric service switch +agent.service.metric.enable=false #============================= Event configuration =============================# # Event switch event.enable=false @@ -112,4 +114,9 @@ service.meta.project=default # Environment for host service instance, used in service governance scenarios such as service registration and label routing service.meta.environment= # Availability Zone for host service instance, used in service governance scenarios such as service registration and label routing -service.meta.zone= \ No newline at end of file +service.meta.zone= +#=============================metric configuration===============================# +# The metric type, currently supports prometheus. +metric.type=prometheus +# The maximum number of metrics. +metric.maxTimeSeries=1000 \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceConfig.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceConfig.java index 69a131062b..63259f0461 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceConfig.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceConfig.java @@ -52,6 +52,9 @@ public class ServiceConfig implements BaseConfig { @ConfigFieldKey("hot.plugging.service.enable") private boolean hotPluggingServiceEnable = false; + @ConfigFieldKey("metric.enable") + private boolean metricEnable = false; + public boolean isHeartBeatEnable() { return heartBeatEnable; } @@ -116,6 +119,14 @@ public void setHotPluggingServiceEnable(boolean hotPluggingServiceEnable) { this.hotPluggingServiceEnable = hotPluggingServiceEnable; } + public boolean isMetricEnable() { + return metricEnable; + } + + public void setMetricEnable(boolean metricEnable) { + this.metricEnable = metricEnable; + } + /** * Check whether the service of the given class name is enabled. * @@ -144,6 +155,9 @@ public boolean checkServiceEnable(String serviceName) { if (ServiceManager.XDS_CORE_SERVICE_IMPL.equals(serviceName)) { return isXdsServiceEnable(); } + if (ServiceManager.METRIC_SERVICE_IMPL.equals(serviceName)) { + return isMetricEnable(); + } if (ServiceManager.HOT_PLUGGING_SERVICE_IMPL.equals(serviceName)) { return isHotPluggingServiceEnable(); } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java index cee6e01003..d1e7199808 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java @@ -91,6 +91,12 @@ public class ServiceManager { public static final String XDS_CORE_SERVICE_IMPL = "io.sermant.implement.service.xds.XdsCoreServiceImpl"; + /** + * Metric Service Discover + */ + public static final String METRIC_SERVICE_IMPL = + "io.sermant.implement.service.metric.MeterMetricServiceImpl"; + /** * Hot plugging service */ diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Counter.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Counter.java new file mode 100644 index 0000000000..acc3660bbb --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Counter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed 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 + * + * https://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. + */ + +/* + * Based on io/micrometer/core/instrument/Counter.java + * from the Micrometer project. + */ + +package io.sermant.core.service.metric.api; + +/** + * metric Counter interface + * + * @author zwmagic + * @since 2024-08-16 + */ +public interface Counter { + /** + * Update the counter by one. + */ + default void increment() { + increment(1.0); + } + + /** + * Update the counter by {@code amount}. + * + * @param amount Amount to add to the counter. + */ + void increment(double amount); + + /** + * get the counter value + * + * @return The cumulative count since this counter was created. + */ + double count(); +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/DistributionStatisticConfig.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/DistributionStatisticConfig.java new file mode 100644 index 0000000000..8545db5382 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/DistributionStatisticConfig.java @@ -0,0 +1,226 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed 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 + * + * https://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. + */ + +/* + * Based on io/micrometer/core/instrument/distribution/DistributionStatisticConfig.java + * from the Micrometer project. + */ + +package io.sermant.core.service.metric.api; + +import java.time.Duration; + +/** + * DistributionStatisticConfig + * + * @author zwmagic + * @since 2024-08-17 + */ +public class DistributionStatisticConfig { + private static final Integer EXPIRY_MINUTE = 2; + + /** + * Defines a static final constant named DEFAULT, which represents a distribution statistic configuration. This + * configuration controls and describes various aspects of data distribution statistics, such as the publication of + * percentile histograms, the precision of percentiles, and the expected value range. These configurations are + * useful for monitoring and analyzing data distributions, particularly in system performance monitoring and log + * analysis. + */ + public static final DistributionStatisticConfig DEFAULT = + new DistributionStatisticConfig().publishPercentileHistogram(false) + .percentilePrecision(1).minimumExpectedValue(1.0).maximumExpectedValue(Double.POSITIVE_INFINITY) + .distributionStatisticExpiry(Duration.ofMinutes(EXPIRY_MINUTE)) + .distributionStatisticBufferLength(3); + + private Boolean percentileHistogram; + + private double[] percentiles; + + private Integer percentilePrecision; + + private Double minimumExpectedValue; + + private Double maximumExpectedValue; + + private Duration expiry; + + private Integer bufferLength; + + /** + * Gets the percentile histogram flag. + * + * @return the percentile histogram flag + */ + public Boolean getPercentileHistogram() { + return percentileHistogram; + } + + /** + * Gets the array of percentiles. + * + * @return the array of percentiles + */ + public double[] getPercentiles() { + return percentiles; + } + + /** + * Gets the precision for percentiles. + * + * @return the precision for percentiles + */ + public Integer getPercentilePrecision() { + return percentilePrecision; + } + + /** + * Gets the minimum expected value. + * + * @return the minimum expected value + */ + public Double getMinimumExpectedValue() { + return minimumExpectedValue; + } + + /** + * Gets the maximum expected value. + * + * @return the maximum expected value + */ + public Double getMaximumExpectedValue() { + return maximumExpectedValue; + } + + /** + * Gets the expiry duration. + * + * @return the expiry duration + */ + public Duration getExpiry() { + return expiry; + } + + /** + * Gets the length of the buffer. + * + * @return the length of the buffer + */ + public Integer getBufferLength() { + return bufferLength; + } + + /** + * Produces an additional time series for each requested percentile. This percentile is computed locally, and so + * can't be aggregated with percentiles computed across other dimensions (e.g. in a different instance). Use + * {@link #publishPercentileHistogram()} to publish a histogram that can be used to generate aggregable percentile + * approximations. + * + * @param percentilesValue Percentiles to compute and publish. The 95th percentile should be expressed as + * {@code 0.95}. + * @return DistributionStatisticConfig + */ + public DistributionStatisticConfig publishPercentiles(double[] percentilesValue) { + this.percentiles = percentilesValue; + return this; + } + + /** + * Adds histogram buckets used to generate aggregable percentile approximations in monitoring systems that have + * query facilities to do so (e.g. Prometheus' {@code histogram_quantile}, Atlas' {@code :percentiles}). + * + * @return DistributionStatisticConfig + */ + public DistributionStatisticConfig publishPercentileHistogram() { + return publishPercentileHistogram(true); + } + + /** + * Adds histogram buckets used to generate aggregable percentile approximations in monitoring systems that have + * query facilities to do so (e.g. Prometheus' {@code histogram_quantile}, Atlas' {@code :percentiles}). + * + * @param enabled Determines whether percentile histograms should be published. + * @return DistributionStatisticConfig + */ + public DistributionStatisticConfig publishPercentileHistogram(Boolean enabled) { + this.percentileHistogram = enabled; + return this; + } + + /** + * Determines the number of digits of precision to maintain on the dynamic range histogram used to compute + * percentile approximations. The higher the degrees of precision, the more accurate the approximation is at the + * cost of more memory. + * + * @param digitsOfPrecision The digits of precision to maintain for percentile approximations. + * @return DistributionStatisticConfig + */ + public DistributionStatisticConfig percentilePrecision(Integer digitsOfPrecision) { + this.percentilePrecision = digitsOfPrecision; + return this; + } + + /** + * Sets the minimum value that this distribution summary is expected to observe. Sets a lower bound on histogram + * buckets that are shipped to monitoring systems that support aggregable percentile approximations. + * + * @param min The minimum value that this distribution summary is expected to observe. + * @return This builder. + */ + public DistributionStatisticConfig minimumExpectedValue(Double min) { + this.minimumExpectedValue = min; + return this; + } + + /** + * Sets the maximum value that this distribution summary is expected to observe. Sets an upper bound on histogram + * buckets that are shipped to monitoring systems that support aggregable percentile approximations. + * + * @param max The maximum value that this distribution summary is expected to observe. + * @return DistributionStatisticConfig + */ + public DistributionStatisticConfig maximumExpectedValue(Double max) { + this.maximumExpectedValue = max; + return this; + } + + /** + * Statistics emanating from a distribution summary like max, percentiles, and histogram counts decay over time to + * give greater weight to recent samples (exception: histogram counts are cumulative for those systems that expect + * cumulative histogram buckets). Samples are accumulated to such statistics in ring buffers which rotate after this + * expiry, with a buffer length of {@link #distributionStatisticBufferLength(Integer)}. + * + * @param expiryDuration The amount of time samples are accumulated to a histogram before it is reset and rotated. + * @return DistributionStatisticConfig + */ + public DistributionStatisticConfig distributionStatisticExpiry(Duration expiryDuration) { + this.expiry = expiryDuration; + return this; + } + + /** + * Statistics emanating from a distribution summary like max, percentiles, and histogram counts decay over time to + * give greater weight to recent samples (exception: histogram counts are cumulative for those systems that expect + * cumulative histogram buckets). Samples are accumulated to such statistics in ring buffers which rotate after + * {@link #distributionStatisticExpiry(Duration)}, with this buffer length. + * + * @param bufferLengthValue The number of histograms to keep in the ring buffer. + * @return DistributionStatisticConfig + */ + public DistributionStatisticConfig distributionStatisticBufferLength(Integer bufferLengthValue) { + this.bufferLength = bufferLengthValue; + return this; + } +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Gauge.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Gauge.java new file mode 100644 index 0000000000..74320a493b --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Gauge.java @@ -0,0 +1,85 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed 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 + * + * https://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. + */ + +/* + * Based on io/micrometer/core/instrument/MeterRegistry.java + * from the Micrometer project. + */ + +package io.sermant.core.service.metric.api; + +import java.util.Collection; +import java.util.Map; +import java.util.function.ToDoubleFunction; + +/** + * metric + * + * @author zwmagic + * @since 2024-08-16 + */ +public interface Gauge { + /** + * Register a gauge that reports the value of the {@link Number}. + * + * @param number Thread-safe implementation of {@link Number} used to access the value. + * @param The type of the state object from which the gauge value is extracted. + * @return The number that was passed in so the registration can be done as part of an assignment statement. + */ + default T gaugeNumber(Number number) { + return (T) gaugeState(number, Number::doubleValue); + } + + /** + * Register a gauge that reports the size of the {@link Collection}. The registration will keep a weak reference to + * the collection so it will not prevent garbage collection. The collection implementation used should be thread + * safe. Note that calling {@link Collection#size()} can be expensive for some collection implementations and should + * be considered before registering. + * + * @param collection Thread-safe implementation of {@link Collection} used to access the value. + * @param The type of the state object from which the gauge value is extracted. + * @return The Collection that was passed in so the registration can be done as part of an assignment statement. + */ + default > T gaugeCollectionSize(T collection) { + return gaugeState(collection, Collection::size); + } + + /** + * Register a gauge that reports the size of the {@link Map}. The registration will keep a weak reference to the + * collection so it will not prevent garbage collection. The collection implementation used should be thread safe. + * Note that calling {@link Map#size()} can be expensive for some collection implementations and should be + * considered before registering. + * + * @param map Thread-safe implementation of {@link Map} used to access the value. + * @param The type of the state object from which the gauge value is extracted. + * @return The Map that was passed in so the registration can be done as part of an assignment statement. + */ + default > T gaugeMapSize(T map) { + return gaugeState(map, Map::size); + } + + /** + * Register a gauge that reports the value of the object after the function {@code valueFunction} is applied. The + * registration will keep a weak reference to the object so it will not prevent garbage collection. Applying + * {@code valueFunction} on the object should be thread safe. + * + * @param stateObject State object used to compute a value. + * @param valueFunction Function that produces an instantaneous gauge value from the state object. + * @param The type of the state object from which the gauge value is extracted. + * @return The state object that was passed in so the registration can be done as part of an assignment statement. + */ + T gaugeState(T stateObject, ToDoubleFunction valueFunction); +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Metric.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Metric.java new file mode 100644 index 0000000000..9385ecf9ce --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Metric.java @@ -0,0 +1,74 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed 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 + * + * https://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. + */ + +/* + * Based on io/micrometer/core/instrument/MeterRegistry.java + * from the Micrometer project. + */ + +package io.sermant.core.service.metric.api; + +/** + * The Metric interface defines methods for creating different types of metrics objects, used for monitoring and + * measuring application performance metrics. It supports Counter, Gauge, and Timer metric types. + * + * @author zwmagic + * @since 2024-08-16 + */ +public interface Metric { + /** + * Creates a tagged counter with a description to record the count of a specific metric. + * + * @param metricName the name of the metric + * @param tags tags that further refine the metric data + * @param description a description of the counter's purpose + * @return the created counter object + */ + Counter counter(String metricName, Tags tags, String description); + + /** + * Creates a tagged gauge with a description to represent the current value of a specific metric. + * + * @param metricName the name of the metric + * @param tags tags that further refine the metric data + * @param description a description of the gauge's purpose + * @return the created gauge object + */ + Gauge gauge(String metricName, Tags tags, String description); + + /** + * Creates a tagged timer with a description to measure the execution time of a specific metric. + * + * @param metricName the name of the metric + * @param tags tags that further refine the metric data + * @param description a description of the timer's purpose + * @return the created timer object + */ + Timer timer(String metricName, Tags tags, String description); + + /** + * Creates a Summary metric to track distribution statistics. + * + * @param metricName the name of the metric to identify the monitoring metric + * @param tags a set of tags for categorizing and filtering the metric + * @param description a description to explain the purpose and meaning of the metric + * @param distributionStatisticConfig the configuration defining what statistics to track + * @return the created Summary metric instance + */ + Summary summary(String metricName, Tags tags, String description, + DistributionStatisticConfig distributionStatisticConfig); +} + diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/MetricService.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/MetricService.java new file mode 100644 index 0000000000..ccb154d3a1 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/MetricService.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.core.service.metric.api; + +import io.sermant.core.service.BaseService; + +/** + * MetricService + * + * @author zwmagic + * @since 2024-08-19 + */ +public interface MetricService extends BaseService { + + /** + * Creates a counter to measure the number of occurrences of an event. + * + * @param metricName The name of the metric. + * @return The created counter instance. + */ + Counter counter(String metricName); + + /** + * Creates a tagged counter to measure the number of occurrences of an event with specific tags. + * + * @param metricName The name of the metric. + * @param tagKey The tag key. + * @param tagValue The tag value. + * @return The created tagged counter instance. + */ + Counter counter(String metricName, String tagKey, String tagValue); + + /** + * Creates a counter with a set of tags to measure the number of occurrences of an event with specific tags. + * + * @param metricName The name of the metric. + * @param tags The set of tags. + * @return The created counter instance with tags. + */ + Counter counter(String metricName, Tags tags); + + /** + * Creates a counter with a set of tags and a description to measure the number of occurrences of an event. + * + * @param metricName The name of the metric. + * @param tags The set of tags. + * @param description The description of the counter. + * @return The created counter instance with tags and description. + */ + Counter counter(String metricName, Tags tags, String description); + + /** + * Creates a gauge to measure the instantaneous value of a metric. + * + * @param metricName The name of the metric. + * @return The created gauge instance. + */ + Gauge gauge(String metricName); + + /** + * Creates a tagged gauge to measure the instantaneous value of a metric with specific tags. + * + * @param metricName The name of the metric. + * @param tagKey The tag key. + * @param tagValue The tag value. + * @return The created tagged gauge instance. + */ + Gauge gauge(String metricName, String tagKey, String tagValue); + + /** + * Creates a gauge with a set of tags to measure the instantaneous value of a metric with specific tags. + * + * @param metricName The name of the metric. + * @param tags The set of tags. + * @return The created gauge instance with tags. + */ + Gauge gauge(String metricName, Tags tags); + + /** + * Creates a gauge with a set of tags and a description to measure the instantaneous value of a metric. + * + * @param metricName The name of the metric. + * @param tags The set of tags. + * @param description The description of the gauge. + * @return The created gauge instance with tags and description. + */ + Gauge gauge(String metricName, Tags tags, String description); + + /** + * Creates a timer to measure the duration of an operation. + * + * @param metricName The name of the metric. + * @return The created timer instance. + */ + Timer timer(String metricName); + + /** + * Creates a tagged timer to measure the duration of an operation with specific tags. + * + * @param metricName The name of the metric. + * @param tagKey The tag key. + * @param tagValue The tag value. + * @return The created tagged timer instance. + */ + Timer timer(String metricName, String tagKey, String tagValue); + + /** + * Creates a timer with a set of tags to measure the duration of an operation with specific tags. + * + * @param metricName The name of the metric. + * @param tags The set of tags. + * @return The created timer instance with tags. + */ + Timer timer(String metricName, Tags tags); + + /** + * Creates a timer with a set of tags and a description to measure the duration of an operation. + * + * @param metricName The name of the metric. + * @param tags The set of tags. + * @param description The description of the timer. + * @return The created timer instance with tags and description. + */ + Timer timer(String metricName, Tags tags, String description); + + /** + * Creates a Summary metric without any tags. + * + * @param metricName the name of the metric + * @return the created Summary metric + */ + Summary summary(String metricName); + + /** + * Creates a Summary metric and adds a single tag to it. + * + * @param metricName the name of the metric + * @param tagKey the key of the tag + * @param tagValue the value of the tag + * @return the created Summary metric + */ + Summary summary(String metricName, String tagKey, String tagValue); + + /** + * Creates a Summary metric and adds a set of tags to it. + * + * @param metricName the name of the metric + * @param tags the set of tags + * @return the created Summary metric + */ + Summary summary(String metricName, Tags tags); + + /** + * Creates a highly customizable Summary metric that can include tags, a description, and distribution statistics + * configuration. + * + * @param metricName the name of the metric + * @param tags the set of tags + * @param description the description of the metric + * @param distributionStatisticConfig the distribution statistics configuration + * @return the created Summary metric + */ + Summary summary(String metricName, Tags tags, String description, + DistributionStatisticConfig distributionStatisticConfig); + +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Summary.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Summary.java new file mode 100644 index 0000000000..79ea1e6842 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Summary.java @@ -0,0 +1,69 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed 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 + * + * https://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. + */ + +/* + * Based on io/micrometer/core/instrument/DistributionSummary.java + * from the Micrometer project. + */ + +package io.sermant.core.service.metric.api; + +/** + * The Summary interface is used to maintain statistical information about a series of events. + * + * @author zwmagic + * @since 2024-08-17 + */ +public interface Summary { + /** + * Updates the statistics kept by the summary with the specified amount. + * + * @param amount Amount for an event being measured. For example, if the size in bytes of responses from a server. + * If the amount is less than 0, the value will be dropped. + */ + void record(double amount); + + /** + * Returns the number of times that record has been called since this timer was created. + * + * @return The number of recorded events. + */ + long count(); + + /** + * Returns the total amount of all recorded events. + * + * @return The total amount. + */ + double totalAmount(); + + /** + * Returns the distribution average for all recorded events. + * + * @return The average event size. + */ + default double mean() { + long count = count(); + return count == 0 ? 0 : totalAmount() / count; + } + + /** + * Returns the maximum time of a single event. + * + * @return The maximum event size. + */ + double max(); +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Tags.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Tags.java new file mode 100644 index 0000000000..e58b301c96 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Tags.java @@ -0,0 +1,123 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed 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 + * + * https://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. + */ + +/* + * Based on io.micrometer.core.instrument/Tags.java + * from the Micrometer project. + */ + +package io.sermant.core.service.metric.api; + +import io.sermant.core.utils.MapUtils; +import io.sermant.core.utils.StringUtils; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * metric tags + * + * @author zwmagic + * @since 2024-08-16 + */ +public final class Tags { + private final Map tags = new HashMap<>(); + + private Tags() { + } + + /** + * Creates an empty Tags object. + * + * @return An empty Tags object. + */ + public static Tags of() { + return new Tags(); + } + + /** + * Creates a Tags object and adds a key-value pair. + * + * @param key The key. + * @param value The value. + * @return A Tags object with the added key-value pair. + */ + public static Tags of(String key, Object value) { + return of().add(key, value); + } + + /** + * Creates a Tags object and copies the content from another Tags object. + * + * @param tags The Tags object to copy. + * @return A new Tags object with copied content. + */ + public static Tags of(Tags tags) { + return of(tags.getTags()); + } + + /** + * Creates a Tags object initialized with a given map of key-value pairs. + * + * @param tags The map of key-value pairs. + * @return An initialized Tags object. + */ + public static Tags of(Map tags) { + Tags result = of(); + if (!MapUtils.isEmpty(tags)) { + result.tags.putAll(tags); + } + return result; + } + + /** + * Adds a key-value pair to the current Tags object. + * + * @param key The key. + * @param value The value. + * @return The current Tags object, for chaining calls. + */ + public Tags add(String key, Object value) { + if (StringUtils.isEmpty(key) || value == null) { + return this; + } + String valueStr = value instanceof String ? (String) value : String.valueOf(value); + if (StringUtils.isEmpty(valueStr)) { + return this; + } + tags.put(key, valueStr); + return this; + } + + /** + * Gets the number of key-value pairs in the current Tags object. + * + * @return The number of key-value pairs. + */ + public int getSize() { + return tags.size(); + } + + /** + * Retrieves all key-value pairs in the current Tags object as an unmodifiable map. + * + * @return An unmodifiable map of key-value pairs. + */ + public Map getTags() { + return Collections.unmodifiableMap(tags); + } +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Timer.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Timer.java new file mode 100644 index 0000000000..7d0a36c850 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Timer.java @@ -0,0 +1,77 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed 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 + * + * https://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. + */ + +/* + * Based on io/micrometer/core/instrument/Timer.java + * from the Micrometer project. + */ + +package io.sermant.core.service.metric.api; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +/** + * A timer interface for measuring the duration of events. + * + * @author zwmagic + * @since 2024-08-16 + */ +public interface Timer { + /** + * Starts the timer. + * + * @return The Timer object for further operations. + */ + Timer start(); + + /** + * Stops the timer and returns the elapsed time. + * + * @return The elapsed time in milliseconds. + */ + long stop(); + + /** + * Records a duration. + * + * @param duration The duration object to be recorded. + */ + void record(Duration duration); + + /** + * Updates the statistics stored by the timer with a specified amount. + * + * @param amount The duration of a single event; if amount is less than 0, the value will be removed. + * @param unit The time unit. + */ + void record(long amount, TimeUnit unit); + + /** + * Gets the number of times that stop has been called on this timer. + * + * @return The number of stop operations. + */ + long count(); + + /** + * Gets the total time of recorded events. + * + * @param unit The base unit of time to scale the total to. + * @return The total time of recorded events. + */ + double totalTime(TimeUnit unit); +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/config/MetricConfig.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/config/MetricConfig.java new file mode 100644 index 0000000000..38070b2fd5 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/config/MetricConfig.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.core.service.metric.config; + +import io.sermant.core.config.common.BaseConfig; +import io.sermant.core.config.common.ConfigFieldKey; +import io.sermant.core.config.common.ConfigTypeKey; + +/** + * Metric Configuration + * + * @author zwmagic + * @since 2024-08-19 + */ +@ConfigTypeKey("metric") +public class MetricConfig implements BaseConfig { + private static final int MAXIMUM_TIME_SERIES_VALUE = 1000; + + /** + * The metric type, currently supports prometheus. + */ + @ConfigFieldKey("type") + private String type = "prometheus"; + + /** + * The maximum number of metrics. + */ + @ConfigFieldKey("maxTimeSeries") + private Integer maxTimeSeries = MAXIMUM_TIME_SERIES_VALUE; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public void setMaxTimeSeries(Integer maximumTimeSeries) { + this.maxTimeSeries = maximumTimeSeries; + } + + public Integer getMaxTimeSeries() { + return maxTimeSeries; + } +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/ReflectUtils.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/ReflectUtils.java index 4510c547ea..5507a4f73b 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/ReflectUtils.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/ReflectUtils.java @@ -300,6 +300,36 @@ private static String buildMethodKey(Class clazz, String methodName, Class return sb.append(")").toString(); } + /** + * Set static field value + * + * @param clazz target class + * @param fieldName field name + * @param value value + * @return set result + */ + public static boolean setStaticFieldValue(Class clazz, String fieldName, Object value) { + if (clazz == null || StringUtils.isBlank(fieldName)) { + return false; + } + + Field field = getField(clazz, fieldName); + if (field == null) { + return false; + } + if (isFinalField(field)) { + updateFinalModifierField(field); + } + try { + field.set(null, value); + return true; + } catch (IllegalAccessException ex) { + LOGGER.warning(String.format(Locale.ENGLISH, "Set value for static field [%s] failed! %s", fieldName, + ex.getMessage())); + return false; + } + } + /** * Set field value * diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/SpiLoadUtils.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/SpiLoadUtils.java index ad370523f8..02c66549c9 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/SpiLoadUtils.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/SpiLoadUtils.java @@ -52,7 +52,7 @@ public static List loadAll(Class serviceClass, ClassLoader classLoader } list.sort(Comparator.comparingInt(obj -> { SpiWeight weight = obj.getClass().getAnnotation(SpiWeight.class); - return weight.value(); + return weight == null ? 0 : weight.value(); })); return list; } @@ -150,7 +150,6 @@ public static T getBetter(T src, T dst, WeightEqualHandler handler) { * WeightEqualHandler * * @param type - * * @since 2021-11-16 */ public interface WeightEqualHandler { diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/resources/META-INF/services/io.sermant.core.config.common.BaseConfig b/sermant-agentcore/sermant-agentcore-core/src/main/resources/META-INF/services/io.sermant.core.config.common.BaseConfig index 8dc8e371d9..8d37e55ce3 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/resources/META-INF/services/io.sermant.core.config.common.BaseConfig +++ b/sermant-agentcore/sermant-agentcore-core/src/main/resources/META-INF/services/io.sermant.core.config.common.BaseConfig @@ -10,3 +10,4 @@ io.sermant.core.plugin.config.ServiceMeta io.sermant.core.notification.config.NotificationConfig io.sermant.core.service.httpserver.config.HttpServerConfig io.sermant.core.service.xds.config.XdsConfig +io.sermant.core.service.metric.config.MetricConfig diff --git a/sermant-agentcore/sermant-agentcore-implement/pom.xml b/sermant-agentcore/sermant-agentcore-implement/pom.xml index cf156171ef..01b1d9dbdf 100644 --- a/sermant-agentcore/sermant-agentcore-implement/pom.xml +++ b/sermant-agentcore/sermant-agentcore-implement/pom.xml @@ -58,6 +58,7 @@ 2.1.2 2.13.4.2 0.1.32 + 1.9.5 @@ -173,6 +174,11 @@ grpc-protobuf ${grpc.version} + + io.micrometer + micrometer-registry-prometheus + ${micrometer.version} + junit junit diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterCounter.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterCounter.java new file mode 100644 index 0000000000..c424dbe695 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterCounter.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric; + +import io.sermant.core.service.metric.api.Counter; + +/** + * metric counter implementation + * + * @author zwmagic + * @since 2024-08-19 + */ +public class MeterCounter implements Counter { + private final io.micrometer.core.instrument.Counter counter; + + /** + * Constructor to initialize the MeterCounter object. + * + * @param counter A Micrometer Counter instance used to create the MeterCounter instance. + */ + public MeterCounter(io.micrometer.core.instrument.Counter counter) { + this.counter = counter; + } + + @Override + public void increment() { + counter.increment(); + } + + @Override + public void increment(double amount) { + counter.increment(amount); + } + + @Override + public double count() { + return counter.count(); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterGauge.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterGauge.java new file mode 100644 index 0000000000..eb90b53228 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterGauge.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.sermant.core.service.metric.api.Gauge; + +import java.util.function.ToDoubleFunction; + +/** + * metric gauge implementation + * + * @author zwmagic + * @since 2024-08-19 + */ +public class MeterGauge implements Gauge { + private final MeterRegistry registry; + + private final String metricName; + + private final Iterable tags; + + private final String description; + + /** + * Constructs a MeterGauge instance to measure and record data for a specified metric name. + * + * @param registry The MeterRegistry instance used for registering and managing metrics. + * @param metricName The name of the metric, used to uniquely identify this metric. + * @param tags A set of tags associated with the metric, used for categorization and filtering. + * @param description The description of the metric, used to explain its purpose and meaning. + */ + public MeterGauge(MeterRegistry registry, String metricName, Iterable tags, String description) { + this.registry = registry; + this.metricName = metricName; + this.tags = tags; + this.description = description; + } + + @Override + public T gaugeState(T stateObject, ToDoubleFunction valueFunction) { + io.micrometer.core.instrument.Gauge.builder(metricName, stateObject, valueFunction) + .tags(tags).description(description).register(registry); + return stateObject; + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetric.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetric.java new file mode 100644 index 0000000000..f6870d1a07 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetric.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric; + +import com.google.common.collect.Lists; + +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.DistributionSummary.Builder; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.config.MeterFilter; +import io.sermant.core.common.BootArgsIndexer; +import io.sermant.core.service.metric.api.Counter; +import io.sermant.core.service.metric.api.DistributionStatisticConfig; +import io.sermant.core.service.metric.api.Gauge; +import io.sermant.core.service.metric.api.Metric; +import io.sermant.core.service.metric.api.Summary; +import io.sermant.core.service.metric.api.Tags; +import io.sermant.core.service.metric.api.Timer; +import io.sermant.core.service.metric.config.MetricConfig; +import io.sermant.core.utils.StringUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * metric + * + * @author zwmagic + * @since 2024-08-19 + */ +public class MeterMetric implements Metric { + private static final Iterable EMPTY = Collections.emptyList(); + + private final MeterRegistry registry; + + /** + * Constructor that initializes a MeterMetric instance + * + * @param registryProvider An instance of MeterRegistryProvider used for creating and managing metrics. + * @param metricConfig MetricConfig + */ + public MeterMetric(MeterRegistryProvider registryProvider, MetricConfig metricConfig) { + this.registry = registryProvider.getRegistry(); + MeterRegistry.Config config = this.registry.config(); + config.commonTags( + "agent", "sermant", + "agent.app.name", BootArgsIndexer.getAppName() + ); + config.meterFilter(MeterFilter.maximumAllowableMetrics(metricConfig.getMaxTimeSeries())); + } + + @Override + public Counter counter(String metricName, Tags tags, String description) { + io.micrometer.core.instrument.Counter counter = io.micrometer.core.instrument.Counter.builder(metricName) + .tags(getMeterTags(tags)) + .description(description) + .register(registry); + return new MeterCounter(counter); + } + + @Override + public Gauge gauge(String metricName, Tags tags, String description) { + return new MeterGauge(registry, metricName, getMeterTags(tags), description); + } + + @Override + public Timer timer(String metricName, Tags tags, String description) { + io.micrometer.core.instrument.Timer timer = io.micrometer.core.instrument.Timer.builder(metricName) + .tags(getMeterTags(tags)).description(description).register(registry); + return new MeterTimer(registry, timer); + } + + @Override + public Summary summary(String metricName, Tags tags, String description, + DistributionStatisticConfig distributionStatisticConfig) { + Builder summaryBuilder = DistributionSummary.builder(metricName) + .tags(getMeterTags(tags)) + .description(description); + + if (distributionStatisticConfig == null) { + DistributionSummary summary = summaryBuilder.register(registry); + return new MeterSummary(summary); + } + Optional.ofNullable(distributionStatisticConfig.getPercentileHistogram()) + .ifPresent(summaryBuilder::publishPercentileHistogram); + + Optional.ofNullable(distributionStatisticConfig.getPercentiles()) + .ifPresent(summaryBuilder::publishPercentiles); + + Optional.ofNullable(distributionStatisticConfig.getPercentilePrecision()) + .ifPresent(summaryBuilder::percentilePrecision); + + Optional.ofNullable(distributionStatisticConfig.getMinimumExpectedValue()) + .ifPresent(summaryBuilder::minimumExpectedValue); + + Optional.ofNullable(distributionStatisticConfig.getMaximumExpectedValue()) + .ifPresent(summaryBuilder::maximumExpectedValue); + + Optional.ofNullable(distributionStatisticConfig.getExpiry()) + .ifPresent(summaryBuilder::distributionStatisticExpiry); + + Optional.ofNullable(distributionStatisticConfig.getBufferLength()) + .ifPresent(summaryBuilder::distributionStatisticBufferLength); + DistributionSummary summary = summaryBuilder.register(registry); + return new MeterSummary(summary); + } + + /** + * Converts a Tags object into an iterable collection of Tag objects. + * + * @param tags The Tags object to convert. + * @return An iterable collection of Tag objects. Returns an empty collection if there are no tags or if tags is + * null. + */ + private Iterable getMeterTags(Tags tags) { + if (tags == null || tags.getSize() == 0) { + return EMPTY; + } + + List result = Lists.newArrayListWithCapacity(tags.getSize()); + for (Map.Entry entry : tags.getTags().entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { + continue; + } + result.add(Tag.of(entry.getKey(), value)); + } + return result; + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetricServiceImpl.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetricServiceImpl.java new file mode 100644 index 0000000000..e61422592c --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetricServiceImpl.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric; + +import io.micrometer.core.instrument.Metrics; +import io.sermant.core.config.ConfigManager; +import io.sermant.core.service.metric.api.Counter; +import io.sermant.core.service.metric.api.DistributionStatisticConfig; +import io.sermant.core.service.metric.api.Gauge; +import io.sermant.core.service.metric.api.Metric; +import io.sermant.core.service.metric.api.MetricService; +import io.sermant.core.service.metric.api.Summary; +import io.sermant.core.service.metric.api.Tags; +import io.sermant.core.service.metric.api.Timer; +import io.sermant.core.service.metric.config.MetricConfig; +import io.sermant.core.utils.SpiLoadUtils; + +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The MeterMetricService class manages Micrometer metrics and provides initialization and data retrieval + * functionalities. + * + * @author zwmagic + * @since 2024-08-19 + */ +public class MeterMetricServiceImpl implements MetricService { + private static MeterRegistryProvider meterRegistryProvider; + private Metric metric; + + /** + * Initializes the metric manager. If the metric manager has not been initialized, it creates and configures a + * PrometheusMeterRegistry, and sets it as the global metric manager. + */ + @Override + public void start() { + if (meterRegistryProvider != null) { + return; + } + Map providerMap = SpiLoadUtils.loadAll(MeterRegistryProvider.class, + this.getClass().getClassLoader()).stream() + .collect(Collectors.toMap(MeterRegistryProvider::getType, provider -> provider)); + MetricConfig metricConfig = ConfigManager.getConfig(MetricConfig.class); + String type = metricConfig.getType(); + meterRegistryProvider = providerMap.get(type); + if (meterRegistryProvider == null) { + throw new IllegalArgumentException("can not find metricRegistry provider by " + type); + } + Metrics.addRegistry(meterRegistryProvider.getRegistry()); + metric = new MeterMetric(meterRegistryProvider, metricConfig); + } + + /** + * Retrieves the current metric data from the metric manager. + * + * @return The metric data of the current metric manager, represented in Prometheus format. + */ + public static String getScrape() { + return meterRegistryProvider.getScrape(); + } + + @Override + public Counter counter(String metricName) { + return counter(metricName, null); + } + + @Override + public Counter counter(String metricName, String tagKey, String tagValue) { + return counter(metricName, Tags.of(tagKey, tagValue)); + } + + @Override + public Counter counter(String metricName, Tags tags) { + return counter(metricName, tags, null); + } + + @Override + public Counter counter(String metricName, Tags tags, String description) { + return metric.counter(metricName, tags, description); + } + + @Override + public Gauge gauge(String metricName) { + return gauge(metricName, null); + } + + @Override + public Gauge gauge(String metricName, String tagKey, String tagValue) { + return gauge(metricName, Tags.of(tagKey, tagValue)); + } + + @Override + public Gauge gauge(String metricName, Tags tags) { + return gauge(metricName, tags, null); + } + + @Override + public Gauge gauge(String metricName, Tags tags, String description) { + return metric.gauge(metricName, tags, description); + } + + @Override + public Timer timer(String metricName) { + return timer(metricName, null); + } + + @Override + public Timer timer(String metricName, String tagKey, String tagValue) { + return timer(metricName, Tags.of(tagKey, tagValue)); + } + + @Override + public Timer timer(String metricName, Tags tags) { + return timer(metricName, tags, null); + } + + @Override + public Timer timer(String metricName, Tags tags, String description) { + return metric.timer(metricName, tags, description); + } + + @Override + public Summary summary(String metricName) { + return summary(metricName, null); + } + + @Override + public Summary summary(String metricName, String tagKey, String tagValue) { + return summary(metricName, Tags.of(tagKey, tagValue)); + } + + @Override + public Summary summary(String metricName, Tags tags) { + return summary(metricName, tags, null, null); + } + + @Override + public Summary summary(String metricName, Tags tags, String description, + DistributionStatisticConfig distributionStatisticConfig) { + return metric.summary(metricName, tags, description, distributionStatisticConfig); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterRegistryProvider.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterRegistryProvider.java new file mode 100644 index 0000000000..04d32e645c --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterRegistryProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric; + +import io.micrometer.core.instrument.MeterRegistry; + +/** + * Interface defining the core components for monitoring data collection. It provides access and configuration + * capabilities for the monitoring data registry, used for collecting and exposing metrics. + * + * @author zwmagic + * @since 2024-08-19 + */ +public interface MeterRegistryProvider { + /** + * Gets the type of the monitoring registry. + * + * @return The type of the monitoring data registry + */ + String getType(); + + /** + * Gets an instance of the monitoring data registry. + * + * @return A MeterRegistry instance + */ + MeterRegistry getRegistry(); + + /** + * Gets the configuration information for scraping monitoring data. + * + * @return The configuration information for scraping monitoring data + */ + String getScrape(); +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterSummary.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterSummary.java new file mode 100644 index 0000000000..36730a2d3f --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterSummary.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric; + +import io.micrometer.core.instrument.DistributionSummary; +import io.sermant.core.service.metric.api.Summary; + +/** + * The MeterSummary class implements the Summary interface and represents a metered summary, capable of recording, + * aggregating, and reporting the distribution of a certain metric. + * + * @author zwmagic + * @since 2024-08-19 + */ +public class MeterSummary implements Summary { + private final DistributionSummary summary; + + /** + * Constructor that creates a MeterSummary instance. + * + * @param distributionSummary A DistributionSummary instance used internally to record and aggregate metric data. + */ + public MeterSummary(DistributionSummary distributionSummary) { + this.summary = distributionSummary; + } + + /** + * Records a new metric value. + * + * @param amount The metric value to be recorded. + */ + @Override + public void record(double amount) { + summary.record(amount); + } + + /** + * Gets the total number of recorded metric values. + * + * @return The total number of recorded metric values. + */ + @Override + public long count() { + return summary.count(); + } + + /** + * Gets the sum of all recorded metric values. + * + * @return The sum of all recorded metric values. + */ + @Override + public double totalAmount() { + return summary.totalAmount(); + } + + /** + * Gets the maximum value among the recorded metric values. + * + * @return The maximum value among the recorded metric values. + */ + @Override + public double max() { + return summary.max(); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterTimer.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterTimer.java new file mode 100644 index 0000000000..1c1714540e --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterTimer.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric; + +import io.micrometer.core.instrument.MeterRegistry; +import io.sermant.core.service.metric.api.Timer; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +/** + * metric + * + * @author zwmagic + * @since 2024-08-19 + */ +public class MeterTimer implements Timer { + private final MeterRegistry registry; + + private final io.micrometer.core.instrument.Timer timer; + + private io.micrometer.core.instrument.Timer.Sample sample; + + /** + * Constructs a timer associated with the specified MeterRegistry and Timer. This constructor initializes a + * MeterTimer object to integrate with the MeterRegistry and Timer from the Micrometer library. + * + * @param registry An instance of MeterRegistry used for creating and managing metrics. + * @param timer An instance of io.micrometer.core.instrument.Timer used for timing and recording durations. + */ + public MeterTimer(MeterRegistry registry, io.micrometer.core.instrument.Timer timer) { + this.registry = registry; + this.timer = timer; + } + + @Override + public Timer start() { + this.sample = io.micrometer.core.instrument.Timer.start(registry); + return this; + } + + @Override + public long stop() { + if (sample == null) { + return -1L; + } + return sample.stop(timer); + } + + @Override + public void record(Duration duration) { + timer.record(duration); + } + + @Override + public void record(long amount, TimeUnit unit) { + timer.record(amount, unit); + } + + @Override + public long count() { + return timer.count(); + } + + @Override + public double totalTime(TimeUnit unit) { + return timer.totalTime(unit); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/handler/MetricHttpRouteHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/handler/MetricHttpRouteHandler.java new file mode 100644 index 0000000000..50d5e85a66 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/handler/MetricHttpRouteHandler.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric.handler; + +import io.prometheus.client.exporter.common.TextFormat; +import io.sermant.core.service.httpserver.annotation.HttpRouteMapping; +import io.sermant.core.service.httpserver.api.HttpMethod; +import io.sermant.core.service.httpserver.api.HttpRequest; +import io.sermant.core.service.httpserver.api.HttpResponse; +import io.sermant.core.service.httpserver.api.HttpRouteHandler; +import io.sermant.implement.service.metric.MeterMetricServiceImpl; + +/** + * provide http metric + * + * @author zwmagic + * @since 2024-08-19 + */ +@HttpRouteMapping(path = "/metrics", method = HttpMethod.GET) +public class MetricHttpRouteHandler implements HttpRouteHandler { + private static final int SUCCESS_CODE = 200; + + @Override + public void handle(HttpRequest request, HttpResponse response) throws Exception { + String scrape = MeterMetricServiceImpl.getScrape(); + response.setStatus(SUCCESS_CODE) + .setContentType(TextFormat.CONTENT_TYPE_004) + .writeBody(scrape); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/prometheus/PrometheusMeterRegistryProvider.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/prometheus/PrometheusMeterRegistryProvider.java new file mode 100644 index 0000000000..dc67a8aab4 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/prometheus/PrometheusMeterRegistryProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed 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 io.sermant.implement.service.metric.prometheus; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.sermant.implement.service.metric.MeterRegistryProvider; + +/** + * Prometheus meter registry provider + * + * @author zwmagic + * @since 2024-08-19 + */ +public class PrometheusMeterRegistryProvider implements MeterRegistryProvider { + private final PrometheusMeterRegistry meterRegistry; + + /** + * Constructor that initializes the Prometheus meter registry. + */ + public PrometheusMeterRegistryProvider() { + this.meterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + } + + @Override + public String getType() { + return "prometheus"; + } + + @Override + public MeterRegistry getRegistry() { + return meterRegistry; + } + + @Override + public String getScrape() { + return meterRegistry.scrape(); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService b/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService index 07bfb871e9..4b1f62ec34 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService @@ -1,3 +1,4 @@ +io.sermant.implement.service.metric.MeterMetricServiceImpl io.sermant.implement.service.heartbeat.HeartbeatServiceImpl io.sermant.implement.service.send.netty.NettyGatewayClient io.sermant.implement.service.dynamicconfig.BufferedDynamicConfigService diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.implement.service.metric.MeterRegistryProvider b/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.implement.service.metric.MeterRegistryProvider new file mode 100644 index 0000000000..25d74a227b --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.implement.service.metric.MeterRegistryProvider @@ -0,0 +1 @@ +io.sermant.implement.service.metric.prometheus.PrometheusMeterRegistryProvider \ No newline at end of file