From c20421f24090c4ec47e6d19d10bf3d957e8885b8 Mon Sep 17 00:00:00 2001 From: Seokhyun Lee <7948302+henrylee97@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:59:41 +0900 Subject: [PATCH] refactor(java): sling-org-apache-sling-commons-metrics (#124) * refactor(java): sling-org-apache-sling-commons-metrics * fix(java): metadata --------- Co-authored-by: junheeL --- .../Dockerfile | 28 + .../src/CODE_OF_CONDUCT.md | 22 + .../src/CONTRIBUTING.md | 24 + .../src/Jenkinsfile | 20 + .../src/LICENSE | 202 ++++++++ .../src/README.md | 11 + .../src/bnd.bnd | 25 + .../src/pom.xml | 235 +++++++++ .../apache/sling/commons/metrics/Counter.java | 53 ++ .../sling/commons/metrics/Counting.java | 33 ++ .../apache/sling/commons/metrics/Gauge.java | 55 ++ .../sling/commons/metrics/Histogram.java | 36 ++ .../apache/sling/commons/metrics/Meter.java | 41 ++ .../apache/sling/commons/metrics/Metric.java | 36 ++ .../sling/commons/metrics/MetricsService.java | 107 ++++ .../metrics/MetricsServiceFactory.java | 62 +++ .../sling/commons/metrics/NoopMetric.java | 95 ++++ .../apache/sling/commons/metrics/Timer.java | 62 +++ .../metrics/internal/BundleMetricsMapper.java | 100 ++++ .../commons/metrics/internal/CounterImpl.java | 65 +++ .../metrics/internal/GaugeManager.java | 119 +++++ .../metrics/internal/HistogramImpl.java | 50 ++ .../InternalMetricsServiceFactory.java | 102 ++++ .../metrics/internal/JSONReporter.java | 299 +++++++++++ .../commons/metrics/internal/JmxUtil.java | 89 ++++ .../commons/metrics/internal/LogReporter.java | 150 ++++++ .../commons/metrics/internal/MeterImpl.java | 54 ++ .../internal/MetricWebConsolePlugin.java | 475 +++++++++++++++++ .../metrics/internal/MetricsServiceImpl.java | 236 +++++++++ .../commons/metrics/internal/TimerImpl.java | 77 +++ .../sling/commons/metrics/package-info.java | 29 ++ .../internal/BundleMetricsMapperTest.java | 70 +++ .../InternalMetricsServiceFactoryTest.java | 98 ++++ .../metrics/internal/JSONReporterTest.java | 91 ++++ .../commons/metrics/internal/JmxUtilTest.java | 51 ++ .../metrics/internal/LogReporterTest.java | 449 +++++++++++++++++ .../metrics/internal/MetricServiceTest.java | 187 +++++++ .../internal/MetricWebConsolePluginTest.java | 200 ++++++++ .../metrics/internal/MetricWrapperTest.java | 132 +++++ .../metrics/test/MetricsServiceFactoryIT.java | 85 ++++ .../Dockerfile | 18 + .../buggy.java | 98 ++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 87 ++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 151 ++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 477 ++++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + 56 files changed, 5352 insertions(+) create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/Dockerfile create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/CODE_OF_CONDUCT.md create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/CONTRIBUTING.md create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/Jenkinsfile create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/LICENSE create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/README.md create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/bnd.bnd create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/pom.xml create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counter.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counting.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Gauge.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Histogram.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Meter.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Metric.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsService.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsServiceFactory.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/NoopMetric.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Timer.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapper.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/CounterImpl.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/GaugeManager.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/HistogramImpl.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactory.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JSONReporter.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JmxUtil.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MeterImpl.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePlugin.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/TimerImpl.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/package-info.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapperTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactoryTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JSONReporterTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JmxUtilTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePluginTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWrapperTest.java create mode 100644 Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/test/MetricsServiceFactoryIT.java create mode 100644 Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/Dockerfile create mode 100644 Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/buggy.java create mode 100644 Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/metadata.json create mode 100644 Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/npe.json create mode 100644 Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/Dockerfile create mode 100644 Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/buggy.java create mode 100644 Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/metadata.json create mode 100644 Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/npe.json create mode 100644 Java/sling-org-apache-sling-commons-metrics-LogReporter_94/Dockerfile create mode 100644 Java/sling-org-apache-sling-commons-metrics-LogReporter_94/buggy.java create mode 100644 Java/sling-org-apache-sling-commons-metrics-LogReporter_94/metadata.json create mode 100644 Java/sling-org-apache-sling-commons-metrics-LogReporter_94/npe.json create mode 100644 Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/Dockerfile create mode 100644 Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/buggy.java create mode 100644 Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/metadata.json create mode 100644 Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/npe.json diff --git a/Java-base/sling-org-apache-sling-commons-metrics/Dockerfile b/Java-base/sling-org-apache-sling-commons-metrics/Dockerfile new file mode 100644 index 000000000..e208c4890 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:22.04 + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y software-properties-common \ + && add-apt-repository ppa:deadsnakes/ppa \ + && apt-get update \ + && apt-get install -y \ + build-essential \ + git \ + vim \ + jq \ + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/list/* + +RUN apt-get -y install sudo \ + openjdk-8-jdk \ + maven + +RUN bash -c "echo 2 | update-alternatives --config java" + +COPY src /workspace +WORKDIR /workspace + +RUN mvn install -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false + +RUN mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 + +ENV TZ=Asia/Seoul diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/CODE_OF_CONDUCT.md b/Java-base/sling-org-apache-sling-commons-metrics/src/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..0fa18e593 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/CODE_OF_CONDUCT.md @@ -0,0 +1,22 @@ + +Apache Software Foundation Code of Conduct +==== + +Being an Apache project, Apache Sling adheres to the Apache Software Foundation's [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html). diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/CONTRIBUTING.md b/Java-base/sling-org-apache-sling-commons-metrics/src/CONTRIBUTING.md new file mode 100644 index 000000000..ac82a1abe --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/CONTRIBUTING.md @@ -0,0 +1,24 @@ + +Contributing +==== + +Thanks for choosing to contribute! + +You will find all the necessary details about how you can do this at https://sling.apache.org/contributing.html. diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/Jenkinsfile b/Java-base/sling-org-apache-sling-commons-metrics/src/Jenkinsfile new file mode 100644 index 000000000..f5825190c --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/Jenkinsfile @@ -0,0 +1,20 @@ +/** + * 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. + */ + +slingOsgiBundleBuild() diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/LICENSE b/Java-base/sling-org-apache-sling-commons-metrics/src/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://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 + + 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. diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/README.md b/Java-base/sling-org-apache-sling-commons-metrics/src/README.md new file mode 100644 index 000000000..8c1a5cdb4 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/README.md @@ -0,0 +1,11 @@ +[](https://sling.apache.org) + + [![Build Status](https://builds.apache.org/buildStatus/icon?job=Sling/sling-org-apache-sling-commons-metrics/master)](https://builds.apache.org/job/Sling/job/sling-org-apache-sling-commons-metrics/job/master) [![Test Status](https://img.shields.io/jenkins/t/https/builds.apache.org/job/Sling/job/sling-org-apache-sling-commons-metrics/job/master.svg)](https://builds.apache.org/job/Sling/job/sling-org-apache-sling-commons-metrics/job/master/test_results_analyzer/) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.sling/org.apache.sling.commons.metrics/badge.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.sling%22%20a%3A%22org.apache.sling.commons.metrics%22) [![JavaDocs](https://www.javadoc.io/badge/org.apache.sling/org.apache.sling.commons.metrics.svg)](https://www.javadoc.io/doc/org.apache.sling/org.apache.sling.commons.metrics) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +# Apache Sling Metrics + +This module is part of the [Apache Sling](https://sling.apache.org) project. + +Refer to [Sling Metrics][1] + +[1]: https://sling.apache.org/documentation/bundles/metrics.html diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/bnd.bnd b/Java-base/sling-org-apache-sling-commons-metrics/src/bnd.bnd new file mode 100644 index 000000000..84d7e0ec1 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/bnd.bnd @@ -0,0 +1,25 @@ +Bundle-DocURL: https://sling.apache.org/documentation/bundles/metrics.html + +DynamicImport-Package:\ + javax.servlet,\ + javax.servlet.http,\ + org.apache.commons.io.output,\ + org.apache.felix.inventory + +Import-Package:\ + javax.servlet;resolution:=optional,\ + javax.servlet.http;resolution:=optional,\ + org.apache.commons.io.output;resolution:=optional,\ + org.apache.felix.inventory;resolution:=optional,\ + * + +Provide-Capability:\ + osgi.service;objectClass=com.codahale.metrics.MetricRegistry,\ + osgi.service;objectClass=org.apache.sling.commons.metrics.MetricsService + +-includeresource:\ + @org.apache.felix.utils-*.jar!/org/apache/felix/utils/json/JSONWriter** + +-removeheaders:\ + Include-Resource,\ + Private-Package diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/pom.xml b/Java-base/sling-org-apache-sling-commons-metrics/src/pom.xml new file mode 100644 index 000000000..79a8db116 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/pom.xml @@ -0,0 +1,235 @@ + + + + + 4.0.0 + + + org.apache.sling + sling-bundle-parent + 38 + + + + org.apache.sling.commons.metrics + 1.2.9-SNAPSHOT + + Apache Sling Commons Metrics + + Integrates Metric library with Sling. Refer to + https://sling.apache.org/documentation/bundles/metrics.html + for details + + + + 4.13.3 + + + + scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics.git + scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics.git + + https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-commons-metrics.git + HEAD + + + + + + biz.aQute.bnd + bnd-maven-plugin + + + biz.aQute.bnd + bnd-baseline-maven-plugin + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + true + + + bundle.filename + ${basedir}/target/${project.build.finalName}.jar + + + + + + org.apache.servicemix.tooling + depends-maven-plugin + + + + + + + + javax.inject + javax.inject + test + + + javax.servlet + javax.servlet-api + true + + + + org.osgi + osgi.core + provided + + + org.osgi + org.osgi.annotation.versioning + provided + + + + commons-io + commons-io + 2.2 + provided + true + + + + org.apache.felix + org.apache.felix.framework + 6.0.3 + test + + + org.apache.felix + org.apache.felix.inventory + 1.0.2 + provided + true + + + org.apache.felix + org.apache.felix.utils + 1.9.0 + provided + + + + org.apache.sling + org.apache.sling.testing.osgi-mock + 2.1.0 + test + + + org.apache.sling + org.apache.sling.testing.paxexam + 3.1.0 + test + + + + io.dropwizard.metrics + metrics-core + 3.2.3 + provided + + + + org.slf4j + slf4j-api + provided + + + org.slf4j + slf4j-simple + test + + + + junit + junit + test + + + junit-addons + junit-addons + 1.4 + test + + + org.hamcrest + hamcrest-all + 1.3 + test + + + org.mockito + mockito-core + 1.10.19 + test + + + net.sourceforge.htmlunit + htmlunit + 2.32 + test + + + org.ops4j.pax.exam + pax-exam + ${org.ops4j.pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-cm + ${org.ops4j.pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-container-forked + ${org.ops4j.pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-junit4 + ${org.ops4j.pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-link-mvn + ${org.ops4j.pax.exam.version} + test + + + + diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counter.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counter.java new file mode 100644 index 000000000..64730ddbe --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counter.java @@ -0,0 +1,53 @@ +/* + * 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.sling.commons.metrics; + + +import org.osgi.annotation.versioning.ProviderType; + +/** + * An incrementing and decrementing counter metric. + */ +@ProviderType +public interface Counter extends Counting, Metric{ + /** + * Increment the counter by one. + */ + void increment(); + + /** + * Decrement the counter by one. + */ + void decrement(); + + /** + * Increment the counter by {@code n}. + * + * @param n the amount by which the counter will be increased + */ + void increment(long n); + + /** + * Decrement the counter by {@code n}. + * + * @param n the amount by which the counter will be decreased + */ + void decrement(long n); +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counting.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counting.java new file mode 100644 index 000000000..f974ea65f --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Counting.java @@ -0,0 +1,33 @@ +/* + * 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.sling.commons.metrics; + + +import org.osgi.annotation.versioning.ProviderType; + +@ProviderType +public interface Counting { + /** + * Returns the current count. + * + * @return the current count + */ + long getCount(); +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Gauge.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Gauge.java new file mode 100644 index 000000000..981312057 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Gauge.java @@ -0,0 +1,55 @@ +/* + * 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.sling.commons.metrics; + + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * A gauge metric is an instantaneous reading of a particular value. To instrument a queue's depth, + * for example:
+ *

+ * final Queue<String> queue = new ConcurrentLinkedQueue<String>();
+ * final Gauge<Integer> queueDepth = new Gauge<Integer>() {
+ *     public Integer getValue() {
+ *         return queue.size();
+ *     }
+ * };
+ * 
+ * + *

A Gauge instance should be registered with OSGi ServiceRegistry with {@code Gauge#NAME} set + * to Gauge name. Then the Gauge instance would be registered with MetricService via whiteboard + * pattern + * + * @param the type of the metric's value + */ +@ConsumerType +public interface Gauge { + /** + * Service property name which determines the name of the Gauge + */ + String NAME = "name"; + /** + * Returns the metric's current value. + * + * @return the metric's current value + */ + T getValue(); +} \ No newline at end of file diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Histogram.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Histogram.java new file mode 100644 index 000000000..d78f3a855 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Histogram.java @@ -0,0 +1,36 @@ +/* + * 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.sling.commons.metrics; + + +import org.osgi.annotation.versioning.ProviderType; + +/** + * A metric which calculates the distribution of a value. + */ +@ProviderType +public interface Histogram extends Counting, Metric { + /** + * Adds a recorded value. + * + * @param value the length of the value + */ + void update(long value); +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Meter.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Meter.java new file mode 100644 index 000000000..6674fbfdb --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Meter.java @@ -0,0 +1,41 @@ +/* + * 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.sling.commons.metrics; + + +import org.osgi.annotation.versioning.ProviderType; + +/** + * A meter metric which measures mean throughput + */ +@ProviderType +public interface Meter extends Counting, Metric{ + /** + * Mark the occurrence of an event. + */ + void mark(); + + /** + * Mark the occurrence of a given number of events. + * + * @param n the number of events + */ + void mark(long n); +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Metric.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Metric.java new file mode 100644 index 000000000..bf07b6908 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Metric.java @@ -0,0 +1,36 @@ +/* + * 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.sling.commons.metrics; + + +import org.osgi.annotation.versioning.ProviderType; + +@ProviderType +public interface Metric { + /** + * Adapts the Metric to the specified type. + * + * @param The type to which this metric is to be adapted. + * @param type Class object for the type to which this metric is to be adapted. + * @return The object, of the specified type, to which this metric has been adapted + * or null if this metric cannot be adapted to the specified type. + */ + A adaptTo(Class type); +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsService.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsService.java new file mode 100644 index 000000000..5bf4eaefe --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsService.java @@ -0,0 +1,107 @@ +/* + * 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.sling.commons.metrics; + + +import org.osgi.annotation.versioning.ProviderType; + +/** + * The {@code MetricsService} enables creation of various types of {@code Metric}. + */ +@ProviderType +public interface MetricsService { + /** + * Dummy variant of MetricsService which does not + * collect any metric + */ + MetricsService NOOP = new MetricsService() { + @Override + public Timer timer(String name) { + return NoopMetric.INSTANCE; + } + + @Override + public Histogram histogram(String name) { + return NoopMetric.INSTANCE; + } + + @Override + public Counter counter(String name) { + return NoopMetric.INSTANCE; + } + + @Override + public Meter meter(String name) { + return NoopMetric.INSTANCE; + } + + @Override + public A adaptTo(Class type) { + return null; + } + }; + + /** + * Creates a new {@link Timer} and registers it under the given name. + * If a timer with same name exists then same instance is returned + * + * @param name the name of the metric + * @return a new {@link Timer} + */ + Timer timer(String name); + + /** + * Creates a new {@link Histogram} and registers it under the given name. + * If a histogram with same name exists then same instance is returned. + * + * @param name the name of the metric + * @return a new {@link Histogram} + */ + Histogram histogram(String name); + + /** + * Creates a new {@link Counter} and registers it under the given name. + * If a counter with same name exists then same instance is returned + * + * @param name the name of the metric + * @return a new {@link Counter} + */ + Counter counter(String name); + + /** + * Creates a new {@link Meter} and registers it under the given name. + * If a meter with same name exists then same instance is returned + * + * @param name the name of the metric + * @return a new {@link Meter} + */ + Meter meter(String name); + + /** + * Adapts the service to the specified type. This can be used to + * get instance to underlying {@code MetricRegistry} + * + * @param The type to which this metric is to be adapted. + * @param type Class object for the type to which this metric is to be adapted. + * @return The object, of the specified type, to which this metric has been adapted + * or null if this metric cannot be adapted to the specified type. + */ + A adaptTo(Class type); +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsServiceFactory.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsServiceFactory.java new file mode 100644 index 000000000..cd1c23fcc --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/MetricsServiceFactory.java @@ -0,0 +1,62 @@ +/* + * 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.sling.commons.metrics; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +/** Utility that provides a MetricsService to any class that + * has been loaded from an OSGi bundle. + * This is meant to make it as easy to access the MetricsService + * as it is to get a Logger, for example. + */ +public class MetricsServiceFactory { + + /** Provide a MetricsService mapped to the Bundle that loaded class c + * @param c a Class loaded by an OSGi bundle + * @return a MetricsService + */ + public static MetricsService getMetricsService(Class c) { + if(c == null) { + throw new IllegalArgumentException("Class parameter is required"); + } + + final Bundle b = FrameworkUtil.getBundle(c); + if(b == null) { + throw new IllegalArgumentException("No BundleContext, Class was not loaded from a Bundle?: " + + c.getClass().getName()); + } + + final BundleContext ctx = b.getBundleContext(); + + // In theory we should unget this reference, but the OSGi framework + // ungets all references held by a bundle when it stops and we cannot + // do much better than that anyway. + final ServiceReference ref = ctx.getServiceReference(MetricsService.class.getName()); + if(ref == null) { + throw new IllegalStateException("MetricsService not found for Bundle " + + b.getSymbolicName()); + } + + return (MetricsService)ctx.getService(ref); + } +} \ No newline at end of file diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/NoopMetric.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/NoopMetric.java new file mode 100644 index 000000000..e2aaac490 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/NoopMetric.java @@ -0,0 +1,95 @@ +/* + * 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.sling.commons.metrics; + +import java.util.concurrent.TimeUnit; + +enum NoopMetric implements Counter, Histogram, Timer, Meter{ + INSTANCE; + + @Override + public long getCount() { + return 0; + } + + @Override + public void increment() { + + } + + @Override + public void decrement() { + + } + + @Override + public void increment(long n) { + + } + + @Override + public void decrement(long n) { + + } + + @Override + public void mark() { + + } + + @Override + public void mark(long n) { + + } + + @Override + public void update(long duration, TimeUnit unit) { + + } + + @Override + public Context time() { + return NoopContext.INSTANCE; + } + + @Override + public void update(long value) { + + } + + @Override + public AdapterType adaptTo(Class type) { + return null; + } + + private enum NoopContext implements Context { + INSTANCE; + + @Override + public long stop() { + return 0; + } + + @Override + public void close() { + + } + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Timer.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Timer.java new file mode 100644 index 000000000..f5d79cb1b --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/Timer.java @@ -0,0 +1,62 @@ +/* + * 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.sling.commons.metrics; + +import java.io.Closeable; +import java.util.concurrent.TimeUnit; + +import org.osgi.annotation.versioning.ProviderType; + + +/** + * A timer metric which aggregates timing durations and provides duration statistics. + */ +@ProviderType +public interface Timer extends Counting, Metric{ + /** + * A timing context. + * + * @see Timer#time() + */ + interface Context extends Closeable { + /** + * Updates the timer with the difference between current and start time. Call to this method will + * not reset the start time. Multiple calls result in multiple updates. + * @return the elapsed time in nanoseconds + */ + long stop(); + } + + /** + * Adds a recorded duration. + * + * @param duration the length of the duration + * @param unit the scale unit of {@code duration} + */ + void update(long duration, TimeUnit unit); + + /** + * Returns a new {@link Context}. + * + * @return a new {@link Context} + * @see Context + */ + Context time(); +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapper.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapper.java new file mode 100644 index 000000000..f19bc8d28 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapper.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.sling.commons.metrics.internal; + +import java.util.Hashtable; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.ObjectNameFactory; +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class BundleMetricsMapper implements ObjectNameFactory{ + public static final String HEADER_DOMAIN_NAME = "Sling-Metrics-Domain"; + public static final String DEFAULT_DOMAIN_NAME = "org.apache.sling"; + static final String JMX_TYPE_METRICS = "Metrics"; + private final Logger log = LoggerFactory.getLogger(getClass()); + private final ConcurrentMap metricToBundleMapping = new ConcurrentHashMap<>(); + private final MetricsServiceImpl metricsService; + private final MetricRegistry registry; + + BundleMetricsMapper(MetricsServiceImpl metricsService, MetricRegistry registry) { + this.metricsService = metricsService; + this.registry = registry; + } + + public void addMapping(String name, Bundle bundle) { + metricToBundleMapping.putIfAbsent(name, bundle); + } + + public void unregister(Set registeredNames) { + for (String name : registeredNames){ + registry.remove(name); + metricToBundleMapping.remove(name); + metricsService.remove(name); + } + log.debug("Removed metrics for {}", registeredNames); + } + + @Override + public ObjectName createName(String type, String domain, String name) { + String mappedDomainName = JmxUtil.safeDomainName(getDomainName(name)); + if (mappedDomainName == null) { + mappedDomainName = domain; + } + + Hashtable table = new Hashtable<>(); + table.put("type", JMX_TYPE_METRICS); + table.put("name", JmxUtil.quoteValueIfRequired(name)); + try { + return new ObjectName(mappedDomainName, table); + } catch (MalformedObjectNameException e) { + log.warn("Unable to register {} {}", type, name, e); + throw new RuntimeException(e); + } + } + + private String getDomainName(String name) { + Bundle bundle = metricToBundleMapping.get(name); + return getDomainName(bundle); + } + + private String getDomainName(Bundle bundle) { + if (bundle == null){ + return null; + } + + String domainNameHeader = bundle.getHeaders().get(HEADER_DOMAIN_NAME); + if (domainNameHeader != null){ + return domainNameHeader; + } + + //Fallback to symbolic name + return bundle.getSymbolicName(); + } + +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/CounterImpl.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/CounterImpl.java new file mode 100644 index 000000000..bda240e4b --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/CounterImpl.java @@ -0,0 +1,65 @@ +/* + * 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.sling.commons.metrics.internal; + + +import org.apache.sling.commons.metrics.Counter; + +final class CounterImpl implements Counter { + private final com.codahale.metrics.Counter counter; + + CounterImpl(com.codahale.metrics.Counter counter) { + this.counter = counter; + } + + @Override + public void increment() { + counter.inc(); + } + + @Override + public void decrement() { + counter.dec(); + } + + @Override + public void increment(long n) { + counter.inc(n); + } + + @Override + public void decrement(long n) { + counter.dec(n); + } + + @Override + public long getCount() { + return counter.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public A adaptTo(Class type) { + if (type == com.codahale.metrics.Counter.class){ + return (A) counter; + } + return null; + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/GaugeManager.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/GaugeManager.java new file mode 100644 index 000000000..bc48066fa --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/GaugeManager.java @@ -0,0 +1,119 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.io.Closeable; +import java.util.Collections; + +import com.codahale.metrics.MetricRegistry; +import org.apache.sling.commons.metrics.Gauge; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class GaugeManager implements ServiceTrackerCustomizer, Closeable { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final MetricRegistry registry; + private final BundleMetricsMapper mapper; + private final BundleContext bundleContext; + private ServiceTracker tracker; + + public GaugeManager(BundleContext context, MetricRegistry registry, BundleMetricsMapper mapper) { + this.registry = registry; + this.mapper = mapper; + this.bundleContext = context; + this.tracker = new ServiceTracker<>(context, Gauge.class, this); + tracker.open(); + } + + //~-------------------------------------< ServiceTrackerCustomizer > + + @Override + public GaugeImpl addingService(ServiceReference reference) { + String name = (String) reference.getProperty(Gauge.NAME); + if (name == null){ + log.warn("A {} service is registered without [{}] property. This Gauge would not be " + + "registered with MetricsRegistry", reference, Gauge.NAME); + return null; + } + + Gauge gauge = bundleContext.getService(reference); + GaugeImpl gaugeImpl = new GaugeImpl(name, gauge); + register(reference, gaugeImpl); + return gaugeImpl; + } + + @Override + public void modifiedService(ServiceReference reference, GaugeImpl service) { + String name = (String) reference.getProperty(Gauge.NAME); + if (name == null){ + return; + } + + if (!name.equals(service.name)){ + unregister(service); + service.name = name; + register(reference, service); + } + } + + @Override + public void removedService(ServiceReference reference, GaugeImpl service) { + unregister(service); + } + + //~------------------------------------< Closeable > + + @Override + public void close() { + tracker.close(); + } + + //~-------------------------------------< Internal > + + private void unregister(GaugeImpl service) { + mapper.unregister(Collections.singleton(service.name)); + } + + private void register(ServiceReference reference, GaugeImpl gaugeImpl) { + mapper.addMapping(gaugeImpl.name, reference.getBundle()); + registry.register(gaugeImpl.name, gaugeImpl); + } + + //~--------------------------------------< GaugeImpl > + + public static class GaugeImpl implements com.codahale.metrics.Gauge { + String name; + final Gauge gauge; + + public GaugeImpl(String name, Gauge gauge) { + this.name = name; + this.gauge = gauge; + } + + @Override + public Object getValue() { + return gauge.getValue(); + } + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/HistogramImpl.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/HistogramImpl.java new file mode 100644 index 000000000..3507e3a18 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/HistogramImpl.java @@ -0,0 +1,50 @@ +/* + * 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.sling.commons.metrics.internal; + + +import org.apache.sling.commons.metrics.Histogram; + +final class HistogramImpl implements Histogram { + private final com.codahale.metrics.Histogram histogram; + + HistogramImpl(com.codahale.metrics.Histogram histogram) { + this.histogram = histogram; + } + + @Override + public void update(long value) { + histogram.update(value); + } + + @Override + public long getCount() { + return histogram.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public A adaptTo(Class type) { + if (type == com.codahale.metrics.Histogram.class){ + return (A) histogram; + } + return null; + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactory.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactory.java new file mode 100644 index 000000000..c3f7fff97 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactory.java @@ -0,0 +1,102 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.sling.commons.metrics.Counter; +import org.apache.sling.commons.metrics.Histogram; +import org.apache.sling.commons.metrics.Meter; +import org.apache.sling.commons.metrics.MetricsService; +import org.apache.sling.commons.metrics.Timer; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; + +class InternalMetricsServiceFactory implements ServiceFactory { + private final MetricsService delegate; + private final BundleMetricsMapper metricsMapper; + + public InternalMetricsServiceFactory(MetricsService delegate, BundleMetricsMapper metricsMapper) { + this.delegate = delegate; + this.metricsMapper = metricsMapper; + } + + @Override + public MetricsService getService(Bundle bundle, ServiceRegistration registration) { + return new BundleMetricService(bundle); + } + + @Override + public void ungetService(Bundle bundle, ServiceRegistration registration, MetricsService service) { + if (service instanceof BundleMetricService){ + ((BundleMetricService) service).unregister(); + } + } + + private class BundleMetricService implements MetricsService { + private final Bundle bundle; + private Set registeredNames = Collections.newSetFromMap(new ConcurrentHashMap()); + + public BundleMetricService(Bundle bundle) { + this.bundle = bundle; + } + + @Override + public Timer timer(String name) { + addMapping(name); + return delegate.timer(name); + } + + @Override + public Histogram histogram(String name) { + addMapping(name); + return delegate.histogram(name); + } + + @Override + public Counter counter(String name) { + addMapping(name); + return delegate.counter(name); + } + + @Override + public Meter meter(String name) { + addMapping(name); + return delegate.meter(name); + } + + @Override + public A adaptTo(Class type) { + return delegate.adaptTo(type); + } + + void unregister(){ + metricsMapper.unregister(registeredNames); + } + + private void addMapping(String name) { + metricsMapper.addMapping(name, bundle); + registeredNames.add(name); + } + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JSONReporter.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JSONReporter.java new file mode 100644 index 000000000..1b24b3e2a --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JSONReporter.java @@ -0,0 +1,299 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Locale; +import java.util.Map; +import java.util.SortedMap; +import java.util.concurrent.TimeUnit; + +import org.apache.felix.utils.json.JSONWriter; + +import com.codahale.metrics.ConsoleReporter; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricFilter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Reporter; +import com.codahale.metrics.Snapshot; +import com.codahale.metrics.Timer; + +class JSONReporter implements Reporter, Closeable { + + public static JSONReporter.Builder forRegistry(MetricRegistry registry) { + return new JSONReporter.Builder(registry); + } + + public static class Builder { + private final MetricRegistry registry; + private PrintStream output; + private MetricFilter filter; + private TimeUnit rateUnit; + private TimeUnit durationUnit; + + private Builder(MetricRegistry registry) { + this.registry = registry; + this.output = System.out; + this.filter = MetricFilter.ALL; + this.rateUnit = TimeUnit.SECONDS; + this.durationUnit = TimeUnit.MILLISECONDS; + } + + /** + * Write to the given {@link PrintStream}. + * + * @param output a {@link PrintStream} instance. + * @return {@code this} + */ + public Builder outputTo(PrintStream output) { + this.output = output; + return this; + } + + /** + * Convert rates to the given time unit. + * + * @param rateUnit a unit of time + * @return {@code this} + */ + public Builder convertRatesTo(TimeUnit rateUnit) { + this.rateUnit = rateUnit; + return this; + } + + /** + * Convert durations to the given time unit. + * + * @param durationUnit a unit of time + * @return {@code this} + */ + public Builder convertDurationsTo(TimeUnit durationUnit) { + this.durationUnit = durationUnit; + return this; + } + + /** + * Only report metrics which match the given filter. + * + * @param filter a {@link MetricFilter} + * @return {@code this} + */ + public Builder filter(MetricFilter filter) { + this.filter = filter; + return this; + } + + /** + * Builds a {@link ConsoleReporter} with the given properties. + * + * @return a {@link ConsoleReporter} + */ + public JSONReporter build() { + return new JSONReporter(registry, + output, + rateUnit, + durationUnit, + filter); + } + } + + private final MetricRegistry registry; + private final MetricFilter filter; + private final double durationFactor; + private final String durationUnit; + private final double rateFactor; + private final String rateUnit; + private final JSONWriter json; + private final PrintWriter pw; + + private JSONReporter(MetricRegistry registry, + PrintStream output, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter){ + this.registry = registry; + this.filter = filter; + this.pw = new PrintWriter(output); + this.json = new JSONWriter(pw); + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = calculateRateUnit(rateUnit); + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); + } + + public void report() { + try { + report(registry.getGauges(filter), + registry.getCounters(filter), + registry.getHistograms(filter), + registry.getMeters(filter), + registry.getTimers(filter)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void close(){ + pw.flush(); + } + + private void report(SortedMap gauges, SortedMap counters, + SortedMap histograms, SortedMap meters, + SortedMap timers) throws IOException { + json.object(); + if (!gauges.isEmpty()) { + json.key("gauges").object(); + for (Map.Entry entry : gauges.entrySet()) { + printGauge(entry); + } + json.endObject(); + } + + if (!counters.isEmpty()) { + json.key("counters").object(); + for (Map.Entry entry : counters.entrySet()) { + printCounter(entry); + } + json.endObject(); + } + + if (!histograms.isEmpty()) { + json.key("histograms").object(); + for (Map.Entry entry : histograms.entrySet()) { + printHistogram(entry); + } + json.endObject(); + } + + if (!meters.isEmpty()) { + json.key("meters").object(); + for (Map.Entry entry : meters.entrySet()) { + printMeter(entry); + } + json.endObject(); + } + + if (!timers.isEmpty()) { + json.key("timers").object(); + for (Map.Entry entry : timers.entrySet()) { + printTimer(entry); + } + json.endObject(); + } + + json.endObject(); + + } + + private void printTimer(Map.Entry e) throws IOException { + json.key(e.getKey()).object(); + Timer timer = e.getValue(); + Snapshot snapshot = timer.getSnapshot(); + + json.key("count").value(timer.getCount()); + json.key("max").value(snapshot.getMax() * durationFactor); + json.key("mean").value(snapshot.getMean() * durationFactor); + json.key("min").value(snapshot.getMin() * durationFactor); + + json.key("p50").value(snapshot.getMedian() * durationFactor); + json.key("p75").value(snapshot.get75thPercentile() * durationFactor); + json.key("p95").value(snapshot.get95thPercentile() * durationFactor); + json.key("p98").value(snapshot.get98thPercentile() * durationFactor); + json.key("p99").value(snapshot.get99thPercentile() * durationFactor); + json.key("p999").value(snapshot.get999thPercentile() * durationFactor); + + json.key("stddev").value(snapshot.getStdDev() * durationFactor); + json.key("m1_rate").value(timer.getOneMinuteRate() * rateFactor); + json.key("m5_rate").value(timer.getFiveMinuteRate() * rateFactor); + json.key("m15_rate").value(timer.getFifteenMinuteRate() * rateFactor); + json.key("mean_rate").value(timer.getMeanRate() * rateFactor); + json.key("duration_units").value(durationUnit); + json.key("rate_units").value(rateUnit); + + json.endObject(); + } + + private void printMeter(Map.Entry e) throws IOException { + json.key(e.getKey()).object(); + Meter meter = e.getValue(); + json.key("count").value(e.getValue().getCount()); + json.key("m1_rate").value(meter.getOneMinuteRate() * rateFactor); + json.key("m5_rate").value(meter.getFiveMinuteRate() * rateFactor); + json.key("m15_rate").value(meter.getFifteenMinuteRate() * rateFactor); + json.key("mean_rate").value(meter.getMeanRate() * rateFactor); + json.key("units").value(rateUnit); + json.endObject(); + } + + private void printHistogram(Map.Entry e) throws IOException { + json.key(e.getKey()).object(); + json.key("count").value(e.getValue().getCount()); + + Snapshot snapshot = e.getValue().getSnapshot(); + json.key("max").value(snapshot.getMax()); + json.key("mean").value(snapshot.getMean()); + json.key("min").value(snapshot.getMin()); + json.key("p50").value(snapshot.getMedian()); + json.key("p75").value(snapshot.get75thPercentile()); + json.key("p95").value(snapshot.get95thPercentile()); + json.key("p98").value(snapshot.get98thPercentile()); + json.key("p99").value(snapshot.get99thPercentile()); + json.key("p999").value(snapshot.get999thPercentile()); + json.key("stddev").value(snapshot.getStdDev()); + + json.endObject(); + } + + private void printCounter(Map.Entry e) throws IOException { + json.key(e.getKey()).object(); + json.key("count").value(e.getValue().getCount()); + json.endObject(); + } + + private void printGauge(Map.Entry e) throws IOException { + json.key(e.getKey()).object(); + Object v = e.getValue().getValue(); + json.key("value").value(jsonSafeValue(v)); + json.endObject(); + } + + private static Object jsonSafeValue(Object v){ + //Json does not allow NaN or infinite doubles. So take care of that + if (v instanceof Number){ + if (v instanceof Double){ + Double d = (Double) v; + if (d.isInfinite() || d.isNaN()){ + return d.toString(); + } + } + } + return v; + } + + private static String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } + +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JmxUtil.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JmxUtil.java new file mode 100644 index 000000000..993863118 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/JmxUtil.java @@ -0,0 +1,89 @@ +/* + * 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.sling.commons.metrics.internal; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +/** + * Utility methods related to JMX + */ +final class JmxUtil { + + /** + * Checks if the passed value string can be used as is as part of + * JMX {@link javax.management.ObjectName} If it cannot be used then + * it would return a quoted string which is then safe to be used + * as part of ObjectName. + * + *

This is meant to avoid unnecessary quoting of value

+ * + * @param unquotedValue to quote if required + * @return passed value or quoted value if required + */ + public static String quoteValueIfRequired(String unquotedValue) { + String result; + String quotedValue = ObjectName.quote(unquotedValue); + + //Check if some chars are escaped or not. In that case + //length of quoted string (excluding quotes) would differ + if (quotedValue.substring(1, quotedValue.length() - 1).equals(unquotedValue)) { + ObjectName on = null; + try { + //Quoting logic in ObjectName does not escape ',', '=' + //etc. So try now by constructing ObjectName. If that + //passes then value can be used as safely + + //Also we cannot just rely on ObjectName as it treats + //*, ? as pattern chars and which should ideally be escaped + on = new ObjectName("dummy", "dummy", unquotedValue); + } catch (MalformedObjectNameException ignore) { + //ignore + } + + if (on != null){ + result = unquotedValue; + } else { + result = quotedValue; + } + } else { + //Some escaping done. So do quote + result = quotedValue; + } + return result; + } + + + public static String safeDomainName(String name){ + if (name == null){ + return null; + } + + name = name.trim(); + + //Taken from javax.management.ObjectName.isDomain() + //Following are special chars in domain name + name = name.replace(':', '_'); + name = name.replace('*', '_'); + name = name.replace('?', '_'); + name = name.replace('\n', '_'); + return name; + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java new file mode 100644 index 000000000..f1f872fdb --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java @@ -0,0 +1,150 @@ +/* + * 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.sling.commons.metrics.internal; + +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricFilter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Slf4jReporter; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +@Component(service = {}, configurationPolicy = ConfigurationPolicy.REQUIRE) +@Designate(ocd = LogReporter.Config.class, factory = true) +public class LogReporter implements ServiceTrackerCustomizer { + + private BundleContext context; + private ServiceTracker tracker; + private Config config; + + @Activate + protected void activate(Config config, BundleContext context) { + this.config = config; + this.context = context; + tracker = new ServiceTracker<>(context, MetricRegistry.class, this); + tracker.open(); + } + + @Deactivate + protected void deactivate(BundleContext context) { + tracker.close(); + } + + //~---------------------------------------------< ServiceTracker > + + @Override + public Slf4jReporter addingService(ServiceReference serviceReference) { + MetricRegistry registry = context.getService(serviceReference); + String metricRegistryName = (String) serviceReference.getProperty(MetricWebConsolePlugin.METRIC_REGISTRY_NAME); + + if (config.registryName() == null || config.registryName().length() == 0 + || config.registryName().equals(metricRegistryName)) { + Slf4jReporter.Builder builder = Slf4jReporter.forRegistry(registry). + outputTo(LoggerFactory.getLogger(config.loggerName())). + withLoggingLevel(config.level()); + + if (config.prefix() != null && config.prefix().length() > 0) { + builder.filter(new PrefixFilter(config.prefix())); + } else if (config.pattern() != null && config.pattern().length() > 0) { + builder.filter(new PatternFilter(config.pattern())); + } + + Slf4jReporter reporter = builder.build(); + reporter.start(config.period(), config.timeUnit()); + return reporter; + } else { + return null; + } + } + + @Override + public void modifiedService(ServiceReference serviceReference, Slf4jReporter reporter) { + // NO OP + } + + @Override + public void removedService(ServiceReference serviceReference, Slf4jReporter reporter) { + if (reporter != null) { + reporter.close(); + } + } + + private class PrefixFilter implements MetricFilter { + private final String prefix; + + private PrefixFilter(String prefix) { + this.prefix = prefix; + } + + @Override + public boolean matches(String s, Metric metric) { + return s.startsWith(prefix); + } + } + + private class PatternFilter implements MetricFilter { + private final Pattern pattern; + + private PatternFilter(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + @Override + public boolean matches(String s, Metric metric) { + return pattern.matcher(s).matches(); + } + } + + @ObjectClassDefinition(name = "Apache Sling Metrics Log Reporter Configuration") + @interface Config { + + @AttributeDefinition(description = "Period at which the metrics data will be logged") + long period() default 5; + + @AttributeDefinition(description = "Unit of time for evaluating the period") + TimeUnit timeUnit() default TimeUnit.MINUTES; + + @AttributeDefinition(description = "The log level to log at.") + Slf4jReporter.LoggingLevel level() default Slf4jReporter.LoggingLevel.INFO; + + @AttributeDefinition(description = "The logger name") + String loggerName() default "metrics"; + + @AttributeDefinition(description = "If specified, only metrics whose name starts with this value are logged. If both prefix and pattern are set, prefix is used.") + String prefix() default ""; + + @AttributeDefinition(description = "If specified, only metrics whose name matches this regular expression will be logged. If both prefix and pattern are set, prefix is used.") + String pattern() default ""; + + @AttributeDefinition(description = "Restrict the metrics logged to a specifically named registry.") + String registryName() default ""; + } + +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MeterImpl.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MeterImpl.java new file mode 100644 index 000000000..83d4ff765 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MeterImpl.java @@ -0,0 +1,54 @@ +/* + * 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.sling.commons.metrics.internal; + +import org.apache.sling.commons.metrics.Meter; + +final class MeterImpl implements Meter { + private final com.codahale.metrics.Meter meter; + + MeterImpl(com.codahale.metrics.Meter meter) { + this.meter = meter; + } + + @Override + public void mark() { + meter.mark(); + } + + @Override + public void mark(long n) { + meter.mark(n); + } + + @Override + public long getCount() { + return meter.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public A adaptTo(Class type) { + if (type == com.codahale.metrics.Meter.class){ + return (A)meter; + } + return null; + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePlugin.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePlugin.java new file mode 100644 index 000000000..ea9bfec2e --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePlugin.java @@ -0,0 +1,475 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.SortedMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +import javax.servlet.Servlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.codahale.metrics.ConsoleReporter; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Snapshot; +import com.codahale.metrics.Timer; +import org.apache.commons.io.output.WriterOutputStream; +import org.apache.felix.inventory.Format; +import org.apache.felix.inventory.InventoryPrinter; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = {InventoryPrinter.class, Servlet.class}, + property = { + "felix.webconsole.label=slingmetrics", + "felix.webconsole.title=Metrics", + "felix.webconsole.category=Sling", + InventoryPrinter.FORMAT + "=TEXT", + InventoryPrinter.FORMAT + "=JSON", + InventoryPrinter.TITLE + "=Sling Metrics", + InventoryPrinter.NAME + "=slingmetrics" + } +) +public class MetricWebConsolePlugin extends HttpServlet implements + InventoryPrinter, ServiceTrackerCustomizer{ + /** + * Service property name which stores the MetricRegistry name as a given OSGi + * ServiceRegistry might have multiple instances of MetricRegistry + */ + public static final String METRIC_REGISTRY_NAME = "name"; + private final Logger log = LoggerFactory.getLogger(getClass()); + private BundleContext context; + private ServiceTracker tracker; + private ConcurrentMap registries + = new ConcurrentHashMap<>(); + + private TimeUnit rateUnit = TimeUnit.SECONDS; + private TimeUnit durationUnit = TimeUnit.MILLISECONDS; + private Map specificDurationUnits = Collections.emptyMap(); + private Map specificRateUnits = Collections.emptyMap(); + private MetricTimeUnits timeUnit; + + @Activate + private void activate(BundleContext context){ + this.context = context; + this.timeUnit = new MetricTimeUnits(rateUnit, durationUnit, specificRateUnits, specificDurationUnits); + tracker = new ServiceTracker<>(context, MetricRegistry.class, this); + tracker.open(); + } + + @Deactivate + private void deactivate(BundleContext context){ + tracker.close(); + } + + //~--------------------------------------------< InventoryPrinter > + + @Override + public void print(PrintWriter printWriter, Format format, boolean isZip) { + if (format == Format.TEXT) { + MetricRegistry registry = getConsolidatedRegistry(); + ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) + .outputTo(new PrintStream(new WriterOutputStream(printWriter))) + .build(); + reporter.report(); + reporter.close(); + } else if (format == Format.JSON) { + MetricRegistry registry = getConsolidatedRegistry(); + JSONReporter reporter = JSONReporter.forRegistry(registry) + .outputTo(new PrintStream(new WriterOutputStream(printWriter))) + .build(); + reporter.report(); + reporter.close(); + } + } + + + //~---------------------------------------------< ServiceTracker > + + @Override + public MetricRegistry addingService(ServiceReference serviceReference) { + MetricRegistry registry = context.getService(serviceReference); + registries.put(serviceReference, registry); + return registry; + } + + @Override + public void modifiedService(ServiceReference serviceReference, MetricRegistry registry) { + registries.put(serviceReference, registry); + } + + @Override + public void removedService(ServiceReference serviceReference, MetricRegistry registry) { + registries.remove(serviceReference); + } + + //~----------------------------------------------< Servlet > + + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { + final PrintWriter pw = resp.getWriter(); + MetricRegistry registry = getConsolidatedRegistry(); + + appendMetricStatus(pw, registry); + addCounterDetails(pw, registry.getCounters()); + addGaugeDetails(pw, registry.getGauges()); + addMeterDetails(pw, registry.getMeters()); + addTimerDetails(pw, registry.getTimers()); + addHistogramDetails(pw, registry.getHistograms()); + } + + private static void appendMetricStatus(PrintWriter pw, MetricRegistry registry) { + pw.printf( + "

Metrics: %d gauges, %d timers, %d meters, %d counters, %d histograms

%n", + registry.getGauges().size(), + registry.getTimers().size(), + registry.getMeters().size(), + registry.getCounters().size(), + registry.getHistograms().size()); + } + + private void addMeterDetails(PrintWriter pw, SortedMap meters) { + if (meters.isEmpty()) { + return; + } + pw.println("
"); + pw.println("
"); + pw.println("
Meters
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : meters.entrySet()) { + Meter m = e.getValue(); + String name = e.getKey(); + + double rateFactor = timeUnit.rateFor(name).toSeconds(1); + String rateUnit = "events/" + calculateRateUnit(timeUnit.rateFor(name)); + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", m.getCount()); + pw.printf("", m.getMeanRate() * rateFactor); + pw.printf("", m.getOneMinuteRate() * rateFactor); + pw.printf("", m.getFiveMinuteRate() * rateFactor); + pw.printf("", m.getFifteenMinuteRate() * rateFactor); + pw.printf("", rateUnit); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCountMean RateOneMinuteRateFiveMinuteRateFifteenMinuteRate"); + pw.println("RateUnit
%s%d%f%f%f%f%s
"); + pw.println("
"); + } + + private void addTimerDetails(PrintWriter pw, SortedMap timers) { + if (timers.isEmpty()) { + return; + } + + pw.println("
"); + pw.println("
"); + pw.println("
Timers
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : timers.entrySet()) { + Timer t = e.getValue(); + Snapshot s = t.getSnapshot(); + String name = e.getKey(); + + double rateFactor = timeUnit.rateFor(name).toSeconds(1); + String rateUnit = "events/" + calculateRateUnit(timeUnit.rateFor(name)); + + double durationFactor = 1.0 / timeUnit.durationFor(name).toNanos(1); + String durationUnit = timeUnit.durationFor(name).toString().toLowerCase(Locale.US); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", t.getCount()); + pw.printf("", t.getMeanRate() * rateFactor); + pw.printf("", t.getOneMinuteRate() * rateFactor); + pw.printf("", t.getFiveMinuteRate() * rateFactor); + pw.printf("", t.getFifteenMinuteRate() * rateFactor); + + pw.printf("", s.getMedian() * durationFactor); + pw.printf("", s.getMin() * durationFactor); + pw.printf("", s.getMax() * durationFactor); + pw.printf("", s.getMean() * durationFactor); + pw.printf("", s.getStdDev() * durationFactor); + + pw.printf("", s.get75thPercentile() * durationFactor); + pw.printf("", s.get95thPercentile() * durationFactor); + pw.printf("", s.get98thPercentile() * durationFactor); + pw.printf("", s.get99thPercentile() * durationFactor); + pw.printf("", s.get999thPercentile() * durationFactor); + + pw.printf("", rateUnit); + pw.printf("", durationUnit); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCountMean Rate1 min rate5 mins rate15 mins rate50%MinMaxMeanStdDev75%95%98%99%999%Rate UnitDuration Unit
%s%d%f%f%f%f%f%f%f%f%f%f%f%f%f%f%s%s
"); + pw.println("
"); + } + + private void addHistogramDetails(PrintWriter pw, SortedMap histograms) { + if (histograms.isEmpty()) { + return; + } + + pw.println("
"); + pw.println("
"); + pw.println("
Histograms
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : histograms.entrySet()) { + Histogram h = e.getValue(); + Snapshot s = h.getSnapshot(); + String name = e.getKey(); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", h.getCount()); + pw.printf("", s.getMedian()); + pw.printf("", s.getMin()); + pw.printf("", s.getMax()); + pw.printf("", s.getMean()); + pw.printf("", s.getStdDev()); + + pw.printf("", s.get75thPercentile()); + pw.printf("", s.get95thPercentile()); + pw.printf("", s.get98thPercentile()); + pw.printf("", s.get99thPercentile()); + pw.printf("", s.get999thPercentile()); + + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCount50%MinMaxMeanStdDev75%95%98%99%999%
%s%d%f%d%d%f%f%f%f%f%f%f
"); + pw.println("
"); + } + + private void addCounterDetails(PrintWriter pw, SortedMap counters) { + if (counters.isEmpty()) { + return; + } + pw.println("
"); + pw.println("
"); + pw.println("
Counters
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : counters.entrySet()) { + Counter c = e.getValue(); + String name = e.getKey(); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", c.getCount()); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCount
%s%d
"); + pw.println("
"); + } + + private void addGaugeDetails(PrintWriter pw, SortedMap gauges) { + if (gauges.isEmpty()) { + return; + } + + pw.println("
"); + pw.println("
"); + pw.println("
Gauges
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : gauges.entrySet()) { + Gauge g = e.getValue(); + String name = e.getKey(); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", g.getValue()); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameValue
%s%s
"); + pw.println("
"); + } + + + //~----------------------------------------------< internal > + + MetricRegistry getConsolidatedRegistry() { + MetricRegistry registry = new MetricRegistry(); + for (Map.Entry registryEntry : registries.entrySet()){ + String metricRegistryName = (String) registryEntry.getKey().getProperty(METRIC_REGISTRY_NAME); + for (Map.Entry metricEntry : registryEntry.getValue().getMetrics().entrySet()){ + String metricName = metricEntry.getKey(); + try{ + if (metricRegistryName != null){ + metricName = metricRegistryName + ":" + metricName; + } + registry.register(metricName, metricEntry.getValue()); + }catch (IllegalArgumentException ex){ + log.warn("Duplicate Metric name found {}", metricName, ex); + } + } + } + return registry; + } + + private static String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } + + private static class MetricTimeUnits { + private final TimeUnit defaultRate; + private final TimeUnit defaultDuration; + private final Map rateOverrides; + private final Map durationOverrides; + + MetricTimeUnits(TimeUnit defaultRate, + TimeUnit defaultDuration, + Map rateOverrides, + Map durationOverrides) { + this.defaultRate = defaultRate; + this.defaultDuration = defaultDuration; + this.rateOverrides = rateOverrides; + this.durationOverrides = durationOverrides; + } + + public TimeUnit durationFor(String name) { + return durationOverrides.containsKey(name) ? durationOverrides.get(name) : defaultDuration; + } + + public TimeUnit rateFor(String name) { + return rateOverrides.containsKey(name) ? rateOverrides.get(name) : defaultRate; + } + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java new file mode 100644 index 000000000..094c51aab --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java @@ -0,0 +1,236 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.management.MBeanServer; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; +import org.apache.sling.commons.metrics.Meter; +import org.apache.sling.commons.metrics.MetricsService; +import org.apache.sling.commons.metrics.Timer; +import org.apache.sling.commons.metrics.Counter; +import org.apache.sling.commons.metrics.Histogram; +import org.apache.sling.commons.metrics.Metric; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; + +@Component(service = {}, immediate = true) +public class MetricsServiceImpl implements MetricsService { + private final List regs = new ArrayList<>(); + private final ConcurrentMap metrics = new ConcurrentHashMap<>(); + private final MetricRegistry registry = new MetricRegistry(); + private final BundleMetricsMapper metricsMapper = new BundleMetricsMapper(this, registry); + private GaugeManager gaugeManager; + + @Reference(cardinality = ReferenceCardinality.OPTIONAL) + private MBeanServer server; + + private JmxReporter reporter; + + /* WARNING: if any OSGi configuration parameters are added to this class + * we’ll need to handle the service restart case to prevent service + * references retrieved using MetricsServiceFactory from becoming + * stale. See discussion in SLING-6702. + */ + @Activate + private void activate(BundleContext context, Map config) { + enableJMXReporter(); + + gaugeManager = new GaugeManager(context, registry, metricsMapper); + + final Dictionary svcProps = new Hashtable<>(); + svcProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Metrics Service"); + svcProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + regs.add(context.registerService(MetricsService.class.getName(), + new InternalMetricsServiceFactory(this, metricsMapper), svcProps)); + + final Dictionary regProps = new Hashtable<>(); + regProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Metrics Registry"); + regProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + regProps.put("name", "sling"); + regs.add(context.registerService(MetricRegistry.class.getName(), registry, regProps)); + } + + @Deactivate + private void deactivate() throws IOException { + for (ServiceRegistration reg : regs) { + reg.unregister(); + } + regs.clear(); + + gaugeManager.close(); + + metrics.clear(); + + if (reporter != null) { + reporter.close(); + } + } + + @Override + public Timer timer(String name) { + return getOrAdd(name, MetricBuilder.TIMERS); + } + + @Override + public Histogram histogram(String name) { + return getOrAdd(name, MetricBuilder.HISTOGRAMS); + } + + @Override + public Counter counter(String name) { + return getOrAdd(name, MetricBuilder.COUNTERS); + } + + @Override + public Meter meter(String name) { + return getOrAdd(name, MetricBuilder.METERS); + } + + @SuppressWarnings("unchecked") + @Override + public
A adaptTo(Class type) { + if (type == MetricRegistry.class){ + return (A) registry; + } + return null; + } + + void remove(String name) { + metrics.remove(name); + } + + MetricRegistry getRegistry() { + return registry; + } + + @SuppressWarnings("unchecked") + private T getOrAdd(String name, MetricBuilder builder) { + final Metric metric = metrics.get(name); + if (builder.isInstance(metric)) { + return (T) metric; + } else if (metric == null) { + try { + return register(name, builder.newMetric(registry, name)); + } catch (IllegalArgumentException e) { + final Metric added = metrics.get(name); + if (builder.isInstance(added)) { + return (T) added; + } + } + } + throw new IllegalArgumentException(name + " is already used for a different type of metric"); + } + + private T register(String name, T metric) throws IllegalArgumentException { + final Metric existing = metrics.putIfAbsent(name, metric); + if (existing != null) { + throw new IllegalArgumentException("A metric named " + name + " already exists"); + } + return metric; + } + + /** + * A quick and easy way of capturing the notion of default metrics. + */ + private interface MetricBuilder { + MetricBuilder COUNTERS = new MetricBuilder() { + @Override + public Counter newMetric(MetricRegistry registry, String name) { + return new CounterImpl(registry.counter(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Counter.class.isInstance(metric); + } + }; + + MetricBuilder HISTOGRAMS = new MetricBuilder() { + @Override + public Histogram newMetric(MetricRegistry registry, String name) { + return new HistogramImpl(registry.histogram(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Histogram.class.isInstance(metric); + } + }; + + MetricBuilder METERS = new MetricBuilder() { + @Override + public Meter newMetric(MetricRegistry registry, String name) { + return new MeterImpl(registry.meter(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Meter.class.isInstance(metric); + } + }; + + MetricBuilder TIMERS = new MetricBuilder() { + @Override + public Timer newMetric(MetricRegistry registry, String name) { + return new TimerImpl(registry.timer(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Timer.class.isInstance(metric); + } + }; + + T newMetric(MetricRegistry registry, String name); + + boolean isInstance(Metric metric); + } + + private void enableJMXReporter() { + if (server == null){ + server = ManagementFactory.getPlatformMBeanServer(); + } + + reporter = JmxReporter.forRegistry(registry) + .inDomain(BundleMetricsMapper.DEFAULT_DOMAIN_NAME) + .createsObjectNamesWith(metricsMapper) + .registerWith(server) + .build(); + reporter.start(); + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/TimerImpl.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/TimerImpl.java new file mode 100644 index 000000000..cf9d87ffa --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/internal/TimerImpl.java @@ -0,0 +1,77 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.util.concurrent.TimeUnit; + +import org.apache.sling.commons.metrics.Timer; + + +final class TimerImpl implements Timer { + private final com.codahale.metrics.Timer timer; + + TimerImpl(com.codahale.metrics.Timer timer) { + this.timer = timer; + } + + @Override + public void update(long duration, TimeUnit unit) { + timer.update(duration, unit); + } + + @Override + public Context time() { + return new ContextImpl(timer.time()); + } + + @Override + public long getCount() { + return timer.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public A adaptTo(Class type) { + if (type == com.codahale.metrics.Timer.class) { + return (A) timer; + } + return null; + } + + private static final class ContextImpl implements Context { + private final com.codahale.metrics.Timer.Context context; + + private ContextImpl(com.codahale.metrics.Timer.Context context) { + this.context = context; + } + + public long stop() { + return context.stop(); + } + + /** + * Equivalent to calling {@link #stop()}. + */ + @Override + public void close() { + stop(); + } + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/package-info.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/package-info.java new file mode 100644 index 000000000..0fa4a337a --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/main/java/org/apache/sling/commons/metrics/package-info.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * Provides an API to generate and collect metrics in various forms + * + * @version 1.0 + */ +@Version("1.2.0") +package org.apache.sling.commons.metrics; + + +import org.osgi.annotation.versioning.Version; \ No newline at end of file diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapperTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapperTest.java new file mode 100644 index 000000000..ba1ca9efe --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapperTest.java @@ -0,0 +1,70 @@ +/* + * 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.sling.commons.metrics.internal; + +import javax.management.ObjectName; + +import com.codahale.metrics.MetricRegistry; +import com.google.common.collect.ImmutableMap; +import org.apache.sling.testing.mock.osgi.MockBundle; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class BundleMetricsMapperTest { + @Rule + public final OsgiContext context = new OsgiContext(); + private MetricRegistry registry = new MetricRegistry(); + + private BundleMetricsMapper mapper = new BundleMetricsMapper(new MetricsServiceImpl(), registry); + + @Test + public void defaultDomainName() throws Exception{ + ObjectName name = mapper.createName("counter", "foo", "bar"); + assertEquals("foo", name.getDomain()); + } + + @Test + public void mappedName_SymbolicName() throws Exception{ + MockBundle bundle = new MockBundle(context.bundleContext()); + bundle.setSymbolicName("com.example"); + + mapper.addMapping("bar", bundle); + + ObjectName name = mapper.createName("counter", "foo", "bar"); + assertEquals("com.example", name.getDomain()); + } + + @Test + public void mappedName_Header() throws Exception{ + MockBundle bundle = new MockBundle(context.bundleContext()); + bundle.setSymbolicName("com.example"); + bundle.setHeaders(ImmutableMap.of(BundleMetricsMapper.HEADER_DOMAIN_NAME, "com.test")); + + mapper.addMapping("bar", bundle); + + ObjectName name = mapper.createName("counter", "foo", "bar"); + assertEquals("com.test", name.getDomain()); + } + + +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactoryTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactoryTest.java new file mode 100644 index 000000000..3d68fae43 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/InternalMetricsServiceFactoryTest.java @@ -0,0 +1,98 @@ +/* + * 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.sling.commons.metrics.internal; + +import com.codahale.metrics.MetricRegistry; +import javax.management.ObjectName; + +import org.apache.sling.commons.metrics.Counter; +import org.apache.sling.commons.metrics.MetricsService; +import org.apache.sling.testing.mock.osgi.MockBundle; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.Test; +import static org.mockito.Mockito.mock; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceRegistration; + +public class InternalMetricsServiceFactoryTest { + @Rule + public final OsgiContext context = new OsgiContext(); + private MetricsServiceImpl serviceImpl = new MetricsServiceImpl(); + private MetricRegistry registry = serviceImpl.getRegistry(); + private BundleMetricsMapper mapper = new BundleMetricsMapper(serviceImpl, registry); + private InternalMetricsServiceFactory srvFactory = new InternalMetricsServiceFactory(serviceImpl, mapper); + private ServiceRegistration reg = mock(ServiceRegistration.class); + + @Test + public void basicWorking() throws Exception{ + MetricsService service = srvFactory.getService(cb("foo"), reg); + service.meter("m1"); + service.timer("t1"); + service.histogram("h1"); + service.counter("c1"); + assertTrue(registry.getMeters().containsKey("m1")); + assertTrue(registry.getTimers().containsKey("t1")); + assertTrue(registry.getHistograms().containsKey("h1")); + assertTrue(registry.getCounters().containsKey("c1")); + + ObjectName name = mapper.createName("meter", "com.foo", "m1"); + + //Domain name should be bundle symbolic name + assertEquals("foo", name.getDomain()); + } + + @Test + public void unRegistration() throws Exception{ + Bundle foo = cb("foo"); + Bundle bar = cb("bar"); + MetricsService srv1 = srvFactory.getService(foo, reg); + MetricsService srv2 = srvFactory.getService(bar, reg); + + srv1.meter("m1"); + Counter c1 = srv1.counter("c1"); + + srv2.meter("m2"); + assertTrue(registry.getMeters().containsKey("m1")); + assertTrue(registry.getMeters().containsKey("m2")); + assertTrue(registry.getCounters().containsKey("c1")); + + srvFactory.ungetService(foo, reg, srv1); + + //Metrics from 'foo' bundle i.e. m1 and c1 must be removed + assertFalse(registry.getMeters().containsKey("m1")); + assertFalse(registry.getCounters().containsKey("c1")); + + assertNotEquals("The MetricsService should not return stale metric references.", c1, serviceImpl.counter("c1")); + assertTrue(registry.getCounters().containsKey("c1")); + + //Metrics from 'bar' bundle should be present + assertTrue(registry.getMeters().containsKey("m2")); + } + + private Bundle cb(String name){ + MockBundle bundle = new MockBundle(context.bundleContext()); + bundle.setSymbolicName(name); + return bundle; + } + + +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JSONReporterTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JSONReporterTest.java new file mode 100644 index 000000000..4de6cc82c --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JSONReporterTest.java @@ -0,0 +1,91 @@ +/* + * 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.sling.commons.metrics.internal; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.PrintStream; +import java.io.StringWriter; +import java.util.Map; + +import org.apache.commons.io.output.WriterOutputStream; +import org.apache.felix.utils.json.JSONParser; +import org.junit.Test; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.JvmAttributeGaugeSet; +import com.codahale.metrics.MetricRegistry; + +public class JSONReporterTest { + + @SuppressWarnings("unchecked") + @Test + public void jsonOutput() throws Exception { + MetricRegistry registry = new MetricRegistry(); + registry.meter("test1").mark(5); + registry.timer("test2").time().close(); + registry.histogram("test3").update(743); + registry.counter("test4").inc(9); + registry.registerAll(new JvmAttributeGaugeSet()); + + Map json = getJSON(registry); + + assertTrue(json.containsKey("meters")); + assertTrue(json.containsKey("gauges")); + assertTrue(json.containsKey("timers")); + assertTrue(json.containsKey("counters")); + assertTrue(json.containsKey("histograms")); + assertTrue(json.containsKey("meters")); + + assertTrue(((Map)json.get("meters")).containsKey("test1")); + assertTrue(((Map)json.get("timers")).containsKey("test2")); + assertTrue(((Map)json.get("counters")).containsKey("test4")); + assertTrue(((Map)json.get("histograms")).containsKey("test3")); + } + + @SuppressWarnings("unchecked") + @Test + public void nan_value() throws Exception{ + MetricRegistry registry = new MetricRegistry(); + + registry.register("test", new Gauge() { + @Override + public Double getValue() { + return Double.POSITIVE_INFINITY; + } + }); + + + Map json = getJSON(registry); + assertTrue(((Map)json.get("gauges")).containsKey("test")); + } + + private static Map getJSON(MetricRegistry registry) throws IOException { + StringWriter sw = new StringWriter(); + JSONReporter reporter = JSONReporter.forRegistry(registry) + .outputTo(new PrintStream(new WriterOutputStream(sw))) + .build(); + reporter.report(); + reporter.close(); + return new JSONParser(sw.toString()).getParsed(); + } + +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JmxUtilTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JmxUtilTest.java new file mode 100644 index 000000000..9db25c7d0 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/JmxUtilTest.java @@ -0,0 +1,51 @@ +/* + * 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.sling.commons.metrics.internal; + +import junit.framework.TestCase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class JmxUtilTest { + + @Test + public void quotation() throws Exception{ + assertEquals("text", JmxUtil.quoteValueIfRequired("text")); + TestCase.assertEquals("", JmxUtil.quoteValueIfRequired("")); + assertTrue(JmxUtil.quoteValueIfRequired("text*with?chars").startsWith("\"")); + } + + @Test + public void quoteAndComma() throws Exception{ + assertTrue(JmxUtil.quoteValueIfRequired("text,withComma").startsWith("\"")); + assertTrue(JmxUtil.quoteValueIfRequired("text=withEqual").startsWith("\"")); + } + + @Test + public void safeDomainName() throws Exception{ + assertEquals("com.foo", JmxUtil.safeDomainName("com.foo")); + assertEquals("com_foo", JmxUtil.safeDomainName("com:foo")); + assertEquals("com_foo", JmxUtil.safeDomainName("com?foo")); + assertEquals("com_foo", JmxUtil.safeDomainName("com*foo")); + } + +} \ No newline at end of file diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java new file mode 100644 index 000000000..67f195097 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java @@ -0,0 +1,449 @@ +/* + * 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.sling.commons.metrics.internal; + +import com.codahale.metrics.MetricFilter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Slf4jReporter; +import junitx.util.PrivateAccessor; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; + + +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +import java.lang.annotation.Annotation; +import java.util.concurrent.TimeUnit; + +@RunWith(MockitoJUnitRunner.class) +public class LogReporterTest { + + @Mock + private BundleContext bundleContext; + + LogReporter reporterService = new LogReporter(); + + @Test + public void testSpecificRegistryNameInclude() { + MetricRegistry registry = new MetricRegistry(); + ServiceReference registryServiceReference = mock(ServiceReference.class); + when(bundleContext.getService(registryServiceReference)).thenReturn(registry); + when(registryServiceReference.getProperty(MetricWebConsolePlugin.METRIC_REGISTRY_NAME)).thenReturn("oak"); + + LogReporter.Config config = createConfigWithRegistryName("oak"); + reporterService.activate(config, bundleContext); + + Slf4jReporter reporter = null; + try { + reporter = reporterService.addingService(registryServiceReference); + assertNotNull(reporter); + } finally { + if (reporter != null) { + reporter.close(); + } + reporterService.deactivate(bundleContext); + } + } + + @Test + public void testSpecificRegistryNameExclude() { + MetricRegistry registry = new MetricRegistry(); + ServiceReference registryServiceReference = mock(ServiceReference.class); + when(bundleContext.getService(registryServiceReference)).thenReturn(registry); + when(registryServiceReference.getProperty(MetricWebConsolePlugin.METRIC_REGISTRY_NAME)).thenReturn("other"); + + LogReporter.Config config = createConfigWithRegistryName("oak"); + reporterService.activate(config, bundleContext); + + Slf4jReporter reporter = null; + try { + reporter = reporterService.addingService(registryServiceReference); + assertNull(reporter); + } finally { + if (reporter != null) { + reporter.close(); + } + reporterService.deactivate(bundleContext); + } + } + + @Test + public void testSpecificRegistryNameExcludeNullName() { + MetricRegistry registry = new MetricRegistry(); + ServiceReference registryServiceReference = mock(ServiceReference.class); + when(bundleContext.getService(registryServiceReference)).thenReturn(registry); + + LogReporter.Config config = createConfigWithRegistryName("oak"); + reporterService.activate(config, bundleContext); + + Slf4jReporter reporter = null; + try { + reporter = reporterService.addingService(registryServiceReference); + assertNull(reporter); + } finally { + if (reporter != null) { + reporter.close(); + } + reporterService.deactivate(bundleContext); + } + } + + @Test + public void testLoggerName() throws Exception { + MetricRegistry registry = new MetricRegistry(); + ServiceReference registryServiceReference = mock(ServiceReference.class); + when(bundleContext.getService(registryServiceReference)).thenReturn(registry); + + LogReporter.Config config = createConfigWithLoggerNameAndLevel("test", Slf4jReporter.LoggingLevel.WARN); + reporterService.activate(config, bundleContext); + + Slf4jReporter reporter = null; + try { + reporter = reporterService.addingService(registryServiceReference); + assertNotNull(reporter); + + Object loggerProxy = PrivateAccessor.getField(reporter, "loggerProxy"); + assertEquals("WarnLoggerProxy", loggerProxy.getClass().getSimpleName()); + + Logger logger = (Logger) PrivateAccessor.getField(loggerProxy, "logger"); + assertEquals("test", logger.getName()); + } finally { + if (reporter != null) { + reporter.close(); + } + reporterService.deactivate(bundleContext); + } + } + + @Test + public void testPrefix() throws Exception { + MetricRegistry registry = new MetricRegistry(); + ServiceReference registryServiceReference = mock(ServiceReference.class); + when(bundleContext.getService(registryServiceReference)).thenReturn(registry); + + LogReporter.Config config = createConfigWithPrefix("testPrefix"); + reporterService.activate(config, bundleContext); + + Slf4jReporter reporter = null; + try { + reporter = reporterService.addingService(registryServiceReference); + assertNotNull(reporter); + + MetricFilter filter = (MetricFilter) PrivateAccessor.getField(reporter, "filter"); + assertEquals("PrefixFilter", filter.getClass().getSimpleName()); + assertTrue(filter.matches("testPrefixedName", null)); + assertFalse(filter.matches("testNonPrefixedName", null)); + } finally { + if (reporter != null) { + reporter.close(); + } + reporterService.deactivate(bundleContext); + } + } + + @Test + public void testPattern() throws Exception { + MetricRegistry registry = new MetricRegistry(); + ServiceReference registryServiceReference = mock(ServiceReference.class); + when(bundleContext.getService(registryServiceReference)).thenReturn(registry); + + LogReporter.Config config = createConfigWithPattern("[0-9]test.*"); + reporterService.activate(config, bundleContext); + + Slf4jReporter reporter = null; + try { + reporter = reporterService.addingService(registryServiceReference); + assertNotNull(reporter); + + MetricFilter filter = (MetricFilter) PrivateAccessor.getField(reporter, "filter"); + assertEquals("PatternFilter", filter.getClass().getSimpleName()); + assertTrue(filter.matches("5testTest", null)); + assertFalse(filter.matches("ZtestTest", null)); + } finally { + if (reporter != null) { + reporter.close(); + } + reporterService.deactivate(bundleContext); + } + } + + @Test + public void testPrefixAndPattern() throws Exception { + MetricRegistry registry = new MetricRegistry(); + ServiceReference registryServiceReference = mock(ServiceReference.class); + when(bundleContext.getService(registryServiceReference)).thenReturn(registry); + + LogReporter.Config config = createConfigWithPrefixAndPattern("testPrefix", "[0-9]test.*"); + reporterService.activate(config, bundleContext); + + Slf4jReporter reporter = null; + try { + reporter = reporterService.addingService(registryServiceReference); + assertNotNull(reporter); + + MetricFilter filter = (MetricFilter) PrivateAccessor.getField(reporter, "filter"); + assertEquals("PrefixFilter", filter.getClass().getSimpleName()); + assertTrue(filter.matches("testPrefixedName", null)); + assertFalse(filter.matches("testNonPrefixedName", null)); + } finally { + if (reporter != null) { + reporter.close(); + } + reporterService.deactivate(bundleContext); + } + } + + @Test + public void testRemove() { + Slf4jReporter reporter = mock(Slf4jReporter.class); + reporterService.removedService(null, reporter); + verify(reporter, times(1)).close(); + } + + @Test + public void testNoOpCalls() { + // extra no-op calls for coverage + reporterService.removedService(null, null); + reporterService.modifiedService(null, null); + } + + private LogReporter.Config createConfigWithRegistryName(final String registryName) { + return new LogReporter.Config() { + @Override + public long period() { + return 5; + } + + @Override + public TimeUnit timeUnit() { + return TimeUnit.MINUTES; + } + + @Override + public Slf4jReporter.LoggingLevel level() { + return Slf4jReporter.LoggingLevel.INFO; + } + + @Override + public String loggerName() { + return "metrics"; + } + + @Override + public String prefix() { + return null; + } + + @Override + public String pattern() { + return null; + } + + @Override + public String registryName() { + return registryName; + } + + @Override + public Class annotationType() { + return LogReporter.Config.class; + } + }; + } + + private LogReporter.Config createConfigWithLoggerNameAndLevel(final String loggerName, final Slf4jReporter.LoggingLevel level) { + return new LogReporter.Config() { + @Override + public long period() { + return 5; + } + + @Override + public TimeUnit timeUnit() { + return TimeUnit.MINUTES; + } + + @Override + public Slf4jReporter.LoggingLevel level() { + return level; + } + + @Override + public String loggerName() { + return loggerName; + } + + @Override + public String prefix() { + return null; + } + + @Override + public String pattern() { + return null; + } + + @Override + public String registryName() { + return ""; + } + + @Override + public Class annotationType() { + return LogReporter.Config.class; + } + }; + } + + private LogReporter.Config createConfigWithPrefix(final String prefix) { + return new LogReporter.Config() { + @Override + public long period() { + return 5; + } + + @Override + public TimeUnit timeUnit() { + return TimeUnit.MINUTES; + } + + @Override + public Slf4jReporter.LoggingLevel level() { + return Slf4jReporter.LoggingLevel.INFO; + } + + @Override + public String loggerName() { + return "metrics"; + } + + @Override + public String prefix() { + return prefix; + } + + @Override + public String pattern() { + return null; + } + + @Override + public String registryName() { + return null; + } + + @Override + public Class annotationType() { + return LogReporter.Config.class; + } + }; + } + + private LogReporter.Config createConfigWithPattern(final String pattern) { + return new LogReporter.Config() { + @Override + public long period() { + return 5; + } + + @Override + public TimeUnit timeUnit() { + return TimeUnit.MINUTES; + } + + @Override + public Slf4jReporter.LoggingLevel level() { + return Slf4jReporter.LoggingLevel.INFO; + } + + @Override + public String loggerName() { + return "metrics"; + } + + @Override + public String prefix() { + return null; + } + + @Override + public String pattern() { + return pattern; + } + + @Override + public String registryName() { + return null; + } + + @Override + public Class annotationType() { + return LogReporter.Config.class; + } + }; + } + + private LogReporter.Config createConfigWithPrefixAndPattern(final String prefix, final String pattern) { + return new LogReporter.Config() { + @Override + public long period() { + return 5; + } + + @Override + public TimeUnit timeUnit() { + return TimeUnit.MINUTES; + } + + @Override + public Slf4jReporter.LoggingLevel level() { + return Slf4jReporter.LoggingLevel.INFO; + } + + @Override + public String loggerName() { + return "metrics"; + } + + @Override + public String prefix() { + return prefix; + } + + @Override + public String pattern() { + return pattern; + } + + @Override + public String registryName() { + return null; + } + + @Override + public Class annotationType() { + return LogReporter.Config.class; + } + }; + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java new file mode 100644 index 000000000..a8af34aaf --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java @@ -0,0 +1,187 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.lang.management.ManagementFactory; +import java.util.Collections; +import java.util.Set; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.Query; +import javax.management.QueryExp; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; +import org.apache.sling.commons.metrics.Counter; +import org.apache.sling.commons.metrics.Gauge; +import org.apache.sling.commons.metrics.Histogram; +import org.apache.sling.commons.metrics.Meter; +import org.apache.sling.commons.metrics.MetricsService; +import org.apache.sling.commons.metrics.Timer; +import org.apache.sling.testing.mock.osgi.MapUtil; +import org.apache.sling.testing.mock.osgi.MockOsgi; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.osgi.framework.ServiceRegistration; + +import static org.apache.sling.commons.metrics.internal.BundleMetricsMapper.JMX_TYPE_METRICS; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class MetricServiceTest { + @Rule + public final OsgiContext context = new OsgiContext(); + + private MetricsServiceImpl service = new MetricsServiceImpl(); + + @After + public void deactivate(){ + MockOsgi.deactivate(service, context.bundleContext()); + } + + @Test + public void defaultSetup() throws Exception{ + activate(); + + assertNotNull(context.getService(MetricRegistry.class)); + assertNotNull(context.getService(MetricsService.class)); + + assertNotNull(service.adaptTo(MetricRegistry.class)); + + MockOsgi.deactivate(service, context.bundleContext()); + + assertNull(context.getService(MetricRegistry.class)); + assertNull(context.getService(MetricsService.class)); + } + + @Test + public void meter() throws Exception{ + activate(); + Meter meter = service.meter("test"); + + assertNotNull(meter); + assertTrue(getRegistry().getMeters().containsKey("test")); + + assertSame(meter, service.meter("test")); + } + + @Test + public void counter() throws Exception{ + activate(); + Counter counter = service.counter("test"); + + assertNotNull(counter); + assertTrue(getRegistry().getCounters().containsKey("test")); + + assertSame(counter, service.counter("test")); + } + + @Test + public void timer() throws Exception{ + activate(); + Timer timer = service.timer("test"); + + assertNotNull(timer); + assertTrue(getRegistry().getTimers().containsKey("test")); + + assertSame(timer, service.timer("test")); + } + + @Test + public void histogram() throws Exception{ + activate(); + Histogram histo = service.histogram("test"); + + assertNotNull(histo); + assertTrue(getRegistry().getHistograms().containsKey("test")); + + assertSame(histo, service.histogram("test")); + } + + @Test(expected = IllegalArgumentException.class) + public void sameNameDifferentTypeMetric() throws Exception{ + activate(); + service.histogram("test"); + service.timer("test"); + } + + @Test + public void jmxRegistration() throws Exception{ + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + activate(); + Meter meter = service.meter("test"); + assertNotNull(meter); + QueryExp q = Query.isInstanceOf(Query.value(JmxReporter.JmxMeterMBean.class.getName())); + Set names = server.queryNames(new ObjectName("org.apache.sling:name=*,type="+ JMX_TYPE_METRICS), q); + assertThat(names, is(not(empty()))); + + MockOsgi.deactivate(service, context.bundleContext()); + + names = server.queryNames(new ObjectName("org.apache.sling:name=*"), q); + assertThat(names, is(empty())); + + } + + @Test + public void gaugeRegistration() throws Exception{ + activate(); + ServiceRegistration reg = context.bundleContext().registerService(Gauge.class, new TestGauge(42), + MapUtil.toDictionary(Gauge.NAME, "foo")); + + assertTrue(getRegistry().getGauges().containsKey("foo")); + assertEquals(42, getRegistry().getGauges().get("foo").getValue()); + + reg.unregister(); + assertFalse(getRegistry().getGauges().containsKey("foo")); + } + + private MetricRegistry getRegistry(){ + return context.getService(MetricRegistry.class); + } + + private void activate() { + MockOsgi.activate(service, context.bundleContext(), Collections.emptyMap()); + } + + private static class TestGauge implements Gauge { + int value; + + public TestGauge(int value){ + this.value = value; + } + + @Override + public Object getValue() { + return value; + } + } + +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePluginTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePluginTest.java new file mode 100644 index 000000000..8f0c3ceee --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePluginTest.java @@ -0,0 +1,200 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.codahale.metrics.JvmAttributeGaugeSet; +import com.codahale.metrics.MetricRegistry; +import com.gargoylesoftware.htmlunit.StringWebResponse; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebResponse; +import com.gargoylesoftware.htmlunit.html.HTMLParser; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlTable; +import org.apache.felix.inventory.Format; +import org.apache.felix.utils.json.JSONParser; +import org.apache.sling.testing.mock.osgi.MockOsgi; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.junit.Rule; +import org.junit.Test; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class MetricWebConsolePluginTest { + @Rule + public final OsgiContext context = new OsgiContext(); + + private MetricWebConsolePlugin plugin = new MetricWebConsolePlugin(); + + private static Map regProps(String name) { + Map props = new HashMap(); + props.put("name", name); + return props; + } + + @Test + public void consolidatedRegistry() throws Exception { + MetricRegistry reg1 = new MetricRegistry(); + reg1.meter("test1"); + context.registerService(MetricRegistry.class, reg1, regProps("foo")); + + activatePlugin(); + + MetricRegistry consolidated = plugin.getConsolidatedRegistry(); + + //Check name decoration + assertEquals(1, consolidated.getMetrics().size()); + assertTrue(consolidated.getMeters().containsKey("foo:test1")); + + MetricRegistry reg2 = new MetricRegistry(); + reg2.meter("test2"); + context.registerService(MetricRegistry.class, reg2); + + //Metric Registry without name would not be decorated + consolidated = plugin.getConsolidatedRegistry(); + assertEquals(2, consolidated.getMetrics().size()); + assertTrue(consolidated.getMeters().containsKey("test2")); + + //Duplicate metric in other registry should not fail. Warning log + //should be generated + MetricRegistry reg3 = new MetricRegistry(); + reg3.meter("test2"); + context.registerService(MetricRegistry.class, reg3); + consolidated = plugin.getConsolidatedRegistry(); + assertEquals(2, consolidated.getMetrics().size()); + + MetricRegistry reg4 = new MetricRegistry(); + reg4.meter("test1"); + context.registerService(MetricRegistry.class, reg4, regProps("bar")); + consolidated = plugin.getConsolidatedRegistry(); + assertTrue(consolidated.getMeters().containsKey("foo:test1")); + assertTrue(consolidated.getMeters().containsKey("bar:test1")); + } + + @Test + public void inventory_text() throws Exception { + MetricRegistry reg1 = new MetricRegistry(); + reg1.meter("test1").mark(5); + context.registerService(MetricRegistry.class, reg1, regProps("foo")); + + activatePlugin(); + + StringWriter sw = new StringWriter(); + PrintWriter pw = spy(new PrintWriter(sw)); + plugin.print(pw, Format.TEXT, false); + + String out = sw.toString(); + assertThat(out, containsString("foo:test1")); + assertThat(out, containsString("Meters")); + verify(pw, never()).close(); + } + + @Test + public void inventory_json() throws Exception{ + MetricRegistry reg1 = new MetricRegistry(); + reg1.meter("test1").mark(5); + context.registerService(MetricRegistry.class, reg1, regProps("foo")); + + activatePlugin(); + + StringWriter sw = new StringWriter(); + PrintWriter pw = spy(new PrintWriter(sw)); + plugin.print(pw, Format.JSON, false); + + Map json = new JSONParser(sw.toString()).getParsed(); + assertTrue(json.containsKey("meters")); + verify(pw, never()).close(); + } + + @Test + public void webConsolePlugin() throws Exception { + MetricRegistry reg1 = new MetricRegistry(); + reg1.meter("test1").mark(5); + reg1.timer("test2").time().close(); + reg1.histogram("test3").update(743); + reg1.counter("test4").inc(9); + reg1.registerAll(new JvmAttributeGaugeSet()); + context.registerService(MetricRegistry.class, reg1, regProps("foo")); + + activatePlugin(); + + StringWriter sw = new StringWriter(); + + HttpServletResponse response = mock(HttpServletResponse.class); + when(response.getWriter()).thenReturn(new PrintWriter(sw)); + + plugin.doGet(mock(HttpServletRequest.class), response); + + WebClient client = new WebClient(); + WebResponse resp = new StringWebResponse(sw.toString(), WebClient.URL_ABOUT_BLANK); + HtmlPage page = HTMLParser.parseHtml(resp, client.getCurrentWindow()); + + assertTable("data-meters", page); + assertTable("data-counters", page); + assertTable("data-timers", page); + assertTable("data-histograms", page); + assertTable("data-gauges", page); + } + + private void assertTable(String name, HtmlPage page) { + HtmlTable table = page.getHtmlElementById(name); + assertNotNull(table); + + //1 for header and 1 for actual metric row + assertThat(table.getRowCount(), greaterThanOrEqualTo(2)); + + } + + private void activatePlugin() { + MockOsgi.activate(plugin, context.bundleContext(), Collections.emptyMap()); + } + + private static class CloseRecordingWriter extends PrintWriter { + + + public CloseRecordingWriter(Writer out) { + super(out); + } + + @Override + public void close() { + super.close(); + } + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWrapperTest.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWrapperTest.java new file mode 100644 index 000000000..3e581232b --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/internal/MetricWrapperTest.java @@ -0,0 +1,132 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.util.concurrent.TimeUnit; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.ExponentiallyDecayingReservoir; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +public class MetricWrapperTest { + private MetricRegistry registry = new MetricRegistry(); + + @Test + public void counter() throws Exception { + Counter counter = registry.counter("test"); + CounterImpl counterStats = new CounterImpl(counter); + + counterStats.increment(); + assertEquals(1, counterStats.getCount()); + assertEquals(1, counter.getCount()); + assertEquals(1, counterStats.getCount()); + + counterStats.increment(); + counterStats.increment(); + assertEquals(3, counterStats.getCount()); + + counterStats.decrement(); + assertEquals(2, counterStats.getCount()); + assertEquals(2, counter.getCount()); + + counterStats.increment(7); + assertEquals(9, counterStats.getCount()); + assertEquals(9, counter.getCount()); + + counterStats.decrement(5); + assertEquals(4, counterStats.getCount()); + assertEquals(4, counter.getCount()); + + assertSame(counter, counterStats.adaptTo(Counter.class)); + } + + @Test + public void meter() throws Exception { + Meter meter = registry.meter("test"); + MeterImpl meterStats = new MeterImpl(meter); + + meterStats.mark(); + assertEquals(1, meterStats.getCount()); + assertEquals(1, meter.getCount()); + + meterStats.mark(5); + assertEquals(6, meterStats.getCount()); + assertEquals(6, meter.getCount()); + assertSame(meter, meterStats.adaptTo(Meter.class)); + } + + @Test + public void timer() throws Exception { + Timer time = registry.timer("test"); + TimerImpl timerStats = new TimerImpl(time); + + timerStats.update(100, TimeUnit.SECONDS); + assertEquals(1, time.getCount()); + assertEquals(TimeUnit.SECONDS.toNanos(100), time.getSnapshot().getMax()); + + timerStats.update(100, TimeUnit.SECONDS); + assertEquals(2, timerStats.getCount()); + + assertSame(time, timerStats.adaptTo(Timer.class)); + } + + @Test + public void histogram() throws Exception { + Histogram histo = registry.histogram("test"); + HistogramImpl histoStats = new HistogramImpl(histo); + + histoStats.update(100); + assertEquals(1, histo.getCount()); + assertEquals(1, histoStats.getCount()); + assertEquals(100, histo.getSnapshot().getMax()); + + assertSame(histo, histoStats.adaptTo(Histogram.class)); + } + + @Test + public void timerContext() throws Exception{ + VirtualClock clock = new VirtualClock(); + Timer time = new Timer(new ExponentiallyDecayingReservoir(), clock); + + TimerImpl timerStats = new TimerImpl(time); + org.apache.sling.commons.metrics.Timer.Context context = timerStats.time(); + + clock.tick = TimeUnit.SECONDS.toNanos(314); + context.close(); + + assertEquals(1, time.getCount()); + assertEquals(TimeUnit.SECONDS.toNanos(314), time.getSnapshot().getMax()); + } + + private static class VirtualClock extends com.codahale.metrics.Clock { + long tick; + @Override + public long getTick() { + return tick; + } + } +} diff --git a/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/test/MetricsServiceFactoryIT.java b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/test/MetricsServiceFactoryIT.java new file mode 100644 index 000000000..dd8f33929 --- /dev/null +++ b/Java-base/sling-org-apache-sling-commons-metrics/src/src/test/java/org/apache/sling/commons/metrics/test/MetricsServiceFactoryIT.java @@ -0,0 +1,85 @@ +/* + * 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.sling.commons.metrics.test; + +import javax.inject.Inject; + +import org.apache.sling.commons.metrics.MetricsService; +import org.apache.sling.commons.metrics.MetricsServiceFactory; +import org.apache.sling.testing.paxexam.TestSupport; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; +import org.osgi.framework.BundleContext; + +import static org.apache.sling.testing.paxexam.SlingOptions.scr; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.ops4j.pax.exam.CoreOptions.junitBundles; +import static org.ops4j.pax.exam.CoreOptions.mavenBundle; +import static org.ops4j.pax.exam.CoreOptions.options; + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class MetricsServiceFactoryIT extends TestSupport { + + @Inject + private BundleContext bundleContext; + + @Configuration + public Option[] configuration() { + return options( + baseConfiguration(), + scr(), + // Commons Metrics + testBundle("bundle.filename"), + mavenBundle().groupId("io.dropwizard.metrics").artifactId("metrics-core").versionAsInProject(), + junitBundles() + ); + } + + @Test + public void nullClass() { + try { + MetricsServiceFactory.getMetricsService(null); + fail("Expecting an Exception"); + } catch (IllegalArgumentException asExpected) { + } + } + + @Test + public void classNotLoadedFromOsgiBundle() { + try { + MetricsServiceFactory.getMetricsService(String.class); + fail("Expecting an Exception"); + } catch (IllegalArgumentException asExpected) { + } + } + + @Test + public void classFromBundle() { + final MetricsService m = MetricsServiceFactory.getMetricsService(getClass()); + assertNotNull("Expecting a MetricsService", m); + } + +} diff --git a/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/Dockerfile b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/Dockerfile new file mode 100644 index 000000000..2da9cbf3c --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:sling-org-apache-sling-commons-metrics + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/buggy.java b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/buggy.java new file mode 100644 index 000000000..253ada71b --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/buggy.java @@ -0,0 +1,98 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.util.Hashtable; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.ObjectNameFactory; +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class BundleMetricsMapper implements ObjectNameFactory{ + public static final String HEADER_DOMAIN_NAME = "Sling-Metrics-Domain"; + public static final String DEFAULT_DOMAIN_NAME = "org.apache.sling"; + static final String JMX_TYPE_METRICS = "Metrics"; + private final Logger log = LoggerFactory.getLogger(getClass()); + private final ConcurrentMap metricToBundleMapping = new ConcurrentHashMap<>(); + private final MetricsServiceImpl metricsService; + private final MetricRegistry registry; + + BundleMetricsMapper(MetricsServiceImpl metricsService, MetricRegistry registry) { + this.metricsService = metricsService; + this.registry = registry; + } + + public void addMapping(String name, Bundle bundle) { + metricToBundleMapping.putIfAbsent(name, bundle); + } + + public void unregister(Set registeredNames) { + for (String name : registeredNames){ + registry.remove(name); + metricToBundleMapping.remove(name); + metricsService.remove(name); + } + log.debug("Removed metrics for {}", registeredNames); + } + + @Override + public ObjectName createName(String type, String domain, String name) { + String mappedDomainName = JmxUtil.safeDomainName(getDomainName(name)); + if (mappedDomainName == null) { + mappedDomainName = domain; + } + + Hashtable table = new Hashtable<>(); + table.put("type", JMX_TYPE_METRICS); + table.put("name", JmxUtil.quoteValueIfRequired(name)); + try { + return new ObjectName(mappedDomainName, table); + } catch (MalformedObjectNameException e) { + log.warn("Unable to register {} {}", type, name, e); + throw new RuntimeException(e); + } + } + + private String getDomainName(String name) { + Bundle bundle = metricToBundleMapping.get(name); + return getDomainName(bundle); + } + +private java.lang.String getDomainName(org.osgi.framework.Bundle bundle) { + { + java.lang.String domainNameHeader = /* NPEX_NULL_EXP */ + bundle.getHeaders().get(org.apache.sling.commons.metrics.internal.BundleMetricsMapper.HEADER_DOMAIN_NAME); + if (domainNameHeader != null) { + return domainNameHeader; + } + // Fallback to symbolic name + return bundle.getSymbolicName(); + } +} + +} diff --git a/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/metadata.json b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/metadata.json new file mode 100644 index 000000000..0b9c98d61 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapper.java", + "line": 89, + "npe_method": "getDomainName", + "deref_field": "bundle", + "npe_class": "BundleMetricsMapper", + "repo": "sling-org-apache-sling-commons-metrics", + "bug_id": "BundleMetricsMapper_87" + } +} diff --git a/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/npe.json b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/npe.json new file mode 100644 index 000000000..792a1cec5 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-BundleMetricsMapper_87/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/BundleMetricsMapper.java", + "line": 89, + "npe_method": "getDomainName", + "deref_field": "bundle", + "npe_class": "BundleMetricsMapper" +} \ No newline at end of file diff --git a/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/Dockerfile b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/Dockerfile new file mode 100644 index 000000000..2da9cbf3c --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:sling-org-apache-sling-commons-metrics + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/buggy.java b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/buggy.java new file mode 100644 index 000000000..3506cf1c5 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/buggy.java @@ -0,0 +1,87 @@ +/* + * 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.sling.commons.metrics.internal; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +/** + * Utility methods related to JMX + */ +final class JmxUtil { + + /** + * Checks if the passed value string can be used as is as part of + * JMX {@link javax.management.ObjectName} If it cannot be used then + * it would return a quoted string which is then safe to be used + * as part of ObjectName. + * + *

This is meant to avoid unnecessary quoting of value

+ * + * @param unquotedValue to quote if required + * @return passed value or quoted value if required + */ + public static String quoteValueIfRequired(String unquotedValue) { + String result; + String quotedValue = ObjectName.quote(unquotedValue); + + //Check if some chars are escaped or not. In that case + //length of quoted string (excluding quotes) would differ + if (quotedValue.substring(1, quotedValue.length() - 1).equals(unquotedValue)) { + ObjectName on = null; + try { + //Quoting logic in ObjectName does not escape ',', '=' + //etc. So try now by constructing ObjectName. If that + //passes then value can be used as safely + + //Also we cannot just rely on ObjectName as it treats + //*, ? as pattern chars and which should ideally be escaped + on = new ObjectName("dummy", "dummy", unquotedValue); + } catch (MalformedObjectNameException ignore) { + //ignore + } + + if (on != null){ + result = unquotedValue; + } else { + result = quotedValue; + } + } else { + //Some escaping done. So do quote + result = quotedValue; + } + return result; + } + + +public static java.lang.String safeDomainName(java.lang.String name) { + { + name = /* NPEX_NULL_EXP */ + name.trim(); + // Taken from javax.management.ObjectName.isDomain() + // Following are special chars in domain name + name = name.replace(':', '_'); + name = name.replace('*', '_'); + name = name.replace('?', '_'); + name = name.replace('\n', '_'); + return name; + } +} +} diff --git a/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/metadata.json b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/metadata.json new file mode 100644 index 000000000..7cd9046d4 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "sling-org-apache-sling-commons-metrics-JmxUtil_75", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/JmxUtil.java", + "line": 77, + "npe_method": "safeDomainName", + "deref_field": "name", + "npe_class": "JmxUtil", + "repo": "sling-org-apache-sling-commons-metrics", + "bug_id": "JmxUtil_75" + } +} diff --git a/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/npe.json b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/npe.json new file mode 100644 index 000000000..4eaaa497a --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-JmxUtil_75/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/JmxUtil.java", + "line": 77, + "npe_method": "safeDomainName", + "deref_field": "name", + "npe_class": "JmxUtil" +} \ No newline at end of file diff --git a/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/Dockerfile b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/Dockerfile new file mode 100644 index 000000000..2da9cbf3c --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:sling-org-apache-sling-commons-metrics + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/buggy.java b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/buggy.java new file mode 100644 index 000000000..b5c0bbaff --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/buggy.java @@ -0,0 +1,151 @@ +/* + * 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.sling.commons.metrics.internal; + +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricFilter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Slf4jReporter; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +@Component(service = {}, configurationPolicy = ConfigurationPolicy.REQUIRE) +@Designate(ocd = LogReporter.Config.class, factory = true) +public class LogReporter implements ServiceTrackerCustomizer { + + private BundleContext context; + private ServiceTracker tracker; + private Config config; + + @Activate + protected void activate(Config config, BundleContext context) { + this.config = config; + this.context = context; + tracker = new ServiceTracker<>(context, MetricRegistry.class, this); + tracker.open(); + } + + @Deactivate + protected void deactivate(BundleContext context) { + tracker.close(); + } + + //~---------------------------------------------< ServiceTracker > + + @Override + public Slf4jReporter addingService(ServiceReference serviceReference) { + MetricRegistry registry = context.getService(serviceReference); + String metricRegistryName = (String) serviceReference.getProperty(MetricWebConsolePlugin.METRIC_REGISTRY_NAME); + + if (config.registryName() == null || config.registryName().length() == 0 + || config.registryName().equals(metricRegistryName)) { + Slf4jReporter.Builder builder = Slf4jReporter.forRegistry(registry). + outputTo(LoggerFactory.getLogger(config.loggerName())). + withLoggingLevel(config.level()); + + if (config.prefix() != null && config.prefix().length() > 0) { + builder.filter(new PrefixFilter(config.prefix())); + } else if (config.pattern() != null && config.pattern().length() > 0) { + builder.filter(new PatternFilter(config.pattern())); + } + + Slf4jReporter reporter = builder.build(); + reporter.start(config.period(), config.timeUnit()); + return reporter; + } else { + return null; + } + } + + @Override + public void modifiedService(ServiceReference serviceReference, Slf4jReporter reporter) { + // NO OP + } + + @Override +public void removedService(org.osgi.framework.ServiceReference serviceReference, com.codahale.metrics.Slf4jReporter reporter) { + { + /* NPEX_NULL_EXP */ + reporter.close(); + } +} + + private class PrefixFilter implements MetricFilter { + private final String prefix; + + private PrefixFilter(String prefix) { + this.prefix = prefix; + } + + @Override + public boolean matches(String s, Metric metric) { + return s.startsWith(prefix); + } + } + + private class PatternFilter implements MetricFilter { + private final Pattern pattern; + + private PatternFilter(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + @Override + public boolean matches(String s, Metric metric) { + return pattern.matcher(s).matches(); + } + } + + @ObjectClassDefinition(name = "Apache Sling Metrics Log Reporter Configuration") + @interface Config { + + @AttributeDefinition(description = "Period at which the metrics data will be logged") + long period() default 5; + + @AttributeDefinition(description = "Unit of time for evaluating the period") + TimeUnit timeUnit() default TimeUnit.MINUTES; + + @AttributeDefinition(description = "The log level to log at.") + Slf4jReporter.LoggingLevel level() default Slf4jReporter.LoggingLevel.INFO; + + @AttributeDefinition(description = "The logger name") + String loggerName() default "metrics"; + + @AttributeDefinition(description = "If specified, only metrics whose name starts with this value are logged. If both prefix and pattern are set, prefix is used.") + String prefix() default ""; + + @AttributeDefinition(description = "If specified, only metrics whose name matches this regular expression will be logged. If both prefix and pattern are set, prefix is used.") + String pattern() default ""; + + @AttributeDefinition(description = "Restrict the metrics logged to a specifically named registry.") + String registryName() default ""; + } + +} diff --git a/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/metadata.json b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/metadata.json new file mode 100644 index 000000000..2525589c7 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "sling-org-apache-sling-commons-metrics-LogReporter_94", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java", + "line": 96, + "npe_method": "removedService", + "deref_field": "reporter", + "npe_class": "LogReporter", + "repo": "sling-org-apache-sling-commons-metrics", + "bug_id": "LogReporter_94" + } +} diff --git a/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/npe.json b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/npe.json new file mode 100644 index 000000000..c43292351 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-LogReporter_94/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java", + "line": 96, + "npe_method": "removedService", + "deref_field": "reporter", + "npe_class": "LogReporter" +} \ No newline at end of file diff --git a/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/Dockerfile b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/Dockerfile new file mode 100644 index 000000000..2da9cbf3c --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:sling-org-apache-sling-commons-metrics + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/buggy.java b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/buggy.java new file mode 100644 index 000000000..269db1ba1 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/buggy.java @@ -0,0 +1,477 @@ +/* + * 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.sling.commons.metrics.internal; + +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.SortedMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +import javax.servlet.Servlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.codahale.metrics.ConsoleReporter; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Snapshot; +import com.codahale.metrics.Timer; +import org.apache.commons.io.output.WriterOutputStream; +import org.apache.felix.inventory.Format; +import org.apache.felix.inventory.InventoryPrinter; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = {InventoryPrinter.class, Servlet.class}, + property = { + "felix.webconsole.label=slingmetrics", + "felix.webconsole.title=Metrics", + "felix.webconsole.category=Sling", + InventoryPrinter.FORMAT + "=TEXT", + InventoryPrinter.FORMAT + "=JSON", + InventoryPrinter.TITLE + "=Sling Metrics", + InventoryPrinter.NAME + "=slingmetrics" + } +) +public class MetricWebConsolePlugin extends HttpServlet implements + InventoryPrinter, ServiceTrackerCustomizer{ + /** + * Service property name which stores the MetricRegistry name as a given OSGi + * ServiceRegistry might have multiple instances of MetricRegistry + */ + public static final String METRIC_REGISTRY_NAME = "name"; + private final Logger log = LoggerFactory.getLogger(getClass()); + private BundleContext context; + private ServiceTracker tracker; + private ConcurrentMap registries + = new ConcurrentHashMap<>(); + + private TimeUnit rateUnit = TimeUnit.SECONDS; + private TimeUnit durationUnit = TimeUnit.MILLISECONDS; + private Map specificDurationUnits = Collections.emptyMap(); + private Map specificRateUnits = Collections.emptyMap(); + private MetricTimeUnits timeUnit; + + @Activate + private void activate(BundleContext context){ + this.context = context; + this.timeUnit = new MetricTimeUnits(rateUnit, durationUnit, specificRateUnits, specificDurationUnits); + tracker = new ServiceTracker<>(context, MetricRegistry.class, this); + tracker.open(); + } + + @Deactivate + private void deactivate(BundleContext context){ + tracker.close(); + } + + //~--------------------------------------------< InventoryPrinter > + + @Override + public void print(PrintWriter printWriter, Format format, boolean isZip) { + if (format == Format.TEXT) { + MetricRegistry registry = getConsolidatedRegistry(); + ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) + .outputTo(new PrintStream(new WriterOutputStream(printWriter))) + .build(); + reporter.report(); + reporter.close(); + } else if (format == Format.JSON) { + MetricRegistry registry = getConsolidatedRegistry(); + JSONReporter reporter = JSONReporter.forRegistry(registry) + .outputTo(new PrintStream(new WriterOutputStream(printWriter))) + .build(); + reporter.report(); + reporter.close(); + } + } + + + //~---------------------------------------------< ServiceTracker > + + @Override + public MetricRegistry addingService(ServiceReference serviceReference) { + MetricRegistry registry = context.getService(serviceReference); + registries.put(serviceReference, registry); + return registry; + } + + @Override + public void modifiedService(ServiceReference serviceReference, MetricRegistry registry) { + registries.put(serviceReference, registry); + } + + @Override + public void removedService(ServiceReference serviceReference, MetricRegistry registry) { + registries.remove(serviceReference); + } + + //~----------------------------------------------< Servlet > + + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { + final PrintWriter pw = resp.getWriter(); + MetricRegistry registry = getConsolidatedRegistry(); + + appendMetricStatus(pw, registry); + addCounterDetails(pw, registry.getCounters()); + addGaugeDetails(pw, registry.getGauges()); + addMeterDetails(pw, registry.getMeters()); + addTimerDetails(pw, registry.getTimers()); + addHistogramDetails(pw, registry.getHistograms()); + } + + private static void appendMetricStatus(PrintWriter pw, MetricRegistry registry) { + pw.printf( + "

Metrics: %d gauges, %d timers, %d meters, %d counters, %d histograms

%n", + registry.getGauges().size(), + registry.getTimers().size(), + registry.getMeters().size(), + registry.getCounters().size(), + registry.getHistograms().size()); + } + + private void addMeterDetails(PrintWriter pw, SortedMap meters) { + if (meters.isEmpty()) { + return; + } + pw.println("
"); + pw.println("
"); + pw.println("
Meters
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : meters.entrySet()) { + Meter m = e.getValue(); + String name = e.getKey(); + + double rateFactor = timeUnit.rateFor(name).toSeconds(1); + String rateUnit = "events/" + calculateRateUnit(timeUnit.rateFor(name)); + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", m.getCount()); + pw.printf("", m.getMeanRate() * rateFactor); + pw.printf("", m.getOneMinuteRate() * rateFactor); + pw.printf("", m.getFiveMinuteRate() * rateFactor); + pw.printf("", m.getFifteenMinuteRate() * rateFactor); + pw.printf("", rateUnit); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCountMean RateOneMinuteRateFiveMinuteRateFifteenMinuteRate"); + pw.println("RateUnit
%s%d%f%f%f%f%s
"); + pw.println("
"); + } + + private void addTimerDetails(PrintWriter pw, SortedMap timers) { + if (timers.isEmpty()) { + return; + } + + pw.println("
"); + pw.println("
"); + pw.println("
Timers
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : timers.entrySet()) { + Timer t = e.getValue(); + Snapshot s = t.getSnapshot(); + String name = e.getKey(); + + double rateFactor = timeUnit.rateFor(name).toSeconds(1); + String rateUnit = "events/" + calculateRateUnit(timeUnit.rateFor(name)); + + double durationFactor = 1.0 / timeUnit.durationFor(name).toNanos(1); + String durationUnit = timeUnit.durationFor(name).toString().toLowerCase(Locale.US); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", t.getCount()); + pw.printf("", t.getMeanRate() * rateFactor); + pw.printf("", t.getOneMinuteRate() * rateFactor); + pw.printf("", t.getFiveMinuteRate() * rateFactor); + pw.printf("", t.getFifteenMinuteRate() * rateFactor); + + pw.printf("", s.getMedian() * durationFactor); + pw.printf("", s.getMin() * durationFactor); + pw.printf("", s.getMax() * durationFactor); + pw.printf("", s.getMean() * durationFactor); + pw.printf("", s.getStdDev() * durationFactor); + + pw.printf("", s.get75thPercentile() * durationFactor); + pw.printf("", s.get95thPercentile() * durationFactor); + pw.printf("", s.get98thPercentile() * durationFactor); + pw.printf("", s.get99thPercentile() * durationFactor); + pw.printf("", s.get999thPercentile() * durationFactor); + + pw.printf("", rateUnit); + pw.printf("", durationUnit); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCountMean Rate1 min rate5 mins rate15 mins rate50%MinMaxMeanStdDev75%95%98%99%999%Rate UnitDuration Unit
%s%d%f%f%f%f%f%f%f%f%f%f%f%f%f%f%s%s
"); + pw.println("
"); + } + + private void addHistogramDetails(PrintWriter pw, SortedMap histograms) { + if (histograms.isEmpty()) { + return; + } + + pw.println("
"); + pw.println("
"); + pw.println("
Histograms
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : histograms.entrySet()) { + Histogram h = e.getValue(); + Snapshot s = h.getSnapshot(); + String name = e.getKey(); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", h.getCount()); + pw.printf("", s.getMedian()); + pw.printf("", s.getMin()); + pw.printf("", s.getMax()); + pw.printf("", s.getMean()); + pw.printf("", s.getStdDev()); + + pw.printf("", s.get75thPercentile()); + pw.printf("", s.get95thPercentile()); + pw.printf("", s.get98thPercentile()); + pw.printf("", s.get99thPercentile()); + pw.printf("", s.get999thPercentile()); + + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCount50%MinMaxMeanStdDev75%95%98%99%999%
%s%d%f%d%d%f%f%f%f%f%f%f
"); + pw.println("
"); + } + + private void addCounterDetails(PrintWriter pw, SortedMap counters) { + if (counters.isEmpty()) { + return; + } + pw.println("
"); + pw.println("
"); + pw.println("
Counters
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : counters.entrySet()) { + Counter c = e.getValue(); + String name = e.getKey(); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", c.getCount()); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameCount
%s%d
"); + pw.println("
"); + } + + private void addGaugeDetails(PrintWriter pw, SortedMap gauges) { + if (gauges.isEmpty()) { + return; + } + + pw.println("
"); + pw.println("
"); + pw.println("
Gauges
"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + String rowClass = "odd"; + for (Map.Entry e : gauges.entrySet()) { + Gauge g = e.getValue(); + String name = e.getKey(); + + pw.printf("%n", rowClass); + + pw.printf("", name); + pw.printf("", g.getValue()); + + pw.println(""); + rowClass = "odd".equals(rowClass) ? "even" : "odd"; + } + + pw.println(""); + pw.println("
NameValue
%s%s
"); + pw.println("
"); + } + + + //~----------------------------------------------< internal > + +// ~----------------------------------------------< internal > +com.codahale.metrics.MetricRegistry getConsolidatedRegistry() { + com.codahale.metrics.MetricRegistry registry = new com.codahale.metrics.MetricRegistry(); + for (java.util.Map.Entry registryEntry : registries.entrySet()) { + java.lang.String metricRegistryName = ((java.lang.String) (registryEntry.getKey().getProperty(org.apache.sling.commons.metrics.internal.MetricWebConsolePlugin.METRIC_REGISTRY_NAME))); + for (java.util.Map.Entry metricEntry : registryEntry.getValue().getMetrics().entrySet()) { + java.lang.String metricName = metricEntry.getKey(); + try { + { + metricName = (/* NPEX_NULL_EXP */ + metricRegistryName + ":") + metricName; + } + registry.register(metricName, metricEntry.getValue()); + } catch (java.lang.IllegalArgumentException ex) { + log.warn("Duplicate Metric name found {}", metricName, ex); + } + } + } + return registry; +} + + private static String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } + + private static class MetricTimeUnits { + private final TimeUnit defaultRate; + private final TimeUnit defaultDuration; + private final Map rateOverrides; + private final Map durationOverrides; + + MetricTimeUnits(TimeUnit defaultRate, + TimeUnit defaultDuration, + Map rateOverrides, + Map durationOverrides) { + this.defaultRate = defaultRate; + this.defaultDuration = defaultDuration; + this.rateOverrides = rateOverrides; + this.durationOverrides = durationOverrides; + } + + public TimeUnit durationFor(String name) { + return durationOverrides.containsKey(name) ? durationOverrides.get(name) : defaultDuration; + } + + public TimeUnit rateFor(String name) { + return rateOverrides.containsKey(name) ? rateOverrides.get(name) : defaultRate; + } + } +} diff --git a/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/metadata.json b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/metadata.json new file mode 100644 index 000000000..8d2a8585e --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePlugin.java", + "line": 437, + "npe_method": "getConsolidatedRegistry", + "deref_field": "metricRegistryName", + "npe_class": "MetricWebConsolePlugin", + "repo": "sling-org-apache-sling-commons-metrics", + "bug_id": "MetricWebConsolePlugin_434" + } +} diff --git a/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/npe.json b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/npe.json new file mode 100644 index 000000000..fd4a7fb98 --- /dev/null +++ b/Java/sling-org-apache-sling-commons-metrics-MetricWebConsolePlugin_434/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/sling/commons/metrics/internal/MetricWebConsolePlugin.java", + "line": 437, + "npe_method": "getConsolidatedRegistry", + "deref_field": "metricRegistryName", + "npe_class": "MetricWebConsolePlugin" +} \ No newline at end of file