├── .github ├── CODEOWNERS └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── build.gradle ├── checkstyle.xml ├── gradle.properties ├── settings.gradle └── src ├── main ├── java │ └── org │ │ ├── compuscene │ │ └── metrics │ │ │ └── prometheus │ │ │ ├── PrometheusMetricsCatalog.java │ │ │ ├── PrometheusMetricsCollector.java │ │ │ ├── PrometheusSettings.java │ │ │ └── package-info.java │ │ └── elasticsearch │ │ ├── action │ │ ├── ClusterStatsData.java │ │ ├── NodePrometheusMetricsAction.java │ │ ├── NodePrometheusMetricsRequest.java │ │ ├── NodePrometheusMetricsResponse.java │ │ ├── NodePrometheusRequestBuilder.java │ │ ├── TransportNodePrometheusMetricsAction.java │ │ ├── admin │ │ │ └── indices │ │ │ │ └── stats │ │ │ │ ├── PackageAccessHelper.java │ │ │ │ └── package-info.java │ │ └── package-info.java │ │ ├── plugin │ │ └── prometheus │ │ │ ├── PrometheusExporterPlugin.java │ │ │ └── package-info.java │ │ └── rest │ │ └── prometheus │ │ ├── RestPrometheusMetricsAction.java │ │ └── package-info.java └── plugin-metadata │ └── plugin-security.policy └── test ├── java └── org │ └── elasticsearch │ └── rest │ ├── PrometheusRestHandlerClientYamlTestSuiteIT.java │ └── package-info.java └── resources └── rest-api-spec ├── api └── prometheus.metrics.json └── test └── resthandler ├── 10_basic.yml ├── 20_00_metrics.yml ├── 20_10_cluster_settings_metrics_disabled.yml ├── 20_11_index_level_metrics_disabled.yml ├── 30_00_index_level_info.yml ├── 30_10_index_indexing.yml ├── 30_11_index_get.yml ├── 30_12_index_search.yml ├── 30_13_index_merges.yml ├── 30_14_index_refresh.yml ├── 30_15_index_flush.yml ├── 30_16_index_querycache.yml ├── 30_17_index_fieldata.yml ├── 30_18_index_completion.yml ├── 30_19_index_segments.yml ├── 30_20_index_suggest.yml ├── 30_21_index_requestcache.yml ├── 30_22_index_recovery.yml ├── 30_23_index_translog.yml ├── 30_24_index_warmer.yml ├── 40_10_cluster_settings_disk_threshold.yml └── 40_20_cluster_settings_disk_watermark_bytes.yml /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @vvanholl @lukas-vlcek 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "CI" 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | - "5.x" 8 | - "6.x" 9 | pull_request: 10 | branches: 11 | - "master" 12 | - "5.x" 13 | - "6.x" 14 | 15 | env: 16 | gradle-version: "7.4.1" 17 | java-version: "17" 18 | 19 | jobs: 20 | build: 21 | runs-on: "ubuntu-latest" 22 | steps: 23 | - name: "Checkout repository" 24 | uses: "actions/checkout@v2" 25 | - name: "Set up JDK" 26 | uses: "actions/setup-java@v1" 27 | with: 28 | java-version: "${{ env.java-version }}" 29 | - name: "Gradle build" 30 | uses: "eskatos/gradle-command-action@v1" 31 | with: 32 | gradle-version: "${{ env.gradle-version }}" 33 | arguments: "--build-cache assemble" 34 | - name: "Gradle check" 35 | uses: "eskatos/gradle-command-action@v1" 36 | with: 37 | gradle-version: "${{ env.gradle-version }}" 38 | arguments: "check" 39 | - name: "GitHub release" 40 | if: "contains('refs/heads/master refs/heads/5.x refs/heads/6.x', github.ref)" 41 | run: | 42 | version=$(cat gradle.properties | grep -e "^version *=" | awk -F" *= *" '{print $NF}') 43 | is_snapshot=$(echo ${version} | grep -e "-SNAPSHOT$" | wc -l) 44 | if [ "$is_snapshot" == "0" ]; then 45 | echo ${{ github.token }} | gh auth login --with-token 46 | gh release create ${version} ./build/distributions/*.zip 47 | fi 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # There is a file left in the after `gradle build` or `gradle check`. This is expected. 2 | # https://discuss.elastic.co/t/leftovers-after-integtestrunner-in-root/152610 3 | \.local*-integTestRunner-execution-times.log 4 | 5 | # Eclipse 6 | .classpath 7 | .project 8 | .settings/ 9 | 10 | # Intellij 11 | .idea/ 12 | *.iml 13 | *.iws 14 | *.ipr 15 | 16 | # Gradle 17 | .gradle 18 | build 19 | gradle-wrapper.jar 20 | gradle-wrapper.properties 21 | 22 | # Mac 23 | .DS_Store 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | This product includes software developed by Prometheus.io 2 | and contributors (https://github.com/prometheus/client_java). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI](https://github.com/vvanholl/elasticsearch-prometheus-exporter/workflows/CI/badge.svg?branch=master) 2 | 3 | # Prometheus Exporter Plugin for Elasticsearch 4 | 5 | This is a builtin exporter from Elasticsearch to Prometheus. 6 | It collects all relevant metrics and makes them available to Prometheus via the Elasticsearch REST API. 7 | 8 | **Currently, the available metrics are:** 9 | 10 | - Cluster status 11 | - Nodes status: 12 | - JVM 13 | - Indices (global) 14 | - Transport 15 | - HTTP 16 | - Scripts 17 | - Process 18 | - Operating System 19 | - File System 20 | - Circuit Breaker 21 | - Indices status 22 | - Cluster settings (selected [disk allocation settings](https://www.elastic.co/guide/en/elasticsearch/reference/master/disk-allocator.html) only) 23 | 24 | ## Compatibility matrix 25 | 26 | ### Version 7.X 27 | 28 | | Elasticsearch | Plugin | Release date | 29 | | -------------- | -------------- | ------------ | 30 | | 7.17.7 | 7.17.7.0 | Oct 26, 2022 | 31 | | 7.17.6 | 7.17.6.0 | Aug 31, 2022 | 32 | | 7.17.5 | 7.17.5.0 | Jul 06, 2022 | 33 | | 7.17.4 | 7.17.4.0 | May 24, 2022 | 34 | | 7.17.3 | 7.17.3.0 | Apr 22, 2022 | 35 | | 7.17.2 | 7.17.2.0 | Apr 08, 2022 | 36 | | 7.17.1 | 7.17.1.0 | Mar 11, 2022 | 37 | | 7.17.0 | 7.17.0.0 | Feb 01, 2022 | 38 | | 7.16.3 | 7.16.3.0 | Jan 14, 2022 | 39 | | 7.16.2 | 7.16.2.0 | Dec 20, 2021 | 40 | | 7.16.1 | 7.16.1.0 | Dec 13, 2021 | 41 | | 7.16.0 | 7.16.0.0 | Dec 09, 2021 | 42 | | 7.15.2 | 7.15.2.0 | Nov 13, 2021 | 43 | | 7.15.1 | 7.15.1.0 | Oct 16, 2021 | 44 | | 7.15.0 | 7.15.0.0 | Oct 02, 2021 | 45 | | 7.14.1 | 7.14.1.0 | Sep 04, 2021 | 46 | | 7.14.0 | 7.14.0.0 | Aug 07, 2021 | 47 | | 7.13.4 | 7.13.4.0 | Jul 21, 2021 | 48 | | 7.13.3 | 7.13.3.0 | Jul 07, 2021 | 49 | | 7.13.2 | 7.13.2.0 | Jun 15, 2021 | 50 | | 7.13.1 | 7.13.1.0 | Jun 12, 2021 | 51 | | 7.13.0 | 7.13.0.0 | May 27, 2021 | 52 | | 7.12.1 | 7.12.1.0 | May 01, 2021 | 53 | | 7.12.0 | 7.12.0.0 | Apr 04, 2021 | 54 | | 7.11.2 | 7.11.2.0 | Mar 20, 2021 | 55 | | 7.11.1 | 7.11.1.0 | Feb 22, 2021 | 56 | | 7.10.2 | 7.10.2.0 | Jan 24, 2021 | 57 | | 7.10.1 | 7.10.1.0 | Dec 13, 2020 | 58 | | 7.10.0 | 7.10.0.0 | Nov 15, 2020 | 59 | | 7.9.3 | 7.9.3.0 | Oct 22, 2020 | 60 | | 7.9.2 | 7.9.2.0 | Oct 04, 2020 | 61 | | 7.9.1 | 7.9.1.0 | Sep 06, 2020 | 62 | | 7.9.0 | 7.9.0.0 | Aug 18, 2020 | 63 | | 7.8.1 | 7.8.1.0 | Aug 10, 2020 | 64 | | 7.8.0 | 7.8.0.0 | Jun 22, 2020 | 65 | | 7.7.1 | 7.7.1.0 | Jun 04, 2020 | 66 | | 7.7.0 | 7.7.0.0 | May 14, 2020 | 67 | | 7.6.2 | 7.6.2.0 | Apr 06, 2020 | 68 | | 7.6.1 | 7.6.1.0 | Mar 30, 2020 | 69 | | 7.6.0 | 7.6.0.0 | Feb 12, 2020 | 70 | | 7.5.2 | 7.5.2.0 | Jan 25, 2020 | 71 | | 7.5.1 | 7.5.1.0 | Jan 21, 2020 | 72 | | 7.5.0 | 7.5.0.0 | Jan 16, 2020 | 73 | | 7.4.2 | 7.4.2.0 | Jan 13, 2020 | 74 | | 7.4.1 | 7.4.1.0 | Jan 13, 2020 | 75 | | 7.4.0 | 7.4.0.0 | Jan 07, 2020 | 76 | | 7.3.2 | 7.3.2.0 | Oct 05, 2019 | 77 | | 7.3.1 | 7.3.1.0 | Sep 18, 2019 | 78 | | 7.3.0 | 7.3.0.0 | Sep 17, 2019 | 79 | | 7.2.1 | 7.2.1.0 | Jul 31, 2019 | 80 | | 7.2.0 | 7.2.0.0 | Jul 12, 2019 | 81 | | 7.1.1 | 7.1.1.0 | May 31, 2019 | 82 | | 7.1.0 | 7.1.0.0 | May 23, 2019 | 83 | | 7.0.1 | 7.0.1.0 | May 08, 2019 | 84 | | 7.0.0 | 7.0.0.0 | Apr 11, 2019 | 85 | 86 | ## Install 87 | 88 | `./bin/elasticsearch-plugin install -b https://github.com/vvanholl/elasticsearch-prometheus-exporter/releases/download/7.17.7.0/prometheus-exporter-7.17.7.0.zip` 89 | 90 | **Do not forget to restart the node after the installation!** 91 | 92 | Note that the plugin needs the following special permissions: 93 | 94 | - java.lang.RuntimePermission accessClassInPackage.sun.misc 95 | - java.lang.RuntimePermission accessDeclaredMembers 96 | - java.lang.reflect.ReflectPermission suppressAccessChecks 97 | 98 | If you have a lot of indices and think this data is irrelevant, you can disable in the main configuration file: 99 | 100 | ``` 101 | prometheus.indices: false 102 | ``` 103 | 104 | To disable exporting cluster settings use: 105 | ``` 106 | prometheus.cluster.settings: false 107 | ``` 108 | 109 | These settings can be also [updated dynamically](https://www.elastic.co/guide/en/elasticsearch/reference/master/cluster-update-settings.html). 110 | 111 | ## Uninstall 112 | 113 | `./bin/elasticsearch-plugin remove prometheus-exporter` 114 | 115 | Do not forget to restart the node after installation! 116 | 117 | ## Usage 118 | 119 | Metrics are directly available at: 120 | 121 | http://:9200/_prometheus/metrics 122 | 123 | As a sample result, you get: 124 | 125 | ``` 126 | # HELP es_process_mem_total_virtual_bytes Memory used by ES process 127 | # TYPE es_process_mem_total_virtual_bytes gauge 128 | es_process_mem_total_virtual_bytes{cluster="develop",node="develop01",} 3.626733568E9 129 | # HELP es_indices_indexing_is_throttled_bool Is indexing throttling ? 130 | # TYPE es_indices_indexing_is_throttled_bool gauge 131 | es_indices_indexing_is_throttled_bool{cluster="develop",node="develop01",} 0.0 132 | # HELP es_jvm_gc_collection_time_seconds Time spent for GC collections 133 | # TYPE es_jvm_gc_collection_time_seconds counter 134 | es_jvm_gc_collection_time_seconds{cluster="develop",node="develop01",gc="old",} 0.0 135 | es_jvm_gc_collection_time_seconds{cluster="develop",node="develop01",gc="young",} 0.0 136 | # HELP es_indices_requestcache_memory_size_bytes Memory used for request cache 137 | # TYPE es_indices_requestcache_memory_size_bytes gauge 138 | es_indices_requestcache_memory_size_bytes{cluster="develop",node="develop01",} 0.0 139 | # HELP es_indices_search_open_contexts_number Number of search open contexts 140 | # TYPE es_indices_search_open_contexts_number gauge 141 | es_indices_search_open_contexts_number{cluster="develop",node="develop01",} 0.0 142 | # HELP es_jvm_mem_nonheap_used_bytes Memory used apart from heap 143 | # TYPE es_jvm_mem_nonheap_used_bytes gauge 144 | es_jvm_mem_nonheap_used_bytes{cluster="develop",node="develop01",} 5.5302736E7 145 | 146 | ... 147 | ``` 148 | 149 | ### Configure the Prometheus target 150 | 151 | On your Prometheus servers, configure a new job as usual. 152 | 153 | For example, if you have a cluster of 3 nodes: 154 | 155 | ```YAML 156 | - job_name: elasticsearch 157 | scrape_interval: 10s 158 | metrics_path: "/_prometheus/metrics" 159 | static_configs: 160 | - targets: 161 | - node1:9200 162 | - node2:9200 163 | - node3:9200 164 | ``` 165 | 166 | Of course, you could use the service discovery service instead of a static config. 167 | 168 | Just keep in mind that `metrics_path` must be `/_prometheus/metrics`, otherwise Prometheus will find no metric. 169 | 170 | ## Project sources 171 | 172 | The Maven project site is available at [GitHub](https://github.com/vvanholl/elasticsearch-prometheus-exporter). 173 | 174 | ## Testing 175 | 176 | Project contains [integration tests](src/test/resources/rest-api-spec) implemented using 177 | [rest layer](https://github.com/elastic/elasticsearch/blob/master/TESTING.asciidoc#testing-the-rest-layer) 178 | framework. 179 | 180 | To run everything similar to the GitHub Actions pipeline you can do: 181 | ``` 182 | docker run -v $(pwd):/home/gradle gradle:7.0.2-jdk16 su gradle -c 'gradle check' 183 | ``` 184 | NOTE: Please keep version in sync with .github/workflows/ci.yml 185 | 186 | 187 | Complete test suite is run using: 188 | ``` 189 | gradle clean check 190 | ``` 191 | 192 | To run individual test file use: 193 | ``` 194 | gradle :integTest \ 195 | -Dtests.class=org.elasticsearch.rest.PrometheusRestHandlerClientYamlTestSuiteIT \ 196 | -Dtests.method="test {yaml=resthandler/20_metrics/Prometheus metrics can be pulled}" 197 | ``` 198 | 199 | ## Credits 200 | 201 | This plugin mainly uses the [Prometheus JVM Client](https://github.com/prometheus/client_java). 202 | 203 | ## License 204 | 205 | Licensed under the Apache License, Version 2.0 (the "License"); 206 | you may not use this file except in compliance with the License. 207 | You may obtain a copy of the License at 208 | 209 | http://www.apache.org/licenses/LICENSE-2.0 210 | 211 | Unless required by applicable law or agreed to in writing, software 212 | distributed under the License is distributed on an "AS IS" BASIS, 213 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 214 | See the License for the specific language governing permissions and 215 | limitations under the License. 216 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import com.github.mgk.gradle.* 2 | 3 | buildscript { 4 | ext { 5 | es_version = version.replaceAll(/\.[0-9]+(|-.+)$/, "") 6 | } 7 | 8 | repositories { 9 | mavenLocal() 10 | mavenCentral() 11 | jcenter() 12 | } 13 | 14 | dependencies { 15 | classpath "org.elasticsearch.gradle:build-tools:${es_version}" 16 | classpath group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.0' 17 | classpath group: 'com.sun.xml.bind', name: 'jaxb-core', version: '2.3.0' 18 | classpath group: 'com.sun.xml.bind', name: 'jaxb-impl', version: '2.3.0' 19 | classpath group: 'javax.activation', name: 'activation', version: '1.1.1' 20 | } 21 | } 22 | 23 | plugins { 24 | id "java" 25 | id "checkstyle" 26 | } 27 | 28 | apply plugin: 'java' 29 | apply plugin: 'idea' 30 | apply plugin: 'elasticsearch.esplugin' 31 | apply plugin: 'elasticsearch.testclusters' 32 | 33 | // Uncomment if you want to use: System.out.println("Emergency!"); 34 | // Logs are found in build/testcluster/integTest-*/logs/ folder. 35 | //forbiddenApis { 36 | // ignoreFailures = true 37 | //} 38 | 39 | // No unit tests in this plugin 40 | test.enabled = false 41 | 42 | println "Host: " + java.net.InetAddress.getLocalHost() 43 | println "Gradle: " + gradle.gradleVersion + " JVM: " + org.gradle.internal.jvm.Jvm.current() + " Groovy: " + GroovySystem.getVersion() 44 | println "Build: group: '${project.group}', name: '${project.name}', version: '${project.version}'" 45 | println "Timestamp: " + java.time.Instant.now().atZone(java.time.ZoneId.systemDefault()).toString() 46 | 47 | repositories { 48 | mavenCentral() 49 | mavenLocal() 50 | } 51 | 52 | ext { 53 | versions = [ 54 | "elasticsearch": es_version, 55 | "prometheus" : "0.8.0", 56 | "log4j" : "2.17.1", 57 | "junit" : "4.12" 58 | ] 59 | } 60 | 61 | configurations { 62 | runtime 63 | releaseJars { 64 | extendsFrom runtime 65 | exclude group: "org.elasticsearch" 66 | exclude group: "com.fasterxml.jackson.core", module: "jackson-core" 67 | exclude group: "org.apache.logging.log4j" 68 | } 69 | } 70 | 71 | dependencies { 72 | implementation "org.elasticsearch:elasticsearch:${versions.elasticsearch}" 73 | implementation "io.prometheus:simpleclient:${versions.prometheus}" 74 | implementation "io.prometheus:simpleclient_common:${versions.prometheus}" 75 | implementation "org.apache.logging.log4j:log4j-api:${versions.log4j}" 76 | testImplementation (group: 'junit', name: 'junit', version: "${versions.junit}") { 77 | exclude group:'org.hamcrest' //also included in ES test framework 78 | } 79 | releaseJars "${project.group}:${project.name}:${project.version}" 80 | } 81 | 82 | tasks.withType(JavaCompile) { 83 | options.compilerArgs << "-Xlint:unchecked,deprecation" 84 | } 85 | 86 | esplugin { 87 | licenseFile rootProject.file('LICENSE.txt') 88 | noticeFile rootProject.file('NOTICE.txt') 89 | name pluginName 90 | description pluginDescription 91 | classname pluginClassname 92 | } 93 | 94 | testClusters.all { 95 | numberOfNodes = 2 96 | 97 | // There does not seem to be any easy way how to setup custom cluster name. 98 | 99 | // This worked in ES 7.4.x, but now results in: 100 | // A problem occurred evaluating root project 'prometheus-exporter'. 101 | // > Cannot set the property 'clusterName' because the backing field is final. 102 | // clusterName = "PrometheusExporterITCluster" 103 | 104 | // This does not work in ES 7.5.x too: 105 | // Execution failed for task ':integTestRunner'. 106 | // > Testclusters does not allow the following settings to be changed:[cluster.name] for node{::integTest-0} 107 | // setting 'cluster.name', 'PrometheusExporterITCluster' 108 | } 109 | 110 | checkstyle { 111 | configFile = new File(rootDir, "checkstyle.xml") 112 | toolVersion = "8.2" 113 | } 114 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 106 | 107 | 108 | 109 | 110 | 112 | 115 | 116 | 117 | 118 | 119 | 120 | 122 | 123 | 124 | 125 | 126 | 127 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 137 | 138 | 139 | 140 | 141 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 173 | 174 | 175 | 176 | 177 | 179 | 180 | 181 | 182 | 183 | 185 | 186 | 187 | 188 | 189 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 205 | 206 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 225 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 254 | 256 | 257 | 258 | 259 | 260 | 265 | 266 | 267 | 272 | 273 | 274 | 275 | 280 | 281 | 282 | 286 | 293 | 294 | 295 | 296 | 297 | 300 | 301 | 302 | 303 | 304 | 307 | 309 | 310 | 311 | 312 | 313 | 314 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | group = org.elasticsearch.plugin.prometheus 2 | 3 | version = 7.17.7.1-SNAPSHOT 4 | 5 | pluginName = prometheus-exporter 6 | pluginClassname = org.elasticsearch.plugin.prometheus.PrometheusExporterPlugin 7 | pluginDescription = Export Elasticsearch metrics to Prometheus 8 | 9 | github_token = secret 10 | github_owner = vvanholl 11 | github_repo = elasticsearch-prometheus-exporter 12 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "prometheus-exporter" 2 | -------------------------------------------------------------------------------- /src/main/java/org/compuscene/metrics/prometheus/PrometheusMetricsCatalog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.compuscene.metrics.prometheus; 19 | 20 | import org.apache.logging.log4j.LogManager; 21 | import org.apache.logging.log4j.Logger; 22 | import org.elasticsearch.rest.prometheus.RestPrometheusMetricsAction; 23 | 24 | import java.io.IOException; 25 | import java.io.StringWriter; 26 | import java.io.Writer; 27 | import java.util.HashMap; 28 | import java.util.Locale; 29 | 30 | import io.prometheus.client.CollectorRegistry; 31 | import io.prometheus.client.Gauge; 32 | import io.prometheus.client.Summary; 33 | import io.prometheus.client.exporter.common.TextFormat; 34 | 35 | /** 36 | * A class that describes a Prometheus metrics catalog. 37 | */ 38 | public class PrometheusMetricsCatalog { 39 | private static final Logger logger = LogManager.getLogger(RestPrometheusMetricsAction.class); 40 | 41 | private String clusterName; 42 | private String nodeName; 43 | private String nodeId; 44 | 45 | private String metricPrefix; 46 | 47 | private HashMap metrics; 48 | private CollectorRegistry registry; 49 | 50 | public PrometheusMetricsCatalog(String clusterName, String nodeName, String nodeId, String metricPrefix) { 51 | this.clusterName = clusterName; 52 | this.nodeName = nodeName; 53 | this.nodeId = nodeId; 54 | 55 | this.metricPrefix = metricPrefix; 56 | 57 | metrics = new HashMap<>(); 58 | registry = new CollectorRegistry(); 59 | } 60 | 61 | private String[] getExtendedClusterLabelNames(String... labelNames) { 62 | String[] extended = new String[labelNames.length + 1]; 63 | extended[0] = "cluster"; 64 | 65 | System.arraycopy(labelNames, 0, extended, 1, labelNames.length); 66 | 67 | return extended; 68 | } 69 | 70 | private String[] getExtendedClusterLabelValues(String... labelValues) { 71 | String[] extended = new String[labelValues.length + 1]; 72 | extended[0] = clusterName; 73 | 74 | System.arraycopy(labelValues, 0, extended, 1, labelValues.length); 75 | 76 | return extended; 77 | } 78 | 79 | private String[] getExtendedNodeLabelNames(String... labelNames) { 80 | String[] extended = new String[labelNames.length + 3]; 81 | extended[0] = "cluster"; 82 | extended[1] = "node"; 83 | extended[2] = "nodeid"; 84 | 85 | System.arraycopy(labelNames, 0, extended, 3, labelNames.length); 86 | 87 | return extended; 88 | } 89 | 90 | private String[] getExtendedNodeLabelValues(String... labelValues) { 91 | String[] extended = new String[labelValues.length + 3]; 92 | extended[0] = clusterName; 93 | extended[1] = nodeName; 94 | extended[2] = nodeId; 95 | 96 | System.arraycopy(labelValues, 0, extended, 3, labelValues.length); 97 | 98 | return extended; 99 | } 100 | 101 | public void registerClusterGauge(String metric, String help, String... labels) { 102 | Gauge gauge = Gauge.build(). 103 | name(metricPrefix + metric). 104 | help(help). 105 | labelNames(getExtendedClusterLabelNames(labels)). 106 | register(registry); 107 | 108 | metrics.put(metric, gauge); 109 | 110 | logger.debug(String.format(Locale.ENGLISH, "Registered new cluster gauge %s", metric)); 111 | } 112 | 113 | public void setClusterGauge(String metric, double value, String... labelValues) { 114 | Gauge gauge = (Gauge) metrics.get(metric); 115 | gauge.labels(getExtendedClusterLabelValues(labelValues)).set(value); 116 | } 117 | 118 | public void registerNodeGauge(String metric, String help, String... labels) { 119 | Gauge gauge = Gauge.build(). 120 | name(metricPrefix + metric). 121 | help(help). 122 | labelNames(getExtendedNodeLabelNames(labels)). 123 | register(registry); 124 | 125 | metrics.put(metric, gauge); 126 | 127 | logger.debug(String.format(Locale.ENGLISH, "Registered new node gauge %s", metric)); 128 | } 129 | 130 | public void setNodeGauge(String metric, double value, String... labelValues) { 131 | Gauge gauge = (Gauge) metrics.get(metric); 132 | gauge.labels(getExtendedNodeLabelValues(labelValues)).set(value); 133 | } 134 | 135 | public void registerSummaryTimer(String metric, String help, String... labels) { 136 | Summary summary = Summary.build(). 137 | name(metricPrefix + metric). 138 | help(help). 139 | labelNames(getExtendedNodeLabelNames(labels)). 140 | register(registry); 141 | 142 | metrics.put(metric, summary); 143 | 144 | logger.debug(String.format(Locale.ENGLISH, "Registered new summary %s", metric)); 145 | } 146 | 147 | public Summary.Timer startSummaryTimer(String metric, String... labelValues) { 148 | Summary summary = (Summary) metrics.get(metric); 149 | return summary.labels(getExtendedNodeLabelValues(labelValues)).startTimer(); 150 | } 151 | 152 | public String toTextFormat() throws IOException { 153 | Writer writer = new StringWriter(); 154 | TextFormat.write004(writer, registry.metricFamilySamples()); 155 | return writer.toString(); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/org/compuscene/metrics/prometheus/PrometheusSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2019] [Lukáš VLČEK] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.compuscene.metrics.prometheus; 19 | 20 | import org.elasticsearch.common.settings.ClusterSettings; 21 | import org.elasticsearch.common.settings.Setting; 22 | import org.elasticsearch.common.settings.Settings; 23 | 24 | /** 25 | * A container to keep settings for prometheus up to date with cluster setting changes. 26 | * 27 | * In order to make the settings dynamically updatable we took some inspiration from implementation 28 | * and use of DiskThresholdSettings class in Elasticsearch. 29 | */ 30 | public class PrometheusSettings { 31 | 32 | // These settings become part of cluster state available via HTTP at 33 | // curl /_cluster/settings?include_defaults=true&filter_path=defaults.prometheus 34 | // It is important to keep it under reasonable namespace to avoid collision with 35 | // other plugins or future/commercial parts of Elastic Stack itself. 36 | // Namespace "prometheus" sounds like safe bet for now. 37 | public static final Setting PROMETHEUS_CLUSTER_SETTINGS = 38 | Setting.boolSetting("prometheus.cluster.settings", true, 39 | Setting.Property.Dynamic, Setting.Property.NodeScope); 40 | public static final Setting PROMETHEUS_INDICES = 41 | Setting.boolSetting("prometheus.indices", true, 42 | Setting.Property.Dynamic, Setting.Property.NodeScope); 43 | 44 | private volatile boolean clusterSettings; 45 | private volatile boolean indices; 46 | 47 | public PrometheusSettings(Settings settings, ClusterSettings clusterSettings) { 48 | setPrometheusClusterSettings(PROMETHEUS_CLUSTER_SETTINGS.get(settings)); 49 | setPrometheusIndices(PROMETHEUS_INDICES.get(settings)); 50 | clusterSettings.addSettingsUpdateConsumer(PROMETHEUS_CLUSTER_SETTINGS, this::setPrometheusClusterSettings); 51 | clusterSettings.addSettingsUpdateConsumer(PROMETHEUS_INDICES, this::setPrometheusIndices); 52 | } 53 | 54 | private void setPrometheusClusterSettings(boolean flag) { 55 | this.clusterSettings = flag; 56 | } 57 | 58 | private void setPrometheusIndices(boolean flag) { 59 | this.indices = flag; 60 | } 61 | 62 | public boolean getPrometheusClusterSettings() { 63 | return this.clusterSettings; 64 | } 65 | 66 | public boolean getPrometheusIndices() { 67 | return this.indices; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/compuscene/metrics/prometheus/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /** 19 | * Classes used to produce the Prometheus Exporter plugin metrics. 20 | */ 21 | package org.compuscene.metrics.prometheus; 22 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/ClusterStatsData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2018] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.elasticsearch.action; 18 | 19 | import static org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings.*; 20 | 21 | import org.elasticsearch.ElasticsearchParseException; 22 | import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; 23 | import org.elasticsearch.cluster.metadata.Metadata; 24 | import org.elasticsearch.core.Nullable; 25 | import org.elasticsearch.common.io.stream.StreamInput; 26 | import org.elasticsearch.common.io.stream.StreamOutput; 27 | import org.elasticsearch.common.settings.ClusterSettings; 28 | import org.elasticsearch.common.settings.Settings; 29 | import org.elasticsearch.common.settings.SettingsException; 30 | import org.elasticsearch.common.unit.RatioValue; 31 | 32 | import java.io.IOException; 33 | 34 | 35 | /** 36 | * Selected settings from Elasticsearch cluster settings. 37 | * 38 | * Disk-based shard allocation [1] settings play important role in how Elasticsearch decides where to allocate 39 | * new shards or if existing shards are relocated to different nodes. The tricky part about these settings is 40 | * that they can be expressed either in percent or bytes value (they cannot be mixed) and they can be updated on the fly. 41 | * 42 | * [1] https://www.elastic.co/guide/en/elasticsearch/reference/master/disk-allocator.html#disk-allocator 43 | * 44 | * In order to make it easy for Prometheus to consume the data we expose these settings in both formats (pct and bytes) 45 | * and we do our best in determining if they are currently set as pct or bytes filling appropriate variables with data 46 | * or null value. 47 | */ 48 | // TODO(lukas-vlcek): should this extend TransportMessage instead? 49 | public class ClusterStatsData extends ActionResponse { 50 | 51 | private Boolean thresholdEnabled = null; 52 | 53 | @Nullable private Long diskLowInBytes; 54 | @Nullable private Long diskHighInBytes; 55 | @Nullable private Long floodStageInBytes; 56 | 57 | @Nullable private Double diskLowInPct; 58 | @Nullable private Double diskHighInPct; 59 | @Nullable private Double floodStageInPct; 60 | 61 | private Long[] diskLowInBytesRef = new Long[]{diskLowInBytes}; 62 | private Long[] diskHighInBytesRef = new Long[]{diskHighInBytes}; 63 | private Long[] floodStageInBytesRef = new Long[]{floodStageInBytes}; 64 | 65 | private Double[] diskLowInPctRef = new Double[]{diskLowInPct}; 66 | private Double[] diskHighInPctRef = new Double[]{diskHighInPct}; 67 | private Double[] floodStageInPctRef = new Double[]{floodStageInPct}; 68 | 69 | public ClusterStatsData(StreamInput in) throws IOException { 70 | super(in); 71 | thresholdEnabled = in.readOptionalBoolean(); 72 | // 73 | diskLowInBytes = in.readOptionalLong(); 74 | diskHighInBytes = in.readOptionalLong(); 75 | floodStageInBytes = in.readOptionalLong(); 76 | // 77 | diskLowInPct = in.readOptionalDouble(); 78 | diskHighInPct = in.readOptionalDouble(); 79 | floodStageInPct = in.readOptionalDouble(); 80 | } 81 | 82 | @SuppressWarnings({"checkstyle:LineLength"}) 83 | ClusterStatsData(ClusterStateResponse clusterStateResponse, Settings settings, ClusterSettings clusterSettings) { 84 | 85 | Metadata m = clusterStateResponse.getState().getMetadata(); 86 | // There are several layers of cluster settings in Elasticsearch each having different priority. 87 | // We need to traverse them from the top priority down to find relevant value of each setting. 88 | // See https://www.elastic.co/guide/en/elasticsearch/reference/master/cluster-update-settings.html#_order_of_precedence 89 | for (Settings s : new Settings[]{ 90 | // See: RestClusterGetSettingsAction#response 91 | // or: https://github.com/elastic/elasticsearch/pull/33247/files 92 | // We do not filter the settings, but we use the clusterSettings.diff() 93 | // In the end we expose just a few selected settings ATM. 94 | m.transientSettings(), 95 | m.persistentSettings(), 96 | clusterSettings.diff(m.settings(), settings) 97 | }) { 98 | thresholdEnabled = thresholdEnabled == null ? 99 | s.getAsBoolean(CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), null) : thresholdEnabled; 100 | 101 | parseValue(s, CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING.getKey(), diskLowInBytesRef, diskLowInPctRef); 102 | parseValue(s, CLUSTER_ROUTING_ALLOCATION_HIGH_DISK_WATERMARK_SETTING.getKey(), diskHighInBytesRef, diskHighInPctRef); 103 | parseValue(s, CLUSTER_ROUTING_ALLOCATION_DISK_FLOOD_STAGE_WATERMARK_SETTING.getKey(), floodStageInBytesRef, floodStageInPctRef); 104 | } 105 | 106 | diskLowInBytes = diskLowInBytesRef[0]; 107 | diskHighInBytes = diskHighInBytesRef[0]; 108 | floodStageInBytes = floodStageInBytesRef[0]; 109 | 110 | diskLowInPct = diskLowInPctRef[0]; 111 | diskHighInPct = diskHighInPctRef[0]; 112 | floodStageInPct = floodStageInPctRef[0]; 113 | } 114 | 115 | /** 116 | * Try to extract and parse value from settings for given key. 117 | * First it tries to parse it as a RatioValue (pct) then as byte size value. 118 | * It assigns parsed value to corresponding argument references (passed via array hack). 119 | * If parsing fails the method fires exception, however, this should not happen - we rely on Elasticsearch 120 | * to already have parsed and validated these values before. Unless we screwed something up... 121 | */ 122 | private void parseValue(Settings s, String key, Long[] bytesPointer, Double[] pctPointer) { 123 | String value = s.get(key); 124 | if (value != null && pctPointer[0] == null) { 125 | try { 126 | pctPointer[0] = RatioValue.parseRatioValue(s.get(key, null)).getAsPercent(); 127 | } catch (SettingsException | ElasticsearchParseException | NullPointerException e1) { 128 | if (bytesPointer[0] == null) { 129 | try { 130 | bytesPointer[0] = s.getAsBytesSize(key, null).getBytes(); 131 | } catch (SettingsException | ElasticsearchParseException | NullPointerException e2) { 132 | // TODO(lvlcek): log.debug("This went wrong, but 'Keep Calm and Carry On'") 133 | // We should avoid using logs in this class (due to perf impact), instead we should 134 | // consider moving this logic to some static helper class/method going forward. 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | @Override 142 | public void writeTo(StreamOutput out) throws IOException { 143 | out.writeOptionalBoolean(thresholdEnabled); 144 | // 145 | out.writeOptionalLong(diskLowInBytes); 146 | out.writeOptionalLong(diskHighInBytes); 147 | out.writeOptionalLong(floodStageInBytes); 148 | // 149 | out.writeOptionalDouble(diskLowInPct); 150 | out.writeOptionalDouble(diskHighInPct); 151 | out.writeOptionalDouble(floodStageInPct); 152 | } 153 | 154 | public Boolean getThresholdEnabled() { 155 | return thresholdEnabled; 156 | } 157 | 158 | @Nullable 159 | public Long getDiskLowInBytes() { 160 | return diskLowInBytes; 161 | } 162 | 163 | @Nullable 164 | public Long getDiskHighInBytes() { 165 | return diskHighInBytes; 166 | } 167 | 168 | @Nullable 169 | public Long getFloodStageInBytes() { 170 | return floodStageInBytes; 171 | } 172 | 173 | @Nullable 174 | public Double getDiskLowInPct() { 175 | return diskLowInPct; 176 | } 177 | 178 | @Nullable 179 | public Double getDiskHighInPct() { 180 | return diskHighInPct; 181 | } 182 | 183 | @Nullable 184 | public Double getFloodStageInPct() { 185 | return floodStageInPct; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/NodePrometheusMetricsAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.elasticsearch.action; 18 | 19 | /** 20 | * Action class for Prometheus Exporter plugin. 21 | */ 22 | public class NodePrometheusMetricsAction extends ActionType { 23 | 24 | public static final NodePrometheusMetricsAction INSTANCE = new NodePrometheusMetricsAction(); 25 | public static final String NAME = "cluster:monitor/prometheus/metrics"; 26 | 27 | private NodePrometheusMetricsAction() { 28 | super(NAME, NodePrometheusMetricsResponse::new); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/NodePrometheusMetricsRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.elasticsearch.action; 19 | 20 | import org.elasticsearch.action.support.master.MasterNodeReadRequest; 21 | import org.elasticsearch.common.io.stream.StreamInput; 22 | 23 | import java.io.IOException; 24 | 25 | /** 26 | * Action request class for Prometheus Exporter plugin. 27 | */ 28 | public class NodePrometheusMetricsRequest extends MasterNodeReadRequest { 29 | 30 | public NodePrometheusMetricsRequest() { 31 | super(); 32 | } 33 | 34 | public NodePrometheusMetricsRequest(StreamInput in) throws IOException { 35 | super(in); 36 | } 37 | 38 | @Override 39 | public ActionRequestValidationException validate() { 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/NodePrometheusMetricsResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.elasticsearch.action; 19 | 20 | import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; 21 | import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; 22 | import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; 23 | import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; 24 | import org.elasticsearch.action.admin.indices.stats.PackageAccessHelper; 25 | import org.elasticsearch.core.Nullable; 26 | import org.elasticsearch.common.io.stream.StreamInput; 27 | import org.elasticsearch.common.io.stream.StreamOutput; 28 | import org.elasticsearch.common.settings.ClusterSettings; 29 | import org.elasticsearch.common.settings.Settings; 30 | 31 | import java.io.IOException; 32 | 33 | /** 34 | * Action response class for Prometheus Exporter plugin. 35 | */ 36 | public class NodePrometheusMetricsResponse extends ActionResponse { 37 | private ClusterHealthResponse clusterHealth; 38 | private NodeStats nodeStats; 39 | @Nullable private IndicesStatsResponse indicesStats; 40 | private ClusterStatsData clusterStatsData = null; 41 | 42 | public NodePrometheusMetricsResponse(StreamInput in) throws IOException { 43 | super(in); 44 | clusterHealth = new ClusterHealthResponse(in); 45 | nodeStats = new NodeStats(in); 46 | indicesStats = PackageAccessHelper.createIndicesStatsResponse(in); 47 | clusterStatsData = new ClusterStatsData(in); 48 | } 49 | 50 | public NodePrometheusMetricsResponse(ClusterHealthResponse clusterHealth, NodeStats nodesStats, 51 | @Nullable IndicesStatsResponse indicesStats, 52 | @Nullable ClusterStateResponse clusterStateResponse, 53 | Settings settings, 54 | ClusterSettings clusterSettings) { 55 | this.clusterHealth = clusterHealth; 56 | this.nodeStats = nodesStats; 57 | this.indicesStats = indicesStats; 58 | if (clusterStateResponse != null) { 59 | this.clusterStatsData = new ClusterStatsData(clusterStateResponse, settings, clusterSettings); 60 | } 61 | } 62 | 63 | public ClusterHealthResponse getClusterHealth() { 64 | return this.clusterHealth; 65 | } 66 | 67 | public NodeStats getNodeStats() { 68 | return this.nodeStats; 69 | } 70 | 71 | @Nullable 72 | public IndicesStatsResponse getIndicesStats() { 73 | return this.indicesStats; 74 | } 75 | 76 | @Nullable 77 | public ClusterStatsData getClusterStatsData() { 78 | return this.clusterStatsData; 79 | } 80 | 81 | @Override 82 | public void writeTo(StreamOutput out) throws IOException { 83 | clusterHealth.writeTo(out); 84 | nodeStats.writeTo(out); 85 | out.writeOptionalWriteable(indicesStats); 86 | clusterStatsData.writeTo(out); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/NodePrometheusRequestBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.elasticsearch.action; 19 | 20 | import org.elasticsearch.action.support.master.MasterNodeReadOperationRequestBuilder; 21 | import org.elasticsearch.client.ElasticsearchClient; 22 | 23 | /** 24 | * Request builder class for Prometheus Exporter plugin. 25 | */ 26 | public class NodePrometheusRequestBuilder extends MasterNodeReadOperationRequestBuilder { 28 | public NodePrometheusRequestBuilder(ElasticsearchClient client, NodePrometheusMetricsAction action) { 29 | super(client, action, new NodePrometheusMetricsRequest().local(true)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/TransportNodePrometheusMetricsAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.elasticsearch.action; 19 | 20 | import org.apache.logging.log4j.LogManager; 21 | import org.apache.logging.log4j.Logger; 22 | import org.compuscene.metrics.prometheus.PrometheusSettings; 23 | import org.elasticsearch.ElasticsearchException; 24 | import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; 25 | import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; 26 | import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequest; 27 | import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; 28 | import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; 29 | import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; 30 | import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest; 31 | import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; 32 | import org.elasticsearch.action.support.ActionFilters; 33 | import org.elasticsearch.action.support.HandledTransportAction; 34 | import org.elasticsearch.client.Client; 35 | import org.elasticsearch.client.Requests; 36 | import org.elasticsearch.core.Nullable; 37 | import org.elasticsearch.common.inject.Inject; 38 | import org.elasticsearch.common.settings.ClusterSettings; 39 | import org.elasticsearch.common.settings.Settings; 40 | import org.elasticsearch.tasks.Task; 41 | import org.elasticsearch.transport.TransportService; 42 | 43 | /** 44 | * Transport action class for Prometheus Exporter plugin. 45 | * 46 | * It performs several requests within the cluster to gather "cluster health", "nodes stats", "indices stats" 47 | * and "cluster state" (i.e. cluster settings) info. Some of those requests are optional depending on plugin 48 | * settings. 49 | */ 50 | public class TransportNodePrometheusMetricsAction extends HandledTransportAction { 52 | private final Client client; 53 | private final Settings settings; 54 | private final ClusterSettings clusterSettings; 55 | private final PrometheusSettings prometheusSettings; 56 | private final Logger logger = LogManager.getLogger(getClass()); 57 | 58 | @Inject 59 | public TransportNodePrometheusMetricsAction(Settings settings, Client client, 60 | TransportService transportService, ActionFilters actionFilters, 61 | ClusterSettings clusterSettings) { 62 | super(NodePrometheusMetricsAction.NAME, transportService, actionFilters, 63 | NodePrometheusMetricsRequest::new); 64 | this.client = client; 65 | this.settings = settings; 66 | this.clusterSettings = clusterSettings; 67 | this.prometheusSettings = new PrometheusSettings(settings, clusterSettings); 68 | } 69 | 70 | @Override 71 | protected void doExecute(Task task, NodePrometheusMetricsRequest request, 72 | ActionListener listener) { 73 | new AsyncAction(listener).start(); 74 | } 75 | 76 | private class AsyncAction { 77 | 78 | private final ActionListener listener; 79 | 80 | private final ClusterHealthRequest healthRequest; 81 | private final NodesStatsRequest nodesStatsRequest; 82 | private final IndicesStatsRequest indicesStatsRequest; 83 | private final ClusterStateRequest clusterStateRequest; 84 | 85 | private ClusterHealthResponse clusterHealthResponse = null; 86 | private NodesStatsResponse nodesStatsResponse = null; 87 | private IndicesStatsResponse indicesStatsResponse = null; 88 | private ClusterStateResponse clusterStateResponse = null; 89 | 90 | // read the state of prometheus dynamic settings only once at the beginning of the async request 91 | private boolean isPrometheusIndices = prometheusSettings.getPrometheusIndices(); 92 | private boolean isPrometheusClusterSettings = prometheusSettings.getPrometheusClusterSettings(); 93 | 94 | // All the requests are executed in sequential non-blocking order. 95 | // It is implemented by wrapping each individual request with ActionListener 96 | // and chaining all of them into a sequence. The last member of the chain call method that gathers 97 | // all the responses from previous requests and pass them to outer listener (i.e. calling client). 98 | // Optional requests are skipped. 99 | // 100 | // In the future we might consider executing all the requests in parallel if needed (CountDownLatch?), 101 | // however, some of the requests can impact cluster performance (especially if the cluster is already overloaded) 102 | // and in this situation it is better to run all requests in predictable order so that collected metrics 103 | // stay consistent. 104 | private AsyncAction(ActionListener listener) { 105 | this.listener = listener; 106 | 107 | // Note: when using ClusterHealthRequest in Java, it pulls data at the shards level, according to ES source 108 | // code comment this is "so it is backward compatible with the transport client behaviour". 109 | // hence we are explicit about ClusterHealthRequest level and do not rely on defaults. 110 | // https://www.elastic.co/guide/en/elasticsearch/reference/6.4/cluster-health.html#request-params 111 | this.healthRequest = Requests.clusterHealthRequest().local(true); 112 | this.healthRequest.level(ClusterHealthRequest.Level.SHARDS); 113 | 114 | this.nodesStatsRequest = Requests.nodesStatsRequest("_local").clear().all(); 115 | 116 | // Indices stats request is not "node-specific", it does not support any "_local" notion 117 | // it is broad-casted to all cluster nodes. 118 | this.indicesStatsRequest = isPrometheusIndices ? new IndicesStatsRequest() : null; 119 | 120 | // Cluster settings are get via ClusterStateRequest (see elasticsearch RestClusterGetSettingsAction for details) 121 | // We prefer to send it to master node (hence local=false; it should be set by default but we want to be sure). 122 | this.clusterStateRequest = isPrometheusClusterSettings ? Requests.clusterStateRequest() 123 | .clear().metadata(true).local(false) : null; 124 | } 125 | 126 | private void gatherRequests() { 127 | listener.onResponse(buildResponse(clusterHealthResponse, nodesStatsResponse, indicesStatsResponse, 128 | clusterStateResponse)); 129 | } 130 | 131 | private ActionListener clusterStateResponseActionListener = 132 | new ActionListener() { 133 | @Override 134 | public void onResponse(ClusterStateResponse response) { 135 | clusterStateResponse = response; 136 | gatherRequests(); 137 | } 138 | 139 | @Override 140 | public void onFailure(Exception e) { 141 | listener.onFailure(new ElasticsearchException("Cluster state request failed", e)); 142 | } 143 | }; 144 | 145 | private ActionListener indicesStatsResponseActionListener = 146 | new ActionListener() { 147 | @Override 148 | public void onResponse(IndicesStatsResponse response) { 149 | indicesStatsResponse = response; 150 | if (isPrometheusClusterSettings) { 151 | client.admin().cluster().state(clusterStateRequest, clusterStateResponseActionListener); 152 | } else { 153 | gatherRequests(); 154 | } 155 | } 156 | 157 | @Override 158 | public void onFailure(Exception e) { 159 | listener.onFailure(new ElasticsearchException("Indices stats request failed", e)); 160 | } 161 | }; 162 | 163 | private ActionListener nodesStatsResponseActionListener = 164 | new ActionListener() { 165 | @Override 166 | public void onResponse(NodesStatsResponse nodeStats) { 167 | nodesStatsResponse = nodeStats; 168 | if (isPrometheusIndices) { 169 | client.admin().indices().stats(indicesStatsRequest, indicesStatsResponseActionListener); 170 | } else { 171 | indicesStatsResponseActionListener.onResponse(null); 172 | } 173 | } 174 | 175 | @Override 176 | public void onFailure(Exception e) { 177 | listener.onFailure(new ElasticsearchException("Nodes stats request failed", e)); 178 | } 179 | }; 180 | 181 | private ActionListener clusterHealthResponseActionListener = 182 | new ActionListener() { 183 | @Override 184 | public void onResponse(ClusterHealthResponse response) { 185 | clusterHealthResponse = response; 186 | client.admin().cluster().nodesStats(nodesStatsRequest, nodesStatsResponseActionListener); 187 | } 188 | 189 | @Override 190 | public void onFailure(Exception e) { 191 | listener.onFailure(new ElasticsearchException("Cluster health request failed", e)); 192 | } 193 | }; 194 | 195 | private void start() { 196 | client.admin().cluster().health(healthRequest, clusterHealthResponseActionListener); 197 | } 198 | 199 | protected NodePrometheusMetricsResponse buildResponse(ClusterHealthResponse clusterHealth, 200 | NodesStatsResponse nodesStats, 201 | @Nullable IndicesStatsResponse indicesStats, 202 | @Nullable ClusterStateResponse clusterStateResponse) { 203 | NodePrometheusMetricsResponse response = new NodePrometheusMetricsResponse(clusterHealth, 204 | nodesStats.getNodes().get(0), indicesStats, clusterStateResponse, 205 | settings, clusterSettings); 206 | if (logger.isTraceEnabled()) { 207 | logger.trace("Return response: [{}]", response); 208 | } 209 | return response; 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/admin/indices/stats/PackageAccessHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.elasticsearch.action.admin.indices.stats; 18 | 19 | import org.elasticsearch.common.io.stream.StreamInput; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * Utility methods. 25 | */ 26 | public class PackageAccessHelper { 27 | 28 | /** 29 | * Shortcut to IndicesStatsResponse constructor which has package access restriction. 30 | * @param in StreamInput 31 | * @return IndicesStatsResponse 32 | * @throws IOException When something goes wrong 33 | */ 34 | public static IndicesStatsResponse createIndicesStatsResponse(StreamInput in) throws IOException { 35 | return in.readOptionalWriteable(IndicesStatsResponse::new); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/admin/indices/stats/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /** 19 | * Utility classes. 20 | */ 21 | package org.elasticsearch.action.admin.indices.stats; 22 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/action/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /** 19 | * Classes used for Prometheus Exporter plugin transport actions. 20 | */ 21 | package org.elasticsearch.action; 22 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugin/prometheus/PrometheusExporterPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.elasticsearch.plugin.prometheus; 19 | 20 | import static java.util.Collections.singletonList; 21 | 22 | import org.apache.logging.log4j.LogManager; 23 | import org.apache.logging.log4j.Logger; 24 | import org.compuscene.metrics.prometheus.PrometheusSettings; 25 | import org.elasticsearch.action.ActionRequest; 26 | import org.elasticsearch.action.ActionResponse; 27 | import org.elasticsearch.action.NodePrometheusMetricsAction; 28 | import org.elasticsearch.action.TransportNodePrometheusMetricsAction; 29 | import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; 30 | import org.elasticsearch.cluster.node.DiscoveryNodes; 31 | import org.elasticsearch.common.settings.*; 32 | import org.elasticsearch.plugins.ActionPlugin; 33 | import org.elasticsearch.plugins.Plugin; 34 | import org.elasticsearch.rest.RestController; 35 | import org.elasticsearch.rest.RestHandler; 36 | import org.elasticsearch.rest.prometheus.RestPrometheusMetricsAction; 37 | 38 | import java.util.Arrays; 39 | import java.util.Collections; 40 | import java.util.List; 41 | import java.util.function.Supplier; 42 | 43 | /** 44 | * Prometheus Exporter plugin main class. 45 | */ 46 | public class PrometheusExporterPlugin extends Plugin implements ActionPlugin { 47 | private static final Logger logger = LogManager.getLogger(PrometheusExporterPlugin.class); 48 | 49 | public PrometheusExporterPlugin() { 50 | logger.info("starting Prometheus exporter plugin"); 51 | } 52 | 53 | @Override 54 | public List> getActions() { 55 | return singletonList( 56 | new ActionHandler<>(NodePrometheusMetricsAction.INSTANCE, TransportNodePrometheusMetricsAction.class) 57 | ); 58 | } 59 | 60 | @Override 61 | public List getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings, 62 | IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, 63 | IndexNameExpressionResolver indexNameExpressionResolver, 64 | Supplier nodesInCluster) { 65 | return singletonList( 66 | new RestPrometheusMetricsAction(settings, clusterSettings) 67 | ); 68 | } 69 | 70 | @Override 71 | public List> getSettings() { 72 | List> settings = Arrays.asList( 73 | PrometheusSettings.PROMETHEUS_CLUSTER_SETTINGS, 74 | PrometheusSettings.PROMETHEUS_INDICES 75 | ); 76 | return Collections.unmodifiableList(settings); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugin/prometheus/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /** 19 | * Prometheus Exporter plugin main classes. 20 | */ 21 | package org.elasticsearch.plugin.prometheus; 22 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/rest/prometheus/RestPrometheusMetricsAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.elasticsearch.rest.prometheus; 19 | 20 | import static org.elasticsearch.action.NodePrometheusMetricsAction.INSTANCE; 21 | import static org.elasticsearch.rest.RestRequest.Method.GET; 22 | 23 | import org.apache.logging.log4j.LogManager; 24 | import org.apache.logging.log4j.Logger; 25 | import org.compuscene.metrics.prometheus.PrometheusMetricsCatalog; 26 | import org.compuscene.metrics.prometheus.PrometheusMetricsCollector; 27 | import org.compuscene.metrics.prometheus.PrometheusSettings; 28 | import org.elasticsearch.action.NodePrometheusMetricsRequest; 29 | import org.elasticsearch.action.NodePrometheusMetricsResponse; 30 | import org.elasticsearch.client.node.NodeClient; 31 | import org.elasticsearch.common.network.NetworkAddress; 32 | import org.elasticsearch.common.settings.ClusterSettings; 33 | import org.elasticsearch.common.settings.Settings; 34 | import org.elasticsearch.rest.*; 35 | import org.elasticsearch.rest.action.RestResponseListener; 36 | 37 | import java.util.List; 38 | import java.util.Locale; 39 | 40 | import static java.util.Arrays.asList; 41 | import static java.util.Collections.unmodifiableList; 42 | 43 | /** 44 | * REST action class for Prometheus Exporter plugin. 45 | */ 46 | public class RestPrometheusMetricsAction extends BaseRestHandler { 47 | 48 | private final PrometheusSettings prometheusSettings; 49 | private final Logger logger = LogManager.getLogger(getClass()); 50 | 51 | public RestPrometheusMetricsAction(Settings settings, ClusterSettings clusterSettings) { 52 | this.prometheusSettings = new PrometheusSettings(settings, clusterSettings); 53 | } 54 | 55 | @Override 56 | public List routes() { 57 | return unmodifiableList(asList( 58 | new Route(GET, "/_prometheus/metrics")) 59 | ); 60 | } 61 | 62 | @Override 63 | public String getName() { 64 | return "prometheus_metrics_action"; 65 | } 66 | 67 | // This method does not throw any IOException because there are no request parameters to be parsed 68 | // and processed. This may change in the future. 69 | @Override 70 | protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { 71 | if (logger.isTraceEnabled()) { 72 | String remoteAddress = NetworkAddress.format(request.getHttpChannel().getRemoteAddress()); 73 | logger.trace(String.format(Locale.ENGLISH, "Received request for Prometheus metrics from %s", 74 | remoteAddress)); 75 | } 76 | 77 | NodePrometheusMetricsRequest metricsRequest = new NodePrometheusMetricsRequest(); 78 | 79 | return channel -> client.execute(INSTANCE, metricsRequest, 80 | new RestResponseListener(channel) { 81 | 82 | @Override 83 | public RestResponse buildResponse(NodePrometheusMetricsResponse response) throws Exception { 84 | String clusterName = response.getClusterHealth().getClusterName(); 85 | String nodeName = response.getNodeStats().getNode().getName(); 86 | String nodeId = response.getNodeStats().getNode().getId(); 87 | if (logger.isTraceEnabled()) { 88 | logger.trace("Prepare new Prometheus metric collector for: [{}], [{}], [{}]", clusterName, nodeId, 89 | nodeName); 90 | } 91 | PrometheusMetricsCatalog catalog = new PrometheusMetricsCatalog(clusterName, nodeName, nodeId, "es_"); 92 | PrometheusMetricsCollector collector = new PrometheusMetricsCollector( 93 | catalog, 94 | prometheusSettings.getPrometheusIndices(), 95 | prometheusSettings.getPrometheusClusterSettings()); 96 | collector.registerMetrics(); 97 | collector.updateMetrics(response.getClusterHealth(), response.getNodeStats(), response.getIndicesStats(), 98 | response.getClusterStatsData()); 99 | return new BytesRestResponse(RestStatus.OK, collector.getCatalog().toTextFormat()); 100 | } 101 | }); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/rest/prometheus/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /** 19 | * Classes used for Prometheus Exporter plugin REST actions. 20 | */ 21 | package org.elasticsearch.rest.prometheus; 22 | -------------------------------------------------------------------------------- /src/main/plugin-metadata/plugin-security.policy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Vincent VAN HOLLEBEKE] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | grant { 19 | permission java.lang.RuntimePermission "accessClassInPackage.sun.misc"; 20 | permission java.lang.RuntimePermission "accessDeclaredMembers"; 21 | permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; 22 | }; 23 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/rest/PrometheusRestHandlerClientYamlTestSuiteIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2018] [Lukas Vlcek] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package org.elasticsearch.rest; 19 | 20 | import com.carrotsearch.randomizedtesting.annotations.Name; 21 | import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; 22 | import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; 23 | import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; 24 | 25 | /** 26 | * Needed class to enable esplugin rest api tests. 27 | */ 28 | public class PrometheusRestHandlerClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { 29 | 30 | public PrometheusRestHandlerClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { 31 | super(testCandidate); 32 | } 33 | 34 | @ParametersFactory 35 | public static Iterable parameters() throws Exception { 36 | return ESClientYamlSuiteTestCase.createParameters(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/rest/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2016] [Lukas Vlcek] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /** 19 | * Classes used for testing. 20 | */ 21 | package org.elasticsearch.rest; 22 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/api/prometheus.metrics.json: -------------------------------------------------------------------------------- 1 | { 2 | "prometheus.metrics": { 3 | "documentation":{ 4 | "url":"https://github.com/vvanholl/elasticsearch-prometheus-exporter/" 5 | }, 6 | "stability": "stable", 7 | "url":{ 8 | "paths":[ 9 | { 10 | "path":"/_prometheus/metrics", 11 | "methods": ["GET"] 12 | } 13 | ] 14 | }, 15 | "body": null 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/10_basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # We set number of cluster nodes in build.gradle file. See `integTestCluster.numNodes` 3 | "Verify we have cluster with expectd number of nodes": 4 | - do: 5 | cluster.health: 6 | wait_for_nodes: 2 7 | 8 | - is_false: timed_out 9 | - gte: { number_of_nodes: 2 } 10 | - gte: { number_of_data_nodes: 2 } 11 | 12 | 13 | --- 14 | "Prometheus exporter plugin installed on every node": 15 | - do: 16 | cat.nodes: 17 | h: name,id 18 | full_id: true 19 | format: json 20 | 21 | # we know there are 2 nodes, keep id of all nodes so that we can iterate over them later... 22 | - set: { 0.id: node0 } 23 | - set: { 1.id: node1 } 24 | 25 | - do: 26 | nodes.info: { } 27 | 28 | # ...now, check that every node had this plugin installed 29 | - match: { nodes.$node0.plugins.0.name: prometheus-exporter } 30 | - match: { nodes.$node1.plugins.0.name: prometheus-exporter } 31 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/20_00_metrics.yml: -------------------------------------------------------------------------------- 1 | # At this moment this is mostly smoke test only. The goal is the have a test to verify 2 | # that metrics are relevant to specific ES node only but it seems that 3 | # current esplugin does not support for communication with specific ES nodes 4 | # only (see below for more details). 5 | # 6 | # Going forward one possible solution would be to implement this in Java similarly 7 | # to PrometheusResponseIntegTestCase in PR#64: 8 | # https://github.com/vvanholl/elasticsearch-prometheus-exporter/pull/64/files#diff-f91e6938f65298463b1935f9311d5e13 9 | --- 10 | "Prometheus metrics can be pulled": 11 | 12 | - do: 13 | prometheus.metrics: {} 14 | 15 | # Output is pure text hence we need to rely solely on regex expressions. 16 | # See: https://github.com/elastic/elasticsearch/tree/master/rest-api-spec/src/main/resources/rest-api-spec/test#test-file-structure 17 | 18 | - match: 19 | $body: | 20 | /.* es_indices_segments_memory_bytes .*/ 21 | 22 | - match: 23 | $body: /# HELP es_os_swap_total_bytes Total swap size\n# TYPE es_os_swap_total_bytes gauge\n.*/ 24 | 25 | 26 | #--- 27 | #"Pull Prometheus metrics from individual nodes": 28 | # - skip: 29 | # features: node_selector 30 | # 31 | # - do: 32 | # cat.nodes: 33 | # h: name,id 34 | # full_id: true 35 | # format: json 36 | # 37 | # # we know there are 2 nodes, keep id and name of all nodes so that we can iterate over them later... 38 | # - set: { 0.id: node0 } 39 | # - set: { 0.name: node0_name } 40 | # - set: { 1.id: node1 } 41 | # - set: { 1.name: node1_name } 42 | # 43 | # # Node selector does not not currently allow to filter specific node 44 | # # or I do not know how to do it using attributes. 45 | # # See: https://discuss.elastic.co/t/interacting-with-individual-nodes-in-rest-api-integration-tests-using-esplugin-support/152095/2 46 | # - do: 47 | # node_selector: 48 | # attribute: 49 | # name: node-0 50 | # prometheus.metrics: {} 51 | # 52 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/20_10_cluster_settings_metrics_disabled.yml: -------------------------------------------------------------------------------- 1 | # Test that disabling cluster settings metrics dynamically 2 | # causes those metrics being not exposed. We test both the transient and persistent 3 | # levels. Notice, however, that we are testing only single metric, which means we need 4 | # another test the verify that all the cluster settings metrics are disabled or enabled. 5 | --- 6 | "Dynamically disable cluster settings metrics": 7 | 8 | # Let's start with just the default OOTB settings, this means 9 | # both the persistent and transient settings levels are empty. 10 | - do: 11 | cluster.get_settings: 12 | flat_settings: true 13 | 14 | - match: {persistent: {}} 15 | - match: {transient: {}} 16 | 17 | - do: 18 | prometheus.metrics: {} 19 | 20 | # We get back some cluster settings metrics... 21 | - match: 22 | $body: | 23 | /.* 24 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 25 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 26 | es_cluster_routing_allocation_disk_threshold_enabled\{ 27 | cluster="integTest" 28 | \,} \s 1\.0 \n? 29 | .*/ 30 | 31 | # ----------------------------------- 32 | # Disable the "prometheus.cluster.settings" at the TRANSIENT level: 33 | - do: 34 | cluster.put_settings: 35 | body: 36 | transient: 37 | prometheus.cluster.settings: false 38 | flat_settings: true 39 | 40 | - is_false: transient.prometheus.cluster.settings 41 | 42 | - do: 43 | cluster.get_settings: 44 | flat_settings: true 45 | 46 | - is_false: transient.prometheus.cluster.settings 47 | - match: {persistent: {}} 48 | 49 | # Verify the metrics are not exported now. By repeating the request twice we verify that the response is consistent 50 | # across all nodes of the cluster, ie. cluster state changes were propagated correctly. 51 | - do: 52 | prometheus.metrics: {} 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge 59 | (\n \# \s (HELP|TYPE).* | \s*) 60 | / 61 | 62 | - do: 63 | prometheus.metrics: {} 64 | 65 | - match: 66 | $body: | 67 | /.* 68 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 69 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge 70 | (\n \# \s (HELP|TYPE).* | \s*) 71 | / 72 | 73 | # Disable the "prometheus.cluster.settings" at the PERSISTENT level too: 74 | - do: 75 | cluster.put_settings: 76 | body: 77 | persistent: 78 | prometheus.cluster.settings: false 79 | flat_settings: true 80 | 81 | - is_false: persistent.prometheus.cluster.settings 82 | 83 | - do: 84 | cluster.get_settings: 85 | flat_settings: true 86 | 87 | - is_false: transient.prometheus.cluster.settings 88 | - is_false: persistent.prometheus.cluster.settings 89 | 90 | # Still cluster settings metrics are not exposed... 91 | - do: 92 | prometheus.metrics: {} 93 | 94 | - match: 95 | $body: | 96 | /.* 97 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 98 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge 99 | (\n \# \s (HELP|TYPE).* | \s*) 100 | / 101 | 102 | # ----------------------------------- 103 | # Clear the "prometheus.cluster.settings" at the TRANSIENT level: 104 | - do: 105 | cluster.put_settings: 106 | body: 107 | transient: 108 | prometheus.cluster.settings: null 109 | 110 | - is_false: transient.prometheus.cluster.settings 111 | 112 | - do: 113 | cluster.get_settings: 114 | flat_settings: true 115 | 116 | - match: {transient: {}} 117 | - is_false: persistent.prometheus.cluster.settings 118 | 119 | # Still cluster settings metrics are not exposed... 120 | - do: 121 | prometheus.metrics: {} 122 | 123 | - match: 124 | $body: | 125 | /.* 126 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 127 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge 128 | (\n \# \s (HELP|TYPE).* | \s*) 129 | / 130 | 131 | # ----------------------------------- 132 | # Clear the "prometheus.cluster.settings" at the PERSISTENT level: 133 | - do: 134 | cluster.put_settings: 135 | body: 136 | persistent: 137 | prometheus.cluster.settings: null 138 | 139 | - do: 140 | cluster.get_settings: 141 | flat_settings: true 142 | 143 | - match: {persistent: {}} 144 | - match: {transient: {}} 145 | 146 | - do: 147 | prometheus.metrics: {} 148 | 149 | # We get back some cluster settings metrics again 150 | - match: 151 | $body: | 152 | /.* 153 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 154 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 155 | es_cluster_routing_allocation_disk_threshold_enabled\{ 156 | cluster="integTest" 157 | \,} \s 1\.0 \n? 158 | .*/ 159 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/20_11_index_level_metrics_disabled.yml: -------------------------------------------------------------------------------- 1 | # Test that disabling index level metrics dynamically 2 | # causes those metrics being not exposed. 3 | --- 4 | "Dynamically disable index level metrics": 5 | 6 | # Assume we have no indices in the cluster 7 | - do: 8 | cluster.stats: {} 9 | 10 | - match: { indices.count: 0 } 11 | 12 | 13 | # twitter index should not be present in the prometheus stats 14 | - do: 15 | prometheus.metrics: {} 16 | 17 | - match: 18 | $body: /.*[^t][^w][^i][^t][^t][^e][^r].*/ 19 | 20 | # Create twitter index 21 | - do: 22 | index: 23 | index: twitter 24 | type: foo 25 | id: 1 26 | body: { foo: bar } 27 | 28 | - do: 29 | indices.refresh: { allow_no_indices: true } 30 | 31 | - do: 32 | cluster.stats: {} 33 | 34 | - match: { indices.count: 1 } 35 | 36 | # Now the twitter index details are present 37 | - do: 38 | prometheus.metrics: {} 39 | 40 | - match: 41 | $body: /.*[t][w][i][t][t][e][r].*/ 42 | 43 | # Let's start with just the default OOTB settings, this means 44 | # both the persistent and transient settings levels are empty. 45 | - do: 46 | cluster.get_settings: 47 | flat_settings: true 48 | 49 | - match: {persistent: {}} 50 | - match: {transient: {}} 51 | 52 | # ----------------------------------- 53 | # Disable the "prometheus.indices" at the TRANSIENT level: 54 | - do: 55 | cluster.put_settings: 56 | body: 57 | transient: 58 | prometheus.indices: false 59 | flat_settings: true 60 | 61 | - is_false: transient.prometheus.indices 62 | 63 | - do: 64 | cluster.get_settings: 65 | flat_settings: true 66 | 67 | - is_false: transient.prometheus.indices 68 | - match: {persistent: {}} 69 | 70 | # Verify indices metrics are not exported now. 71 | - do: 72 | prometheus.metrics: {} 73 | 74 | - match: 75 | $body: /.*[^t][^w][^i][^t][^t][^e][^r].*/ 76 | 77 | # ----------------------------------- 78 | # Clear the "prometheus.indices" settings: 79 | - do: 80 | cluster.put_settings: 81 | body: 82 | transient: 83 | prometheus.indices: null 84 | flat_settings: true 85 | 86 | # And we should see the twitter index details back in prometheus metrics 87 | - do: 88 | prometheus.metrics: {} 89 | 90 | - match: 91 | $body: /.*[t][w][i][t][t][e][r].*/ 92 | 93 | # ----------------------------------- 94 | # Test clean up... 95 | - do: 96 | indices.delete: 97 | index: twitter 98 | 99 | - do: 100 | cluster.get_settings: 101 | flat_settings: true 102 | 103 | - match: {persistent: {}} 104 | - match: {transient: {}} 105 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_00_index_level_info.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (from ClusterHealth)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_shards_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_shards_number \s gauge \n 35 | ( 36 | es_index_shards_number\{ 37 | cluster="integTest", 38 | type="(active_primary|initializing|relocating|shards|active|unassigned)", 39 | index="twitter" 40 | ,\} \s+ \d+\.\d+ \n? 41 | ){6} 42 | .*/ 43 | 44 | - match: 45 | $body: | 46 | /.* 47 | \# \s HELP \s es_index_status (\s|\w|\d)+ \n 48 | \# \s TYPE \s es_index_status \s gauge \n 49 | es_index_status\{ 50 | cluster="integTest",index="twitter" 51 | \,} \s \d+\.\d+ \n? 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_replicas_number (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_replicas_number \s gauge \n 59 | es_index_replicas_number\{ 60 | cluster="integTest",index="twitter" 61 | ,\} \s+ \d+\.\d+ \n? 62 | .*/ 63 | 64 | --- 65 | "Index level statistics (general)": 66 | 67 | # ----------------------------------- 68 | # We expect no indices in the cluster 69 | - do: 70 | indices.refresh: { allow_no_indices: true } 71 | 72 | - do: 73 | cluster.stats: {} 74 | 75 | - match: { indices.count: 0 } 76 | 77 | - do: 78 | index: 79 | index: twitter 80 | type: foo 81 | id: 1 82 | body: { foo: bar } 83 | 84 | - do: 85 | indices.refresh: { allow_no_indices: true } 86 | 87 | # ----------------------------------- 88 | # We expect index level stats present now (by default) 89 | 90 | - do: 91 | prometheus.metrics: {} 92 | 93 | - match: 94 | $body: | 95 | /.* 96 | \# \s HELP \s es_index_doc_number (\s|\w|\d)+ \n 97 | \# \s TYPE \s es_index_doc_number \s gauge \n 98 | ( 99 | es_index_doc_number\{ 100 | cluster="integTest",index="twitter",context="(primaries|total)" 101 | ,\} \s+ \d+\.\d+ \n? 102 | ){2} 103 | .*/ 104 | 105 | - match: 106 | $body: | 107 | /.* 108 | \# \s HELP \s es_index_doc_deleted_number (\s|\w|\d)+ \n 109 | \# \s TYPE \s es_index_doc_deleted_number \s gauge \n 110 | ( 111 | es_index_doc_deleted_number\{ 112 | cluster="integTest",index="twitter",context="(primaries|total)" 113 | ,\} \s+ \d+\.\d+ \n? 114 | ){2} 115 | .*/ 116 | 117 | - match: 118 | $body: | 119 | /.* 120 | \# \s HELP \s es_index_store_size_bytes (\s|\w|\d)+ \n 121 | \# \s TYPE \s es_index_store_size_bytes \s gauge \n 122 | ( 123 | es_index_store_size_bytes\{ 124 | cluster="integTest",index="twitter",context="(primaries|total)" 125 | ,\} \s+ \d+\.\d+ \n? 126 | ){2} 127 | .*/ 128 | 129 | # ----------------------------------- 130 | # Delete the index and check index stats are not exposed anymore 131 | 132 | # Make sure the following regex matches because later we are using just negative version of it to prove otherwise 133 | - match: 134 | $body: /.*[t][w][i][t][t][e][r].*/ 135 | 136 | - do: 137 | indices.get: 138 | index: twitter 139 | 140 | - is_true: twitter 141 | 142 | - do: 143 | indices.delete: 144 | index: twitter 145 | 146 | - do: 147 | indices.get: 148 | index: twitter 149 | ignore_unavailable: true 150 | 151 | - is_false: twitter 152 | 153 | # twitter index should not be present in the prometheus stats now 154 | - do: 155 | prometheus.metrics: {} 156 | 157 | - match: 158 | $body: /.*[^t][^w][^i][^t][^t][^e][^r].*/ 159 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_10_index_indexing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_indexing_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_indexing_delete_count (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_indexing_delete_count \s gauge \n 35 | ( 36 | es_index_indexing_delete_count\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_indexing_delete_current_number (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_indexing_delete_current_number \s gauge \n 47 | ( 48 | es_index_indexing_delete_current_number\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_indexing_delete_time_seconds (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_indexing_delete_time_seconds \s gauge \n 59 | ( 60 | es_index_indexing_delete_time_seconds\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_index_indexing_index_count (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_index_indexing_index_count \s gauge \n 71 | ( 72 | es_index_indexing_index_count\{ 73 | cluster="integTest",index="twitter",context="(primaries|total)" 74 | ,\} \s+ \d+\.\d+ \n? 75 | ){2} 76 | .*/ 77 | 78 | - match: 79 | $body: | 80 | /.* 81 | \# \s HELP \s es_index_indexing_index_current_number (\s|\w|\d)+ \n 82 | \# \s TYPE \s es_index_indexing_index_current_number \s gauge \n 83 | ( 84 | es_index_indexing_index_current_number\{ 85 | cluster="integTest",index="twitter",context="(primaries|total)" 86 | ,\} \s+ \d+\.\d+ \n? 87 | ){2} 88 | .*/ 89 | 90 | - match: 91 | $body: | 92 | /.* 93 | \# \s HELP \s es_index_indexing_index_failed_count (\s|\w|\d)+ \n 94 | \# \s TYPE \s es_index_indexing_index_failed_count \s gauge \n 95 | ( 96 | es_index_indexing_index_failed_count\{ 97 | cluster="integTest",index="twitter",context="(primaries|total)" 98 | ,\} \s+ \d+\.\d+ \n? 99 | ){2} 100 | .*/ 101 | 102 | - match: 103 | $body: | 104 | /.* 105 | \# \s HELP \s es_index_indexing_index_time_seconds (\s|\w|\d)+ \n 106 | \# \s TYPE \s es_index_indexing_index_time_seconds \s gauge \n 107 | ( 108 | es_index_indexing_index_time_seconds\{ 109 | cluster="integTest",index="twitter",context="(primaries|total)" 110 | ,\} \s+ \d+\.\d+ \n? 111 | ){2} 112 | .*/ 113 | 114 | - match: 115 | $body: | 116 | /.* 117 | \# \s HELP \s es_index_indexing_noop_update_count (\s|\w|\d)+ \n 118 | \# \s TYPE \s es_index_indexing_noop_update_count \s gauge \n 119 | ( 120 | es_index_indexing_noop_update_count\{ 121 | cluster="integTest",index="twitter",context="(primaries|total)" 122 | ,\} \s+ \d+\.\d+ \n? 123 | ){2} 124 | .*/ 125 | 126 | - match: 127 | $body: | 128 | /.* 129 | \# \s HELP \s es_index_indexing_is_throttled_bool (\s|\w|\d)+ \? \n 130 | \# \s TYPE \s es_index_indexing_is_throttled_bool \s gauge \n 131 | ( 132 | es_index_indexing_is_throttled_bool\{ 133 | cluster="integTest",index="twitter",context="(primaries|total)" 134 | ,\} \s+ \d+\.\d+ \n? 135 | ){2} 136 | .*/ 137 | 138 | - match: 139 | $body: | 140 | /.* 141 | \# \s HELP \s es_index_indexing_throttle_time_seconds (\s|\w|\d)+ \n 142 | \# \s TYPE \s es_index_indexing_throttle_time_seconds \s gauge \n 143 | ( 144 | es_index_indexing_throttle_time_seconds\{ 145 | cluster="integTest",index="twitter",context="(primaries|total)" 146 | ,\} \s+ \d+\.\d+ \n? 147 | ){2} 148 | .*/ 149 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_11_index_get.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_get_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_get_count (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_get_count \s gauge \n 35 | ( 36 | es_index_get_count\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_get_time_seconds (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_get_time_seconds \s gauge \n 47 | ( 48 | es_index_get_time_seconds\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_get_exists_count (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_get_exists_count \s gauge \n 59 | ( 60 | es_index_get_exists_count\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_index_get_exists_time_seconds (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_index_get_exists_time_seconds \s gauge \n 71 | ( 72 | es_index_get_exists_time_seconds\{ 73 | cluster="integTest",index="twitter",context="(primaries|total)" 74 | ,\} \s+ \d+\.\d+ \n? 75 | ){2} 76 | .*/ 77 | 78 | - match: 79 | $body: | 80 | /.* 81 | \# \s HELP \s es_index_get_missing_count (\s|\w|\d)+ \n 82 | \# \s TYPE \s es_index_get_missing_count \s gauge \n 83 | ( 84 | es_index_get_missing_count\{ 85 | cluster="integTest",index="twitter",context="(primaries|total)" 86 | ,\} \s+ \d+\.\d+ \n? 87 | ){2} 88 | .*/ 89 | 90 | - match: 91 | $body: | 92 | /.* 93 | \# \s HELP \s es_index_get_missing_time_seconds (\s|\w|\d)+ \n 94 | \# \s TYPE \s es_index_get_missing_time_seconds \s gauge \n 95 | ( 96 | es_index_get_missing_time_seconds\{ 97 | cluster="integTest",index="twitter",context="(primaries|total)" 98 | ,\} \s+ \d+\.\d+ \n? 99 | ){2} 100 | .*/ 101 | 102 | - match: 103 | $body: | 104 | /.* 105 | \# \s HELP \s es_index_get_current_number (\s|\w|\d)+ \n 106 | \# \s TYPE \s es_index_get_current_number \s gauge \n 107 | ( 108 | es_index_get_current_number\{ 109 | cluster="integTest",index="twitter",context="(primaries|total)" 110 | ,\} \s+ \d+\.\d+ \n? 111 | ){2} 112 | .*/ 113 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_12_index_search.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_search_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_search_open_contexts_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_search_open_contexts_number \s gauge \n 35 | ( 36 | es_index_search_open_contexts_number\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_search_fetch_count (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_search_fetch_count \s gauge \n 47 | ( 48 | es_index_search_fetch_count\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_search_fetch_current_number (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_search_fetch_current_number \s gauge \n 59 | ( 60 | es_index_search_fetch_current_number\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_index_search_fetch_time_seconds (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_index_search_fetch_time_seconds \s gauge \n 71 | ( 72 | es_index_search_fetch_time_seconds\{ 73 | cluster="integTest",index="twitter",context="(primaries|total)" 74 | ,\} \s+ \d+\.\d+ \n? 75 | ){2} 76 | .*/ 77 | 78 | - match: 79 | $body: | 80 | /.* 81 | \# \s HELP \s es_index_search_query_count (\s|\w|\d)+ \n 82 | \# \s TYPE \s es_index_search_query_count \s gauge \n 83 | ( 84 | es_index_search_query_count\{ 85 | cluster="integTest",index="twitter",context="(primaries|total)" 86 | ,\} \s+ \d+\.\d+ \n? 87 | ){2} 88 | .*/ 89 | 90 | - match: 91 | $body: | 92 | /.* 93 | \# \s HELP \s es_index_search_query_current_number (\s|\w|\d)+ \n 94 | \# \s TYPE \s es_index_search_query_current_number \s gauge \n 95 | ( 96 | es_index_search_query_current_number\{ 97 | cluster="integTest",index="twitter",context="(primaries|total)" 98 | ,\} \s+ \d+\.\d+ \n? 99 | ){2} 100 | .*/ 101 | 102 | - match: 103 | $body: | 104 | /.* 105 | \# \s HELP \s es_index_search_query_time_seconds (\s|\w|\d)+ \n 106 | \# \s TYPE \s es_index_search_query_time_seconds \s gauge \n 107 | ( 108 | es_index_search_query_time_seconds\{ 109 | cluster="integTest",index="twitter",context="(primaries|total)" 110 | ,\} \s+ \d+\.\d+ \n? 111 | ){2} 112 | .*/ 113 | 114 | - match: 115 | $body: | 116 | /.* 117 | \# \s HELP \s es_index_search_scroll_count (\s|\w|\d)+ \n 118 | \# \s TYPE \s es_index_search_scroll_count \s gauge \n 119 | ( 120 | es_index_search_scroll_count\{ 121 | cluster="integTest",index="twitter",context="(primaries|total)" 122 | ,\} \s+ \d+\.\d+ \n? 123 | ){2} 124 | .*/ 125 | 126 | - match: 127 | $body: | 128 | /.* 129 | \# \s HELP \s es_index_search_scroll_current_number (\s|\w|\d)+ \n 130 | \# \s TYPE \s es_index_search_scroll_current_number \s gauge \n 131 | ( 132 | es_index_search_scroll_current_number\{ 133 | cluster="integTest",index="twitter",context="(primaries|total)" 134 | ,\} \s+ \d+\.\d+ \n? 135 | ){2} 136 | .*/ 137 | 138 | - match: 139 | $body: | 140 | /.* 141 | \# \s HELP \s es_index_search_scroll_time_seconds (\s|\w|\d)+ \n 142 | \# \s TYPE \s es_index_search_scroll_time_seconds \s gauge \n 143 | ( 144 | es_index_search_scroll_time_seconds\{ 145 | cluster="integTest",index="twitter",context="(primaries|total)" 146 | ,\} \s+ \d+\.\d+ \n? 147 | ){2} 148 | .*/ 149 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_13_index_merges.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_merges_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_merges_current_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_merges_current_number \s gauge \n 35 | ( 36 | es_index_merges_current_number\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_merges_current_docs_number (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_merges_current_docs_number \s gauge \n 47 | ( 48 | es_index_merges_current_docs_number\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_merges_current_size_bytes (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_merges_current_size_bytes \s gauge \n 59 | ( 60 | es_index_merges_current_size_bytes\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_index_merges_total_number (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_index_merges_total_number \s gauge \n 71 | ( 72 | es_index_merges_total_number\{ 73 | cluster="integTest",index="twitter",context="(primaries|total)" 74 | ,\} \s+ \d+\.\d+ \n? 75 | ){2} 76 | .*/ 77 | 78 | - match: 79 | $body: | 80 | /.* 81 | \# \s HELP \s es_index_merges_total_time_seconds (\s|\w|\d)+ \n 82 | \# \s TYPE \s es_index_merges_total_time_seconds \s gauge \n 83 | ( 84 | es_index_merges_total_time_seconds\{ 85 | cluster="integTest",index="twitter",context="(primaries|total)" 86 | ,\} \s+ \d+\.\d+ \n? 87 | ){2} 88 | .*/ 89 | 90 | - match: 91 | $body: | 92 | /.* 93 | \# \s HELP \s es_index_merges_total_docs_count (\s|\w|\d)+ \n 94 | \# \s TYPE \s es_index_merges_total_docs_count \s gauge \n 95 | ( 96 | es_index_merges_total_docs_count\{ 97 | cluster="integTest",index="twitter",context="(primaries|total)" 98 | ,\} \s+ \d+\.\d+ \n? 99 | ){2} 100 | .*/ 101 | 102 | - match: 103 | $body: | 104 | /.* 105 | \# \s HELP \s es_index_merges_total_size_bytes (\s|\w|\d)+ \n 106 | \# \s TYPE \s es_index_merges_total_size_bytes \s gauge \n 107 | ( 108 | es_index_merges_total_size_bytes\{ 109 | cluster="integTest",index="twitter",context="(primaries|total)" 110 | ,\} \s+ \d+\.\d+ \n? 111 | ){2} 112 | .*/ 113 | 114 | - match: 115 | $body: | 116 | /.* 117 | \# \s HELP \s es_index_merges_total_stopped_time_seconds (\s|\w|\d)+ \n 118 | \# \s TYPE \s es_index_merges_total_stopped_time_seconds \s gauge \n 119 | ( 120 | es_index_merges_total_stopped_time_seconds\{ 121 | cluster="integTest",index="twitter",context="(primaries|total)" 122 | ,\} \s+ \d+\.\d+ \n? 123 | ){2} 124 | .*/ 125 | 126 | - match: 127 | $body: | 128 | /.* 129 | \# \s HELP \s es_index_merges_total_throttled_time_seconds (\s|\w|\d)+ \n 130 | \# \s TYPE \s es_index_merges_total_throttled_time_seconds \s gauge \n 131 | ( 132 | es_index_merges_total_throttled_time_seconds\{ 133 | cluster="integTest",index="twitter",context="(primaries|total)" 134 | ,\} \s+ \d+\.\d+ \n? 135 | ){2} 136 | .*/ 137 | 138 | - match: 139 | $body: | 140 | /.* 141 | \# \s HELP \s es_index_merges_total_auto_throttle_bytes (\s|\w|\d)+ \n 142 | \# \s TYPE \s es_index_merges_total_auto_throttle_bytes \s gauge \n 143 | ( 144 | es_index_merges_total_auto_throttle_bytes\{ 145 | cluster="integTest",index="twitter",context="(primaries|total)" 146 | ,\} \s+ \d+\.\d+([eE]\d+)? \n? 147 | ){2} 148 | .*/ 149 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_14_index_refresh.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_refresh_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_refresh_total_count (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_refresh_total_count \s gauge \n 35 | ( 36 | es_index_refresh_total_count\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_refresh_total_time_seconds (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_refresh_total_time_seconds \s gauge \n 47 | ( 48 | es_index_refresh_total_time_seconds\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_refresh_listeners_number (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_refresh_listeners_number \s gauge \n 59 | ( 60 | es_index_refresh_listeners_number\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_15_index_flush.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_flush_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_flush_total_count (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_flush_total_count \s gauge \n 35 | ( 36 | es_index_flush_total_count\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_flush_total_time_seconds (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_flush_total_time_seconds \s gauge \n 47 | ( 48 | es_index_flush_total_time_seconds\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_16_index_querycache.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_querycache_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_querycache_cache_count (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_querycache_cache_count \s gauge \n 35 | ( 36 | es_index_querycache_cache_count\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_querycache_cache_size_bytes (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_querycache_cache_size_bytes \s gauge \n 47 | ( 48 | es_index_querycache_cache_size_bytes\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_querycache_evictions_count (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_querycache_evictions_count \s gauge \n 59 | ( 60 | es_index_querycache_evictions_count\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_index_querycache_hit_count (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_index_querycache_hit_count \s gauge \n 71 | ( 72 | es_index_querycache_hit_count\{ 73 | cluster="integTest",index="twitter",context="(primaries|total)" 74 | ,\} \s+ \d+\.\d+ \n? 75 | ){2} 76 | .*/ 77 | 78 | - match: 79 | $body: | 80 | /.* 81 | \# \s HELP \s es_index_querycache_memory_size_bytes (\s|\w|\d)+ \n 82 | \# \s TYPE \s es_index_querycache_memory_size_bytes \s gauge \n 83 | ( 84 | es_index_querycache_memory_size_bytes\{ 85 | cluster="integTest",index="twitter",context="(primaries|total)" 86 | ,\} \s+ \d+\.\d+ \n? 87 | ){2} 88 | .*/ 89 | 90 | - match: 91 | $body: | 92 | /.* 93 | \# \s HELP \s es_index_querycache_miss_number (\s|\w|\d)+ \n 94 | \# \s TYPE \s es_index_querycache_miss_number \s gauge \n 95 | ( 96 | es_index_querycache_miss_number\{ 97 | cluster="integTest",index="twitter",context="(primaries|total)" 98 | ,\} \s+ \d+\.\d+ \n? 99 | ){2} 100 | .*/ 101 | 102 | - match: 103 | $body: | 104 | /.* 105 | \# \s HELP \s es_index_querycache_total_number (\s|\w|\d)+ \n 106 | \# \s TYPE \s es_index_querycache_total_number \s gauge \n 107 | ( 108 | es_index_querycache_total_number\{ 109 | cluster="integTest",index="twitter",context="(primaries|total)" 110 | ,\} \s+ \d+\.\d+ \n? 111 | ){2} 112 | .*/ 113 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_17_index_fieldata.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_fieldata_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_fielddata_memory_size_bytes (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_fielddata_memory_size_bytes \s gauge \n 35 | ( 36 | es_index_fielddata_memory_size_bytes\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_fielddata_evictions_count (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_fielddata_evictions_count \s gauge \n 47 | ( 48 | es_index_fielddata_evictions_count\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_18_index_completion.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_completion_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_completion_size_bytes (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_completion_size_bytes \s gauge \n 35 | ( 36 | es_index_completion_size_bytes\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_19_index_segments.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_segments_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_segments_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_segments_number \s gauge \n 35 | ( 36 | es_index_segments_number\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_segments_memory_bytes (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_segments_memory_bytes \s gauge \n 47 | ( 48 | es_index_segments_memory_bytes\{ 49 | cluster="integTest",type="(all|bitset|docvalues|indexwriter|norms|storefields|terms|termvectors|versionmap|points)",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){20} 52 | .*/ 53 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_20_index_suggest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_suggest_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_suggest_current_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_suggest_current_number \s gauge \n 35 | ( 36 | es_index_suggest_current_number\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_suggest_count (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_suggest_count \s gauge \n 47 | ( 48 | es_index_suggest_count\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_suggest_time_seconds (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_suggest_time_seconds \s gauge \n 59 | ( 60 | es_index_suggest_time_seconds\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_21_index_requestcache.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_requestcache_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_requestcache_memory_size_bytes (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_requestcache_memory_size_bytes \s gauge \n 35 | ( 36 | es_index_requestcache_memory_size_bytes\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_requestcache_hit_count (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_requestcache_hit_count \s gauge \n 47 | ( 48 | es_index_requestcache_hit_count\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_requestcache_miss_count (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_requestcache_miss_count \s gauge \n 59 | ( 60 | es_index_requestcache_miss_count\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_index_requestcache_evictions_count (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_index_requestcache_evictions_count \s gauge \n 71 | ( 72 | es_index_requestcache_evictions_count\{ 73 | cluster="integTest",index="twitter",context="(primaries|total)" 74 | ,\} \s+ \d+\.\d+ \n? 75 | ){2} 76 | .*/ 77 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_22_index_recovery.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_recovery_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_recovery_current_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_recovery_current_number \s gauge \n 35 | ( 36 | es_index_recovery_current_number\{ 37 | cluster="integTest",type="(source|target)",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){4} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_recovery_throttle_time_seconds (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_recovery_throttle_time_seconds \s gauge \n 47 | ( 48 | es_index_recovery_throttle_time_seconds\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_23_index_translog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_translog_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_translog_operations_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_translog_operations_number \s gauge \n 35 | ( 36 | es_index_translog_operations_number\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_translog_size_bytes (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_translog_size_bytes \s gauge \n 47 | ( 48 | es_index_translog_size_bytes\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_translog_uncommitted_operations_number (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_translog_uncommitted_operations_number \s gauge \n 59 | ( 60 | es_index_translog_uncommitted_operations_number\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_index_translog_uncommitted_size_bytes (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_index_translog_uncommitted_size_bytes \s gauge \n 71 | ( 72 | es_index_translog_uncommitted_size_bytes\{ 73 | cluster="integTest",index="twitter",context="(primaries|total)" 74 | ,\} \s+ \d+\.\d+ \n? 75 | ){2} 76 | .*/ 77 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/30_24_index_warmer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Index level statistics (index_warmer_*)": 3 | 4 | # ----------------------------------- 5 | # We expect no indices in the cluster 6 | - do: 7 | indices.refresh: { allow_no_indices: true } 8 | 9 | - do: 10 | cluster.stats: {} 11 | 12 | - match: { indices.count: 0 } 13 | 14 | - do: 15 | index: 16 | index: twitter 17 | type: foo 18 | id: 1 19 | body: { foo: bar } 20 | 21 | - do: 22 | indices.refresh: { allow_no_indices: true } 23 | 24 | # ----------------------------------- 25 | # We expect index level stats present now (by default) 26 | 27 | - do: 28 | prometheus.metrics: {} 29 | 30 | - match: 31 | $body: | 32 | /.* 33 | \# \s HELP \s es_index_warmer_current_number (\s|\w|\d)+ \n 34 | \# \s TYPE \s es_index_warmer_current_number \s gauge \n 35 | ( 36 | es_index_warmer_current_number\{ 37 | cluster="integTest",index="twitter",context="(primaries|total)" 38 | ,\} \s+ \d+\.\d+ \n? 39 | ){2} 40 | .*/ 41 | 42 | - match: 43 | $body: | 44 | /.* 45 | \# \s HELP \s es_index_warmer_time_seconds (\s|\w|\d)+ \n 46 | \# \s TYPE \s es_index_warmer_time_seconds \s gauge \n 47 | ( 48 | es_index_warmer_time_seconds\{ 49 | cluster="integTest",index="twitter",context="(primaries|total)" 50 | ,\} \s+ \d+\.\d+ \n? 51 | ){2} 52 | .*/ 53 | 54 | - match: 55 | $body: | 56 | /.* 57 | \# \s HELP \s es_index_warmer_count (\s|\w|\d)+ \n 58 | \# \s TYPE \s es_index_warmer_count \s gauge \n 59 | ( 60 | es_index_warmer_count\{ 61 | cluster="integTest",index="twitter",context="(primaries|total)" 62 | ,\} \s+ \d+\.\d+ \n? 63 | ){2} 64 | .*/ 65 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/40_10_cluster_settings_disk_threshold.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Cluster settings statistics: disk threshold": 3 | 4 | # ----------------------------------- 5 | # By default cluster.routing.allocation.disk.threshold_enabled is set to true. 6 | # https://www.elastic.co/guide/en/elasticsearch/reference/master/disk-allocator.html 7 | # OOTB there are no transient or persistent settings in the cluster: 8 | - do: 9 | cluster.get_settings: 10 | flat_settings: true 11 | 12 | - match: {persistent: {}} 13 | - match: {transient: {}} 14 | 15 | # "Disk allocation threshold" is enabled (by default) 16 | # Verify in Prometheus metrics: 17 | - do: 18 | prometheus.metrics: {} 19 | 20 | - match: 21 | $body: | 22 | /.* 23 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 24 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 25 | es_cluster_routing_allocation_disk_threshold_enabled\{ 26 | cluster="integTest" 27 | \,} \s 1\.0 \n? 28 | .*/ 29 | 30 | # ----------------------------------- 31 | # Disable the "Disk allocation threshold" at the PERSISTENT level: 32 | - do: 33 | cluster.put_settings: 34 | body: 35 | persistent: 36 | cluster.routing.allocation.disk.threshold_enabled: false 37 | flat_settings: true 38 | 39 | - match: {persistent: {cluster.routing.allocation.disk.threshold_enabled: "false"}} 40 | 41 | - do: 42 | cluster.get_settings: 43 | flat_settings: true 44 | 45 | - match: {transient: {}} 46 | - match: {persistent: {cluster.routing.allocation.disk.threshold_enabled: "false"}} 47 | 48 | # Verify in Prometheus metrics: 49 | - do: 50 | prometheus.metrics: {} 51 | 52 | - match: 53 | $body: | 54 | /.* 55 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 56 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 57 | es_cluster_routing_allocation_disk_threshold_enabled\{ 58 | cluster="integTest" 59 | \,} \s 0\.0 \n? 60 | .*/ 61 | 62 | # ----------------------------------- 63 | # Enable the "Disk allocation threshold" at the TRANSIENT level: 64 | - do: 65 | cluster.put_settings: 66 | body: 67 | transient: 68 | cluster.routing.allocation.disk.threshold_enabled: true 69 | flat_settings: true 70 | 71 | - match: {transient: {cluster.routing.allocation.disk.threshold_enabled: "true"}} 72 | 73 | - do: 74 | cluster.get_settings: 75 | flat_settings: true 76 | 77 | - match: {transient: {cluster.routing.allocation.disk.threshold_enabled: "true"}} 78 | - match: {persistent: {cluster.routing.allocation.disk.threshold_enabled: "false"}} 79 | 80 | # Verify in Prometheus metrics: 81 | - do: 82 | prometheus.metrics: {} 83 | 84 | - match: 85 | $body: | 86 | /.* 87 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 88 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 89 | es_cluster_routing_allocation_disk_threshold_enabled\{ 90 | cluster="integTest" 91 | \,} \s 1\.0 \n? 92 | .*/ 93 | 94 | # ----------------------------------- 95 | # Wipe out the "Disk allocation threshold" setting at the PERSISTENT level. 96 | # There is still override at the TRANSIENT (ie. higher) level: 97 | - do: 98 | cluster.put_settings: 99 | body: 100 | persistent: 101 | cluster.routing.allocation.disk.threshold_enabled: null 102 | flat_settings: true 103 | 104 | - match: {persistent: {}} 105 | 106 | - do: 107 | cluster.get_settings: 108 | flat_settings: true 109 | 110 | - match: {transient: {cluster.routing.allocation.disk.threshold_enabled: "true"}} 111 | - match: {persistent: {}} 112 | 113 | # Verify in Prometheus metrics: 114 | - do: 115 | prometheus.metrics: {} 116 | 117 | - match: 118 | $body: | 119 | /.* 120 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 121 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 122 | es_cluster_routing_allocation_disk_threshold_enabled\{ 123 | cluster="integTest" 124 | \,} \s 1\.0 \n? 125 | .*/ 126 | 127 | # ----------------------------------- 128 | # Change the "Disk allocation threshold" setting at the TRANSIENT level: 129 | - do: 130 | cluster.put_settings: 131 | body: 132 | persistent: 133 | cluster.routing.allocation.disk.threshold_enabled: null 134 | flat_settings: true 135 | 136 | - match: {persistent: {}} 137 | 138 | - do: 139 | cluster.get_settings: 140 | flat_settings: true 141 | 142 | - match: {transient: {cluster.routing.allocation.disk.threshold_enabled: "true"}} 143 | - match: {persistent: {}} 144 | 145 | # Verify in Prometheus metrics: 146 | - do: 147 | prometheus.metrics: {} 148 | 149 | - match: 150 | $body: | 151 | /.* 152 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 153 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 154 | es_cluster_routing_allocation_disk_threshold_enabled\{ 155 | cluster="integTest" 156 | \,} \s 1\.0 \n? 157 | .*/ 158 | 159 | # ----------------------------------- 160 | # Finally, wipe out the "Disk allocation threshold" setting from the TRANSIENT level: 161 | - do: 162 | cluster.put_settings: 163 | body: 164 | transient: 165 | cluster.routing.allocation.disk.threshold_enabled: null 166 | flat_settings: true 167 | 168 | - match: {transient: {}} 169 | 170 | - do: 171 | cluster.get_settings: 172 | flat_settings: true 173 | 174 | - match: {transient: {}} 175 | - match: {persistent: {}} 176 | 177 | # Verify in Prometheus metrics: 178 | - do: 179 | prometheus.metrics: {} 180 | 181 | - match: 182 | $body: | 183 | /.* 184 | \# \s HELP \s es_cluster_routing_allocation_disk_threshold_enabled (\s|\w|\d)+ \n 185 | \# \s TYPE \s es_cluster_routing_allocation_disk_threshold_enabled \s gauge \n 186 | es_cluster_routing_allocation_disk_threshold_enabled\{ 187 | cluster="integTest" 188 | \,} \s 1\.0 \n? 189 | .*/ 190 | -------------------------------------------------------------------------------- /src/test/resources/rest-api-spec/test/resthandler/40_20_cluster_settings_disk_watermark_bytes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Cluster settings statistics: watermark": 3 | 4 | # ----------------------------------- 5 | # By default cluster.routing.allocation.disk.watermark.* are set according to: 6 | # https://www.elastic.co/guide/en/elasticsearch/reference/master/disk-allocator.html 7 | # --- 8 | # However, the REST test framework sets these settings in ClusterFormationTasks.groovy to '1b'. 9 | # Or see buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java 10 | # Things seems to be still moving in Elasticsearch... 11 | - do: 12 | cluster.get_settings: 13 | include_defaults: true 14 | 15 | - match: {persistent: {}} 16 | - match: {transient: {}} 17 | - match: {defaults.cluster.routing.allocation.disk.watermark.low: "1b"} 18 | - match: {defaults.cluster.routing.allocation.disk.watermark.high: "1b"} 19 | - match: {defaults.cluster.routing.allocation.disk.watermark.flood_stage: "1b"} 20 | 21 | # Verify in Prometheus metrics: 22 | - do: 23 | prometheus.metrics: {} 24 | 25 | - match: 26 | $body: | 27 | /.* 28 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_low_bytes (\s|\w|\d)+ \n 29 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_low_bytes \s gauge \n 30 | es_cluster_routing_allocation_disk_watermark_low_bytes\{ 31 | cluster="integTest" 32 | \,} \s 1\.0 \n? 33 | .*/ 34 | 35 | - match: 36 | $body: | 37 | /.* 38 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_high_bytes (\s|\w|\d)+ \n 39 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_high_bytes \s gauge \n 40 | es_cluster_routing_allocation_disk_watermark_high_bytes\{ 41 | cluster="integTest" 42 | \,} \s 1\.0 \n? 43 | .*/ 44 | 45 | - match: 46 | $body: | 47 | /.* 48 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_flood_stage_bytes (\s|\w|\d)+ \n 49 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_flood_stage_bytes \s gauge \n 50 | es_cluster_routing_allocation_disk_watermark_flood_stage_bytes\{ 51 | cluster="integTest" 52 | \,} \s 1\.0 \n? 53 | .*/ 54 | 55 | # At the same time all pct alternatives do not report any metric values but they are still present. 56 | # This is recommended, see https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics 57 | # 58 | # The following regexp-s work as follows: 59 | # After the metric HELP and TYPE is found then only one the following two options is expected: 60 | # a) another "# HELP/TYPE" pattern is found because other metric is following right away 61 | # b) "" This is the case we are in the end of the report 62 | # Anything else is a bug. 63 | # 64 | # (tip: test regex online at https://regexr.com/) 65 | 66 | - match: 67 | $body: | 68 | /.* 69 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_low_pct (\s|\w|\d)+ \n 70 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_low_pct \s gauge 71 | (\n \# \s (HELP|TYPE).* | \s*) 72 | / 73 | 74 | - match: 75 | $body: | 76 | /.* 77 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_high_pct (\s|\w|\d)+ \n 78 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_high_pct \s gauge 79 | (\n \# \s (HELP|TYPE).* | \s*) 80 | / 81 | 82 | - match: 83 | $body: | 84 | /.* 85 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_flood_stage_pct (\s|\w|\d)+ \n 86 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_flood_stage_pct \s gauge 87 | (\n \# \s (HELP|TYPE).* | \s*) 88 | / 89 | 90 | # ----------------------------------- 91 | # Switch to pct based watermark values: 92 | - do: 93 | cluster.put_settings: 94 | body: 95 | persistent: 96 | cluster.routing.allocation.disk.watermark.low: "99.9%" 97 | cluster.routing.allocation.disk.watermark.high: "99.9%" 98 | cluster.routing.allocation.disk.watermark.flood_stage: "99.9%" 99 | flat_settings: true 100 | 101 | - do: 102 | cluster.get_settings: 103 | include_defaults: true 104 | 105 | - match: {transient: {}} 106 | - match: {persistent.cluster.routing.allocation.disk.watermark.low: "99.9%"} 107 | - match: {persistent.cluster.routing.allocation.disk.watermark.high: "99.9%"} 108 | - match: {persistent.cluster.routing.allocation.disk.watermark.flood_stage: "99.9%"} 109 | 110 | # Verify in Prometheus metrics: 111 | - do: 112 | prometheus.metrics: {} 113 | 114 | - match: 115 | $body: | 116 | /.* 117 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_low_pct (\s|\w|\d)+ \n 118 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_low_pct \s gauge \n 119 | es_cluster_routing_allocation_disk_watermark_low_pct\{ 120 | cluster="integTest" 121 | \,} \s 99\.9 \n? 122 | .*/ 123 | 124 | - match: 125 | $body: | 126 | /.* 127 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_high_pct (\s|\w|\d)+ \n 128 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_high_pct \s gauge \n 129 | es_cluster_routing_allocation_disk_watermark_high_pct\{ 130 | cluster="integTest" 131 | \,} \s 99\.9 \n? 132 | .*/ 133 | 134 | - match: 135 | $body: | 136 | /.* 137 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_flood_stage_pct (\s|\w|\d)+ \n 138 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_flood_stage_pct \s gauge \n 139 | es_cluster_routing_allocation_disk_watermark_flood_stage_pct\{ 140 | cluster="integTest" 141 | \,} \s 99\.9 \n? 142 | .*/ 143 | 144 | # At the same time all bytes alternatives do not report any metric values but they are still present. 145 | # This is recommended, see https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics 146 | - match: 147 | $body: | 148 | /.* 149 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_low_bytes (\s|\w|\d)+ \n 150 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_low_bytes \s gauge 151 | (\n \# \s (HELP|TYPE).* | \s*) 152 | / 153 | 154 | - match: 155 | $body: | 156 | /.* 157 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_high_bytes (\s|\w|\d)+ \n 158 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_high_bytes \s gauge 159 | (\n \# \s (HELP|TYPE).* | \s*) 160 | / 161 | 162 | - match: 163 | $body: | 164 | /.* 165 | \# \s HELP \s es_cluster_routing_allocation_disk_watermark_flood_stage_bytes (\s|\w|\d)+ \n 166 | \# \s TYPE \s es_cluster_routing_allocation_disk_watermark_flood_stage_bytes \s gauge 167 | (\n \# \s (HELP|TYPE).* | \s*) 168 | / 169 | 170 | # Clean up the cluster state. See https://github.com/vvanholl/elasticsearch-prometheus-exporter/issues/154 171 | - do: 172 | cluster.put_settings: 173 | body: 174 | persistent: 175 | cluster.routing.allocation.disk.watermark.low: null 176 | cluster.routing.allocation.disk.watermark.high: null 177 | cluster.routing.allocation.disk.watermark.flood_stage: null 178 | flat_settings: true 179 | --------------------------------------------------------------------------------